diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-26 16:22:17 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-26 16:22:17 +0000 |
commit | 80c75f6850ba88b4a2305663c63069fec9d7580a (patch) | |
tree | e9aef636347e0f10a7ff7356cb759976c665f073 /net | |
parent | 0969d1b4edbc89ee74421a20b2fb4f78b7e43448 (diff) | |
download | chromium_src-80c75f6850ba88b4a2305663c63069fec9d7580a.zip chromium_src-80c75f6850ba88b4a2305663c63069fec9d7580a.tar.gz chromium_src-80c75f6850ba88b4a2305663c63069fec9d7580a.tar.bz2 |
Use TLS 1.1.
Enable SSL 3.0 ~ TLS 1.1 by default. If the SSLClientSocket class does
not support TLS 1.1, enable SSL 3.0 ~ TLS 1.0 by default.
TLS intolerant servers are handled by falling back to the next lower
protocol version at a time, rather than falling back to SSL 3.0 directly.
In the SSLConfig structure, replace the ssl3_enabled and tls1_enabled
members by version_min and version_max to allow multiple, contiguous
protocol versions to be enabled, and rename the ssl3_fallback member to
version_fallback.
The preferences prefs::kSSL3Enabled and prefs::kTLS1Enabled are not
yet removed. Generalize prefs::kTLS1Enabled to mean enabling or
disabling all TLS versions.
R=agl@chromium.org,rsleevi@chromium.org
BUG=126340
TEST=net_unittests --gtest_filter=HTTPSRequestTest.TLSv1Fallback
Review URL: https://chromiumcodereview.appspot.com/10377022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139204 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/ssl_config_service.cc | 44 | ||||
-rw-r--r-- | net/base/ssl_config_service.h | 38 | ||||
-rw-r--r-- | net/base/ssl_config_service_unittest.cc | 12 | ||||
-rw-r--r-- | net/base/ssl_connection_status_flags.h | 6 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 46 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy2_unittest.cc | 41 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy3_unittest.cc | 41 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.cc | 26 | ||||
-rw-r--r-- | net/socket/client_socket_factory.cc | 10 | ||||
-rw-r--r-- | net/socket/client_socket_pool_manager.cc | 34 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_mac.cc | 16 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 26 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_openssl.cc | 26 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_win.cc | 17 | ||||
-rw-r--r-- | net/socket/ssl_server_socket_nss.cc | 19 | ||||
-rw-r--r-- | net/socket/ssl_server_socket_unittest.cc | 4 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 40 |
17 files changed, 348 insertions, 98 deletions
diff --git a/net/base/ssl_config_service.cc b/net/base/ssl_config_service.cc index 602bb21..3a79540 100644 --- a/net/base/ssl_config_service.cc +++ b/net/base/ssl_config_service.cc @@ -10,22 +10,39 @@ #include "net/base/crl_set.h" #include "net/base/ssl_config_service_defaults.h" +#if defined(USE_OPENSSL) +#include <openssl/ssl.h> +#endif + namespace net { +static uint16 g_default_version_min = SSL_PROTOCOL_VERSION_SSL3; + +static uint16 g_default_version_max = +#if defined(USE_OPENSSL) +#if defined(SSL_OP_NO_TLSv1_1) + SSL_PROTOCOL_VERSION_TLS1_1; +#else + SSL_PROTOCOL_VERSION_TLS1; +#endif +#else + SSL_PROTOCOL_VERSION_TLS1_1; +#endif + SSLConfig::CertAndStatus::CertAndStatus() : cert_status(0) {} SSLConfig::CertAndStatus::~CertAndStatus() {} SSLConfig::SSLConfig() : rev_checking_enabled(false), - ssl3_enabled(true), - tls1_enabled(true), + version_min(g_default_version_min), + version_max(g_default_version_max), cached_info_enabled(false), domain_bound_certs_enabled(false), false_start_enabled(true), send_client_cert(false), verify_ev_cert(false), - ssl3_fallback(false), + version_fallback(false), cert_io_enabled(true) { } @@ -102,6 +119,21 @@ bool SSLConfigService::cached_info_enabled() { } // static +uint16 SSLConfigService::default_version_min() { + return g_default_version_min; +} + +// static +void SSLConfigService::SetDefaultVersionMax(uint16 version_max) { + g_default_version_max = version_max; +} + +// static +uint16 SSLConfigService::default_version_max() { + return g_default_version_max; +} + +// static void SSLConfigService::EnableDomainBoundCertsTrial() { g_domain_bound_certs_trial = true; } @@ -128,8 +160,8 @@ void SSLConfigService::ProcessConfigUpdate(const SSLConfig& orig_config, const SSLConfig& new_config) { bool config_changed = (orig_config.rev_checking_enabled != new_config.rev_checking_enabled) || - (orig_config.ssl3_enabled != new_config.ssl3_enabled) || - (orig_config.tls1_enabled != new_config.tls1_enabled) || + (orig_config.version_min != new_config.version_min) || + (orig_config.version_max != new_config.version_max) || (orig_config.disabled_cipher_suites != new_config.disabled_cipher_suites) || (orig_config.domain_bound_certs_enabled != @@ -148,7 +180,7 @@ bool SSLConfigService::IsSNIAvailable(SSLConfigService* service) { SSLConfig ssl_config; service->GetSSLConfig(&ssl_config); - return ssl_config.tls1_enabled; + return ssl_config.version_max >= SSL_PROTOCOL_VERSION_TLS1; } } // namespace net diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h index f977370..37f66d2 100644 --- a/net/base/ssl_config_service.h +++ b/net/base/ssl_config_service.h @@ -19,10 +19,24 @@ namespace net { +// Various TLS/SSL ProtocolVersion values encoded as uint16 +// struct { +// uint8 major; +// uint8 minor; +// } ProtocolVersion; +// The most significant byte is |major|, and the least significant byte +// is |minor|. +enum { + SSL_PROTOCOL_VERSION_SSL3 = 0x0300, + SSL_PROTOCOL_VERSION_TLS1 = 0x0301, + SSL_PROTOCOL_VERSION_TLS1_1 = 0x0302, + SSL_PROTOCOL_VERSION_TLS1_2 = 0x0303, +}; + // A collection of SSL-related configuration settings. struct NET_EXPORT SSLConfig { // Default to revocation checking. - // Default to SSL 3.0 on and TLS 1.0 on. + // Default to SSL 3.0 ~ default_version_max() on. SSLConfig(); ~SSLConfig(); @@ -43,9 +57,13 @@ struct NET_EXPORT SSLConfig { // cached revocation information will be considered. bool rev_checking_enabled; - // SSL 2.0 is not supported. - bool ssl3_enabled; // True if SSL 3.0 is enabled. - bool tls1_enabled; // True if TLS 1.0 is enabled. + // The minimum and maximum protocol versions that are enabled. + // SSL 3.0 is 0x0300, TLS 1.0 is 0x0301, TLS 1.1 is 0x0302, and so on. + // (Use the SSL_PROTOCOL_VERSION_xxx enumerators defined above.) + // SSL 2.0 is not supported. If version_max < version_min, it means no + // protocol versions are enabled. + uint16 version_min; + uint16 version_max; // Presorted list of cipher suites which should be explicitly prevented from // being used in addition to those disabled by the net built-in policy. @@ -96,8 +114,9 @@ struct NET_EXPORT SSLConfig { bool verify_ev_cert; // True if we should verify the certificate for EV. - bool ssl3_fallback; // True if we are falling back to SSL 3.0 (one still - // needs to clear tls1_enabled). + bool version_fallback; // True if we are falling back to an older protocol + // version (one still needs to decrement + // version_max). // If cert_io_enabled is false, then certificate verification will not // result in additional HTTP requests. (For example: to fetch missing @@ -154,6 +173,13 @@ class NET_EXPORT SSLConfigService static void EnableCachedInfo(); static bool cached_info_enabled(); + // Gets the default minimum protocol version. + static uint16 default_version_min(); + + // Sets and gets the default maximum protocol version. + static void SetDefaultVersionMax(uint16 version_max); + static uint16 default_version_max(); + // Force domain bound cert support to be enabled. static void EnableDomainBoundCertsTrial(); diff --git a/net/base/ssl_config_service_unittest.cc b/net/base/ssl_config_service_unittest.cc index 2ac2a9b..7c28b61 100644 --- a/net/base/ssl_config_service_unittest.cc +++ b/net/base/ssl_config_service_unittest.cc @@ -50,8 +50,8 @@ class MockSSLConfigServiceObserver : public SSLConfigService::Observer { TEST(SSLConfigServiceTest, NoChangesWontNotifyObservers) { SSLConfig initial_config; initial_config.rev_checking_enabled = true; - initial_config.ssl3_enabled = true; - initial_config.tls1_enabled = true; + initial_config.version_min = SSL_PROTOCOL_VERSION_SSL3; + initial_config.version_max = SSL_PROTOCOL_VERSION_TLS1_1; scoped_refptr<MockSSLConfigService> mock_service( new MockSSLConfigService(initial_config)); @@ -67,8 +67,8 @@ TEST(SSLConfigServiceTest, NoChangesWontNotifyObservers) { TEST(SSLConfigServiceTest, ConfigUpdatesNotifyObservers) { SSLConfig initial_config; initial_config.rev_checking_enabled = true; - initial_config.ssl3_enabled = true; - initial_config.tls1_enabled = true; + initial_config.version_min = SSL_PROTOCOL_VERSION_SSL3; + initial_config.version_max = SSL_PROTOCOL_VERSION_TLS1_1; scoped_refptr<MockSSLConfigService> mock_service( new MockSSLConfigService(initial_config)); @@ -80,11 +80,11 @@ TEST(SSLConfigServiceTest, ConfigUpdatesNotifyObservers) { EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1); mock_service->SetSSLConfig(initial_config); - initial_config.ssl3_enabled = false; + initial_config.version_min = SSL_PROTOCOL_VERSION_TLS1; EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1); mock_service->SetSSLConfig(initial_config); - initial_config.tls1_enabled = false; + initial_config.version_max = SSL_PROTOCOL_VERSION_SSL3; EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1); mock_service->SetSSLConfig(initial_config); diff --git a/net/base/ssl_connection_status_flags.h b/net/base/ssl_connection_status_flags.h index 9596f00..bf349ce 100644 --- a/net/base/ssl_connection_status_flags.h +++ b/net/base/ssl_connection_status_flags.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,8 +18,8 @@ enum { SSL_CONNECTION_COMPRESSION_SHIFT = 16, SSL_CONNECTION_COMPRESSION_MASK = 3, - // We fell back to SSLv3 for this connection. - SSL_CONNECTION_SSL3_FALLBACK = 1 << 18, + // We fell back to an older protocol version for this connection. + SSL_CONNECTION_VERSION_FALLBACK = 1 << 18, // The server doesn't support the renegotiation_info extension. If this bit // is not set then either the extension isn't supported, or we don't have any diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 7b52766..8f94732 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -1162,16 +1162,39 @@ int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { switch (error) { case ERR_SSL_PROTOCOL_ERROR: case ERR_SSL_VERSION_OR_CIPHER_MISMATCH: + if (server_ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 && + server_ssl_config_.version_max > server_ssl_config_.version_min) { + // This could be a TLS-intolerant server or a server that chose a + // cipher suite defined only for higher protocol versions (such as + // an SSL 3.0 server that chose a TLS-only cipher suite). Fall + // back to the next lower version and retry. + // NOTE: if the SSLClientSocket class doesn't support TLS 1.1, + // specifying TLS 1.1 in version_max will result in a TLS 1.0 + // handshake, so falling back from TLS 1.1 to TLS 1.0 will simply + // repeat the TLS 1.0 handshake. To avoid this problem, the default + // version_max should match the maximum protocol version supported + // by the SSLClientSocket class. + LOG(WARNING) << "Falling back one version because host is " + "TLS intolerant: " << GetHostAndPort(request_->url) + << " error: " << error; + server_ssl_config_.version_max--; + server_ssl_config_.version_fallback = true; + ResetConnectionAndRequestForResend(); + error = OK; + } + break; case ERR_SSL_DECOMPRESSION_FAILURE_ALERT: case ERR_SSL_BAD_RECORD_MAC_ALERT: - if (server_ssl_config_.tls1_enabled) { - // This could be a TLS-intolerant server, an SSL 3.0 server that - // chose a TLS-only cipher suite or a server with buggy DEFLATE - // support. Turn off TLS 1.0, DEFLATE support and retry. - LOG(WARNING) << "Falling back to SSLv3 because host is TLS intolerant: " - << GetHostAndPort(request_->url); - server_ssl_config_.tls1_enabled = false; - server_ssl_config_.ssl3_fallback = true; + if (server_ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 && + server_ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3) { + // This could be a server with buggy DEFLATE support. Turn off TLS, + // DEFLATE support and retry. + // TODO(wtc): turn off DEFLATE support only. Do not tie it to TLS. + LOG(WARNING) << "Falling back to SSLv3 because host has buggy TLS " + "compression support: " + << GetHostAndPort(request_->url) << " error: " << error; + server_ssl_config_.version_max = SSL_PROTOCOL_VERSION_SSL3; + server_ssl_config_.version_fallback = true; ResetConnectionAndRequestForResend(); error = OK; } @@ -1188,10 +1211,9 @@ int HttpNetworkTransaction::HandleIOError(int error) { // SSL errors may happen at any time during the stream and indicate issues // with the underlying connection. Because the peer may request // renegotiation at any time, check and handle any possible SSL handshake - // related errors. In addition to renegotiation, TLS False/Snap Start may - // cause SSL handshake errors to be delayed until the first or second Write - // (Snap Start) or the first Read (False & Snap Start) on the underlying - // connection. + // related errors. In addition to renegotiation, TLS False Start may cause + // SSL handshake errors (specifically servers with buggy DEFLATE support) + // to be delayed until the first Read on the underlying connection. error = HandleSSLHandshakeError(error); switch (error) { diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc index 27470e0..fdb18b0 100644 --- a/net/http/http_network_transaction_spdy2_unittest.cc +++ b/net/http/http_network_transaction_spdy2_unittest.cc @@ -9101,8 +9101,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, // [ssl_]data3 contains the data for the third SSL handshake. When a // connection to a server fails during an SSL handshake, - // HttpNetworkTransaction will attempt to fallback to SSLv3 if the initial - // connection was attempted with TLSv1. This is transparent to the caller + // HttpNetworkTransaction will attempt to fallback to TLSv1 if the previous + // connection was attempted with TLSv1.1. This is transparent to the caller // of the HttpNetworkTransaction. Because this test failure is due to // requiring a client certificate, this fallback handshake should also // fail. @@ -9112,6 +9112,19 @@ TEST_F(HttpNetworkTransactionSpdy2Test, net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data3); + // [ssl_]data4 contains the data for the fourth SSL handshake. When a + // connection to a server fails during an SSL handshake, + // HttpNetworkTransaction will attempt to fallback to SSLv3 if the previous + // connection was attempted with TLSv1. This is transparent to the caller + // of the HttpNetworkTransaction. Because this test failure is due to + // requiring a client certificate, this fallback handshake should also + // fail. + SSLSocketDataProvider ssl_data4(ASYNC, net::ERR_SSL_PROTOCOL_ERROR); + ssl_data4.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data4); + net::StaticSocketDataProvider data4(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data4); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); @@ -9140,8 +9153,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, ASSERT_EQ(NULL, client_cert.get()); // Restart the handshake. This will consume ssl_data2, which fails, and - // then consume ssl_data3, which should also fail. The result code is - // checked against what ssl_data3 should return. + // then consume ssl_data3 and ssl_data4, both of which should also fail. + // The result code is checked against what ssl_data4 should return. rv = callback.WaitForResult(); ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); @@ -9208,8 +9221,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, session_deps.socket_factory.AddSocketDataProvider(&data2); // As described in ClientAuthCertCache_Direct_NoFalseStart, [ssl_]data3 is - // the data for the SSL handshake once the TLSv1 connection falls back to - // SSLv3. It has the same behaviour as [ssl_]data2. + // the data for the SSL handshake once the TLSv1.1 connection falls back to + // TLSv1. It has the same behaviour as [ssl_]data2. SSLSocketDataProvider ssl_data3(ASYNC, net::OK); ssl_data3.cert_request_info = cert_request.get(); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); @@ -9217,6 +9230,15 @@ TEST_F(HttpNetworkTransactionSpdy2Test, data2_reads, arraysize(data2_reads), NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data3); + // [ssl_]data4 is the data for the SSL handshake once the TLSv1 connection + // falls back to SSLv3. It has the same behaviour as [ssl_]data2. + SSLSocketDataProvider ssl_data4(ASYNC, net::OK); + ssl_data4.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data4); + net::StaticSocketDataProvider data4( + data2_reads, arraysize(data2_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data4); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); @@ -9246,8 +9268,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, // Restart the handshake. This will consume ssl_data2, which fails, and - // then consume ssl_data3, which should also fail. The result code is - // checked against what ssl_data3 should return. + // then consume ssl_data3 and ssl_data4, both of which should also fail. + // The result code is checked against what ssl_data4 should return. rv = callback.WaitForResult(); ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); @@ -9290,11 +9312,14 @@ TEST_F(HttpNetworkTransactionSpdy2Test, ClientAuthCertCache_Proxy_Fail) { net::StaticSocketDataProvider data2(NULL, 0, NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data2); + // TODO(wtc): find out why this unit test doesn't need [ssl_]data3. +#if 0 SSLSocketDataProvider ssl_data3(ASYNC, net::ERR_SSL_PROTOCOL_ERROR); ssl_data3.cert_request_info = cert_request.get(); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data3); +#endif net::HttpRequestInfo requests[2]; requests[0].url = GURL("https://www.example.com/"); diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc index 6f7468d..81051b2 100644 --- a/net/http/http_network_transaction_spdy3_unittest.cc +++ b/net/http/http_network_transaction_spdy3_unittest.cc @@ -9100,8 +9100,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, // [ssl_]data3 contains the data for the third SSL handshake. When a // connection to a server fails during an SSL handshake, - // HttpNetworkTransaction will attempt to fallback to SSLv3 if the initial - // connection was attempted with TLSv1. This is transparent to the caller + // HttpNetworkTransaction will attempt to fallback to TLSv1 if the previous + // connection was attempted with TLSv1.1. This is transparent to the caller // of the HttpNetworkTransaction. Because this test failure is due to // requiring a client certificate, this fallback handshake should also // fail. @@ -9111,6 +9111,19 @@ TEST_F(HttpNetworkTransactionSpdy3Test, net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data3); + // [ssl_]data4 contains the data for the fourth SSL handshake. When a + // connection to a server fails during an SSL handshake, + // HttpNetworkTransaction will attempt to fallback to SSLv3 if the previous + // connection was attempted with TLSv1. This is transparent to the caller + // of the HttpNetworkTransaction. Because this test failure is due to + // requiring a client certificate, this fallback handshake should also + // fail. + SSLSocketDataProvider ssl_data4(ASYNC, net::ERR_SSL_PROTOCOL_ERROR); + ssl_data4.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data4); + net::StaticSocketDataProvider data4(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data4); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); @@ -9139,8 +9152,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, ASSERT_EQ(NULL, client_cert.get()); // Restart the handshake. This will consume ssl_data2, which fails, and - // then consume ssl_data3, which should also fail. The result code is - // checked against what ssl_data3 should return. + // then consume ssl_data3 and ssl_data4, both of which should also fail. + // The result code is checked against what ssl_data4 should return. rv = callback.WaitForResult(); ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); @@ -9206,8 +9219,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, ClientAuthCertCache_Direct_FalseStart) { session_deps.socket_factory.AddSocketDataProvider(&data2); // As described in ClientAuthCertCache_Direct_NoFalseStart, [ssl_]data3 is - // the data for the SSL handshake once the TLSv1 connection falls back to - // SSLv3. It has the same behaviour as [ssl_]data2. + // the data for the SSL handshake once the TLSv1.1 connection falls back to + // TLSv1. It has the same behaviour as [ssl_]data2. SSLSocketDataProvider ssl_data3(ASYNC, net::OK); ssl_data3.cert_request_info = cert_request.get(); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); @@ -9215,6 +9228,15 @@ TEST_F(HttpNetworkTransactionSpdy3Test, ClientAuthCertCache_Direct_FalseStart) { data2_reads, arraysize(data2_reads), NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data3); + // [ssl_]data4 is the data for the SSL handshake once the TLSv1 connection + // falls back to SSLv3. It has the same behaviour as [ssl_]data2. + SSLSocketDataProvider ssl_data4(ASYNC, net::OK); + ssl_data4.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data4); + net::StaticSocketDataProvider data4( + data2_reads, arraysize(data2_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data4); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); @@ -9244,8 +9266,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, ClientAuthCertCache_Direct_FalseStart) { // Restart the handshake. This will consume ssl_data2, which fails, and - // then consume ssl_data3, which should also fail. The result code is - // checked against what ssl_data3 should return. + // then consume ssl_data3 and ssl_data4, both of which should also fail. + // The result code is checked against what ssl_data4 should return. rv = callback.WaitForResult(); ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); @@ -9288,11 +9310,14 @@ TEST_F(HttpNetworkTransactionSpdy3Test, ClientAuthCertCache_Proxy_Fail) { net::StaticSocketDataProvider data2(NULL, 0, NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data2); + // TODO(wtc): find out why this unit test doesn't need [ssl_]data3. +#if 0 SSLSocketDataProvider ssl_data3(ASYNC, net::ERR_SSL_PROTOCOL_ERROR); ssl_data3.cert_request_info = cert_request.get(); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data3); +#endif net::HttpRequestInfo requests[2]; requests[0].url = GURL("https://www.example.com/"); diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index 7a2aa87..15cba9d 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -1071,8 +1071,30 @@ void HttpStreamFactoryImpl::Job::InitSSLConfig( ssl_config->false_start_enabled = false; } - UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLv3Fallback", - static_cast<int>(ssl_config->ssl3_fallback), 2); + enum { + FALLBACK_NONE = 0, // SSL version fallback did not occur. + FALLBACK_SSL3 = 1, // Fell back to SSL 3.0. + FALLBACK_TLS1 = 2, // Fell back to TLS 1.0. + FALLBACK_TLS1_1 = 3, // Fell back to TLS 1.1. + FALLBACK_MAX + }; + + int fallback = FALLBACK_NONE; + if (ssl_config->version_fallback) { + switch (ssl_config->version_max) { + case SSL_PROTOCOL_VERSION_SSL3: + fallback = FALLBACK_SSL3; + break; + case SSL_PROTOCOL_VERSION_TLS1: + fallback = FALLBACK_TLS1; + break; + case SSL_PROTOCOL_VERSION_TLS1_1: + fallback = FALLBACK_TLS1_1; + break; + } + } + UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLVersionFallback", + fallback, FALLBACK_MAX); if (request_info_.load_flags & LOAD_VERIFY_EV_CERT) ssl_config->verify_ev_cert = true; diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc index 60daef4..42f6d4f 100644 --- a/net/socket/client_socket_factory.cc +++ b/net/socket/client_socket_factory.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -134,6 +134,14 @@ ClientSocketFactory* ClientSocketFactory::GetDefaultFactory() { // static void ClientSocketFactory::UseSystemSSL() { g_use_system_ssl = true; + +#if defined(OS_WIN) + // Reflect the capability of SSLClientSocketWin. + SSLConfigService::SetDefaultVersionMax(SSL_PROTOCOL_VERSION_TLS1); +#elif defined(OS_MACOSX) + // Reflect the capability of SSLClientSocketMac. + SSLConfigService::SetDefaultVersionMax(SSL_PROTOCOL_VERSION_TLS1); +#endif } } // namespace net diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc index 20b02f4..70f1edb 100644 --- a/net/socket/client_socket_pool_manager.cc +++ b/net/socket/client_socket_pool_manager.cc @@ -112,11 +112,35 @@ int InitSocketPoolHelper(const GURL& request_url, std::string connection_group = origin_host_port.ToString(); DCHECK(!connection_group.empty()); if (using_ssl) { - std::string prefix; - if (ssl_config_for_origin.tls1_enabled) { - prefix = "ssl/"; - } else { - prefix = "sslv3/"; + // All connections in a group should use the same SSLConfig settings. + // Encode version_max in the connection group's name, unless it's the + // default version_max. (We want the common case to use the shortest + // encoding). A version_max of TLS 1.1 is encoded as "ssl(max:3.2)/" + // rather than "tlsv1.1/" because the actual protocol version, which + // is selected by the server, may not be TLS 1.1. Do not encode + // version_min in the connection group's name because version_min + // should be the same for all connections, whereas version_max may + // change for version fallbacks. + std::string prefix = "ssl/"; + if (ssl_config_for_origin.version_max != + SSLConfigService::default_version_max()) { + switch (ssl_config_for_origin.version_max) { + case SSL_PROTOCOL_VERSION_TLS1_2: + prefix = "ssl(max:3.3)/"; + break; + case SSL_PROTOCOL_VERSION_TLS1_1: + prefix = "ssl(max:3.2)/"; + break; + case SSL_PROTOCOL_VERSION_TLS1: + prefix = "ssl(max:3.1)/"; + break; + case SSL_PROTOCOL_VERSION_SSL3: + prefix = "sslv3/"; + break; + default: + CHECK(false); + break; + } } connection_group = prefix + connection_group; } diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc index f84f3fb..862ef52 100644 --- a/net/socket/ssl_client_socket_mac.cc +++ b/net/socket/ssl_client_socket_mac.cc @@ -738,8 +738,8 @@ void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) { SSL_CONNECTION_CIPHERSUITE_SHIFT; } - if (ssl_config_.ssl3_fallback) - ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK; + if (ssl_config_.version_fallback) + ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK; } void SSLClientSocketMac::GetSSLCertRequestInfo( @@ -812,15 +812,23 @@ int SSLClientSocketMac::InitializeSSLContext() { if (status) return NetErrorFromOSStatus(status); + // If ssl_config_.version_max > SSL_PROTOCOL_VERSION_TLS1, it means the + // SSLConfigService::SetDefaultVersionMax(SSL_PROTOCOL_VERSION_TLS1) call + // in ClientSocketFactory::UseSystemSSL() is not effective. + DCHECK_LE(ssl_config_.version_max, SSL_PROTOCOL_VERSION_TLS1); + + bool ssl3_enabled = (ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3); status = SSLSetProtocolVersionEnabled(ssl_context_, kSSLProtocol3, - ssl_config_.ssl3_enabled); + ssl3_enabled); if (status) return NetErrorFromOSStatus(status); + bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1); status = SSLSetProtocolVersionEnabled(ssl_context_, kTLSProtocol1, - ssl_config_.tls1_enabled); + tls1_enabled); if (status) return NetErrorFromOSStatus(status); diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index ed99bbf..d45b429 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -864,16 +864,13 @@ int SSLClientSocketNSS::InitializeSSLOptions() { return ERR_UNEXPECTED; } - rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled); + SSLVersionRange version_range; + version_range.min = ssl_config_.version_min; + version_range.max = ssl_config_.version_max; + rv = SSL_VersionRangeSet(nss_fd_, &version_range); if (rv != SECSuccess) { - LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_SSL3"); - return ERR_UNEXPECTED; - } - - rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled); - if (rv != SECSuccess) { - LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_TLS"); - return ERR_UNEXPECTED; + LogFailedNSSFunction(net_log_, "SSL_VersionRangeSet", ""); + return ERR_NO_SSL_VERSIONS_ENABLED; } for (std::vector<uint16>::const_iterator it = @@ -900,7 +897,8 @@ int SSLClientSocketNSS::InitializeSSLOptions() { // is advertised. Thus, if TLS is disabled (probably because we are doing // SSLv3 fallback), we disable DEFLATE also. // See http://crbug.com/31628 - rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_DEFLATE, ssl_config_.tls1_enabled); + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_DEFLATE, + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1); if (rv != SECSuccess) LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_DEFLATE"); #endif @@ -1135,8 +1133,8 @@ void SSLClientSocketNSS::UpdateConnectionStatus() { } #endif - if (ssl_config_.ssl3_fallback) - ssl_connection_status_ |= SSL_CONNECTION_SSL3_FALLBACK; + if (ssl_config_.version_fallback) + ssl_connection_status_ |= SSL_CONNECTION_VERSION_FALLBACK; } void SSLClientSocketNSS::DoReadCallback(int rv) { @@ -1759,7 +1757,9 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) { IsCertStatusMinorError(cert_status))) && server_cert_verify_result_->is_issued_by_known_root && transport_security_state_) { - bool sni_available = ssl_config_.tls1_enabled || ssl_config_.ssl3_fallback; + bool sni_available = + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 || + ssl_config_.version_fallback; const std::string& host = host_and_port_.host(); TransportSecurityState::DomainState domain_state; diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index 55f9fbf..ba52ec0 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc @@ -467,13 +467,29 @@ bool SSLClientSocketOpenSSL::Init() { // set everything we care about to an absolute value. SslSetClearMask options; options.ConfigureFlag(SSL_OP_NO_SSLv2, true); - options.ConfigureFlag(SSL_OP_NO_SSLv3, !ssl_config_.ssl3_enabled); - options.ConfigureFlag(SSL_OP_NO_TLSv1, !ssl_config_.tls1_enabled); + bool ssl3_enabled = (ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3); + options.ConfigureFlag(SSL_OP_NO_SSLv3, !ssl3_enabled); + bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1); + options.ConfigureFlag(SSL_OP_NO_TLSv1, !tls1_enabled); +#if defined(SSL_OP_NO_TLSv1_1) + bool tls1_1_enabled = + (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_1 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_1); + options.ConfigureFlag(SSL_OP_NO_TLSv1_1, !tls1_1_enabled); +#endif +#if defined(SSL_OP_NO_TLSv1_2) + bool tls1_2_enabled = + (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_2 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_2); + options.ConfigureFlag(SSL_OP_NO_TLSv1_2, !tls1_2_enabled); +#endif #if defined(SSL_OP_NO_COMPRESSION) // If TLS was disabled also disable compression, to provide maximum site // compatibility in the case of protocol fallback. See http://crbug.com/31628 - options.ConfigureFlag(SSL_OP_NO_COMPRESSION, !ssl_config_.tls1_enabled); + options.ConfigureFlag(SSL_OP_NO_COMPRESSION, + ssl_config_.version_max < SSL_PROTOCOL_VERSION_TLS1); #endif // TODO(joth): Set this conditionally, see http://crbug.com/55410 @@ -601,8 +617,8 @@ void SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported", implicit_cast<int>(peer_supports_renego_ext), 2); - if (ssl_config_.ssl3_fallback) - ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK; + if (ssl_config_.version_fallback) + ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK; DVLOG(3) << "Encoded connection status: cipher suite = " << SSLConnectionStatusToCipherSuite(ssl_info->connection_status) diff --git a/net/socket/ssl_client_socket_win.cc b/net/socket/ssl_client_socket_win.cc index 1442ad6..8728532 100644 --- a/net/socket/ssl_client_socket_win.cc +++ b/net/socket/ssl_client_socket_win.cc @@ -116,6 +116,7 @@ static int MapSecurityError(SECURITY_STATUS err) { // A bitmask consisting of these bit flags encodes which versions of the SSL // protocol (SSL 3.0 and TLS 1.0) are enabled. +// TODO(wtc): support TLS 1.1 and TLS 1.2 on Windows Vista and later. enum { SSL3 = 1 << 0, TLS1 = 1 << 1, @@ -423,6 +424,8 @@ void SSLClientSocketWin::GetSSLInfo(SSLInfo* ssl_info) { // dwExchStrength and dwHashStrength. dwExchStrength needs to be // normalized. ssl_info->security_bits = connection_info.dwCipherStrength; + // TODO(wtc): connection_info.dwProtocol is the negotiated version. + // Save it in ssl_info->connection_status. } // SecPkgContext_CipherInfo comes from CNG and is available on Vista or // later only. On XP, the next QueryContextAttributes call fails with @@ -442,8 +445,8 @@ void SSLClientSocketWin::GetSSLInfo(SSLInfo* ssl_info) { // any field related to the compression method. } - if (ssl_config_.ssl3_fallback) - ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK; + if (ssl_config_.version_fallback) + ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK; } void SSLClientSocketWin::GetSSLCertRequestInfo( @@ -585,11 +588,17 @@ int SSLClientSocketWin::Connect(const CompletionCallback& callback) { } int SSLClientSocketWin::InitializeSSLContext() { + // If ssl_config_.version_max > SSL_PROTOCOL_VERSION_TLS1, it means the + // SSLConfigService::SetDefaultVersionMax(SSL_PROTOCOL_VERSION_TLS1) call + // in ClientSocketFactory::UseSystemSSL() is not effective. + DCHECK_LE(ssl_config_.version_max, SSL_PROTOCOL_VERSION_TLS1); int ssl_version_mask = 0; - if (ssl_config_.ssl3_enabled) + if (ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3) ssl_version_mask |= SSL3; - if (ssl_config_.tls1_enabled) + if (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1) { ssl_version_mask |= TLS1; + } // If we pass 0 to GetCredHandle, we will let Schannel select the protocols, // rather than enabling no protocols. So we have to fail here. if (ssl_version_mask == 0) diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc index 9ba0aaa..76d5559 100644 --- a/net/socket/ssl_server_socket_nss.cc +++ b/net/socket/ssl_server_socket_nss.cc @@ -68,8 +68,8 @@ SSLServerSocketNSS::SSLServerSocketNSS( next_handshake_state_(STATE_NONE), completed_handshake_(false) { ssl_config_.false_start_enabled = false; - ssl_config_.ssl3_enabled = true; - ssl_config_.tls1_enabled = true; + ssl_config_.version_min = SSL_PROTOCOL_VERSION_SSL3; + ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1_1; // TODO(hclam): Need a better way to clone a key. std::vector<uint8> key_bytes; @@ -284,16 +284,13 @@ int SSLServerSocketNSS::InitializeSSLOptions() { return ERR_UNEXPECTED; } - rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, PR_TRUE); + SSLVersionRange version_range; + version_range.min = ssl_config_.version_min; + version_range.max = ssl_config_.version_max; + rv = SSL_VersionRangeSet(nss_fd_, &version_range); if (rv != SECSuccess) { - LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_SSL3"); - return ERR_UNEXPECTED; - } - - rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled); - if (rv != SECSuccess) { - LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_TLS"); - return ERR_UNEXPECTED; + LogFailedNSSFunction(net_log_, "SSL_VersionRangeSet", ""); + return ERR_NO_SSL_VERSIONS_ENABLED; } for (std::vector<uint16>::const_iterator it = diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc index 53af569..806a598 100644 --- a/net/socket/ssl_server_socket_unittest.cc +++ b/net/socket/ssl_server_socket_unittest.cc @@ -326,8 +326,8 @@ class SSLServerSocketTest : public PlatformTest { ssl_config.cached_info_enabled = false; ssl_config.false_start_enabled = false; ssl_config.domain_bound_certs_enabled = false; - ssl_config.ssl3_enabled = true; - ssl_config.tls1_enabled = true; + ssl_config.version_min = SSL_PROTOCOL_VERSION_SSL3; + ssl_config.version_max = SSL_PROTOCOL_VERSION_TLS1_1; // Certificate provided by the host doesn't need authority. net::SSLConfig::CertAndStatus cert_and_status; diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index aae4b4e..e6b7658 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -1751,9 +1751,45 @@ TEST_F(HTTPSRequestTest, SSLv3Fallback) { EXPECT_EQ(1, d.response_started_count()); EXPECT_NE(0, d.bytes_received()); - EXPECT_EQ(SSL_CONNECTION_VERSION_SSL3, + EXPECT_EQ(static_cast<int>(SSL_CONNECTION_VERSION_SSL3), SSLConnectionStatusToVersion(r.ssl_info().connection_status)); - EXPECT_TRUE(r.ssl_info().connection_status & SSL_CONNECTION_SSL3_FALLBACK); + EXPECT_TRUE(r.ssl_info().connection_status & SSL_CONNECTION_VERSION_FALLBACK); +} + +// Tests TLSv1.1 -> TLSv1 fallback. Verifies that we don't fall back more +// than necessary. +TEST_F(HTTPSRequestTest, TLSv1Fallback) { + uint16 default_version_max = SSLConfigService::default_version_max(); + // The OpenSSL library in use may not support TLS 1.1. +#if !defined(USE_OPENSSL) + EXPECT_GT(default_version_max, SSL_PROTOCOL_VERSION_TLS1); +#endif + if (default_version_max <= SSL_PROTOCOL_VERSION_TLS1) + return; + + TestServer::HTTPSOptions https_options( + TestServer::HTTPSOptions::CERT_OK); + https_options.tls_intolerant = + TestServer::HTTPSOptions::TLS_INTOLERANT_TLS1_1; + TestServer test_server(https_options, + FilePath(FILE_PATH_LITERAL("net/data/ssl"))); + ASSERT_TRUE(test_server.Start()); + + TestDelegate d; + TestURLRequestContext context(true); + context.Init(); + d.set_allow_certificate_errors(true); + URLRequest r(test_server.GetURL(""), &d); + r.set_context(&context); + r.Start(); + + MessageLoop::current()->Run(); + + EXPECT_EQ(1, d.response_started_count()); + EXPECT_NE(0, d.bytes_received()); + EXPECT_EQ(static_cast<int>(SSL_CONNECTION_VERSION_TLS1), + SSLConnectionStatusToVersion(r.ssl_info().connection_status)); + EXPECT_TRUE(r.ssl_info().connection_status & SSL_CONNECTION_VERSION_FALLBACK); } // This tests that a load of www.google.com with a certificate error sets |