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