From 9e8d9d658a87a35031c1ef9b182997ce77b2f041 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 15 Feb 2017 00:40:43 +0900 Subject: [PATCH] src: Enable TLSv1.3 if OpenSSL supports it If OpenSSL supports TLSv1.3, enable it by default for all applications under src. BoringSSL can work at the moment although it does not unlock all the features nghttpx offers. OpenSSL's TLSv1.3 support is still WIP at the time of writing. --- src/HttpServer.cc | 7 +++++ src/h2load.cc | 7 +++++ src/nghttp.cc | 9 +++++++ src/shrpx.cc | 42 +++++++++++++++++++---------- src/shrpx_ssl.cc | 68 ++++++++--------------------------------------- src/ssl.cc | 43 ++++++++++++++++++++++++++++++ src/ssl.h | 29 ++++++++++++++++++++ 7 files changed, 134 insertions(+), 71 deletions(-) diff --git a/src/HttpServer.cc b/src/HttpServer.cc index f4dec3c5..7dc16765 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -2122,6 +2122,13 @@ int HttpServer::run() { SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + if (nghttp2::ssl::ssl_ctx_set_proto_versions( + ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION, + nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) { + std::cerr << "Could not set TLS versions" << std::endl; + return -1; + } + if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; diff --git a/src/h2load.cc b/src/h2load.cc index 6fdd0b9b..cbf0e51a 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -2242,6 +2242,13 @@ int main(int argc, char **argv) { SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + if (nghttp2::ssl::ssl_ctx_set_proto_versions( + ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION, + nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) { + std::cerr << "Could not set TLS versions" << std::endl; + exit(EXIT_FAILURE); + } + if (SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers.c_str()) == 0) { std::cerr << "SSL_CTX_set_cipher_list with " << config.ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr) diff --git a/src/nghttp.cc b/src/nghttp.cc index 38e85a82..b94e2ba9 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -2246,6 +2246,15 @@ int communicate( SSL_CTX_set_options(ssl_ctx, ssl_opts); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + + if (nghttp2::ssl::ssl_ctx_set_proto_versions( + ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION, + nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) { + std::cerr << "[ERROR] Could not set TLS versions" << std::endl; + result = -1; + goto fin; + } + if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; diff --git a/src/shrpx.cc b/src/shrpx.cc index a43aaa42..88e41d89 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1369,7 +1369,11 @@ constexpr auto DEFAULT_NPN_LIST = StringRef::from_lit("h2,h2-16,h2-14," namespace { constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.1"); +#ifdef TLS1_3_VERSION +constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.3"); +#else // !TLS1_3_VERSION constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.2"); +#endif // !TLS1_3_VERSION } // namespace namespace { @@ -2060,23 +2064,33 @@ SSL/TLS: Path to file that contains client certificate used in backend client authentication. --tls-min-proto-version= - Specify minimum SSL/TLS protocol. The following - protocols are available: TLSv1.2, TLSv1.1 and TLSv1.0. - The name matching is done in case-insensitive manner. - The versions between --tls-min-proto-version and - --tls-max-proto-version are enabled. If the protocol - list advertised by client does not overlap this range, - you will receive the error message "unknown protocol". + Specify minimum SSL/TLS protocol. The name matching is + done in case-insensitive manner. The versions between + --tls-min-proto-version and --tls-max-proto-version are + enabled. If the protocol list advertised by client does + not overlap this range, you will receive the error + message "unknown protocol". The available versions are: + )" +#ifdef TLS1_3_VERSION + "TLSv1.3, " +#endif // TLS1_3_VERSION + "TLSv1.2, TLSv1.1, and TLSv1.0" + R"( Default: )" << DEFAULT_TLS_MIN_PROTO_VERSION << R"( --tls-max-proto-version= - Specify maximum SSL/TLS protocol. The following - protocols are available: TLSv1.2, TLSv1.1 and TLSv1.0. - The name matching is done in case-insensitive manner. - The versions between --tls-min-proto-version and - --tls-max-proto-version are enabled. If the protocol - list advertised by client does not overlap this range, - you will receive the error message "unknown protocol". + Specify maximum SSL/TLS protocol. The name matching is + done in case-insensitive manner. The versions between + --tls-min-proto-version and --tls-max-proto-version are + enabled. If the protocol list advertised by client does + not overlap this range, you will receive the error + message "unknown protocol". The available versions are: + )" +#ifdef TLS1_3_VERSION + "TLSv1.3, " +#endif // TLS1_3_VERSION + "TLSv1.2, TLSv1.1, and TLSv1.0" + R"( Default: )" << DEFAULT_TLS_MAX_PROTO_VERSION << R"( --tls-ticket-key-file= diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 26379586..f648bda2 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -639,55 +639,6 @@ long int create_tls_proto_mask(const std::vector &tls_proto_list) { return res; } -#if defined(OPENSSL_IS_BORINGSSL) -namespace { -int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) { - SSL_CTX_set_min_version(ssl_ctx, min); - SSL_CTX_set_max_version(ssl_ctx, max); - return 0; -} -} // namespace -#elif OPENSSL_1_1_API -namespace { -int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) { - if (SSL_CTX_set_min_proto_version(ssl_ctx, min) != 1 || - SSL_CTX_set_max_proto_version(ssl_ctx, max) != 1) { - return -1; - } - return 0; -} -} // namespace -#else // !OPENSSL_1_1_API -namespace { -int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) { - long int opts = 0; - - // TODO We depends on the ordering of protocol version macro in - // OpenSSL. - if (min > TLS1_VERSION) { - opts |= SSL_OP_NO_TLSv1; - } - if (min > TLS1_1_VERSION) { - opts |= SSL_OP_NO_TLSv1_1; - } - if (min > TLS1_2_VERSION) { - opts |= SSL_OP_NO_TLSv1_2; - } - - if (max < TLS1_2_VERSION) { - opts |= SSL_OP_NO_TLSv1_2; - } - if (max < TLS1_1_VERSION) { - opts |= SSL_OP_NO_TLSv1_1; - } - - SSL_CTX_set_options(ssl_ctx, opts); - - return 0; -} -} // namespace -#endif // !OPENSSL_1_1_API - SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, const std::vector &sct_data #ifdef HAVE_NEVERBLEED @@ -712,10 +663,9 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask); - if (ssl_ctx_set_proto_versions(ssl_ctx, tlsconf.min_proto_version, - tlsconf.max_proto_version) != 0) { - LOG(FATAL) << "Could not set TLS protocol version: " - << ERR_error_string(ERR_get_error(), nullptr); + if (nghttp2::ssl::ssl_ctx_set_proto_versions( + ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) { + LOG(FATAL) << "Could not set TLS protocol version"; DIE(); } @@ -953,10 +903,9 @@ SSL_CTX *create_ssl_client_context( SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask); - if (ssl_ctx_set_proto_versions(ssl_ctx, tlsconf.min_proto_version, - tlsconf.max_proto_version) != 0) { - LOG(FATAL) << "Could not set TLS protocol version: " - << ERR_error_string(ERR_get_error(), nullptr); + if (nghttp2::ssl::ssl_ctx_set_proto_versions( + ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) { + LOG(FATAL) << "Could not set TLS protocol version"; DIE(); } @@ -1742,6 +1691,11 @@ SSL_SESSION *reuse_tls_session(const TLSSessionCache &cache) { } int proto_version_from_string(const StringRef &v) { +#ifdef TLS1_3_VERSION + if (util::strieq_l("TLSv1.3", v)) { + return TLS1_3_VERSION; + } +#endif // TLS1_3_VERSION if (util::strieq_l("TLSv1.2", v)) { return TLS1_2_VERSION; } diff --git a/src/ssl.cc b/src/ssl.cc index ed7ec056..5425cb8e 100644 --- a/src/ssl.cc +++ b/src/ssl.cc @@ -83,6 +83,10 @@ const char *get_tls_protocol(SSL *ssl) { return "SSLv2"; case SSL3_VERSION: return "SSLv3"; +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: + return "TLSv1.3"; +#endif // TLS1_3_VERSION case TLS1_2_VERSION: return "TLSv1.2"; case TLS1_1_VERSION: @@ -159,6 +163,45 @@ void libssl_init() { OpenSSL_add_all_algorithms(); } +int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) { +#if OPENSSL_1_1_API + if (SSL_CTX_set_min_proto_version(ssl_ctx, min) != 1 || + SSL_CTX_set_max_proto_version(ssl_ctx, max) != 1) { + return -1; + } + return 0; +#elif defined(OPENSSL_IS_BORINGSSL) + SSL_CTX_set_min_version(ssl_ctx, min); + SSL_CTX_set_max_version(ssl_ctx, max); + return 0; +#else // !defined(OPENSSL_IS_BORINGSSL) + long int opts = 0; + + // TODO We depends on the ordering of protocol version macro in + // OpenSSL. + if (min > TLS1_VERSION) { + opts |= SSL_OP_NO_TLSv1; + } + if (min > TLS1_1_VERSION) { + opts |= SSL_OP_NO_TLSv1_1; + } + if (min > TLS1_2_VERSION) { + opts |= SSL_OP_NO_TLSv1_2; + } + + if (max < TLS1_2_VERSION) { + opts |= SSL_OP_NO_TLSv1_2; + } + if (max < TLS1_1_VERSION) { + opts |= SSL_OP_NO_TLSv1_1; + } + + SSL_CTX_set_options(ssl_ctx, opts); + + return 0; +#endif // !defined(OPENSSL_IS_BORINGSSL) +} + } // namespace ssl } // namespace nghttp2 diff --git a/src/ssl.h b/src/ssl.h index 2b3f232b..23c71fa9 100644 --- a/src/ssl.h +++ b/src/ssl.h @@ -49,7 +49,25 @@ public: // suites by mozilla. // // https://wiki.mozilla.org/Security/Server_Side_TLS +// +// Plus TLSv1.3 cipher suites if defined. constexpr char DEFAULT_CIPHER_LIST[] = +#ifdef TLS1_3_TXT_AES_256_GCM_SHA384 + TLS1_3_TXT_AES_256_GCM_SHA384 + ":" +#endif // TLS1_3_TXT_AES_256_GCM_SHA384 +#ifdef TLS1_3_TXT_CHACHA20_POLY1305_SHA256 + TLS1_3_TXT_CHACHA20_POLY1305_SHA256 ":" +#endif // TLS1_3_TXT_CHACHA20_POLY1305_SHA256 +#ifdef TLS1_3_TXT_AES_128_GCM_SHA256 + TLS1_3_TXT_AES_128_GCM_SHA256 ":" +#endif // TLS1_3_TXT_AES_128_GCM_SHA256 +#ifdef TLS1_3_TXT_AES_128_CCM_SHA256 + TLS1_3_TXT_AES_128_CCM_SHA256 ":" +#endif // TLS1_3_TXT_AES_128_CCM_SHA256 +#ifdef TLS1_3_TXT_AES_128_CCM_8_SHA256 + TLS1_3_TXT_AES_128_CCM_8_SHA256 ":" +#endif // TLS1_3_TXT_AES_128_CCM_8_SHA256 "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-" "AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-" "SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-" @@ -61,6 +79,13 @@ constexpr char DEFAULT_CIPHER_LIST[] = "SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-" "SHA:DES-CBC3-SHA:!DSS"; +constexpr auto NGHTTP2_TLS_MIN_VERSION = TLS1_VERSION; +#ifdef TLS1_3_VERSION +constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_3_VERSION; +#else // !TLS1_3_VERSION +constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_2_VERSION; +#endif // !TLS1_3_VERSION + const char *get_tls_protocol(SSL *ssl); struct TLSSessionInfo { @@ -91,6 +116,10 @@ bool check_http2_requirement(SSL *ssl); // Initializes OpenSSL library void libssl_init(); +// Sets TLS min and max versions to |ssl_ctx|. This function returns +// 0 if it succeeds, or -1. +int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max); + } // namespace ssl } // namespace nghttp2