| // |
| // ======================================================================== |
| // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.server.ssl; |
| |
| import java.io.IOException; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| |
| import javax.net.ssl.HandshakeCompletedEvent; |
| import javax.net.ssl.HandshakeCompletedListener; |
| import javax.net.ssl.SSLContext; |
| import javax.net.ssl.SSLException; |
| import javax.net.ssl.SSLServerSocket; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.SSLSocket; |
| |
| import org.eclipse.jetty.http.HttpSchemes; |
| import org.eclipse.jetty.io.EndPoint; |
| import org.eclipse.jetty.io.RuntimeIOException; |
| import org.eclipse.jetty.io.bio.SocketEndPoint; |
| import org.eclipse.jetty.server.Request; |
| import org.eclipse.jetty.server.bio.SocketConnector; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| import org.eclipse.jetty.util.ssl.SslContextFactory; |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * SSL Socket Connector. |
| * |
| * This specialization of SocketConnector is an abstract listener that can be used as the basis for a |
| * specific JSSE listener. |
| * |
| * The original of this class was heavily based on the work from Court Demas, which in turn is |
| * based on the work from Forge Research. Since JSSE, this class has evolved significantly from |
| * that early work. |
| * |
| * @org.apache.xbean.XBean element="sslSocketConnector" description="Creates an ssl socket connector" |
| * |
| * |
| */ |
| public class SslSocketConnector extends SocketConnector implements SslConnector |
| { |
| private static final Logger LOG = Log.getLogger(SslSocketConnector.class); |
| |
| private final SslContextFactory _sslContextFactory; |
| private int _handshakeTimeout = 0; //0 means use maxIdleTime |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Constructor. |
| */ |
| public SslSocketConnector() |
| { |
| this(new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH)); |
| setSoLingerTime(30000); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public SslSocketConnector(SslContextFactory sslContextFactory) |
| { |
| _sslContextFactory = sslContextFactory; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return True if SSL re-negotiation is allowed (default false) |
| */ |
| public boolean isAllowRenegotiate() |
| { |
| return _sslContextFactory.isAllowRenegotiate(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered |
| * a vulnerability in SSL/TLS with re-negotiation. If your JVM |
| * does not have CVE-2009-3555 fixed, then re-negotiation should |
| * not be allowed. |
| * @param allowRenegotiate true if re-negotiation is allowed (default false) |
| */ |
| public void setAllowRenegotiate(boolean allowRenegotiate) |
| { |
| _sslContextFactory.setAllowRenegotiate(allowRenegotiate); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public void accept(int acceptorID) |
| throws IOException, InterruptedException |
| { |
| Socket socket = _serverSocket.accept(); |
| configure(socket); |
| |
| ConnectorEndPoint connection=new SslConnectorEndPoint(socket); |
| connection.dispatch(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| protected void configure(Socket socket) |
| throws IOException |
| { |
| super.configure(socket); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Allow the Listener a chance to customise the request. before the server does its stuff. <br> |
| * This allows the required attributes to be set for SSL requests. <br> |
| * The requirements of the Servlet specs are: |
| * <ul> |
| * <li> an attribute named "javax.servlet.request.ssl_id" of type String (since Spec 3.0).</li> |
| * <li> an attribute named "javax.servlet.request.cipher_suite" of type String.</li> |
| * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li> |
| * <li> an attribute named "javax.servlet.request.X509Certificate" of type |
| * java.security.cert.X509Certificate[]. This is an array of objects of type X509Certificate, |
| * the order of this array is defined as being in ascending order of trust. The first |
| * certificate in the chain is the one set by the client, the next is the one used to |
| * authenticate the first, and so on. </li> |
| * </ul> |
| * |
| * @param endpoint The Socket the request arrived on. |
| * This should be a {@link SocketEndPoint} wrapping a {@link SSLSocket}. |
| * @param request HttpRequest to be customised. |
| */ |
| @Override |
| public void customize(EndPoint endpoint, Request request) |
| throws IOException |
| { |
| super.customize(endpoint, request); |
| request.setScheme(HttpSchemes.HTTPS); |
| |
| SocketEndPoint socket_end_point = (SocketEndPoint)endpoint; |
| SSLSocket sslSocket = (SSLSocket)socket_end_point.getTransport(); |
| SSLSession sslSession = sslSocket.getSession(); |
| |
| SslCertificates.customize(sslSession,endpoint,request); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getExcludeCipherSuites() |
| * @deprecated |
| */ |
| @Deprecated |
| public String[] getExcludeCipherSuites() { |
| return _sslContextFactory.getExcludeCipherSuites(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getIncludeCipherSuites() |
| * @deprecated |
| */ |
| @Deprecated |
| public String[] getIncludeCipherSuites() |
| { |
| return _sslContextFactory.getIncludeCipherSuites(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystore() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getKeystore() |
| { |
| return _sslContextFactory.getKeyStorePath(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystoreType() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getKeystoreType() |
| { |
| return _sslContextFactory.getKeyStoreType(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getNeedClientAuth() |
| * @deprecated |
| */ |
| @Deprecated |
| public boolean getNeedClientAuth() |
| { |
| return _sslContextFactory.getNeedClientAuth(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getProtocol() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getProtocol() |
| { |
| return _sslContextFactory.getProtocol(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getProvider() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getProvider() { |
| return _sslContextFactory.getProvider(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getSecureRandomAlgorithm() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getSecureRandomAlgorithm() |
| { |
| return _sslContextFactory.getSecureRandomAlgorithm(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getSslKeyManagerFactoryAlgorithm() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getSslKeyManagerFactoryAlgorithm() |
| { |
| return _sslContextFactory.getSslKeyManagerFactoryAlgorithm(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getSslTrustManagerFactoryAlgorithm() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getSslTrustManagerFactoryAlgorithm() |
| { |
| return _sslContextFactory.getTrustManagerFactoryAlgorithm(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststore() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getTruststore() |
| { |
| return _sslContextFactory.getTrustStore(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getSslContextFactory() |
| */ |
| // @Override |
| public SslContextFactory getSslContextFactory() |
| { |
| return _sslContextFactory; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststoreType() |
| * @deprecated |
| */ |
| @Deprecated |
| public String getTruststoreType() |
| { |
| return _sslContextFactory.getTrustStoreType(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#getWantClientAuth() |
| * @deprecated |
| */ |
| @Deprecated |
| public boolean getWantClientAuth() |
| { |
| return _sslContextFactory.getWantClientAuth(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * By default, we're confidential, given we speak SSL. But, if we've been told about an |
| * confidential port, and said port is not our port, then we're not. This allows separation of |
| * listeners providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener |
| * configured to require client certs providing CONFIDENTIAL, whereas another SSL listener not |
| * requiring client certs providing mere INTEGRAL constraints. |
| */ |
| @Override |
| public boolean isConfidential(Request request) |
| { |
| final int confidentialPort = getConfidentialPort(); |
| return confidentialPort == 0 || confidentialPort == request.getServerPort(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * By default, we're integral, given we speak SSL. But, if we've been told about an integral |
| * port, and said port is not our port, then we're not. This allows separation of listeners |
| * providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener configured to |
| * require client certs providing CONFIDENTIAL, whereas another SSL listener not requiring |
| * client certs providing mere INTEGRAL constraints. |
| */ |
| @Override |
| public boolean isIntegral(Request request) |
| { |
| final int integralPort = getIntegralPort(); |
| return integralPort == 0 || integralPort == request.getServerPort(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public void open() throws IOException |
| { |
| _sslContextFactory.checkKeyStore(); |
| try |
| { |
| _sslContextFactory.start(); |
| } |
| catch(Exception e) |
| { |
| throw new RuntimeIOException(e); |
| } |
| super.open(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void doStart() throws Exception |
| { |
| _sslContextFactory.checkKeyStore(); |
| _sslContextFactory.start(); |
| |
| super.doStart(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.bio.SocketConnector#doStop() |
| */ |
| @Override |
| protected void doStop() throws Exception |
| { |
| _sslContextFactory.stop(); |
| |
| super.doStop(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param host The host name that this server should listen on |
| * @param port the port that this server should listen on |
| * @param backlog See {@link ServerSocket#bind(java.net.SocketAddress, int)} |
| * @return A new {@link ServerSocket socket object} bound to the supplied address with all other |
| * settings as per the current configuration of this connector. |
| * @see #setWantClientAuth(boolean) |
| * @see #setNeedClientAuth(boolean) |
| * @exception IOException |
| */ |
| @Override |
| protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException |
| { |
| return _sslContextFactory.newSslServerSocket(host,port,backlog); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setExcludeCipherSuites(java.lang.String[]) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setExcludeCipherSuites(String[] cipherSuites) |
| { |
| _sslContextFactory.setExcludeCipherSuites(cipherSuites); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setIncludeCipherSuites(java.lang.String[]) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setIncludeCipherSuites(String[] cipherSuites) |
| { |
| _sslContextFactory.setIncludeCipherSuites(cipherSuites); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setKeyPassword(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setKeyPassword(String password) |
| { |
| _sslContextFactory.setKeyManagerPassword(password); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param keystore The resource path to the keystore, or null for built in keystores. |
| * @deprecated |
| */ |
| @Deprecated |
| public void setKeystore(String keystore) |
| { |
| _sslContextFactory.setKeyStorePath(keystore); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setKeystoreType(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setKeystoreType(String keystoreType) |
| { |
| _sslContextFactory.setKeyStoreType(keystoreType); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Set the value of the needClientAuth property |
| * |
| * @param needClientAuth true iff we require client certificate authentication. |
| * @deprecated |
| */ |
| @Deprecated |
| public void setNeedClientAuth(boolean needClientAuth) |
| { |
| _sslContextFactory.setNeedClientAuth(needClientAuth); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setPassword(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setPassword(String password) |
| { |
| _sslContextFactory.setKeyStorePassword(password); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setTrustPassword(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setTrustPassword(String password) |
| { |
| _sslContextFactory.setTrustStorePassword(password); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setProtocol(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setProtocol(String protocol) |
| { |
| _sslContextFactory.setProtocol(protocol); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setProvider(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setProvider(String provider) { |
| _sslContextFactory.setProvider(provider); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setSecureRandomAlgorithm(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setSecureRandomAlgorithm(String algorithm) |
| { |
| _sslContextFactory.setSecureRandomAlgorithm(algorithm); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setSslKeyManagerFactoryAlgorithm(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setSslKeyManagerFactoryAlgorithm(String algorithm) |
| { |
| _sslContextFactory.setSslKeyManagerFactoryAlgorithm(algorithm); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setSslTrustManagerFactoryAlgorithm(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setSslTrustManagerFactoryAlgorithm(String algorithm) |
| { |
| _sslContextFactory.setTrustManagerFactoryAlgorithm(algorithm); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststore(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setTruststore(String truststore) |
| { |
| _sslContextFactory.setTrustStore(truststore); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststoreType(java.lang.String) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setTruststoreType(String truststoreType) |
| { |
| _sslContextFactory.setTrustStoreType(truststoreType); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setSslContext(SSLContext sslContext) |
| { |
| _sslContextFactory.setSslContext(sslContext); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext) |
| * @deprecated |
| */ |
| @Deprecated |
| public SSLContext getSslContext() |
| { |
| return _sslContextFactory.getSslContext(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Set the value of the _wantClientAuth property. This property is used |
| * internally when opening server sockets. |
| * |
| * @param wantClientAuth true if we want client certificate authentication. |
| * @see SSLServerSocket#setWantClientAuth |
| * @deprecated |
| */ |
| @Deprecated |
| public void setWantClientAuth(boolean wantClientAuth) |
| { |
| _sslContextFactory.setWantClientAuth(wantClientAuth); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Set the time in milliseconds for so_timeout during ssl handshaking |
| * @param msec a non-zero value will be used to set so_timeout during |
| * ssl handshakes. A zero value means the maxIdleTime is used instead. |
| */ |
| public void setHandshakeTimeout (int msec) |
| { |
| _handshakeTimeout = msec; |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| public int getHandshakeTimeout () |
| { |
| return _handshakeTimeout; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public class SslConnectorEndPoint extends ConnectorEndPoint |
| { |
| public SslConnectorEndPoint(Socket socket) throws IOException |
| { |
| super(socket); |
| } |
| |
| @Override |
| public void shutdownOutput() throws IOException |
| { |
| close(); |
| } |
| |
| @Override |
| public void shutdownInput() throws IOException |
| { |
| close(); |
| } |
| |
| @Override |
| public void run() |
| { |
| try |
| { |
| int handshakeTimeout = getHandshakeTimeout(); |
| int oldTimeout = _socket.getSoTimeout(); |
| if (handshakeTimeout > 0) |
| _socket.setSoTimeout(handshakeTimeout); |
| |
| final SSLSocket ssl=(SSLSocket)_socket; |
| ssl.addHandshakeCompletedListener(new HandshakeCompletedListener() |
| { |
| boolean handshook=false; |
| public void handshakeCompleted(HandshakeCompletedEvent event) |
| { |
| if (handshook) |
| { |
| if (!_sslContextFactory.isAllowRenegotiate()) |
| { |
| LOG.warn("SSL renegotiate denied: "+ssl); |
| try{ssl.close();}catch(IOException e){LOG.warn(e);} |
| } |
| } |
| else |
| handshook=true; |
| } |
| }); |
| ssl.startHandshake(); |
| |
| if (handshakeTimeout>0) |
| _socket.setSoTimeout(oldTimeout); |
| |
| super.run(); |
| } |
| catch (SSLException e) |
| { |
| LOG.debug(e); |
| try{close();} |
| catch(IOException e2){LOG.ignore(e2);} |
| } |
| catch (IOException e) |
| { |
| LOG.debug(e); |
| try{close();} |
| catch(IOException e2){LOG.ignore(e2);} |
| } |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Unsupported. |
| * |
| * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past) |
| * @deprecated |
| */ |
| @Deprecated |
| public String getAlgorithm() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Unsupported. |
| * |
| * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past) |
| * @deprecated |
| */ |
| @Deprecated |
| public void setAlgorithm(String algorithm) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| } |