diff options
author | thaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-19 23:29:23 +0000 |
---|---|---|
committer | thaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-19 23:29:23 +0000 |
commit | 158ac974a78a52b21e9f9c008a2aa59c6833be9f (patch) | |
tree | dfb9eadfdd911839c7edb4b7438432ad434643c0 | |
parent | 7f42935675d2de06b26a78166880dc45b1de84e0 (diff) | |
download | chromium_src-158ac974a78a52b21e9f9c008a2aa59c6833be9f.zip chromium_src-158ac974a78a52b21e9f9c008a2aa59c6833be9f.tar.gz chromium_src-158ac974a78a52b21e9f9c008a2aa59c6833be9f.tar.bz2 |
Do not roll back to SSL 3.0 for Google properties.
SSL 3.0 fallback for Google properties can be enabled again with --enable-unrestricted-ssl3-fallback.
Delete the obsolete SSL 3.0 fallback on TLS decompression failure.
BUG=230171
Review URL: https://chromiumcodereview.appspot.com/14125003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195335 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/net/ssl_config_service_manager_pref.cc | 8 | ||||
-rw-r--r-- | chrome/browser/net/ssl_config_service_manager_pref_unittest.cc | 24 | ||||
-rw-r--r-- | chrome/browser/prefs/command_line_pref_store.cc | 2 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 7 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 2 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 1 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 61 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 2 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy2_unittest.cc | 115 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy3_unittest.cc | 115 | ||||
-rw-r--r-- | net/http/http_network_transaction_ssl_unittest.cc | 292 | ||||
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/socket/socket_test_util.h | 2 | ||||
-rw-r--r-- | net/ssl/ssl_config_service.cc | 7 | ||||
-rw-r--r-- | net/ssl/ssl_config_service.h | 7 | ||||
-rw-r--r-- | net/ssl/ssl_config_service_unittest.cc | 5 |
17 files changed, 387 insertions, 265 deletions
diff --git a/chrome/browser/net/ssl_config_service_manager_pref.cc b/chrome/browser/net/ssl_config_service_manager_pref.cc index c193463..869f55b 100644 --- a/chrome/browser/net/ssl_config_service_manager_pref.cc +++ b/chrome/browser/net/ssl_config_service_manager_pref.cc @@ -180,6 +180,7 @@ class SSLConfigServiceManagerPref StringPrefMember ssl_version_max_; BooleanPrefMember channel_id_enabled_; BooleanPrefMember ssl_record_splitting_disabled_; + BooleanPrefMember unrestricted_ssl3_fallback_enabled_; // The cached list of disabled SSL cipher suites. std::vector<uint16> disabled_cipher_suites_; @@ -219,6 +220,9 @@ SSLConfigServiceManagerPref::SSLConfigServiceManagerPref( prefs::kEnableOriginBoundCerts, local_state, local_state_callback); ssl_record_splitting_disabled_.Init( prefs::kDisableSSLRecordSplitting, local_state, local_state_callback); + unrestricted_ssl3_fallback_enabled_.Init( + prefs::kEnableUnrestrictedSSL3Fallback, local_state, + local_state_callback); local_state_change_registrar_.Init(local_state); local_state_change_registrar_.Add( @@ -260,6 +264,8 @@ void SSLConfigServiceManagerPref::RegisterPrefs(PrefRegistrySimple* registry) { default_config.channel_id_enabled); registry->RegisterBooleanPref(prefs::kDisableSSLRecordSplitting, !default_config.false_start_enabled); + registry->RegisterBooleanPref(prefs::kEnableUnrestrictedSSL3Fallback, + default_config.unrestricted_ssl3_fallback_enabled); registry->RegisterListPref(prefs::kCipherSuiteBlacklist); } @@ -321,6 +327,8 @@ void SSLConfigServiceManagerPref::GetSSLConfigFromPrefs( config->channel_id_enabled = false; // disabling False Start also happens to disable record splitting. config->false_start_enabled = !ssl_record_splitting_disabled_.GetValue(); + config->unrestricted_ssl3_fallback_enabled = + unrestricted_ssl3_fallback_enabled_.GetValue(); SSLConfigServicePref::SetSSLConfigFlags(config); } diff --git a/chrome/browser/net/ssl_config_service_manager_pref_unittest.cc b/chrome/browser/net/ssl_config_service_manager_pref_unittest.cc index 42ee693..4108f97 100644 --- a/chrome/browser/net/ssl_config_service_manager_pref_unittest.cc +++ b/chrome/browser/net/ssl_config_service_manager_pref_unittest.cc @@ -220,8 +220,11 @@ TEST_F(SSLConfigServiceManagerPrefTest, BadDisabledCipherSuites) { EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]); } -// Test that without command-line settings for minimum and maximum SSL -// versions, SSL 3.0 ~ default_version_max() are enabled. +// Test that +// * without command-line settings for minimum and maximum SSL versions, +// SSL 3.0 ~ default_version_max() are enabled; +// * without --enable-unrestricted-ssl3-fallback, +// |unrestricted_ssl3_fallback_enabled| is false. TEST_F(SSLConfigServiceManagerPrefTest, NoCommandLinePrefs) { scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore()); @@ -245,10 +248,13 @@ TEST_F(SSLConfigServiceManagerPrefTest, NoCommandLinePrefs) { EXPECT_EQ(net::SSL_PROTOCOL_VERSION_SSL3, ssl_config.version_min); EXPECT_EQ(net::SSLConfigService::default_version_max(), ssl_config.version_max); + EXPECT_FALSE(ssl_config.unrestricted_ssl3_fallback_enabled); // The settings should not be added to the local_state. EXPECT_FALSE(local_state->HasPrefPath(prefs::kSSLVersionMin)); EXPECT_FALSE(local_state->HasPrefPath(prefs::kSSLVersionMax)); + EXPECT_FALSE(local_state->HasPrefPath( + prefs::kEnableUnrestrictedSSL3Fallback)); // Explicitly double-check the settings are not in the preference store. std::string version_min_str; @@ -257,6 +263,10 @@ TEST_F(SSLConfigServiceManagerPrefTest, NoCommandLinePrefs) { &version_min_str)); EXPECT_FALSE(local_state_store->GetString(prefs::kSSLVersionMax, &version_max_str)); + bool unrestricted_ssl3_fallback_enabled; + EXPECT_FALSE(local_state_store->GetBoolean( + prefs::kEnableUnrestrictedSSL3Fallback, + &unrestricted_ssl3_fallback_enabled)); } // Test that command-line settings for minimum and maximum SSL versions are @@ -267,6 +277,7 @@ TEST_F(SSLConfigServiceManagerPrefTest, CommandLinePrefs) { CommandLine command_line(CommandLine::NO_PROGRAM); command_line.AppendSwitchASCII(switches::kSSLVersionMin, "tls1"); command_line.AppendSwitchASCII(switches::kSSLVersionMax, "ssl3"); + command_line.AppendSwitch(switches::kEnableUnrestrictedSSL3Fallback); PrefServiceMockBuilder builder; builder.WithUserPrefs(local_state_store.get()); @@ -287,6 +298,7 @@ TEST_F(SSLConfigServiceManagerPrefTest, CommandLinePrefs) { // Command-line flags should be respected. EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_min); EXPECT_EQ(net::SSL_PROTOCOL_VERSION_SSL3, ssl_config.version_max); + EXPECT_TRUE(ssl_config.unrestricted_ssl3_fallback_enabled); // Explicitly double-check the settings are not in the preference store. const PrefService::Preference* version_min_pref = @@ -297,10 +309,18 @@ TEST_F(SSLConfigServiceManagerPrefTest, CommandLinePrefs) { local_state->FindPreference(prefs::kSSLVersionMax); EXPECT_FALSE(version_max_pref->IsUserModifiable()); + const PrefService::Preference* ssl3_fallback_pref = + local_state->FindPreference(prefs::kEnableUnrestrictedSSL3Fallback); + EXPECT_FALSE(ssl3_fallback_pref->IsUserModifiable()); + std::string version_min_str; std::string version_max_str; EXPECT_FALSE(local_state_store->GetString(prefs::kSSLVersionMin, &version_min_str)); EXPECT_FALSE(local_state_store->GetString(prefs::kSSLVersionMax, &version_max_str)); + bool unrestricted_ssl3_fallback_enabled; + EXPECT_FALSE(local_state_store->GetBoolean( + prefs::kEnableUnrestrictedSSL3Fallback, + &unrestricted_ssl3_fallback_enabled)); } diff --git a/chrome/browser/prefs/command_line_pref_store.cc b/chrome/browser/prefs/command_line_pref_store.cc index 8bf0b96..24f6559 100644 --- a/chrome/browser/prefs/command_line_pref_store.cc +++ b/chrome/browser/prefs/command_line_pref_store.cc @@ -53,6 +53,8 @@ const CommandLinePrefStore::BooleanSwitchToPreferenceMapEntry { switches::kDisableTLSChannelID, prefs::kEnableOriginBoundCerts, false }, { switches::kDisableSSLFalseStart, prefs::kDisableSSLRecordSplitting, true }, + { switches::kEnableUnrestrictedSSL3Fallback, + prefs::kEnableUnrestrictedSSL3Fallback, true }, { switches::kEnableMemoryInfo, prefs::kEnableMemoryInfo, true }, #if defined(GOOGLE_CHROME_BUILD) { switches::kDisablePrintPreview, prefs::kPrintPreviewDisabled, true }, diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index ac09210..27ed72d 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -660,6 +660,13 @@ const char kEnableTabGroupsContextMenu[] = "enable-tab-groups-context-menu"; const char kEnableTranslateAlphaLanguages[] = "enable-translate-alpha-languages"; +// Enables unrestricted SSL 3.0 fallback. +// With this switch, SSL 3.0 fallback will be enabled for all sites. +// Without this switch, SSL 3.0 fallback will be disabled for a site +// pinned to the Google pin list (indicating that it is a Google site). +const char kEnableUnrestrictedSSL3Fallback[] = + "enable-unrestricted-ssl3-fallback"; + // Enables Alternate-Protocol when the port is user controlled (> 1024). const char kEnableUserAlternateProtocolPorts[] = "enable-user-controlled-alternate-protocol-ports"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 62fcceb..526b4e1 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -188,6 +188,7 @@ extern const char kEnableSyncSyncedNotifications[]; extern const char kEnableSyncFavicons[]; extern const char kEnableTabGroupsContextMenu[]; extern const char kEnableTranslateAlphaLanguages[]; +extern const char kEnableUnrestrictedSSL3Fallback[]; extern const char kEnableUserAlternateProtocolPorts[]; extern const char kEnableWatchdog[]; extern const char kEnableWebSocketOverSpdy[]; diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 820868f..07c635c 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -1242,6 +1242,8 @@ const char kSSLVersionMax[] = "ssl.version_max"; const char kCipherSuiteBlacklist[] = "ssl.cipher_suites.blacklist"; const char kEnableOriginBoundCerts[] = "ssl.origin_bound_certs.enabled"; const char kDisableSSLRecordSplitting[] = "ssl.ssl_record_splitting.disabled"; +const char kEnableUnrestrictedSSL3Fallback[] = + "ssl.unrestricted_ssl3_fallback.enabled"; // A boolean pref of the EULA accepted flag. const char kEulaAccepted[] = "EulaAccepted"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 222af2d..510a2a2 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -431,6 +431,7 @@ extern const char kSSLVersionMax[]; extern const char kCipherSuiteBlacklist[]; extern const char kEnableOriginBoundCerts[]; extern const char kDisableSSLRecordSplitting[]; +extern const char kEnableUnrestrictedSSL3Fallback[]; extern const char kEnableMemoryInfo[]; extern const char kGLVendorString[]; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 9496c07..f3e95d5 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -47,6 +47,7 @@ #include "net/http/http_stream_base.h" #include "net/http/http_stream_factory.h" #include "net/http/http_util.h" +#include "net/http/transport_security_state.h" #include "net/http/url_security_manager.h" #include "net/socket/client_socket_factory.h" #include "net/socket/socks_client_socket_pool.h" @@ -1183,11 +1184,13 @@ int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { GetHostAndPort(request_->url)); } + uint16 version_max = server_ssl_config_.version_max; + 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) { + if (version_max >= SSL_PROTOCOL_VERSION_TLS1 && + 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 @@ -1198,38 +1201,34 @@ int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { // 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. - uint16 version_before = server_ssl_config_.version_max; - server_ssl_config_.version_max--; - net_log_.AddEvent( - NetLog::TYPE_SSL_VERSION_FALLBACK, - base::Bind(&NetLogSSLVersionFallbackCallback, - &request_->url, error, version_before, - 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_.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. - uint16 version_before = server_ssl_config_.version_max; - server_ssl_config_.version_max = SSL_PROTOCOL_VERSION_SSL3; - net_log_.AddEvent( - NetLog::TYPE_SSL_VERSION_FALLBACK, - base::Bind(&NetLogSSLVersionFallbackCallback, - &request_->url, error, version_before, - server_ssl_config_.version_max)); - server_ssl_config_.version_fallback = true; - ResetConnectionAndRequestForResend(); - error = OK; + version_max--; + + // Fallback to the lower SSL version. + // While SSL 3.0 fallback should be eliminated because of security + // reasons, there is a high risk of breaking the servers if this is + // done in general. + // For now SSL 3.0 fallback is disabled for Google servers first, + // and will be expanded to other servers after enough experiences + // have been gained showing that this experiment works well with + // today's Internet. + if (version_max > SSL_PROTOCOL_VERSION_SSL3 || + (server_ssl_config_.unrestricted_ssl3_fallback_enabled || + !TransportSecurityState::IsGooglePinnedProperty( + request_->url.host(), true /* include SNI */))) { + net_log_.AddEvent( + NetLog::TYPE_SSL_VERSION_FALLBACK, + base::Bind(&NetLogSSLVersionFallbackCallback, + &request_->url, error, server_ssl_config_.version_max, + version_max)); + server_ssl_config_.version_max = version_max; + server_ssl_config_.version_fallback = true; + ResetConnectionAndRequestForResend(); + error = OK; + } } break; } + return error; } diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index ca7a79d..a98778d 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -87,6 +87,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction HttpStreamBase* stream) OVERRIDE; private: + friend class HttpNetworkTransactionSSLTest; + FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionSpdy2Test, ResetStateForRestart); FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionSpdy3Test, diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc index 10e83a4..f01fcb4 100644 --- a/net/http/http_network_transaction_spdy2_unittest.cc +++ b/net/http/http_network_transaction_spdy2_unittest.cc @@ -9493,121 +9493,6 @@ TEST_F(HttpNetworkTransactionSpdy2Test, MultiRoundAuth) { EXPECT_EQ(1, transport_pool->IdleSocketCountInGroup(kSocketGroup)); } -class TLSDecompressionFailureSocketDataProvider : public SocketDataProvider { - public: - explicit TLSDecompressionFailureSocketDataProvider(bool fail_all) - : fail_all_(fail_all) { - } - - virtual MockRead GetNextRead() OVERRIDE { - if (fail_all_) - return MockRead(SYNCHRONOUS, ERR_SSL_DECOMPRESSION_FAILURE_ALERT); - - return MockRead(SYNCHRONOUS, - "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nok.\r\n"); - } - - virtual MockWriteResult OnWrite(const std::string& data) OVERRIDE { - return MockWriteResult(SYNCHRONOUS /* async */, data.size()); - } - - virtual void Reset() OVERRIDE { - } - - private: - const bool fail_all_; -}; - -// Test that we restart a connection when we see a decompression failure from -// the peer during the handshake. (In the real world we'll restart with SSLv3 -// and we won't offer DEFLATE in that case.) -TEST_F(HttpNetworkTransactionSpdy2Test, RestartAfterTLSDecompressionFailure) { - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://tlsdecompressionfailure.example.com/"); - request.load_flags = 0; - - SpdySessionDependencies session_deps; - TLSDecompressionFailureSocketDataProvider socket_data_provider1( - false /* fail all reads */); - TLSDecompressionFailureSocketDataProvider socket_data_provider2(false); - SSLSocketDataProvider ssl_socket_data_provider1( - SYNCHRONOUS, ERR_SSL_DECOMPRESSION_FAILURE_ALERT); - SSLSocketDataProvider ssl_socket_data_provider2(SYNCHRONOUS, OK); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider1); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider2); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider1); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider2); - - // Work around http://crbug.com/37454 - StaticSocketDataProvider bug37454_connection; - bug37454_connection.set_connect_data(MockConnect(ASYNC, ERR_UNEXPECTED)); - session_deps.socket_factory->AddSocketDataProvider(&bug37454_connection); - - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); - TestCompletionCallback callback; - - int rv = trans->Start(&request, callback.callback(), BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_EQ(OK, callback.WaitForResult()); - - const HttpResponseInfo* response = trans->GetResponseInfo(); - ASSERT_TRUE(response != NULL); - ASSERT_TRUE(response->headers != NULL); - EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); - - std::string response_data; - ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); - EXPECT_EQ("ok.", response_data); -} - -// Test that we restart a connection if we get a decompression failure from the -// peer while reading the first bytes from the connection. This occurs when the -// peer cannot handle DEFLATE but we're using False Start, so we don't notice -// in the handshake. -TEST_F(HttpNetworkTransactionSpdy2Test, - RestartAfterTLSDecompressionFailureWithFalseStart) { - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://tlsdecompressionfailure2.example.com/"); - request.load_flags = 0; - - SpdySessionDependencies session_deps; - TLSDecompressionFailureSocketDataProvider socket_data_provider1( - true /* fail all reads */); - TLSDecompressionFailureSocketDataProvider socket_data_provider2(false); - SSLSocketDataProvider ssl_socket_data_provider1(SYNCHRONOUS, OK); - SSLSocketDataProvider ssl_socket_data_provider2(SYNCHRONOUS, OK); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider1); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider2); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider1); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider2); - - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); - TestCompletionCallback callback; - - int rv = trans->Start(&request, callback.callback(), BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_EQ(OK, callback.WaitForResult()); - - const HttpResponseInfo* response = trans->GetResponseInfo(); - ASSERT_TRUE(response != NULL); - ASSERT_TRUE(response->headers != NULL); - EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); - - std::string response_data; - ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); - EXPECT_EQ("ok.", response_data); -} - // This tests the case that a request is issued via http instead of spdy after // npn is negotiated. TEST_F(HttpNetworkTransactionSpdy2Test, NpnWithHttpOverSSL) { diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc index 1238fd5..01b85aa 100644 --- a/net/http/http_network_transaction_spdy3_unittest.cc +++ b/net/http/http_network_transaction_spdy3_unittest.cc @@ -9470,121 +9470,6 @@ TEST_F(HttpNetworkTransactionSpdy3Test, MultiRoundAuth) { EXPECT_EQ(1, transport_pool->IdleSocketCountInGroup(kSocketGroup)); } -class TLSDecompressionFailureSocketDataProvider : public SocketDataProvider { - public: - explicit TLSDecompressionFailureSocketDataProvider(bool fail_all) - : fail_all_(fail_all) { - } - - virtual MockRead GetNextRead() OVERRIDE { - if (fail_all_) - return MockRead(SYNCHRONOUS, ERR_SSL_DECOMPRESSION_FAILURE_ALERT); - - return MockRead(SYNCHRONOUS, - "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nok.\r\n"); - } - - virtual MockWriteResult OnWrite(const std::string& data) OVERRIDE { - return MockWriteResult(SYNCHRONOUS /* async */, data.size()); - } - - virtual void Reset() OVERRIDE { - } - - private: - const bool fail_all_; -}; - -// Test that we restart a connection when we see a decompression failure from -// the peer during the handshake. (In the real world we'll restart with SSLv3 -// and we won't offer DEFLATE in that case.) -TEST_F(HttpNetworkTransactionSpdy3Test, RestartAfterTLSDecompressionFailure) { - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://tlsdecompressionfailure.example.com/"); - request.load_flags = 0; - - SpdySessionDependencies session_deps; - TLSDecompressionFailureSocketDataProvider socket_data_provider1( - false /* fail all reads */); - TLSDecompressionFailureSocketDataProvider socket_data_provider2(false); - SSLSocketDataProvider ssl_socket_data_provider1( - SYNCHRONOUS, ERR_SSL_DECOMPRESSION_FAILURE_ALERT); - SSLSocketDataProvider ssl_socket_data_provider2(SYNCHRONOUS, OK); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider1); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider2); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider1); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider2); - - // Work around http://crbug.com/37454 - StaticSocketDataProvider bug37454_connection; - bug37454_connection.set_connect_data(MockConnect(ASYNC, ERR_UNEXPECTED)); - session_deps.socket_factory->AddSocketDataProvider(&bug37454_connection); - - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); - TestCompletionCallback callback; - - int rv = trans->Start(&request, callback.callback(), BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_EQ(OK, callback.WaitForResult()); - - const HttpResponseInfo* response = trans->GetResponseInfo(); - ASSERT_TRUE(response != NULL); - ASSERT_TRUE(response->headers != NULL); - EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); - - std::string response_data; - ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); - EXPECT_EQ("ok.", response_data); -} - -// Test that we restart a connection if we get a decompression failure from the -// peer while reading the first bytes from the connection. This occurs when the -// peer cannot handle DEFLATE but we're using False Start, so we don't notice -// in the handshake. -TEST_F(HttpNetworkTransactionSpdy3Test, - RestartAfterTLSDecompressionFailureWithFalseStart) { - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://tlsdecompressionfailure2.example.com/"); - request.load_flags = 0; - - SpdySessionDependencies session_deps; - TLSDecompressionFailureSocketDataProvider socket_data_provider1( - true /* fail all reads */); - TLSDecompressionFailureSocketDataProvider socket_data_provider2(false); - SSLSocketDataProvider ssl_socket_data_provider1(SYNCHRONOUS, OK); - SSLSocketDataProvider ssl_socket_data_provider2(SYNCHRONOUS, OK); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider1); - session_deps.socket_factory->AddSocketDataProvider(&socket_data_provider2); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider1); - session_deps.socket_factory->AddSSLSocketDataProvider( - &ssl_socket_data_provider2); - - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); - TestCompletionCallback callback; - - int rv = trans->Start(&request, callback.callback(), BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - EXPECT_EQ(OK, callback.WaitForResult()); - - const HttpResponseInfo* response = trans->GetResponseInfo(); - ASSERT_TRUE(response != NULL); - ASSERT_TRUE(response->headers != NULL); - EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); - - std::string response_data; - ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); - EXPECT_EQ("ok.", response_data); -} - // This tests the case that a request is issued via http instead of spdy after // npn is negotiated. TEST_F(HttpNetworkTransactionSpdy3Test, NpnWithHttpOverSSL) { diff --git a/net/http/http_network_transaction_ssl_unittest.cc b/net/http/http_network_transaction_ssl_unittest.cc new file mode 100644 index 0000000..de5a3fd --- /dev/null +++ b/net/http/http_network_transaction_ssl_unittest.cc @@ -0,0 +1,292 @@ +// 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. + +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/net_util.h" +#include "net/base/request_priority.h" +#include "net/dns/mock_host_resolver.h" +#include "net/http/http_auth_handler_mock.h" +#include "net/http/http_network_session.h" +#include "net/http/http_network_transaction.h" +#include "net/http/http_request_info.h" +#include "net/http/http_server_properties_impl.h" +#include "net/http/transport_security_state.h" +#include "net/proxy/proxy_service.h" +#include "net/socket/socket_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +class TLS10SSLConfigService : public SSLConfigService { + public: + TLS10SSLConfigService() { + ssl_config_.version_min = SSL_PROTOCOL_VERSION_SSL3; + ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1; + } + + virtual void GetSSLConfig(SSLConfig* config) OVERRIDE { + *config = ssl_config_; + } + + private: + virtual ~TLS10SSLConfigService() {} + + SSLConfig ssl_config_; +}; + +class TLS11SSLConfigService : public SSLConfigService { + public: + TLS11SSLConfigService() { + ssl_config_.version_min = SSL_PROTOCOL_VERSION_SSL3; + ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1_1; + } + + virtual void GetSSLConfig(SSLConfig* config) OVERRIDE { + *config = ssl_config_; + } + + private: + virtual ~TLS11SSLConfigService() {} + + SSLConfig ssl_config_; +}; + +} // namespace + +class HttpNetworkTransactionSSLTest : public testing::Test { + protected: + virtual void SetUp() { + ssl_config_service_ = new TLS10SSLConfigService; + session_params_.ssl_config_service = ssl_config_service_.get(); + + auth_handler_factory_.reset(new HttpAuthHandlerMock::Factory()); + session_params_.http_auth_handler_factory = auth_handler_factory_.get(); + + proxy_service_.reset(ProxyService::CreateDirect()); + session_params_.proxy_service = proxy_service_.get(); + + session_params_.client_socket_factory = &mock_socket_factory_; + session_params_.host_resolver = &mock_resolver_; + session_params_.http_server_properties = &http_server_properties_; + session_params_.transport_security_state = &transport_security_state_; + } + + HttpRequestInfo* GetRequestInfo(std::string url) { + HttpRequestInfo* request_info = new HttpRequestInfo; + request_info->url = GURL(url); + request_info->method = "GET"; + return request_info; + } + + SSLConfig& GetServerSSLConfig(HttpNetworkTransaction* trans) { + return trans->server_ssl_config_; + } + + scoped_refptr<SSLConfigService> ssl_config_service_; + scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory_; + scoped_ptr<ProxyService> proxy_service_; + + MockClientSocketFactory mock_socket_factory_; + MockHostResolver mock_resolver_; + HttpServerPropertiesImpl http_server_properties_; + TransportSecurityState transport_security_state_; + HttpNetworkSession::Params session_params_; +}; + +// Tests that HttpNetworkTransaction does not attempt to +// fallback to SSL 3.0 when a TLS 1.0 handshake fails and: +// * the site is pinned to the Google pin list (indicating that +// it is a Google site); +// * unrestricted SSL 3.0 fallback is disabled. +TEST_F(HttpNetworkTransactionSSLTest, SSL3FallbackDisabled_Google) { + // |ssl_data1| is for the first handshake (TLS 1.0), which will fail for + // protocol reasons (e.g., simulating a version rollback attack). + // Because unrestricted SSL 3.0 fallback is disabled, only this simulated + // SSL handshake is consumed. + SSLSocketDataProvider ssl_data1(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data1); + StaticSocketDataProvider data1(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data1); + + // This extra handshake, which should be unconsumed, is provided to ensure + // that even if the behaviour being tested here ever breaks (and Google + // properties begin SSL 3.0 fallbacks), this test will not crash (and bring + // down all of net_unittests), but it will fail gracefully. + SSLSocketDataProvider ssl_data2(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data2); + StaticSocketDataProvider data2(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data2); + + scoped_refptr<HttpNetworkSession> session( + new HttpNetworkSession(session_params_)); + scoped_ptr<HttpNetworkTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + + TestCompletionCallback callback; + // This will consume only |ssl_data1|. |ssl_data2| will not be consumed. + int rv = callback.GetResult( + trans->Start(GetRequestInfo("https://www.google.com/"), + callback.callback(), BoundNetLog())); + EXPECT_EQ(ERR_SSL_PROTOCOL_ERROR, rv); + + SocketDataProviderArray<SocketDataProvider>& mock_data = + mock_socket_factory_.mock_data(); + // Confirms that only |ssl_data1| is consumed. + EXPECT_EQ(1u, mock_data.next_index()); + + SSLConfig& ssl_config = GetServerSSLConfig(trans.get()); + // |version_max| never fallbacks to SSLv3 for Google properties. + EXPECT_EQ(SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_max); + EXPECT_FALSE(ssl_config.version_fallback); +} + +// Tests that HttpNetworkTransaction attempts to fallback to SSL 3.0 +// when a TLS 1.0 handshake fails and: +// * the site is pinned to the Google pin list (indicating that +// it is a Google site); +// * unrestricted SSL 3.0 fallback is enabled. +TEST_F(HttpNetworkTransactionSSLTest, SSL3FallbackEnabled_Google) { + // |ssl_data1| is for the first handshake (TLS 1.0), which will fail + // for protocol reasons (e.g., simulating a version rollback attack). + SSLSocketDataProvider ssl_data1(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data1); + StaticSocketDataProvider data1(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data1); + + // |ssl_data2| contains the handshake result for a SSL 3.0 + // handshake which will be attempted after the TLS 1.0 + // handshake fails. + SSLSocketDataProvider ssl_data2(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data2); + StaticSocketDataProvider data2(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data2); + + scoped_refptr<HttpNetworkSession> session( + new HttpNetworkSession(session_params_)); + scoped_ptr<HttpNetworkTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + + SSLConfig& ssl_config = GetServerSSLConfig(trans.get()); + ssl_config.unrestricted_ssl3_fallback_enabled = true; + + TestCompletionCallback callback; + // This will consume |ssl_data1| and |ssl_data2|. + int rv = callback.GetResult( + trans->Start(GetRequestInfo("https://www.google.com/"), + callback.callback(), BoundNetLog())); + EXPECT_EQ(ERR_SSL_PROTOCOL_ERROR, rv); + + SocketDataProviderArray<SocketDataProvider>& mock_data = + mock_socket_factory_.mock_data(); + // Confirms that both |ssl_data1| and |ssl_data2| are consumed. + EXPECT_EQ(2u, mock_data.next_index()); + + // |version_max| fallbacks to SSL 3.0 for Google properties when + // |unrestricted_ssl3_fallback_enabled| is true. + EXPECT_EQ(SSL_PROTOCOL_VERSION_SSL3, ssl_config.version_max); + EXPECT_TRUE(ssl_config.version_fallback); +} + +// Tests that HttpNetworkTransaction attempts to fallback to SSL 3.0 +// when a TLS 1.0 handshake fails and the site is not a Google domain, +// even if unrestricted SSL 3.0 fallback is disabled. +// TODO(thaidn): revise the above comment and this test when the +// SSL 3.0 fallback experiment is applied for non-Google domains. +TEST_F(HttpNetworkTransactionSSLTest, SSL3FallbackDisabled_Paypal) { + // |ssl_data1| is for the first handshake (TLS 1.0), which will fail + // for protocol reasons (e.g., simulating a version rollback attack). + SSLSocketDataProvider ssl_data1(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data1); + StaticSocketDataProvider data1(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data1); + + // |ssl_data2| contains the handshake result for a SSL 3.0 + // handshake which will be attempted after the TLS 1.0 + // handshake fails. + SSLSocketDataProvider ssl_data2(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data2); + StaticSocketDataProvider data2(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data2); + + scoped_refptr<HttpNetworkSession> session( + new HttpNetworkSession(session_params_)); + scoped_ptr<HttpNetworkTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + + TestCompletionCallback callback; + // This will consume |ssl_data1| and |ssl_data2|. + int rv = callback.GetResult( + trans->Start(GetRequestInfo("https://www.paypal.com/"), + callback.callback(), BoundNetLog())); + EXPECT_EQ(ERR_SSL_PROTOCOL_ERROR, rv); + + SocketDataProviderArray<SocketDataProvider>& mock_data = + mock_socket_factory_.mock_data(); + // Confirms that both |ssl_data1| and |ssl_data2| are consumed. + EXPECT_EQ(2u, mock_data.next_index()); + + SSLConfig& ssl_config = GetServerSSLConfig(trans.get()); + // |version_max| fallbacks to SSL 3.0. + EXPECT_EQ(SSL_PROTOCOL_VERSION_SSL3, ssl_config.version_max); + EXPECT_TRUE(ssl_config.version_fallback); +} + +// Tests that HttpNetworkTransaction attempts to fallback from +// TLS 1.1 to TLS 1.0, then from TLS 1.0 to SSL 3.0. +TEST_F(HttpNetworkTransactionSSLTest, SSLFallback) { + ssl_config_service_ = new TLS11SSLConfigService; + session_params_.ssl_config_service = ssl_config_service_.get(); + // |ssl_data1| is for the first handshake (TLS 1.1), which will fail + // for protocol reasons (e.g., simulating a version rollback attack). + SSLSocketDataProvider ssl_data1(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data1); + StaticSocketDataProvider data1(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data1); + + // |ssl_data2| contains the handshake result for a TLS 1.0 + // handshake which will be attempted after the TLS 1.1 + // handshake fails. + SSLSocketDataProvider ssl_data2(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data2); + StaticSocketDataProvider data2(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data2); + + // |ssl_data3| contains the handshake result for a SSL 3.0 + // handshake which will be attempted after the TLS 1.0 + // handshake fails. + SSLSocketDataProvider ssl_data3(ASYNC, ERR_SSL_PROTOCOL_ERROR); + mock_socket_factory_.AddSSLSocketDataProvider(&ssl_data3); + StaticSocketDataProvider data3(NULL, 0, NULL, 0); + mock_socket_factory_.AddSocketDataProvider(&data3); + + scoped_refptr<HttpNetworkSession> session( + new HttpNetworkSession(session_params_)); + scoped_ptr<HttpNetworkTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + + TestCompletionCallback callback; + // This will consume |ssl_data1|, |ssl_data2| and |ssl_data3|. + int rv = callback.GetResult( + trans->Start(GetRequestInfo("https://www.paypal.com/"), + callback.callback(), BoundNetLog())); + EXPECT_EQ(ERR_SSL_PROTOCOL_ERROR, rv); + + SocketDataProviderArray<SocketDataProvider>& mock_data = + mock_socket_factory_.mock_data(); + // Confirms that |ssl_data1|, |ssl_data2| and |ssl_data3| are consumed. + EXPECT_EQ(3u, mock_data.next_index()); + + SSLConfig& ssl_config = GetServerSSLConfig(trans.get()); + // |version_max| fallbacks to SSL 3.0. + EXPECT_EQ(SSL_PROTOCOL_VERSION_SSL3, ssl_config.version_max); + EXPECT_TRUE(ssl_config.version_fallback); +} + +} // namespace net + diff --git a/net/net.gyp b/net/net.gyp index 12459a5..cdfcdde 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -1533,6 +1533,7 @@ 'http/http_network_layer_unittest.cc', 'http/http_network_transaction_spdy3_unittest.cc', 'http/http_network_transaction_spdy2_unittest.cc', + 'http/http_network_transaction_ssl_unittest.cc', 'http/http_pipelined_connection_impl_unittest.cc', 'http/http_pipelined_host_forced_unittest.cc', 'http/http_pipelined_host_impl_unittest.cc', diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index b8ccbc0..995859c 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -529,6 +529,8 @@ class SocketDataProviderArray { data_providers_.push_back(data_provider); } + size_t next_index() { return next_index_; } + void ResetNextIndex() { next_index_ = 0; } diff --git a/net/ssl/ssl_config_service.cc b/net/ssl/ssl_config_service.cc index f46dd7b..f195887 100644 --- a/net/ssl/ssl_config_service.cc +++ b/net/ssl/ssl_config_service.cc @@ -40,6 +40,7 @@ SSLConfig::SSLConfig() cached_info_enabled(false), channel_id_enabled(true), false_start_enabled(true), + unrestricted_ssl3_fallback_enabled(false), send_client_cert(false), verify_ev_cert(false), version_fallback(false), @@ -154,9 +155,11 @@ void SSLConfigService::ProcessConfigUpdate(const SSLConfig& orig_config, (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) || + new_config.disabled_cipher_suites) || (orig_config.channel_id_enabled != new_config.channel_id_enabled) || - (orig_config.false_start_enabled != new_config.false_start_enabled); + (orig_config.false_start_enabled != new_config.false_start_enabled) || + (orig_config.unrestricted_ssl3_fallback_enabled != + new_config.unrestricted_ssl3_fallback_enabled); if (config_changed) NotifySSLConfigChange(); diff --git a/net/ssl/ssl_config_service.h b/net/ssl/ssl_config_service.h index a111594..f8ee03b 100644 --- a/net/ssl/ssl_config_service.h +++ b/net/ssl/ssl_config_service.h @@ -91,6 +91,13 @@ struct NET_EXPORT SSLConfig { bool channel_id_enabled; // True if TLS channel ID extension is enabled. bool false_start_enabled; // True if we'll use TLS False Start. + // If |unrestricted_ssl3_fallback_enabled| is true, SSL 3.0 fallback will be + // enabled for all sites. + // If |unrestricted_ssl3_fallback_enabled| is false, SSL 3.0 fallback will be + // disabled for a site pinned to the Google pin list (indicating that it is a + // Google site). + bool unrestricted_ssl3_fallback_enabled; + // TODO(wtc): move the following members to a new SSLParams structure. They // are not SSL configuration settings. diff --git a/net/ssl/ssl_config_service_unittest.cc b/net/ssl/ssl_config_service_unittest.cc index e8a4c33..42c8ae4 100644 --- a/net/ssl/ssl_config_service_unittest.cc +++ b/net/ssl/ssl_config_service_unittest.cc @@ -69,6 +69,7 @@ TEST(SSLConfigServiceTest, ConfigUpdatesNotifyObservers) { SSLConfig initial_config; initial_config.rev_checking_enabled = true; initial_config.false_start_enabled = false; + initial_config.unrestricted_ssl3_fallback_enabled = false; initial_config.version_min = SSL_PROTOCOL_VERSION_SSL3; initial_config.version_max = SSL_PROTOCOL_VERSION_TLS1_1; @@ -86,6 +87,10 @@ TEST(SSLConfigServiceTest, ConfigUpdatesNotifyObservers) { EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1); mock_service->SetSSLConfig(initial_config); + initial_config.unrestricted_ssl3_fallback_enabled = true; + EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1); + mock_service->SetSSLConfig(initial_config); + // Test that changing the SSL version range triggers updates. initial_config.version_min = SSL_PROTOCOL_VERSION_TLS1; EXPECT_CALL(observer, OnSSLConfigChanged()).Times(1); |