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