Commit 1e757503 authored by bnc's avatar bnc Committed by Commit bot

Do not use HTTP/2 without adequate security.

Stop using HTTP/2 in case TLS 1.2 is not supported, connection has been
downgraded to below TLS 1.2, or AES-GCM cipher required by HTTP/2 draft
specification is not supported.

BUG=436835

Review URL: https://codereview.chromium.org/757033004

Cr-Commit-Position: refs/heads/master@{#308226}
parent fc5224c2
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_pool_manager_impl.h" #include "net/socket/client_socket_pool_manager_impl.h"
#include "net/socket/next_proto.h" #include "net/socket/next_proto.h"
#include "net/socket/ssl_client_socket.h"
#include "net/spdy/hpack_huffman_aggregator.h" #include "net/spdy/hpack_huffman_aggregator.h"
#include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_session_pool.h"
......
...@@ -153,6 +153,8 @@ ...@@ -153,6 +153,8 @@
'ssl/signed_certificate_timestamp_and_status.h', 'ssl/signed_certificate_timestamp_and_status.h',
'ssl/ssl_cert_request_info.cc', 'ssl/ssl_cert_request_info.cc',
'ssl/ssl_cert_request_info.h', 'ssl/ssl_cert_request_info.h',
'ssl/ssl_cipher_suite_names.cc',
'ssl/ssl_cipher_suite_names.h',
'ssl/ssl_client_auth_cache.cc', 'ssl/ssl_client_auth_cache.cc',
'ssl/ssl_client_auth_cache.h', 'ssl/ssl_client_auth_cache.h',
'ssl/ssl_client_cert_type.h', 'ssl/ssl_client_cert_type.h',
...@@ -1107,8 +1109,6 @@ ...@@ -1107,8 +1109,6 @@
'ssl/client_cert_store_nss.h', 'ssl/client_cert_store_nss.h',
'ssl/client_cert_store_win.cc', 'ssl/client_cert_store_win.cc',
'ssl/client_cert_store_win.h', 'ssl/client_cert_store_win.h',
'ssl/ssl_cipher_suite_names.cc',
'ssl/ssl_cipher_suite_names.h',
'ssl/ssl_config_service_defaults.cc', 'ssl/ssl_config_service_defaults.cc',
'ssl/ssl_config_service_defaults.h', 'ssl/ssl_config_service_defaults.h',
'third_party/mozilla_security_manager/nsKeygenHandler.cpp', 'third_party/mozilla_security_manager/nsKeygenHandler.cpp',
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "net/base/connection_type_histograms.h" #include "net/base/connection_type_histograms.h"
#include "net/base/host_port_pair.h" #include "net/base/host_port_pair.h"
#include "net/ssl/channel_id_service.h" #include "net/ssl/channel_id_service.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_config_service.h" #include "net/ssl/ssl_config_service.h"
#include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_connection_status_flags.h"
...@@ -233,11 +234,32 @@ bool SSLClientSocket::IsChannelIDEnabled( ...@@ -233,11 +234,32 @@ bool SSLClientSocket::IsChannelIDEnabled(
return true; return true;
} }
// static
bool SSLClientSocket::HasCipherAdequateForHTTP2(
const std::vector<uint16>& cipher_suites) {
for (uint16 cipher : cipher_suites) {
if (IsSecureTLSCipherSuite(cipher))
return true;
}
return false;
}
// static
bool SSLClientSocket::IsTLSVersionAdequateForHTTP2(
const SSLConfig& ssl_config) {
return ssl_config.version_max >= SSL_PROTOCOL_VERSION_TLS1_2;
}
// static // static
std::vector<uint8_t> SSLClientSocket::SerializeNextProtos( std::vector<uint8_t> SSLClientSocket::SerializeNextProtos(
const NextProtoVector& next_protos) { const NextProtoVector& next_protos,
bool can_advertise_http2) {
std::vector<uint8_t> wire_protos; std::vector<uint8_t> wire_protos;
for (const NextProto next_proto : next_protos) { for (const NextProto next_proto : next_protos) {
if (!can_advertise_http2 && kProtoSPDY4MinimumVersion <= next_proto &&
next_proto <= kProtoSPDY4MaximumVersion) {
continue;
}
const std::string proto = NextProtoToString(next_proto); const std::string proto = NextProtoToString(next_proto);
if (proto.size() > 255) { if (proto.size() > 255) {
LOG(WARNING) << "Ignoring overlong NPN/ALPN protocol: " << proto; LOG(WARNING) << "Ignoring overlong NPN/ALPN protocol: " << proto;
......
...@@ -209,10 +209,23 @@ class NET_EXPORT SSLClientSocket : public SSLSocket { ...@@ -209,10 +209,23 @@ class NET_EXPORT SSLClientSocket : public SSLSocket {
const SSLConfig& ssl_config, const SSLConfig& ssl_config,
ChannelIDService* channel_id_service); ChannelIDService* channel_id_service);
// Determine if there is at least one enabled cipher suite that satisfies
// Section 9.2 of the HTTP/2 specification. Note that the server might still
// pick an inadequate cipher suite.
static bool HasCipherAdequateForHTTP2(
const std::vector<uint16>& cipher_suites);
// Determine if the TLS version required by Section 9.2 of the HTTP/2
// specification is enabled. Note that the server might still pick an
// inadequate TLS version.
static bool IsTLSVersionAdequateForHTTP2(const SSLConfig& ssl_config);
// Serializes |next_protos| in the wire format for ALPN: protocols are listed // Serializes |next_protos| in the wire format for ALPN: protocols are listed
// in order, each prefixed by a one-byte length. // in order, each prefixed by a one-byte length. Any HTTP/2 protocols in
// |next_protos| are ignored if |can_advertise_http2| is false.
static std::vector<uint8_t> SerializeNextProtos( static std::vector<uint8_t> SerializeNextProtos(
const NextProtoVector& next_protos); const NextProtoVector& next_protos,
bool can_advertise_http2);
// For unit testing only. // For unit testing only.
// Returns the unverified certificate chain as presented by server. // Returns the unverified certificate chain as presented by server.
......
...@@ -149,6 +149,14 @@ namespace net { ...@@ -149,6 +149,14 @@ namespace net {
} while (0) } while (0)
#endif #endif
#if !defined(CKM_AES_GCM)
#define CKM_AES_GCM 0x00001087
#endif
#if !defined(CKM_NSS_CHACHA20_POLY1305)
#define CKM_NSS_CHACHA20_POLY1305 (CKM_NSS + 26)
#endif
namespace { namespace {
// SSL plaintext fragments are shorter than 16KB. Although the record layer // SSL plaintext fragments are shorter than 16KB. Although the record layer
...@@ -973,8 +981,16 @@ bool SSLClientSocketNSS::Core::Init(PRFileDesc* socket, ...@@ -973,8 +981,16 @@ bool SSLClientSocketNSS::Core::Init(PRFileDesc* socket,
SECStatus rv = SECSuccess; SECStatus rv = SECSuccess;
if (!ssl_config_.next_protos.empty()) { if (!ssl_config_.next_protos.empty()) {
// TODO(bnc): Check ssl_config_.disabled_cipher_suites.
const bool adequate_encryption =
PK11_TokenExists(CKM_AES_GCM) ||
PK11_TokenExists(CKM_NSS_CHACHA20_POLY1305);
const bool adequate_key_agreement = PK11_TokenExists(CKM_DH_PKCS_DERIVE) ||
PK11_TokenExists(CKM_ECDH1_DERIVE);
std::vector<uint8_t> wire_protos = std::vector<uint8_t> wire_protos =
SerializeNextProtos(ssl_config_.next_protos); SerializeNextProtos(ssl_config_.next_protos,
adequate_encryption && adequate_key_agreement &&
IsTLSVersionAdequateForHTTP2(ssl_config_));
rv = SSL_SetNextProtoNego( rv = SSL_SetNextProtoNego(
nss_fd_, wire_protos.empty() ? NULL : &wire_protos[0], nss_fd_, wire_protos.empty() ? NULL : &wire_protos[0],
wire_protos.size()); wire_protos.size());
......
...@@ -788,8 +788,8 @@ int SSLClientSocketOpenSSL::Init() { ...@@ -788,8 +788,8 @@ int SSLClientSocketOpenSSL::Init() {
// disabled by default. Note that !SHA256 and !SHA384 only remove HMAC-SHA256 // disabled by default. Note that !SHA256 and !SHA384 only remove HMAC-SHA256
// and HMAC-SHA384 cipher suites, not GCM cipher suites with SHA256 or SHA384 // and HMAC-SHA384 cipher suites, not GCM cipher suites with SHA256 or SHA384
// as the handshake hash. // as the handshake hash.
std::string command("DEFAULT:!NULL:!aNULL:!IDEA:!FZA:!SRP:!SHA256:!SHA384:" std::string command(
"!aECDH:!AESGCM+AES256"); "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK");
// Walk through all the installed ciphers, seeing if any need to be // Walk through all the installed ciphers, seeing if any need to be
// appended to the cipher removal |command|. // appended to the cipher removal |command|.
for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) { for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) {
...@@ -838,8 +838,20 @@ int SSLClientSocketOpenSSL::Init() { ...@@ -838,8 +838,20 @@ int SSLClientSocketOpenSSL::Init() {
} }
if (!ssl_config_.next_protos.empty()) { if (!ssl_config_.next_protos.empty()) {
// Get list of ciphers that are enabled.
STACK_OF(SSL_CIPHER)* enabled_ciphers = SSL_get_ciphers(ssl_);
DCHECK(enabled_ciphers);
std::vector<uint16> enabled_ciphers_vector;
for (size_t i = 0; i < sk_SSL_CIPHER_num(enabled_ciphers); ++i) {
const SSL_CIPHER* cipher = sk_SSL_CIPHER_value(enabled_ciphers, i);
const uint16 id = static_cast<uint16>(SSL_CIPHER_get_id(cipher));
enabled_ciphers_vector.push_back(id);
}
std::vector<uint8_t> wire_protos = std::vector<uint8_t> wire_protos =
SerializeNextProtos(ssl_config_.next_protos); SerializeNextProtos(ssl_config_.next_protos,
HasCipherAdequateForHTTP2(enabled_ciphers_vector) &&
IsTLSVersionAdequateForHTTP2(ssl_config_));
SSL_set_alpn_protos(ssl_, wire_protos.empty() ? NULL : &wire_protos[0], SSL_set_alpn_protos(ssl_, wire_protos.empty() ? NULL : &wire_protos[0],
wire_protos.size()); wire_protos.size());
} }
......
...@@ -2356,7 +2356,7 @@ TEST(SSLClientSocket, SerializeNextProtos) { ...@@ -2356,7 +2356,7 @@ TEST(SSLClientSocket, SerializeNextProtos) {
next_protos.push_back(kProtoHTTP11); next_protos.push_back(kProtoHTTP11);
next_protos.push_back(kProtoSPDY31); next_protos.push_back(kProtoSPDY31);
static std::vector<uint8_t> serialized = static std::vector<uint8_t> serialized =
SSLClientSocket::SerializeNextProtos(next_protos); SSLClientSocket::SerializeNextProtos(next_protos, true);
ASSERT_EQ(18u, serialized.size()); ASSERT_EQ(18u, serialized.size());
EXPECT_EQ(8, serialized[0]); // length("http/1.1") EXPECT_EQ(8, serialized[0]); // length("http/1.1")
EXPECT_EQ('h', serialized[1]); EXPECT_EQ('h', serialized[1]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment