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}
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index e39920e4..405c88c 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -26,6 +26,7 @@
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_pool_manager_impl.h"
#include "net/socket/next_proto.h"
+#include "net/socket/ssl_client_socket.h"
#include "net/spdy/hpack_huffman_aggregator.h"
#include "net/spdy/spdy_session_pool.h"
diff --git a/net/net.gypi b/net/net.gypi
index 1350edd..5ca70f7 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -153,6 +153,8 @@
'ssl/signed_certificate_timestamp_and_status.h',
'ssl/ssl_cert_request_info.cc',
'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.h',
'ssl/ssl_client_cert_type.h',
@@ -1107,8 +1109,6 @@
'ssl/client_cert_store_nss.h',
'ssl/client_cert_store_win.cc',
'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.h',
'third_party/mozilla_security_manager/nsKeygenHandler.cpp',
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc
index e0a6d8d..2ea403f7 100644
--- a/net/socket/ssl_client_socket.cc
+++ b/net/socket/ssl_client_socket.cc
@@ -11,6 +11,7 @@
#include "net/base/connection_type_histograms.h"
#include "net/base/host_port_pair.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_connection_status_flags.h"
@@ -234,10 +235,31 @@
}
// 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
std::vector<uint8_t> SSLClientSocket::SerializeNextProtos(
- const NextProtoVector& next_protos) {
+ const NextProtoVector& next_protos,
+ bool can_advertise_http2) {
std::vector<uint8_t> wire_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);
if (proto.size() > 255) {
LOG(WARNING) << "Ignoring overlong NPN/ALPN protocol: " << proto;
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index d72dee5..46461df 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -209,10 +209,23 @@
const SSLConfig& ssl_config,
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
- // 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(
- const NextProtoVector& next_protos);
+ const NextProtoVector& next_protos,
+ bool can_advertise_http2);
// For unit testing only.
// Returns the unverified certificate chain as presented by server.
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 3651e8d..5e33e12 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -149,6 +149,14 @@
} while (0)
#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 {
// SSL plaintext fragments are shorter than 16KB. Although the record layer
@@ -973,8 +981,16 @@
SECStatus rv = SECSuccess;
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 =
- SerializeNextProtos(ssl_config_.next_protos);
+ SerializeNextProtos(ssl_config_.next_protos,
+ adequate_encryption && adequate_key_agreement &&
+ IsTLSVersionAdequateForHTTP2(ssl_config_));
rv = SSL_SetNextProtoNego(
nss_fd_, wire_protos.empty() ? NULL : &wire_protos[0],
wire_protos.size());
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index 5415755f..c78a9742 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -788,8 +788,8 @@
// 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
// as the handshake hash.
- std::string command("DEFAULT:!NULL:!aNULL:!IDEA:!FZA:!SRP:!SHA256:!SHA384:"
- "!aECDH:!AESGCM+AES256");
+ std::string command(
+ "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK");
// Walk through all the installed ciphers, seeing if any need to be
// appended to the cipher removal |command|.
for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) {
@@ -838,8 +838,20 @@
}
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 =
- 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],
wire_protos.size());
}
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index d650f627df..d14f1fd 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -2356,7 +2356,7 @@
next_protos.push_back(kProtoHTTP11);
next_protos.push_back(kProtoSPDY31);
static std::vector<uint8_t> serialized =
- SSLClientSocket::SerializeNextProtos(next_protos);
+ SSLClientSocket::SerializeNextProtos(next_protos, true);
ASSERT_EQ(18u, serialized.size());
EXPECT_EQ(8, serialized[0]); // length("http/1.1")
EXPECT_EQ('h', serialized[1]);