Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2019-02-07 10:44:40 +01:00
commit 6d44b13945
6 changed files with 100 additions and 7 deletions

View File

@ -33,11 +33,10 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.NegotiatingClientConnectionFactory; import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
import org.eclipse.jetty.io.ssl.ALPNProcessor.Client; import org.eclipse.jetty.io.ssl.ALPNProcessor.Client;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory implements SslHandshakeListener public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory
{ {
private static final Logger LOG = Log.getLogger(ALPNClientConnectionFactory.class); private static final Logger LOG = Log.getLogger(ALPNClientConnectionFactory.class);

View File

@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
@ -325,7 +326,6 @@ public class HttpClientTLSTest
@Test @Test
public void testHandshakeSucceededWithSessionResumption() throws Exception public void testHandshakeSucceededWithSessionResumption() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory serverTLSFactory = createSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
@ -405,7 +405,6 @@ public class HttpClientTLSTest
@Test @Test
public void testClientRawCloseDoesNotInvalidateSession() throws Exception public void testClientRawCloseDoesNotInvalidateSession() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory serverTLSFactory = createSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
@ -527,4 +526,30 @@ public class HttpClientTLSTest
assertTrue(latch.await(5, TimeUnit.SECONDS)); assertTrue(latch.await(5, TimeUnit.SECONDS));
} }
} }
@Test
public void testHostNameVerificationFailure() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler());
SslContextFactory clientTLSFactory = createSslContextFactory();
// Make sure the host name is not verified at the TLS level.
clientTLSFactory.setEndpointIdentificationAlgorithm(null);
// Add host name verification after the TLS handshake.
clientTLSFactory.setHostnameVerifier((host, session) -> false);
startClient(clientTLSFactory);
CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(HttpScheme.HTTPS.asString())
.send(result ->
{
Throwable failure = result.getFailure();
if (failure instanceof SSLPeerUnverifiedException)
latch.countDown();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
} }

View File

@ -24,7 +24,10 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnectionFactory;
@ -98,6 +101,7 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint(); EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context)); appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));
sslConnection.addHandshakeListener(new HTTPSHandshakeListener(context));
customize(sslConnection, context); customize(sslConnection, context);
return sslConnection; return sslConnection;
@ -123,4 +127,37 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
} }
return ClientConnectionFactory.super.customize(connection, context); return ClientConnectionFactory.super.customize(connection, context);
} }
private class HTTPSHandshakeListener implements SslHandshakeListener
{
private final Map<String, Object> context;
private HTTPSHandshakeListener(Map<String, Object> context)
{
this.context = context;
}
@Override
public void handshakeSucceeded(Event event) throws SSLException
{
HostnameVerifier verifier = sslContextFactory.getHostnameVerifier();
if (verifier != null)
{
String host = (String)context.get(SSL_PEER_HOST_CONTEXT_KEY);
try
{
if (!verifier.verify(host, event.getSSLEngine().getSession()))
throw new SSLPeerUnverifiedException("Host name verification failed for host: " + host);
}
catch (SSLException x)
{
throw x;
}
catch (Throwable x)
{
throw (SSLException)new SSLPeerUnverifiedException("Host name verification failed for host: " + host).initCause(x);
}
}
}
}
} }

View File

@ -759,7 +759,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
} }
} }
private void handshakeSucceeded() private void handshakeSucceeded() throws SSLException
{ {
if (_handshake.compareAndSet(Handshake.INITIAL, Handshake.SUCCEEDED)) if (_handshake.compareAndSet(Handshake.INITIAL, Handshake.SUCCEEDED))
{ {
@ -1182,7 +1182,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
} }
} }
private void notifyHandshakeSucceeded(SSLEngine sslEngine) private void notifyHandshakeSucceeded(SSLEngine sslEngine) throws SSLException
{ {
SslHandshakeListener.Event event = null; SslHandshakeListener.Event event = null;
for (SslHandshakeListener listener : handshakeListeners) for (SslHandshakeListener listener : handshakeListeners)
@ -1193,6 +1193,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
{ {
listener.handshakeSucceeded(event); listener.handshakeSucceeded(event);
} }
catch (SSLException x)
{
throw x;
}
catch (Throwable x) catch (Throwable x)
{ {
LOG.info("Exception while notifying listener " + listener, x); LOG.info("Exception while notifying listener " + listener, x);

View File

@ -22,6 +22,7 @@ import java.util.EventListener;
import java.util.EventObject; import java.util.EventObject;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
/** /**
* <p>Implementations of this interface are notified of TLS handshake events.</p> * <p>Implementations of this interface are notified of TLS handshake events.</p>
@ -36,7 +37,7 @@ public interface SslHandshakeListener extends EventListener
* *
* @param event the event object carrying information about the TLS handshake event * @param event the event object carrying information about the TLS handshake event
*/ */
default void handshakeSucceeded(Event event) default void handshakeSucceeded(Event event) throws SSLException
{ {
} }

View File

@ -51,6 +51,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIHostName;
@ -194,6 +195,7 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
private int _renegotiationLimit = 5; private int _renegotiationLimit = 5;
private Factory _factory; private Factory _factory;
private PKIXCertPathChecker _pkixCertPathChecker; private PKIXCertPathChecker _pkixCertPathChecker;
private HostnameVerifier _hostnameVerifier;
/** /**
* Construct an instance of SslContextFactory * Construct an instance of SslContextFactory
@ -1565,6 +1567,31 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
_sslSessionTimeout = sslSessionTimeout; _sslSessionTimeout = sslSessionTimeout;
} }
/**
* @return the HostnameVerifier used by a client to verify host names in the server certificate
*/
public HostnameVerifier getHostnameVerifier()
{
return _hostnameVerifier;
}
/**
* <p>Sets a {@code HostnameVerifier} used by a client to verify host names in the server certificate.</p>
* <p>The {@code HostnameVerifier} works in conjunction with {@link #setEndpointIdentificationAlgorithm(String)}.</p>
* <p>When {@code endpointIdentificationAlgorithm=="HTTPS"} (the default) the JDK TLS implementation
* checks that the host name indication set by the client matches the host names in the server certificate.
* If this check passes successfully, the {@code HostnameVerifier} is invoked and the application
* can perform additional checks and allow/deny the connection to the server.</p>
* <p>When {@code endpointIdentificationAlgorithm==null} the JDK TLS implementation will not check
* the host names, and any check is therefore performed only by the {@code HostnameVerifier.}</p>
*
* @param hostnameVerifier the HostnameVerifier used by a client to verify host names in the server certificate
*/
public void setHostnameVerifier(HostnameVerifier hostnameVerifier)
{
_hostnameVerifier = hostnameVerifier;
}
/** /**
* Returns the password object for the given realm. * Returns the password object for the given realm.
* *