View Javadoc

1   // ========================================================================
2   // Copyright (c) 2000-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.server.ssl;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.net.InetAddress;
19  import java.net.ServerSocket;
20  import java.net.Socket;
21  import java.security.KeyStore;
22  import java.security.SecureRandom;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  
27  import javax.net.ssl.HandshakeCompletedEvent;
28  import javax.net.ssl.HandshakeCompletedListener;
29  import javax.net.ssl.KeyManager;
30  import javax.net.ssl.KeyManagerFactory;
31  import javax.net.ssl.SSLContext;
32  import javax.net.ssl.SSLException;
33  import javax.net.ssl.SSLServerSocket;
34  import javax.net.ssl.SSLServerSocketFactory;
35  import javax.net.ssl.SSLSession;
36  import javax.net.ssl.SSLSocket;
37  import javax.net.ssl.TrustManager;
38  import javax.net.ssl.TrustManagerFactory;
39  
40  import org.eclipse.jetty.http.HttpSchemes;
41  import org.eclipse.jetty.http.security.Password;
42  import org.eclipse.jetty.io.EndPoint;
43  import org.eclipse.jetty.io.bio.SocketEndPoint;
44  import org.eclipse.jetty.server.Request;
45  import org.eclipse.jetty.server.bio.SocketConnector;
46  import org.eclipse.jetty.util.log.Log;
47  import org.eclipse.jetty.util.resource.Resource;
48  
49  /* ------------------------------------------------------------ */
50  /**
51   * SSL Socket Connector.
52   * 
53   * This specialization of SocketConnector is an abstract listener that can be used as the basis for a
54   * specific JSSE listener.
55   * 
56   * The original of this class was heavily based on the work from Court Demas, which in turn is 
57   * based on the work from Forge Research. Since JSSE, this class has evolved significantly from
58   * that early work.
59   * 
60   * @org.apache.xbean.XBean element="sslSocketConnector" description="Creates an ssl socket connector"
61   *
62   * 
63   */
64  public class SslSocketConnector extends SocketConnector  implements SslConnector
65  {
66      /** Default value for the cipher Suites. */
67      private String _excludeCipherSuites[] = null;
68      /** Default value for the included cipher Suites. */
69      private String _includeCipherSuites[]=null;
70      
71      /** Default value for the keystore location path. */
72      private String _keystorePath=DEFAULT_KEYSTORE ;
73      private String _keystoreType = "JKS"; // type of the key store
74      
75      /** Set to true if we require client certificate authentication. */
76      private boolean _needClientAuth = false;
77      private transient Password _password;
78      private transient Password _keyPassword;
79      private transient Password _trustPassword;
80      private String _protocol= "TLS";
81      private String _provider;
82      private String _secureRandomAlgorithm; // cert algorithm
83      private String _sslKeyManagerFactoryAlgorithm = DEFAULT_KEYSTORE_ALGORITHM;
84      private String _sslTrustManagerFactoryAlgorithm = DEFAULT_TRUSTSTORE_ALGORITHM;
85      private String _truststorePath;
86      private String _truststoreType = "JKS"; // type of the key store
87  
88      /** Set to true if we would like client certificate authentication. */
89      private boolean _wantClientAuth = false;
90      private int _handshakeTimeout = 0; //0 means use maxIdleTime
91      
92      private SSLContext _context;
93      private boolean _allowRenegotiate =false;
94  
95  
96      /* ------------------------------------------------------------ */
97      /**
98       * Constructor.
99       */
100     public SslSocketConnector()
101     {
102         super();
103     }
104 
105     /* ------------------------------------------------------------ */
106     /**
107      * @return True if SSL re-negotiation is allowed (default false)
108      */
109     public boolean isAllowRenegotiate()
110     {
111         return _allowRenegotiate;
112     }
113 
114     /* ------------------------------------------------------------ */
115     /**
116      * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
117      * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
118      * does not have CVE-2009-3555 fixed, then re-negotiation should 
119      * not be allowed.
120      * @param allowRenegotiate true if re-negotiation is allowed (default false)
121      */
122     public void setAllowRenegotiate(boolean allowRenegotiate)
123     {
124         _allowRenegotiate = allowRenegotiate;
125     }
126 
127     /* ------------------------------------------------------------ */
128     @Override
129     public void accept(int acceptorID)
130         throws IOException, InterruptedException
131     {   
132         Socket socket = _serverSocket.accept();
133         configure(socket);
134         
135         ConnectorEndPoint connection=new SslConnectorEndPoint(socket);
136         connection.dispatch();
137     }
138     
139     /* ------------------------------------------------------------ */
140     @Override
141     protected void configure(Socket socket)
142         throws IOException
143     {   
144         super.configure(socket);
145     }
146 
147     /* ------------------------------------------------------------ */
148     protected SSLContext createSSLContext() throws Exception
149     {
150         KeyManager[] keyManagers = getKeyManagers();
151         TrustManager[] trustManagers = getTrustManagers();
152         SecureRandom secureRandom = _secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
153         SSLContext context = _provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol, _provider);
154         context.init(keyManagers, trustManagers, secureRandom);
155         return context;
156     }
157     
158     /* ------------------------------------------------------------ */
159     protected SSLServerSocketFactory createFactory() 
160         throws Exception
161     {
162         if (_context==null)
163             _context=createSSLContext();
164     	
165         return _context.getServerSocketFactory();
166     }
167     
168 
169     /* ------------------------------------------------------------ */
170     protected KeyManager[] getKeyManagers() throws Exception
171     {
172         KeyStore keyStore = getKeyStore(_keystorePath, _keystoreType, _password==null?null:_password.toString());
173         
174         KeyManagerFactory keyManagerFactory=KeyManagerFactory.getInstance(_sslKeyManagerFactoryAlgorithm);
175         keyManagerFactory.init(keyStore,_keyPassword==null?(_password==null?null:_password.toString().toCharArray()):_keyPassword.toString().toCharArray());
176         return keyManagerFactory.getKeyManagers();
177     }
178     
179     protected TrustManager[] getTrustManagers() throws Exception
180     {        
181         if (_truststorePath==null)
182         {
183             _truststorePath=_keystorePath;
184             _truststoreType=_keystoreType;
185             //TODO is this right? it wasn't in the code before refactoring
186             _trustPassword = _password;
187             _sslTrustManagerFactoryAlgorithm = _sslKeyManagerFactoryAlgorithm;
188         }
189         KeyStore trustStore = getKeyStore(_truststorePath, _truststoreType, _trustPassword==null?null:_trustPassword.toString());
190 
191         TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm);
192         trustManagerFactory.init(trustStore);
193         return trustManagerFactory.getTrustManagers();
194     }
195     
196     protected KeyStore getKeyStore(String keystorePath, String keystoreType, String keystorePassword) throws Exception
197     {
198     	KeyStore keystore;
199     	InputStream keystoreInputStream = null;
200     	try
201         {
202             if (keystorePath!=null)
203                 keystoreInputStream = Resource.newResource(keystorePath).getInputStream();
204             keystore=KeyStore.getInstance(keystoreType);
205             keystore.load(keystoreInputStream,keystorePassword==null?null:keystorePassword.toString().toCharArray());
206             return keystore;
207         }
208         finally
209         {
210             if (keystoreInputStream != null)
211             	keystoreInputStream.close();
212         }
213     }
214 
215     /* ------------------------------------------------------------ */
216     /**
217      * Allow the Listener a chance to customise the request. before the server does its stuff. <br>
218      * This allows the required attributes to be set for SSL requests. <br>
219      * The requirements of the Servlet specs are:
220      * <ul>
221      * <li> an attribute named "javax.servlet.request.ssl_id" of type String (since Spec 3.0).</li>
222      * <li> an attribute named "javax.servlet.request.cipher_suite" of type String.</li>
223      * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
224      * <li> an attribute named "javax.servlet.request.X509Certificate" of type
225      * java.security.cert.X509Certificate[]. This is an array of objects of type X509Certificate,
226      * the order of this array is defined as being in ascending order of trust. The first
227      * certificate in the chain is the one set by the client, the next is the one used to
228      * authenticate the first, and so on. </li>
229      * </ul>
230      * 
231      * @param endpoint The Socket the request arrived on. 
232      *        This should be a {@link SocketEndPoint} wrapping a {@link SSLSocket}.
233      * @param request HttpRequest to be customised.
234      */
235     @Override
236     public void customize(EndPoint endpoint, Request request)
237         throws IOException
238     {
239         super.customize(endpoint, request);
240         request.setScheme(HttpSchemes.HTTPS);
241         
242         SocketEndPoint socket_end_point = (SocketEndPoint)endpoint;
243         SSLSocket sslSocket = (SSLSocket)socket_end_point.getTransport();
244         SSLSession sslSession = sslSocket.getSession();
245 
246         SslCertificates.customize(sslSession,endpoint,request);
247     }
248 
249     /* ------------------------------------------------------------ */    
250     public String[] getExcludeCipherSuites() {
251         return _excludeCipherSuites;
252     }
253     
254     /* ------------------------------------------------------------ */
255     public String[] getIncludeCipherSuites()
256     {
257         return _includeCipherSuites;
258     }
259 
260     /* ------------------------------------------------------------ */
261     public String getKeystore()
262     {
263         return _keystorePath;
264     }
265 
266     /* ------------------------------------------------------------ */
267     public String getKeystoreType() 
268     {
269         return (_keystoreType);
270     }
271 
272     /* ------------------------------------------------------------ */
273     public boolean getNeedClientAuth()
274     {
275         return _needClientAuth;
276     }
277 
278     /* ------------------------------------------------------------ */
279     public String getProtocol() 
280     {
281         return _protocol;
282     }
283 
284     /* ------------------------------------------------------------ */
285     public String getProvider() {
286 	return _provider;
287     }
288 
289     /* ------------------------------------------------------------ */
290     public String getSecureRandomAlgorithm() 
291     {
292         return (this._secureRandomAlgorithm);
293     }
294 
295     /* ------------------------------------------------------------ */
296     public String getSslKeyManagerFactoryAlgorithm() 
297     {
298         return (this._sslKeyManagerFactoryAlgorithm);
299     }
300 
301     /* ------------------------------------------------------------ */
302     public String getSslTrustManagerFactoryAlgorithm() 
303     {
304         return (this._sslTrustManagerFactoryAlgorithm);
305     }
306 
307     /* ------------------------------------------------------------ */
308     public String getTruststore()
309     {
310         return _truststorePath;
311     }
312 
313     /* ------------------------------------------------------------ */
314     public String getTruststoreType()
315     {
316         return _truststoreType;
317     }
318 
319     /* ------------------------------------------------------------ */
320     public boolean getWantClientAuth()
321     {
322         return _wantClientAuth;
323     }
324 
325     /* ------------------------------------------------------------ */
326     /**
327      * By default, we're confidential, given we speak SSL. But, if we've been told about an
328      * confidential port, and said port is not our port, then we're not. This allows separation of
329      * listeners providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener
330      * configured to require client certs providing CONFIDENTIAL, whereas another SSL listener not
331      * requiring client certs providing mere INTEGRAL constraints.
332      */
333     @Override
334     public boolean isConfidential(Request request)
335     {
336         final int confidentialPort = getConfidentialPort();
337         return confidentialPort == 0 || confidentialPort == request.getServerPort();
338     }
339     
340     /* ------------------------------------------------------------ */
341     /**
342      * By default, we're integral, given we speak SSL. But, if we've been told about an integral
343      * port, and said port is not our port, then we're not. This allows separation of listeners
344      * providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener configured to
345      * require client certs providing CONFIDENTIAL, whereas another SSL listener not requiring
346      * client certs providing mere INTEGRAL constraints.
347      */
348     @Override
349     public boolean isIntegral(Request request)
350     {
351         final int integralPort = getIntegralPort();
352         return integralPort == 0 || integralPort == request.getServerPort();
353     }
354     
355     /* ------------------------------------------------------------ */
356     /**
357      * @param host The host name that this server should listen on
358      * @param port the port that this server should listen on 
359      * @param backlog See {@link ServerSocket#bind(java.net.SocketAddress, int)}
360      * @return A new {@link ServerSocket socket object} bound to the supplied address with all other
361      * settings as per the current configuration of this connector. 
362      * @see #setWantClientAuth(boolean)
363      * @see #setNeedClientAuth(boolean)
364      * @exception IOException
365      */
366 
367     /* ------------------------------------------------------------ */
368     @Override
369     protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException
370     {
371         SSLServerSocketFactory factory = null;
372         SSLServerSocket socket = null;
373 
374         try
375         {
376             factory = createFactory();
377 
378             socket = (SSLServerSocket) (host==null?
379                             factory.createServerSocket(port,backlog):
380                             factory.createServerSocket(port,backlog,InetAddress.getByName(host)));
381 
382             if (_wantClientAuth)
383                 socket.setWantClientAuth(_wantClientAuth);
384             if (_needClientAuth)
385                 socket.setNeedClientAuth(_needClientAuth);
386 
387             if ((_excludeCipherSuites!=null&&_excludeCipherSuites.length>0)
388                 	|| (_includeCipherSuites!=null&&_includeCipherSuites.length>0))
389             {
390             	List<String> includedCSList;
391                 if (_includeCipherSuites!=null)
392                 {
393                 	includedCSList = Arrays.asList(_includeCipherSuites);
394                 } else {
395                 	includedCSList = new ArrayList<String>();
396                 }
397                 List<String> excludedCSList;
398                 if (_excludeCipherSuites!=null)
399                 {
400                 	excludedCSList = Arrays.asList(_excludeCipherSuites);
401                 } else {
402                 	excludedCSList = new ArrayList<String>();
403                 }
404                 String[] enabledCipherSuites = socket.getEnabledCipherSuites();
405                 List<String> enabledCSList = new ArrayList<String>(Arrays.asList(enabledCipherSuites));
406                 
407                 String[] supportedCipherSuites = socket.getSupportedCipherSuites();
408                 List<String> supportedCSList = Arrays.asList(supportedCipherSuites);
409                 
410             	for (String cipherName : includedCSList)
411                 {
412                     if ((!enabledCSList.contains(cipherName))
413                     		&& supportedCSList.contains(cipherName))
414                     {
415                         enabledCSList.add(cipherName);
416                     }
417                 }
418 
419                 for (String cipherName : excludedCSList)
420                 {
421                     if (enabledCSList.contains(cipherName))
422                     {
423                         enabledCSList.remove(cipherName);
424                     }
425                 }
426                 enabledCipherSuites = enabledCSList.toArray(new String[enabledCSList.size()]);
427 
428                 socket.setEnabledCipherSuites(enabledCipherSuites);
429             }
430             
431         }
432         catch (IOException e)
433         {
434             throw e;
435         }
436         catch (Exception e)
437         {
438             Log.warn(e.toString());
439             Log.debug(e);
440             throw new IOException("!JsseListener: " + e);
441         }
442         return socket;
443     }
444 
445     /* ------------------------------------------------------------ */
446     /** 
447      * 
448      */
449     public void setExcludeCipherSuites(String[] cipherSuites) {
450         this._excludeCipherSuites = cipherSuites;
451     }
452 
453     /* ------------------------------------------------------------ */
454     public void setIncludeCipherSuites(String[] cipherSuites)
455     {
456         this._includeCipherSuites=cipherSuites;
457     }
458 
459     /* ------------------------------------------------------------ */
460     public void setKeyPassword(String password)
461     {
462         _keyPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
463     }
464 
465     /* ------------------------------------------------------------ */
466     /**
467      * @param keystore The resource path to the keystore, or null for built in keystores.
468      */
469     public void setKeystore(String keystore)
470     {
471         _keystorePath = keystore;
472     }
473 
474     /* ------------------------------------------------------------ */
475     public void setKeystoreType(String keystoreType) 
476     {
477         _keystoreType = keystoreType;
478     }
479 
480     /* ------------------------------------------------------------ */
481     /**
482      * Set the value of the needClientAuth property
483      * 
484      * @param needClientAuth true iff we require client certificate authentication.
485      */
486     public void setNeedClientAuth(boolean needClientAuth)
487     {
488         _needClientAuth = needClientAuth;
489     }
490     
491     /* ------------------------------------------------------------ */
492     public void setPassword(String password)
493     {
494         _password = Password.getPassword(PASSWORD_PROPERTY,password,null);
495     }
496     
497     /* ------------------------------------------------------------ */
498     public void setTrustPassword(String password)
499     {
500         _trustPassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
501     }
502 
503     /* ------------------------------------------------------------ */
504     public void setProtocol(String protocol) 
505     {
506         _protocol = protocol;
507     }
508 
509     /* ------------------------------------------------------------ */
510     public void setProvider(String _provider) {
511 	this._provider = _provider;
512     }
513 
514     /* ------------------------------------------------------------ */
515     public void setSecureRandomAlgorithm(String algorithm) 
516     {
517         this._secureRandomAlgorithm = algorithm;
518     }
519 
520     /* ------------------------------------------------------------ */
521     public void setSslKeyManagerFactoryAlgorithm(String algorithm) 
522     {
523         this._sslKeyManagerFactoryAlgorithm = algorithm;
524     }
525     
526     /* ------------------------------------------------------------ */
527     public void setSslTrustManagerFactoryAlgorithm(String algorithm) 
528     {
529         this._sslTrustManagerFactoryAlgorithm = algorithm;
530     }
531 
532 
533     public void setTruststore(String truststore)
534     {
535         _truststorePath = truststore;
536     }
537     
538 
539     public void setTruststoreType(String truststoreType)
540     {
541         _truststoreType = truststoreType;
542     }
543     
544     public void setSslContext(SSLContext sslContext)
545     {
546     	_context = sslContext;
547     }
548 
549     /* ------------------------------------------------------------ */
550     /**
551      * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext)
552      */
553     public SSLContext getSslContext()
554     {
555         try
556         {
557             if (_context == null)
558                 _context=createSSLContext();
559         }
560         catch(Exception e)
561         {
562             throw new RuntimeException(e);
563         }
564          
565         return _context;
566     }
567 
568     /* ------------------------------------------------------------ */
569     /**
570      * Set the value of the _wantClientAuth property. This property is used 
571      * internally when opening server sockets.
572      * 
573      * @param wantClientAuth true if we want client certificate authentication.
574      * @see SSLServerSocket#setWantClientAuth
575      */
576     public void setWantClientAuth(boolean wantClientAuth)
577     {
578         _wantClientAuth = wantClientAuth;
579     }
580 
581     /* ------------------------------------------------------------ */
582     /**
583      * Set the time in milliseconds for so_timeout during ssl handshaking
584      * @param msec a non-zero value will be used to set so_timeout during
585      * ssl handshakes. A zero value means the maxIdleTime is used instead.
586      */
587     public void setHandshakeTimeout (int msec)
588     {
589         _handshakeTimeout = msec;
590     }
591     
592 
593     /* ------------------------------------------------------------ */
594     public int getHandshakeTimeout ()
595     {
596         return _handshakeTimeout;
597     }
598 
599     /* ------------------------------------------------------------ */
600     public class SslConnectorEndPoint extends ConnectorEndPoint
601     {
602         public SslConnectorEndPoint(Socket socket) throws IOException
603         {
604             super(socket);
605         }
606         
607         @Override
608         public void shutdownOutput() throws IOException
609         {
610             close();
611         }
612         
613         @Override
614         public void run()
615         {
616             try
617             {
618                 int handshakeTimeout = getHandshakeTimeout();
619                 int oldTimeout = _socket.getSoTimeout();
620                 if (handshakeTimeout > 0)            
621                     _socket.setSoTimeout(handshakeTimeout);
622 
623                 final SSLSocket ssl=(SSLSocket)_socket;
624                 ssl.addHandshakeCompletedListener(new HandshakeCompletedListener()
625                 {
626                     boolean handshook=false;
627                     public void handshakeCompleted(HandshakeCompletedEvent event)
628                     {
629                         if (handshook)
630                         {
631                             if (!_allowRenegotiate)
632                             {
633                                 Log.warn("SSL renegotiate denied: "+ssl);
634                                 try{ssl.close();}catch(IOException e){Log.warn(e);}
635                             }
636                         }
637                         else
638                             handshook=true;
639                     }
640                 });
641                 ssl.startHandshake();
642 
643                 if (handshakeTimeout>0)
644                     _socket.setSoTimeout(oldTimeout);
645 
646                 super.run();
647             }
648             catch (SSLException e)
649             {
650                 Log.debug(e); 
651                 try{close();}
652                 catch(IOException e2){Log.ignore(e2);}
653             }
654             catch (IOException e)
655             {
656                 Log.debug(e);
657                 try{close();}
658                 catch(IOException e2){Log.ignore(e2);}
659             } 
660         }
661     }
662 
663     /* ------------------------------------------------------------ */
664     /**
665      * Unsupported.
666      * 
667      * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past)
668      */
669     public String getAlgorithm()
670     {
671         throw new UnsupportedOperationException();
672     }
673 
674     /* ------------------------------------------------------------ */
675     /**
676      * Unsupported.
677      * 
678      * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past)
679      */
680     public void setAlgorithm(String algorithm)
681     {
682         throw new UnsupportedOperationException();
683     }
684 }