From 6c66bd5c7c6b624966f7521fed778f1d1d7e3a6a Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 26 Apr 2014 22:37:48 +0900 Subject: [PATCH] ALPN: Do not negotiate HTTP/2 unless TLSv1.2 or TLSv1.1 was used --- src/shrpx_ssl.cc | 67 +++++++++++++++++++++++++++++++++++++++--------- src/shrpx_ssl.h | 3 +++ 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index fc1fc23b..af14a5d6 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -155,6 +155,24 @@ void info_callback(const SSL *ssl, int where, int ret) } // namespace #if OPENSSL_VERSION_NUMBER >= 0x10002000L +namespace { +// Returns true if ALPN identifier list |in| of length |inlen| +// contains http/1.1. +bool check_http1_available_in_alpn_list(const unsigned char *in, + unsigned int inlen) +{ + for(unsigned int i = 0; i < inlen; i += 1 + in[i]) { + if(in[i] == 8 && i + 1 + in[i] <= inlen && + memcmp("http/1.1", &in[i + 1], in[i]) == 0) { + + return true; + } + } + + return false; +} +} // namespace + namespace { int alpn_select_proto_cb(SSL* ssl, const unsigned char **out, @@ -164,31 +182,36 @@ int alpn_select_proto_cb(SSL* ssl, { int rv; - rv = nghttp2_select_next_protocol - (const_cast(out), outlen, in, inlen); + if(check_http2_requirement(ssl)) { - if(rv == 1) { - // HTTP/2 was selected - return SSL_TLSEXT_ERR_OK; + rv = nghttp2_select_next_protocol + (const_cast(out), outlen, in, inlen); + + if(rv == 1) { + // HTTP/2 was selected + return SSL_TLSEXT_ERR_OK; + } + } else if(check_http1_available_in_alpn_list(in, inlen)) { + *out = reinterpret_cast("http/1.1"); + *outlen = strlen("http/1.1"); + + rv = 0; + } else { + rv = -1; } #ifdef HAVE_SPDYLAY rv = spdylay_select_next_protocol (const_cast(out), outlen, in, inlen); - - if(rv > 0) { - // SPDY was selected - return SSL_TLSEXT_ERR_OK; - } #endif // HAVE_SPDYLAY if(rv == -1) { // No selection was made - return SSL_TLSEXT_ERR_OK; + return SSL_TLSEXT_ERR_NOACK; } // We selected http/1.1 - return SSL_TLSEXT_ERR_NOACK; + return SSL_TLSEXT_ERR_OK; } } // namespace #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L @@ -895,6 +918,26 @@ bool in_proto_list(char **protos, size_t len, return false; } +bool check_http2_requirement(SSL *ssl) +{ + auto tls_ver = SSL_version(ssl); + + switch(tls_ver) { + case TLS1_1_VERSION: + case TLS1_2_VERSION: + break; + default: + LOG(INFO) << "TLSv1.2 or TLSv1.1 was not negotiated. " + << "HTTP/2 must not be negotiated."; + return false; + } + + // TODO Figure out that ECDHE or DHE was negotiated and their key + // size. SSL_get_server_tmp_key() cannot be used on server side. + + return true; +} + } // namespace ssl } // namespace shrpx diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h index 1153867f..ac6aed87 100644 --- a/src/shrpx_ssl.h +++ b/src/shrpx_ssl.h @@ -129,6 +129,9 @@ int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, bool in_proto_list(char **protos, size_t len, const unsigned char *proto, size_t protolen); +// Returns true if security requirement for HTTP/2 is fulfilled. +bool check_http2_requirement(SSL *ssl); + } // namespace ssl } // namespace shrpx