1   //========================================================================
2   //Copyright 2004-2008 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at 
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.jetty.ssl;
16  
17  import java.io.ByteArrayInputStream;
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.nio.ByteBuffer;
22  import java.nio.channels.SelectionKey;
23  import java.nio.channels.SocketChannel;
24  import java.security.KeyStore;
25  import java.security.SecureRandom;
26  import java.security.Security;
27  import java.security.cert.X509Certificate;
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.List;
31  import java.util.concurrent.ConcurrentLinkedQueue;
32  
33  import javax.net.ssl.KeyManager;
34  import javax.net.ssl.KeyManagerFactory;
35  import javax.net.ssl.SSLContext;
36  import javax.net.ssl.SSLEngine;
37  import javax.net.ssl.SSLPeerUnverifiedException;
38  import javax.net.ssl.SSLSession;
39  import javax.net.ssl.SSLSocket;
40  import javax.net.ssl.TrustManager;
41  import javax.net.ssl.TrustManagerFactory;
42  
43  import org.mortbay.io.Buffer;
44  import org.mortbay.io.Connection;
45  import org.mortbay.io.EndPoint;
46  import org.mortbay.io.bio.SocketEndPoint;
47  import org.mortbay.io.nio.DirectNIOBuffer;
48  import org.mortbay.io.nio.IndirectNIOBuffer;
49  import org.mortbay.io.nio.NIOBuffer;
50  import org.mortbay.io.nio.SelectChannelEndPoint;
51  import org.mortbay.io.nio.SelectorManager.SelectSet;
52  import org.mortbay.jetty.HttpConnection;
53  import org.mortbay.jetty.HttpParser;
54  import org.mortbay.jetty.HttpSchemes;
55  import org.mortbay.jetty.Request;
56  import org.mortbay.jetty.nio.SelectChannelConnector;
57  import org.mortbay.jetty.security.Password;
58  import org.mortbay.log.Log;
59  import org.mortbay.resource.Resource;
60  
61  /* ------------------------------------------------------------ */
62  /**
63   * SslSelectChannelConnector.
64   * 
65   * @org.apache.xbean.XBean element="sslConnector" description="Creates an NIO ssl connector"
66   *
67   * @author Nik Gonzalez <ngonzalez@exist.com>
68   * @author Greg Wilkins <gregw@mortbay.com>
69   */
70  public class SslSelectChannelConnector extends SelectChannelConnector
71  {
72      /**
73       * The name of the SSLSession attribute that will contain any cached
74       * information.
75       */
76      static final String CACHED_INFO_ATTR=CachedInfo.class.getName();
77  
78      /** Default value for the keystore location path. */
79      public static final String DEFAULT_KEYSTORE=System.getProperty("user.home")+File.separator+".keystore";
80  
81      /** String name of key password property. */
82      public static final String KEYPASSWORD_PROPERTY="jetty.ssl.keypassword";
83  
84      /** String name of keystore password property. */
85      public static final String PASSWORD_PROPERTY="jetty.ssl.password";
86  
87      /** Default value for the cipher Suites. */
88      private String _excludeCipherSuites[]=null;
89  
90      /** Default value for the keystore location path. */
91      private String _keystore=DEFAULT_KEYSTORE;
92      private String _keystoreType="JKS"; // type of the key store
93  
94      /** Set to true if we require client certificate authentication. */
95      private boolean _needClientAuth=false;
96      private boolean _wantClientAuth=false;
97  
98      private transient Password _password;
99      private transient Password _keyPassword;
100     private transient Password _trustPassword;
101     private String _protocol="TLS";
102     private String _algorithm="SunX509"; // cert algorithm
103     private String _provider;
104     private String _secureRandomAlgorithm; // cert algorithm
105     private String _sslKeyManagerFactoryAlgorithm=(Security.getProperty("ssl.KeyManagerFactory.algorithm")==null?"SunX509":Security
106             .getProperty("ssl.KeyManagerFactory.algorithm")); // cert
107                                                                 // algorithm
108 
109     private String _sslTrustManagerFactoryAlgorithm=(Security.getProperty("ssl.TrustManagerFactory.algorithm")==null?"SunX509":Security
110             .getProperty("ssl.TrustManagerFactory.algorithm")); // cert
111                                                                 // algorithm
112 
113     private String _truststore;
114     private String _truststoreType="JKS"; // type of the key store
115     private SSLContext _context;
116 
117     private int _packetBufferSize;
118     private int _applicationBufferSize;
119     private ConcurrentLinkedQueue<Buffer> _packetBuffers = new ConcurrentLinkedQueue<Buffer>();
120     private ConcurrentLinkedQueue<Buffer> _applicationBuffers = new ConcurrentLinkedQueue<Buffer>();
121     
122     /* ------------------------------------------------------------ */
123     /* (non-Javadoc)
124      * @see org.mortbay.jetty.AbstractBuffers#getBuffer(int)
125      */
126     public Buffer getBuffer(int size)
127     {
128         // TODO why is this reimplemented?
129         Buffer buffer;
130         if (size==_applicationBufferSize)
131         {   
132             buffer = _applicationBuffers.poll();
133             if (buffer==null)
134                 buffer=new IndirectNIOBuffer(size);
135         }
136         else if (size==_packetBufferSize)
137         {   
138             buffer = _packetBuffers.poll();
139             if (buffer==null)
140                 buffer=getUseDirectBuffers()
141                     ?(NIOBuffer)new DirectNIOBuffer(size)
142                     :(NIOBuffer)new IndirectNIOBuffer(size);
143         }
144         else 
145             buffer=super.getBuffer(size);
146         
147         return buffer;
148     }
149 
150     /* ------------------------------------------------------------ */
151     /* (non-Javadoc)
152      * @see org.mortbay.jetty.AbstractBuffers#returnBuffer(org.mortbay.io.Buffer)
153      */
154     public void returnBuffer(Buffer buffer)
155     {
156         buffer.clear();
157         int size=buffer.capacity();
158         ByteBuffer bbuf = ((NIOBuffer)buffer).getByteBuffer();
159         bbuf.position(0);
160         bbuf.limit(size);
161         
162         if (size==_applicationBufferSize)
163             _applicationBuffers.add(buffer);
164         else if (size==_packetBufferSize)
165             _packetBuffers.add(buffer);
166         else 
167             super.returnBuffer(buffer);
168     }
169     
170     
171 
172     /**
173      * Return the chain of X509 certificates used to negotiate the SSL Session.
174      * <p>
175      * Note: in order to do this we must convert a
176      * javax.security.cert.X509Certificate[], as used by JSSE to a
177      * java.security.cert.X509Certificate[],as required by the Servlet specs.
178      * 
179      * @param sslSession
180      *                the javax.net.ssl.SSLSession to use as the source of the
181      *                cert chain.
182      * @return the chain of java.security.cert.X509Certificates used to
183      *         negotiate the SSL connection. <br>
184      *         Will be null if the chain is missing or empty.
185      */
186     private static X509Certificate[] getCertChain(SSLSession sslSession)
187     {
188         try
189         {
190             javax.security.cert.X509Certificate javaxCerts[]=sslSession.getPeerCertificateChain();
191             if (javaxCerts==null||javaxCerts.length==0)
192                 return null;
193 
194             int length=javaxCerts.length;
195             X509Certificate[] javaCerts=new X509Certificate[length];
196 
197             java.security.cert.CertificateFactory cf=java.security.cert.CertificateFactory.getInstance("X.509");
198             for (int i=0; i<length; i++)
199             {
200                 byte bytes[]=javaxCerts[i].getEncoded();
201                 ByteArrayInputStream stream=new ByteArrayInputStream(bytes);
202                 javaCerts[i]=(X509Certificate)cf.generateCertificate(stream);
203             }
204 
205             return javaCerts;
206         }
207         catch (SSLPeerUnverifiedException pue)
208         {
209             return null;
210         }
211         catch (Exception e)
212         {
213             Log.warn(Log.EXCEPTION,e);
214             return null;
215         }
216     }
217 
218     /* ------------------------------------------------------------ */
219     /**
220      * Allow the Listener a chance to customise the request. before the server
221      * does its stuff. <br>
222      * This allows the required attributes to be set for SSL requests. <br>
223      * The requirements of the Servlet specs are:
224      * <ul>
225      * <li> an attribute named "javax.servlet.request.cipher_suite" of type
226      * String.</li>
227      * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
228      * <li> an attribute named "javax.servlet.request.X509Certificate" of type
229      * java.security.cert.X509Certificate[]. This is an array of objects of type
230      * X509Certificate, the order of this array is defined as being in ascending
231      * order of trust. The first certificate in the chain is the one set by the
232      * client, the next is the one used to authenticate the first, and so on.
233      * </li>
234      * </ul>
235      * 
236      * @param endpoint
237      *                The Socket the request arrived on. This should be a
238      *                {@link SocketEndPoint} wrapping a {@link SSLSocket}.
239      * @param request
240      *                HttpRequest to be customised.
241      */
242     @Override
243     public void customize(EndPoint endpoint, Request request) throws IOException
244     {
245         super.customize(endpoint,request);
246         request.setScheme(HttpSchemes.HTTPS);
247         
248         SslSelectChannelEndPoint sslHttpChannelEndpoint=(SslSelectChannelEndPoint)endpoint;
249         
250         SSLEngine sslEngine=sslHttpChannelEndpoint.getSSLEngine();
251 
252         try
253         {
254             SSLSession sslSession=sslEngine.getSession();
255             String cipherSuite=sslSession.getCipherSuite();
256             Integer keySize;
257             X509Certificate[] certs;
258 
259             CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR);
260             if (cachedInfo!=null)
261             {
262                 keySize=cachedInfo.getKeySize();
263                 certs=cachedInfo.getCerts();
264             }
265             else
266             {
267                 keySize=new Integer(ServletSSL.deduceKeyLength(cipherSuite));
268                 certs=getCertChain(sslSession);
269                 cachedInfo=new CachedInfo(keySize,certs);
270                 sslSession.putValue(CACHED_INFO_ATTR,cachedInfo);
271             }
272 
273             if (certs!=null)
274                 request.setAttribute("javax.servlet.request.X509Certificate",certs);
275 
276             request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite);
277             request.setAttribute("javax.servlet.request.key_size",keySize);
278         }
279         catch (Exception e)
280         {
281             Log.warn(Log.EXCEPTION,e);
282         }
283     }
284 
285     /* ------------------------------------------------------------ */
286     public SslSelectChannelConnector()
287     {
288     }
289 
290     /**
291      * 
292      * @deprecated As of Java Servlet API 2.0, with no replacement.
293      * 
294      */
295     public String[] getCipherSuites()
296     {
297         return getExcludeCipherSuites();
298     }
299 
300     public String[] getExcludeCipherSuites()
301     {
302         return _excludeCipherSuites;
303     }
304 
305     /**
306      * 
307      * @deprecated As of Java Servlet API 2.0, with no replacement.
308      * 
309      * @author Tony Jiang
310      */
311     public void setCipherSuites(String[] cipherSuites)
312     {
313         setExcludeCipherSuites(cipherSuites);
314     }
315 
316     public void setExcludeCipherSuites(String[] cipherSuites)
317     {
318         this._excludeCipherSuites=cipherSuites;
319     }
320 
321     /* ------------------------------------------------------------ */
322     public void setPassword(String password)
323     {
324         _password=Password.getPassword(PASSWORD_PROPERTY,password,null);
325     }
326 
327     /* ------------------------------------------------------------ */
328     public void setTrustPassword(String password)
329     {
330         _trustPassword=Password.getPassword(PASSWORD_PROPERTY,password,null);
331     }
332 
333     /* ------------------------------------------------------------ */
334     public void setKeyPassword(String password)
335     {
336         _keyPassword=Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
337     }
338 
339     /* ------------------------------------------------------------ */
340     public String getAlgorithm()
341     {
342         return (this._algorithm);
343     }
344 
345     /* ------------------------------------------------------------ */
346     public void setAlgorithm(String algorithm)
347     {
348         this._algorithm=algorithm;
349     }
350 
351     /* ------------------------------------------------------------ */
352     public String getProtocol()
353     {
354         return _protocol;
355     }
356 
357     /* ------------------------------------------------------------ */
358     public void setProtocol(String protocol)
359     {
360         _protocol=protocol;
361     }
362 
363     /* ------------------------------------------------------------ */
364     public void setKeystore(String keystore)
365     {
366         _keystore=keystore;
367     }
368 
369     /* ------------------------------------------------------------ */
370     public String getKeystore()
371     {
372         return _keystore;
373     }
374 
375     /* ------------------------------------------------------------ */
376     public String getKeystoreType()
377     {
378         return (_keystoreType);
379     }
380 
381     /* ------------------------------------------------------------ */
382     public boolean getNeedClientAuth()
383     {
384         return _needClientAuth;
385     }
386 
387     /* ------------------------------------------------------------ */
388     public boolean getWantClientAuth()
389     {
390         return _wantClientAuth;
391     }
392 
393     /* ------------------------------------------------------------ */
394     /**
395      * Set the value of the needClientAuth property
396      * 
397      * @param needClientAuth
398      *                true iff we require client certificate authentication.
399      */
400     public void setNeedClientAuth(boolean needClientAuth)
401     {
402         _needClientAuth=needClientAuth;
403     }
404 
405     public void setWantClientAuth(boolean wantClientAuth)
406     {
407         _wantClientAuth=wantClientAuth;
408     }
409 
410     /* ------------------------------------------------------------ */
411     public void setKeystoreType(String keystoreType)
412     {
413         _keystoreType=keystoreType;
414     }
415 
416     /* ------------------------------------------------------------ */
417     public String getProvider()
418     {
419         return _provider;
420     }
421 
422     public String getSecureRandomAlgorithm()
423     {
424         return (this._secureRandomAlgorithm);
425     }
426 
427     /* ------------------------------------------------------------ */
428     public String getSslKeyManagerFactoryAlgorithm()
429     {
430         return (this._sslKeyManagerFactoryAlgorithm);
431     }
432 
433     /* ------------------------------------------------------------ */
434     public String getSslTrustManagerFactoryAlgorithm()
435     {
436         return (this._sslTrustManagerFactoryAlgorithm);
437     }
438 
439     /* ------------------------------------------------------------ */
440     public String getTruststore()
441     {
442         return _truststore;
443     }
444 
445     /* ------------------------------------------------------------ */
446     public String getTruststoreType()
447     {
448         return _truststoreType;
449     }
450 
451     /* ------------------------------------------------------------ */
452     public void setProvider(String _provider)
453     {
454         this._provider=_provider;
455     }
456 
457     /* ------------------------------------------------------------ */
458     public void setSecureRandomAlgorithm(String algorithm)
459     {
460         this._secureRandomAlgorithm=algorithm;
461     }
462 
463     /* ------------------------------------------------------------ */
464     public void setSslKeyManagerFactoryAlgorithm(String algorithm)
465     {
466         this._sslKeyManagerFactoryAlgorithm=algorithm;
467     }
468 
469     /* ------------------------------------------------------------ */
470     public void setSslTrustManagerFactoryAlgorithm(String algorithm)
471     {
472         this._sslTrustManagerFactoryAlgorithm=algorithm;
473     }
474 
475     public void setTruststore(String truststore)
476     {
477         _truststore=truststore;
478     }
479 
480     public void setTruststoreType(String truststoreType)
481     {
482         _truststoreType=truststoreType;
483     }
484     
485     public void setSslContext(SSLContext sslContext) {
486 		this._context = sslContext;
487 	}
488     
489     /* ------------------------------------------------------------ */
490     /**
491      * By default, we're confidential, given we speak SSL. But, if we've been
492      * told about an confidential port, and said port is not our port, then
493      * we're not. This allows separation of listeners providing INTEGRAL versus
494      * CONFIDENTIAL constraints, such as one SSL listener configured to require
495      * client certs providing CONFIDENTIAL, whereas another SSL listener not
496      * requiring client certs providing mere INTEGRAL constraints.
497      */
498     public boolean isConfidential(Request request)
499     {
500         final int confidentialPort=getConfidentialPort();
501         return confidentialPort==0||confidentialPort==request.getServerPort();
502     }
503 
504     /* ------------------------------------------------------------ */
505     /**
506      * By default, we're integral, given we speak SSL. But, if we've been told
507      * about an integral port, and said port is not our port, then we're not.
508      * This allows separation of listeners providing INTEGRAL versus
509      * CONFIDENTIAL constraints, such as one SSL listener configured to require
510      * client certs providing CONFIDENTIAL, whereas another SSL listener not
511      * requiring client certs providing mere INTEGRAL constraints.
512      */
513     public boolean isIntegral(Request request)
514     {
515         final int integralPort=getIntegralPort();
516         return integralPort==0||integralPort==request.getServerPort();
517     }
518 
519     /* ------------------------------------------------------------------------------- */
520     protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
521     {
522         return new SslSelectChannelEndPoint(this,channel,selectSet,key,createSSLEngine())
523         {
524             // TODO remove this hack
525             public boolean isReadyForDispatch()
526             {
527                 return super.isReadyForDispatch() && !((HttpConnection)getConnection()).getRequest().isSuspended();
528             }
529         };
530     }
531 
532     /* ------------------------------------------------------------------------------- */
533     protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
534     {
535         HttpConnection connection=(HttpConnection)super.newConnection(channel,endpoint);
536         ((HttpParser)connection.getParser()).setForceContentBuffer(true);
537         return connection;
538     }
539 
540     /* ------------------------------------------------------------ */
541     protected SSLEngine createSSLEngine() throws IOException
542     {
543         SSLEngine engine=null;
544         try
545         {
546             engine=_context.createSSLEngine();
547             engine.setUseClientMode(false);
548             
549             if (_wantClientAuth)
550                 engine.setWantClientAuth(_wantClientAuth);
551             if (_needClientAuth)
552                 engine.setNeedClientAuth(_needClientAuth);
553             
554             if (_excludeCipherSuites!=null&&_excludeCipherSuites.length>0)
555             {
556                 List<String> excludedCSList=Arrays.asList(_excludeCipherSuites);
557                 String[] enabledCipherSuites=engine.getEnabledCipherSuites();
558                 List<String> enabledCSList=new ArrayList<String>(Arrays.asList(enabledCipherSuites));
559 
560                 for (String cipherName : excludedCSList)
561                 {
562                     if (enabledCSList.contains(cipherName))
563                     {
564                         enabledCSList.remove(cipherName);
565                     }
566                 }
567                 enabledCipherSuites=enabledCSList.toArray(new String[enabledCSList.size()]);
568 
569                 engine.setEnabledCipherSuites(enabledCipherSuites);
570             }
571 
572         }
573         catch (Exception e)
574         {
575             Log.warn("Error creating sslEngine -- closing this connector",e);
576             close();
577             throw new IllegalStateException(e);
578         }
579         return engine;
580     }
581 
582    
583     protected void doStart() throws Exception
584     {
585     	if (_context == null) {
586            _context=createSSLContext();
587     	}
588         
589         SSLEngine engine=createSSLEngine();
590         SSLSession ssl_session=engine.getSession();
591         
592         setHeaderBufferSize(ssl_session.getApplicationBufferSize());
593         setRequestBufferSize(ssl_session.getApplicationBufferSize());
594         setResponseBufferSize(ssl_session.getApplicationBufferSize());
595         
596         super.doStart();
597     }
598 
599     protected SSLContext createSSLContext() throws Exception
600     {
601         if (_truststore==null)
602         {
603             _truststore=_keystore;
604             _truststoreType=_keystoreType;
605         }
606 
607         KeyManager[] keyManagers=null;
608         InputStream keystoreInputStream = null;
609         if (_keystore!=null)
610             keystoreInputStream=Resource.newResource(_keystore).getInputStream();
611         KeyStore keyStore=KeyStore.getInstance(_keystoreType);
612         keyStore.load(keystoreInputStream,_password==null?null:_password.toString().toCharArray());
613 
614         KeyManagerFactory keyManagerFactory=KeyManagerFactory.getInstance(_sslKeyManagerFactoryAlgorithm);
615         keyManagerFactory.init(keyStore,_keyPassword==null?null:_keyPassword.toString().toCharArray());
616         keyManagers=keyManagerFactory.getKeyManagers();
617 
618 
619         TrustManager[] trustManagers=null;
620         InputStream truststoreInputStream = null;
621         if (_truststore!=null)
622             truststoreInputStream = Resource.newResource(_truststore).getInputStream();
623         KeyStore trustStore=KeyStore.getInstance(_truststoreType);
624         trustStore.load(truststoreInputStream,_trustPassword==null?null:_trustPassword.toString().toCharArray());
625 
626         TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm);
627         trustManagerFactory.init(trustStore);
628         trustManagers=trustManagerFactory.getTrustManagers();
629 
630         SecureRandom secureRandom=_secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
631         SSLContext context=_provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol,_provider);
632         context.init(keyManagers,trustManagers,secureRandom);
633         return context;
634     }
635 
636     /**
637      * Simple bundle of information that is cached in the SSLSession. Stores the
638      * effective keySize and the client certificate chain.
639      */
640     private class CachedInfo
641     {
642         private X509Certificate[] _certs;
643         private Integer _keySize;
644 
645         CachedInfo(Integer keySize, X509Certificate[] certs)
646         {
647             this._keySize=keySize;
648             this._certs=certs;
649         }
650 
651         X509Certificate[] getCerts()
652         {
653             return _certs;
654         }
655 
656         Integer getKeySize()
657         {
658             return _keySize;
659         }
660     }
661 
662 }