From 6f9301b19c1040ae4e350f3fdba799dc3d2d88fa Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 12 Sep 2018 14:37:42 +0200 Subject: [PATCH] Add support for TLSv1.3 when compiled against openssl 1.1.1 Motivation: OpenSSL 1.1.1 was released which supports TLSv1.3 and it is the now the current LTS release. We should be able to compile against it and also support TLSv1.3. Modifications: - Add some new native methods to allow to set TLSv1.3 ciphersuites - Depending on if TLSv1.3 is supported or not set some flags Result: Be able to compile against OpenSSL 1.1.1 and make use of TLSv1.3. Fixes https://github.com/netty/netty-tcnative/issues/345 and https://github.com/netty/netty-tcnative/issues/256 --- openssl-dynamic/src/main/c/native_constants.c | 5 +++ openssl-dynamic/src/main/c/ssl.c | 30 +++++++++++----- openssl-dynamic/src/main/c/ssl_private.h | 16 +++++++++ openssl-dynamic/src/main/c/sslcontext.c | 36 ++++++++++++------- openssl-dynamic/src/main/c/sslutils.c | 6 ++++ .../NativeStaticallyReferencedJniMethods.java | 1 + .../java/io/netty/internal/tcnative/SSL.java | 31 ++++++++++++++-- .../netty/internal/tcnative/SSLContext.java | 25 ++++++++++++- pom.xml | 6 ++-- 9 files changed, 128 insertions(+), 28 deletions(-) diff --git a/openssl-dynamic/src/main/c/native_constants.c b/openssl-dynamic/src/main/c/native_constants.c index dbc6a8b1a..6d857e8c2 100644 --- a/openssl-dynamic/src/main/c/native_constants.c +++ b/openssl-dynamic/src/main/c/native_constants.c @@ -42,6 +42,10 @@ TCN_IMPLEMENT_CALL(jint, NativeStaticallyReferencedJniMethods, sslOpNoTLSv12)(TC return SSL_OP_NO_TLSv1_2; } +TCN_IMPLEMENT_CALL(jint, NativeStaticallyReferencedJniMethods, sslOpNoTLSv13)(TCN_STDARGS) { + return SSL_OP_NO_TLSv1_3; +} + TCN_IMPLEMENT_CALL(jint, NativeStaticallyReferencedJniMethods, sslOpNoTicket)(TCN_STDARGS) { return SSL_OP_NO_TICKET; } @@ -484,6 +488,7 @@ static const JNINativeMethod method_table[] = { { TCN_METHOD_TABLE_ENTRY(sslOpNoTLSv1, ()I, NativeStaticallyReferencedJniMethods) }, { TCN_METHOD_TABLE_ENTRY(sslOpNoTLSv11, ()I, NativeStaticallyReferencedJniMethods) }, { TCN_METHOD_TABLE_ENTRY(sslOpNoTLSv12, ()I, NativeStaticallyReferencedJniMethods) }, + { TCN_METHOD_TABLE_ENTRY(sslOpNoTLSv13, ()I, NativeStaticallyReferencedJniMethods) }, { TCN_METHOD_TABLE_ENTRY(sslOpNoTicket, ()I, NativeStaticallyReferencedJniMethods) }, { TCN_METHOD_TABLE_ENTRY(sslOpNoCompression, ()I, NativeStaticallyReferencedJniMethods) }, { TCN_METHOD_TABLE_ENTRY(sslSessCacheOff, ()I, NativeStaticallyReferencedJniMethods) }, diff --git a/openssl-dynamic/src/main/c/ssl.c b/openssl-dynamic/src/main/c/ssl.c index 237b1e6a7..3005fda74 100644 --- a/openssl-dynamic/src/main/c/ssl.c +++ b/openssl-dynamic/src/main/c/ssl.c @@ -1559,27 +1559,41 @@ TCN_IMPLEMENT_CALL(jobjectArray, SSL, getCiphers)(TCN_STDARGS, jlong ssl) } TCN_IMPLEMENT_CALL(jboolean, SSL, setCipherSuites)(TCN_STDARGS, jlong ssl, - jstring ciphers) + jstring ciphers, jboolean tlsv13) { jboolean rv = JNI_TRUE; - TCN_ALLOC_CSTRING(ciphers); SSL *ssl_ = J2P(ssl, SSL *); TCN_CHECK_NULL(ssl_, ssl, JNI_FALSE); - UNREFERENCED(o); +#ifdef OPENSSL_NO_TLS1_3 + if (tlsv13 == JNI_TRUE) { + tcn_Throw(e, "TLSv1.3 not supported"); + return JNI_FALSE; + } + #endif + TCN_ALLOC_CSTRING(ciphers); + UNREFERENCED(o); if (!J2S(ciphers)) { return JNI_FALSE; } - if (!SSL_set_cipher_list(ssl_, J2S(ciphers))) { - char err[ERR_LEN]; +#ifdef OPENSSL_NO_TLS1_3 + rv = SSL_set_cipher_list(ssl_, J2S(ciphers)) == 0 ? JNI_FALSE : JNI_TRUE; +#else + if (tlsv13 == JNI_TRUE) { + rv = SSL_set_ciphersuites(ssl_, J2S(ciphers)) == 0 ? JNI_FALSE : JNI_TRUE; + } else { + rv = SSL_set_cipher_list(ssl_, J2S(ciphers)) == 0 ? JNI_FALSE : JNI_TRUE; + } +#endif + + if (rv == JNI_FALSE) { + char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err); - rv = JNI_FALSE; } - TCN_FREE_CSTRING(ciphers); return rv; } @@ -2307,7 +2321,7 @@ static const JNINativeMethod method_table[] = { { TCN_METHOD_TABLE_ENTRY(getMode, (J)I, SSL) }, { TCN_METHOD_TABLE_ENTRY(getMaxWrapOverhead, (J)I, SSL) }, { TCN_METHOD_TABLE_ENTRY(getCiphers, (J)[Ljava/lang/String;, SSL) }, - { TCN_METHOD_TABLE_ENTRY(setCipherSuites, (JLjava/lang/String;)Z, SSL) }, + { TCN_METHOD_TABLE_ENTRY(setCipherSuites, (JLjava/lang/String;Z)Z, SSL) }, { TCN_METHOD_TABLE_ENTRY(getSessionId, (J)[B, SSL) }, { TCN_METHOD_TABLE_ENTRY(getHandshakeCount, (J)I, SSL) }, { TCN_METHOD_TABLE_ENTRY(clearError, ()V, SSL) }, diff --git a/openssl-dynamic/src/main/c/ssl_private.h b/openssl-dynamic/src/main/c/ssl_private.h index 5a28946b8..825afe181 100644 --- a/openssl-dynamic/src/main/c/ssl_private.h +++ b/openssl-dynamic/src/main/c/ssl_private.h @@ -131,6 +131,22 @@ extern const char* TCN_UNKNOWN_AUTH_METHOD; #define TLS1_3_VERSION 0x0304 #endif +#ifndef SSL_OP_NO_TLSv1_3 +// TLSV1_3 is not really supported by the underlying OPENSSL version +#ifndef OPENSSL_NO_TLS1_3 +#define OPENSSL_NO_TLS1_3 +#endif // OPENSSL_NO_TLS1_3 + +#define SSL_OP_NO_TLSv1_3 0x20000000U +#endif // SSL_OP_NO_TLSv1_3 + +// BoringSSL does not support TLSv1.3 for now +#ifdef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_TLS1_3 +#define OPENSSL_NO_TLS1_3 +#endif // OPENSSL_NO_TLS1_3 +#endif // OPENSSL_IS_BORINGSSL + /* OpenSSL 1.0.2 compatibility */ #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define TLS_method SSLv23_method diff --git a/openssl-dynamic/src/main/c/sslcontext.c b/openssl-dynamic/src/main/c/sslcontext.c index 454897d88..0a8457383 100644 --- a/openssl-dynamic/src/main/c/sslcontext.c +++ b/openssl-dynamic/src/main/c/sslcontext.c @@ -422,24 +422,40 @@ TCN_IMPLEMENT_CALL(void, SSLContext, clearOptions)(TCN_STDARGS, jlong ctx, } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx, - jstring ciphers) + jstring ciphers, jboolean tlsv13) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_TRUE; TCN_CHECK_NULL(c, ctx, JNI_FALSE); - TCN_ALLOC_CSTRING(ciphers); +#ifdef OPENSSL_NO_TLS1_3 + if (tlsv13 == JNI_TRUE) { + tcn_Throw(e, "TLSv1.3 not supported"); + return JNI_FALSE; + } +#endif + TCN_ALLOC_CSTRING(ciphers); UNREFERENCED(o); - if (!J2S(ciphers)) + if (!J2S(ciphers)) { return JNI_FALSE; + } + +#ifdef OPENSSL_NO_TLS1_3 + rv = SSL_CTX_set_cipher_list(c->ctx, J2S(ciphers)) == 0 ? JNI_FALSE : JNI_TRUE; +#else + if (tlsv13 == JNI_TRUE) { + rv = SSL_CTX_set_ciphersuites(c->ctx, J2S(ciphers)) == 0 ? JNI_FALSE : JNI_TRUE; + } else { + rv = SSL_CTX_set_cipher_list(c->ctx, J2S(ciphers)) == 0 ? JNI_FALSE : JNI_TRUE; + } +#endif - if (!SSL_CTX_set_cipher_list(c->ctx, J2S(ciphers))) { + if (rv == JNI_FALSE) { char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err); - rv = JNI_FALSE; } TCN_FREE_CSTRING(ciphers); return rv; @@ -1434,13 +1450,12 @@ TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong c } } - static jbyteArray keyTypes(JNIEnv* e, SSL* ssl) { jbyte* ctype_bytes; jbyteArray types; int ctype_num = tcn_SSL_get0_certificate_types(ssl, (const uint8_t **) &ctype_bytes); if (ctype_num <= 0) { - // Use no certificate + // No idea what we should use... Let the caller handle it. return NULL; } types = (*e)->NewByteArray(e, ctype_num); @@ -1451,7 +1466,6 @@ static jbyteArray keyTypes(JNIEnv* e, SSL* ssl) { return types; } - /** * Returns an array containing all the X500 principal's bytes. * @@ -1527,10 +1541,6 @@ static int cert_requested(SSL* ssl, X509** x509Out, EVP_PKEY** pkeyOut) { tcn_get_java_env(&e); types = keyTypes(e, ssl); - if (types == NULL) { - return 0; - } - issuers = principalBytes(e, SSL_get_client_CA_list(ssl)); // Execute the java callback @@ -1870,7 +1880,7 @@ static const JNINativeMethod fixed_method_table[] = { { TCN_METHOD_TABLE_ENTRY(setOptions, (JI)V, SSLContext) }, { TCN_METHOD_TABLE_ENTRY(getOptions, (J)I, SSLContext) }, { TCN_METHOD_TABLE_ENTRY(clearOptions, (JI)V, SSLContext) }, - { TCN_METHOD_TABLE_ENTRY(setCipherSuite, (JLjava/lang/String;)Z, SSLContext) }, + { TCN_METHOD_TABLE_ENTRY(setCipherSuite, (JLjava/lang/String;Z)Z, SSLContext) }, { TCN_METHOD_TABLE_ENTRY(setCertificateChainFile, (JLjava/lang/String;Z)Z, SSLContext) }, { TCN_METHOD_TABLE_ENTRY(setCertificateChainBio, (JJZ)Z, SSLContext) }, { TCN_METHOD_TABLE_ENTRY(setCACertificateBio, (JJ)Z, SSLContext) }, diff --git a/openssl-dynamic/src/main/c/sslutils.c b/openssl-dynamic/src/main/c/sslutils.c index 9939d2813..9c71de344 100644 --- a/openssl-dynamic/src/main/c/sslutils.c +++ b/openssl-dynamic/src/main/c/sslutils.c @@ -106,6 +106,12 @@ const char* tcn_SSL_cipher_authentication_method(const SSL_CIPHER* cipher){ default: return TCN_UNKNOWN_AUTH_METHOD; } +#ifndef OPENSSL_NO_TLS1_3 + case NID_kx_any: + // Let us just pick one as we could use whatever we want. + // See https://www.openssl.org/docs/man1.1.1/man3/SSL_CIPHER_get_kx_nid.html + return "ECDHE_" SSL_TXT_RSA; +#endif default: return TCN_UNKNOWN_AUTH_METHOD; } diff --git a/openssl-dynamic/src/main/java/io/netty/internal/tcnative/NativeStaticallyReferencedJniMethods.java b/openssl-dynamic/src/main/java/io/netty/internal/tcnative/NativeStaticallyReferencedJniMethods.java index 1ba6fef57..992bceffc 100644 --- a/openssl-dynamic/src/main/java/io/netty/internal/tcnative/NativeStaticallyReferencedJniMethods.java +++ b/openssl-dynamic/src/main/java/io/netty/internal/tcnative/NativeStaticallyReferencedJniMethods.java @@ -40,6 +40,7 @@ private NativeStaticallyReferencedJniMethods() { static native int sslOpNoTLSv1(); static native int sslOpNoTLSv11(); static native int sslOpNoTLSv12(); + static native int sslOpNoTLSv13(); static native int sslOpNoTicket(); /** diff --git a/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSL.java b/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSL.java index b7a9882e4..09e739c03 100644 --- a/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSL.java +++ b/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSL.java @@ -50,9 +50,10 @@ private SSL() { } public static final int SSL_PROTOCOL_TLSV1 = (1<<2); public static final int SSL_PROTOCOL_TLSV1_1 = (1<<3); public static final int SSL_PROTOCOL_TLSV1_2 = (1<<4); + public static final int SSL_PROTOCOL_TLSV1_3 = (1<<5); /** TLS_*method according to SSL_CTX_new */ - public static final int SSL_PROTOCOL_TLS = (SSL_PROTOCOL_SSLV3 | SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2); + public static final int SSL_PROTOCOL_TLS = (SSL_PROTOCOL_SSLV3 | SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2 | SSL_PROTOCOL_TLSV1_3); public static final int SSL_PROTOCOL_ALL = (SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_TLS); /* @@ -69,6 +70,7 @@ private SSL() { } public static final int SSL_OP_NO_TLSv1 = sslOpNoTLSv1(); public static final int SSL_OP_NO_TLSv1_1 = sslOpNoTLSv11(); public static final int SSL_OP_NO_TLSv1_2 = sslOpNoTLSv12(); + public static final int SSL_OP_NO_TLSv1_3 = sslOpNoTLSv13(); public static final int SSL_OP_NO_TICKET = sslOpNoTicket(); public static final int SSL_OP_NO_COMPRESSION = sslOpNoCompression(); @@ -500,10 +502,33 @@ private SSL() { } * @param ciphers an SSL cipher specification * @return {@code true} if successful * @throws Exception if an error happened + * @deprecated Use {@link #setCipherSuites(long, String, boolean)} */ - public static native boolean setCipherSuites(long ssl, String ciphers) - throws Exception; + @Deprecated + public static boolean setCipherSuites(long ssl, String ciphers) + throws Exception { + return setCipherSuites(ssl, ciphers, false); + } + /** + * Returns the cipher suites available for negotiation in SSL handshake. + *

+ * This complex directive uses a colon-separated cipher-spec string consisting + * of OpenSSL cipher specifications to configure the Cipher Suite the client + * is permitted to negotiate in the SSL handshake phase. Notice that this + * directive can be used both in per-server and per-directory context. + * In per-server context it applies to the standard SSL handshake when a + * connection is established. In per-directory context it forces a SSL + * renegotiation with the reconfigured Cipher Suite after the HTTP request + * was read but before the HTTP response is sent. + * @param ssl the SSL instance (SSL *) + * @param ciphers an SSL cipher specification + * @param tlsv13 {@code true} if the ciphers are for TLSv1.3 + * @return {@code true} if successful + * @throws Exception if an error happened + */ + public static native boolean setCipherSuites(long ssl, String ciphers, boolean tlsv13) + throws Exception; /** * Returns the ID of the session as byte array representation. * diff --git a/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSLContext.java b/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSLContext.java index 0b5fe4237..8111cd64c 100644 --- a/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSLContext.java +++ b/openssl-dynamic/src/main/java/io/netty/internal/tcnative/SSLContext.java @@ -110,8 +110,31 @@ public static native long make(int protocol, int mode) * @param ciphers An SSL cipher specification. * @return {@code true} if successful * @throws Exception if an error happened + * @deprecated Use {@link #setCipherSuite(long, String, boolean)}. */ - public static native boolean setCipherSuite(long ctx, String ciphers) throws Exception; + @Deprecated + public static boolean setCipherSuite(long ctx, String ciphers) throws Exception { + return setCipherSuite(ctx, ciphers, false); + } + + /** + * Cipher Suite available for negotiation in SSL handshake. + *
+ * This complex directive uses a colon-separated cipher-spec string consisting + * of OpenSSL cipher specifications to configure the Cipher Suite the client + * is permitted to negotiate in the SSL handshake phase. Notice that this + * directive can be used both in per-server and per-directory context. + * In per-server context it applies to the standard SSL handshake when a + * connection is established. In per-directory context it forces a SSL + * renegotiation with the reconfigured Cipher Suite after the HTTP request + * was read but before the HTTP response is sent. + * @param ctx Server or Client context to use. + * @param ciphers An SSL cipher specification. + * @param tlsv13 {@code true} if the ciphers are for TLSv1.3 + * @return {@code true} if successful + * @throws Exception if an error happened + */ + public static native boolean setCipherSuite(long ctx, String ciphers, boolean tlsv13) throws Exception; /** * Set File of PEM-encoded Server CA Certificates diff --git a/pom.xml b/pom.xml index 96fc322d4..a96566df0 100644 --- a/pom.xml +++ b/pom.xml @@ -65,10 +65,10 @@ - Record the sha256: sha1sum -a 256 libressl-{libresslVersion}.tar.gz (shasum on osx) --> 1e3a9fada06c1c060011470ad0ff960de28f9a0515277d7336f7e09362517da6 - 1.1.0 - i + 1.1.1 + ${opensslMinorVersion}${opensslPatchVersion} - 50a98e07b1a89eb8f6a99477f262df71c6fa7bef77df4dc83025a2845c827d00 + 2836875a0f89c03d0fdf483941512613a50cfb421d6fd94b9f41d7279d586a3d ${project.build.directory}/apr ${project.build.directory}/apr-${aprVersion} 64