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