diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 165b5cbc89..6ad934fbc6 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -217,19 +217,19 @@ public final class OpenSsl { Set protocols = new LinkedHashSet(6); // Seems like there is no way to explicitly disable SSLv2Hello in openssl so it is always enabled protocols.add(PROTOCOL_SSL_V2_HELLO); - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2, SSL.SSL_OP_NO_SSLv2)) { protocols.add(PROTOCOL_SSL_V2); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3, SSL.SSL_OP_NO_SSLv3)) { protocols.add(PROTOCOL_SSL_V3); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1, SSL.SSL_OP_NO_TLSv1)) { protocols.add(PROTOCOL_TLS_V1); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1, SSL.SSL_OP_NO_TLSv1_1)) { protocols.add(PROTOCOL_TLS_V1_1); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_OP_NO_TLSv1_2)) { protocols.add(PROTOCOL_TLS_V1_2); } @@ -237,7 +237,7 @@ public final class OpenSsl { SUPPORTS_OCSP = doesSupportOcsp(); if (logger.isDebugEnabled()) { - logger.debug("Supported protocols (OpenSSL): {} ", Arrays.asList(SUPPORTED_PROTOCOLS_SET)); + logger.debug("Supported protocols (OpenSSL): {} ", SUPPORTED_PROTOCOLS_SET); logger.debug("Default cipher suites (OpenSSL): {}", DEFAULT_CIPHERS); } } else { @@ -271,7 +271,11 @@ public final class OpenSsl { } return supportsOcsp; } - private static boolean doesSupportProtocol(int protocol) { + private static boolean doesSupportProtocol(int protocol, int opt) { + if (opt == 0) { + // If the opt is 0 the protocol is not supported. This is for example the case with BoringSSL and SSLv2. + return false; + } long sslCtx = -1; try { sslCtx = SSLContext.make(protocol, SSL.SSL_MODE_COMBINED); diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 7d86fa278e..9924a5f1f3 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -44,6 +44,7 @@ import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -62,7 +63,9 @@ import java.security.Provider; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -76,7 +79,11 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.security.cert.X509Certificate; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2_HELLO; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; @@ -2355,4 +2362,47 @@ public abstract class SSLEngineTest { cert.delete(); } } + + @Test + public void testDisableProtocols() throws Exception { + testDisableProtocols(PROTOCOL_SSL_V2, PROTOCOL_SSL_V2); + testDisableProtocols(PROTOCOL_SSL_V3, PROTOCOL_SSL_V2, PROTOCOL_SSL_V3); + testDisableProtocols(PROTOCOL_TLS_V1, PROTOCOL_SSL_V2, PROTOCOL_SSL_V3, PROTOCOL_TLS_V1); + testDisableProtocols(PROTOCOL_TLS_V1_1, PROTOCOL_SSL_V2, PROTOCOL_SSL_V3, PROTOCOL_TLS_V1, PROTOCOL_TLS_V1_1); + testDisableProtocols(PROTOCOL_TLS_V1_2, PROTOCOL_SSL_V2, + PROTOCOL_SSL_V3, PROTOCOL_TLS_V1, PROTOCOL_TLS_V1_1, PROTOCOL_TLS_V1_2); + } + + private void testDisableProtocols(String protocol, String... disabledProtocols) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + + SslContext ctx = SslContextBuilder + .forServer(cert.certificate(), cert.privateKey()) + .sslProvider(sslServerProvider()) + .build(); + SSLEngine server = ctx.newEngine(UnpooledByteBufAllocator.DEFAULT); + + try { + Set supported = new HashSet(Arrays.asList(server.getSupportedProtocols())); + if (supported.contains(protocol)) { + server.setEnabledProtocols(server.getSupportedProtocols()); + Assert.assertEquals(supported, new HashSet(Arrays.asList(server.getSupportedProtocols()))); + + for (String disabled: disabledProtocols) { + supported.remove(disabled); + } + if (supported.contains(SslUtils.PROTOCOL_SSL_V2_HELLO) && supported.size() == 1) { + // It's not allowed to set only PROTOCOL_SSL_V2_HELLO if using JDK SSLEngine. + return; + } + server.setEnabledProtocols(supported.toArray(new String[0])); + Assert.assertEquals(supported, new HashSet(Arrays.asList(server.getEnabledProtocols()))); + server.setEnabledProtocols(server.getSupportedProtocols()); + } + } finally { + cleanupServerSslEngine(server); + cleanupClientSslContext(ctx); + cert.delete(); + } + } }