Stop using set_options to set protocol support. (#500)
The *_set_min_protocol_version and *_set_max_protocol_version
functions were introduced to be explicit about setting supported
versions. Use those functions instead of SSL_set_options with
disabling constants.
There aren't any higher-level tests because this is already covered by
tests like SSLSocket.test_SSLSocket_setEnabledProtocols,
SSLSocket.test_SSLSocket_noncontiguousProtocols_useLower, etc.
diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
index 6875f10..07ca573 100644
--- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc
+++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
@@ -6475,14 +6475,14 @@
SSL_CTX_set_options(
sslCtx.get(),
SSL_OP_ALL
- // Note: We explicitly do not allow SSLv2 to be used.
- | SSL_OP_NO_SSLv2
// We also disable session tickets for better compatibility b/2682876
| SSL_OP_NO_TICKET
// We also disable compression for better compatibility b/2710492 b/2710497
| SSL_OP_NO_COMPRESSION
// Generate a fresh ECDH keypair for each key exchange.
| SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_min_proto_version(sslCtx.get(), TLS1_VERSION);
+ SSL_CTX_set_max_proto_version(sslCtx.get(), TLS1_2_VERSION);
uint32_t mode = SSL_CTX_get_mode(sslCtx.get());
/*
@@ -6877,6 +6877,26 @@
return result;
}
+static jint NativeCrypto_SSL_set_protocol_versions(JNIEnv* env, jclass, jlong ssl_address, CONSCRYPT_UNUSED jobject ssl_holder, jint min_version, jint max_version) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_protocol_versions min=0x%x max=0x%x", ssl, min_version, max_version);
+ if (ssl == nullptr) {
+ return 0;
+ }
+ int min_result = SSL_set_min_proto_version(ssl, static_cast<uint16_t>(min_version));
+ int max_result = SSL_set_max_proto_version(ssl, static_cast<uint16_t>(max_version));
+ // Return failure if either call failed.
+ int result = 1;
+ if (!min_result || !max_result) {
+ result = 0;
+ // The only possible error is an invalid version, so we don't need the details.
+ ERR_clear_error();
+ }
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_protocol_versions => (min: %d, max: %d) == %d", ssl, min_result, max_result, result);
+ return result;
+}
+
/**
* public static native void SSL_enable_signed_cert_timestamps(long ssl);
*/
@@ -10128,6 +10148,7 @@
CONSCRYPT_NATIVE_METHOD(SSL_set_mode, "(J" REF_SSL "J)J"),
CONSCRYPT_NATIVE_METHOD(SSL_set_options, "(J" REF_SSL "J)J"),
CONSCRYPT_NATIVE_METHOD(SSL_clear_options, "(J" REF_SSL "J)J"),
+ CONSCRYPT_NATIVE_METHOD(SSL_set_protocol_versions, "(J" REF_SSL "II)I"),
CONSCRYPT_NATIVE_METHOD(SSL_enable_signed_cert_timestamps, "(J" REF_SSL ")V"),
CONSCRYPT_NATIVE_METHOD(SSL_get_signed_cert_timestamp_list, "(J" REF_SSL ")[B"),
CONSCRYPT_NATIVE_METHOD(SSL_set_signed_cert_timestamp_list, "(J" REF_SSL "[B)V"),
diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java
index b2c8379..d40e25e 100644
--- a/common/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/common/src/main/java/org/conscrypt/NativeCrypto.java
@@ -31,6 +31,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
@@ -891,6 +892,8 @@
static native long SSL_clear_options(long ssl, NativeSsl ssl_holder, long options);
+ static native int SSL_set_protocol_versions(long ssl, NativeSsl ssl_holder, int min_version, int max_version);
+
static native void SSL_enable_signed_cert_timestamps(long ssl, NativeSsl ssl_holder);
static native byte[] SSL_get_signed_cert_timestamp_list(long ssl, NativeSsl ssl_holder);
@@ -925,55 +928,55 @@
};
/** Protocols to enable by default when "TLSv1.1" is requested. */
- static final String[] TLSV11_PROTOCOLS = new String[] {
- SUPPORTED_PROTOCOL_TLSV1,
- SUPPORTED_PROTOCOL_TLSV1_1,
- SUPPORTED_PROTOCOL_TLSV1_2,
- };
+ static final String[] TLSV11_PROTOCOLS = TLSV12_PROTOCOLS;
/** Protocols to enable by default when "TLSv1" is requested. */
- static final String[] TLSV1_PROTOCOLS = new String[] {
- SUPPORTED_PROTOCOL_TLSV1,
- SUPPORTED_PROTOCOL_TLSV1_1,
- SUPPORTED_PROTOCOL_TLSV1_2,
- };
+ static final String[] TLSV1_PROTOCOLS = TLSV11_PROTOCOLS;
static final String[] DEFAULT_PROTOCOLS = TLSV12_PROTOCOLS;
+ private static final String[] SUPPORTED_PROTOCOLS = DEFAULT_PROTOCOLS;
static String[] getSupportedProtocols() {
- return TLSV12_PROTOCOLS.clone();
+ return SUPPORTED_PROTOCOLS.clone();
}
static void setEnabledProtocols(long ssl, NativeSsl ssl_holder, String[] protocols) {
checkEnabledProtocols(protocols);
- // openssl uses negative logic letting you disable protocols.
- // so first, assume we need to set all (disable all) and clear none (enable none).
- // in the loop, selectively move bits from set to clear (from disable to enable)
- long optionsToSet = (NativeConstants.SSL_OP_NO_SSLv3 | NativeConstants.SSL_OP_NO_TLSv1
- | NativeConstants.SSL_OP_NO_TLSv1_1 | NativeConstants.SSL_OP_NO_TLSv1_2);
- long optionsToClear = 0;
- for (String protocol : protocols) {
- if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1)) {
- optionsToSet &= ~NativeConstants.SSL_OP_NO_TLSv1;
- optionsToClear |= NativeConstants.SSL_OP_NO_TLSv1;
- } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_1)) {
- optionsToSet &= ~NativeConstants.SSL_OP_NO_TLSv1_1;
- optionsToClear |= NativeConstants.SSL_OP_NO_TLSv1_1;
- } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_2)) {
- optionsToSet &= ~NativeConstants.SSL_OP_NO_TLSv1_2;
- optionsToClear |= NativeConstants.SSL_OP_NO_TLSv1_2;
- } else if (protocol.equals(OBSOLETE_PROTOCOL_SSLV3)) {
- // Do nothing since we no longer support this protocol, but
- // allow it in the list of protocols so we can give an error
- // message about it if the handshake fails.
- } else {
- // error checked by checkEnabledProtocols
- throw new IllegalStateException();
+ // TLS protocol negotiation only allows a min and max version
+ // to be set, despite the Java API allowing a sparse set of
+ // protocols to be enabled. Use the lowest contiguous range
+ // of protocols provided by the caller, which is what we've
+ // done historically.
+ List<String> protocolsList = Arrays.asList(protocols);
+ String min = null;
+ String max = null;
+ for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) {
+ String protocol = SUPPORTED_PROTOCOLS[i];
+ if (protocolsList.contains(protocol)) {
+ if (min == null) {
+ min = protocol;
+ }
+ max = protocol;
+ } else if (min != null) {
+ break;
}
}
+ if ((min == null) || (max == null)) {
+ throw new IllegalArgumentException("No protocols enabled.");
+ }
+ SSL_set_protocol_versions(ssl, ssl_holder, getProtocolConstant(min), getProtocolConstant(max));
+ }
- SSL_set_options(ssl, ssl_holder, optionsToSet);
- SSL_clear_options(ssl, ssl_holder, optionsToClear);
+ private static int getProtocolConstant(String protocol) {
+ if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1)) {
+ return NativeConstants.TLS1_VERSION;
+ } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_1)) {
+ return NativeConstants.TLS1_1_VERSION;
+ } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_2)) {
+ return NativeConstants.TLS1_2_VERSION;
+ } else {
+ throw new AssertionError("Unknown protocol encountered: " + protocol);
+ }
}
static String[] checkEnabledProtocols(String[] protocols) {
diff --git a/constants/src/gen/cpp/generate_constants.cc b/constants/src/gen/cpp/generate_constants.cc
index 5725abd..b5ea68d 100644
--- a/constants/src/gen/cpp/generate_constants.cc
+++ b/constants/src/gen/cpp/generate_constants.cc
@@ -66,16 +66,16 @@
CONST(SSL_OP_CIPHER_SERVER_PREFERENCE);
CONST(SSL_OP_NO_TICKET);
- CONST(SSL_OP_NO_SSLv3);
- CONST(SSL_OP_NO_TLSv1);
- CONST(SSL_OP_NO_TLSv1_1);
- CONST(SSL_OP_NO_TLSv1_2);
CONST(SSL_ERROR_NONE);
CONST(SSL_ERROR_WANT_READ);
CONST(SSL_ERROR_WANT_WRITE);
CONST(SSL_ERROR_ZERO_RETURN);
+ CONST(TLS1_VERSION);
+ CONST(TLS1_1_VERSION);
+ CONST(TLS1_2_VERSION);
+
CONST(SSL_SENT_SHUTDOWN);
CONST(SSL_RECEIVED_SHUTDOWN);
diff --git a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
index 71ffa84..e2738aa 100644
--- a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -18,13 +18,14 @@
import static org.conscrypt.NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING;
import static org.conscrypt.NativeConstants.SSL_MODE_ENABLE_FALSE_START;
-import static org.conscrypt.NativeConstants.SSL_OP_NO_SSLv3;
-import static org.conscrypt.NativeConstants.SSL_OP_NO_TLSv1;
-import static org.conscrypt.NativeConstants.SSL_OP_NO_TLSv1_1;
-import static org.conscrypt.NativeConstants.SSL_OP_NO_TLSv1_2;
+import static org.conscrypt.NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE;
+import static org.conscrypt.NativeConstants.SSL_OP_NO_TICKET;
import static org.conscrypt.NativeConstants.SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
import static org.conscrypt.NativeConstants.SSL_VERIFY_NONE;
import static org.conscrypt.NativeConstants.SSL_VERIFY_PEER;
+import static org.conscrypt.NativeConstants.TLS1_1_VERSION;
+import static org.conscrypt.NativeConstants.TLS1_2_VERSION;
+import static org.conscrypt.NativeConstants.TLS1_VERSION;
import static org.conscrypt.TestUtils.openTestFile;
import static org.conscrypt.TestUtils.readTestFile;
import static org.junit.Assert.assertEquals;
@@ -383,10 +384,7 @@
long s = NativeCrypto.SSL_new(c, null);
assertTrue(s != NULL);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_SSLv3) == 0);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_TLSv1) == 0);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_TLSv1_1) == 0);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_TLSv1_2) == 0);
+ assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_TICKET) != 0);
long s2 = NativeCrypto.SSL_new(c, null);
assertTrue(s != s2);
@@ -535,9 +533,9 @@
public void test_SSL_set_options() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_SSLv3) == 0);
- NativeCrypto.SSL_set_options(s, null, SSL_OP_NO_SSLv3);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_SSLv3) != 0);
+ assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
+ NativeCrypto.SSL_set_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@@ -551,11 +549,28 @@
public void test_SSL_clear_options() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_SSLv3) == 0);
- NativeCrypto.SSL_set_options(s, null, SSL_OP_NO_SSLv3);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_SSLv3) != 0);
- NativeCrypto.SSL_clear_options(s, null, SSL_OP_NO_SSLv3);
- assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_SSLv3) == 0);
+ assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
+ NativeCrypto.SSL_set_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
+ NativeCrypto.SSL_clear_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
+ NativeCrypto.SSL_free(s, null);
+ NativeCrypto.SSL_CTX_free(c, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_protocol_versions_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_protocol_versions(NULL, null, 0, 0);
+ }
+
+ @Test
+ public void SSL_set_protocol_versions() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c, null);
+ assertEquals(1, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_VERSION, TLS1_1_VERSION));
+ assertEquals(1, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_2_VERSION, TLS1_2_VERSION));
+ assertEquals(0, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_2_VERSION + 413, TLS1_1_VERSION));
+ assertEquals(0, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_1_VERSION, TLS1_2_VERSION + 413));
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}