diff options
-rw-r--r-- | net/http/http_network_transaction.cc | 6 | ||||
-rw-r--r-- | net/http/http_stream_factory.h | 3 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl.cc | 9 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl.h | 4 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.cc | 7 | ||||
-rw-r--r-- | net/test/base_test_server.cc | 8 | ||||
-rw-r--r-- | net/test/base_test_server.h | 4 | ||||
-rwxr-xr-x | net/tools/testserver/testserver.py | 14 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 23 | ||||
-rw-r--r-- | third_party/tlslite/README.chromium | 1 | ||||
-rw-r--r-- | third_party/tlslite/patches/tls_intolerant.patch | 60 | ||||
-rw-r--r-- | third_party/tlslite/tlslite/TLSConnection.py | 16 |
12 files changed, 120 insertions, 35 deletions
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 43df7ce..7b52766 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -1168,8 +1168,10 @@ int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { // 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. - session_->http_stream_factory()->AddTLSIntolerantServer( - HostPortPair::FromURL(request_->url)); + 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; ResetConnectionAndRequestForResend(); error = OK; } diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h index 58efdf3..df9cecf 100644 --- a/net/http/http_stream_factory.h +++ b/net/http/http_stream_factory.h @@ -180,9 +180,6 @@ class NET_EXPORT HttpStreamFactory { const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config) = 0; - virtual void AddTLSIntolerantServer(const HostPortPair& server) = 0; - virtual bool IsTLSIntolerantServer(const HostPortPair& server) const = 0; - // If pipelining is supported, creates a Value summary of the currently active // pipelines. Caller assumes ownership of the returned value. Otherwise, // returns an empty Value. diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc index c142224..75e7365 100644 --- a/net/http/http_stream_factory_impl.cc +++ b/net/http/http_stream_factory_impl.cc @@ -119,15 +119,6 @@ void HttpStreamFactoryImpl::PreconnectStreams( job->Preconnect(num_streams); } -void HttpStreamFactoryImpl::AddTLSIntolerantServer(const HostPortPair& server) { - tls_intolerant_servers_.insert(server); -} - -bool HttpStreamFactoryImpl::IsTLSIntolerantServer( - const HostPortPair& server) const { - return ContainsKey(tls_intolerant_servers_, server); -} - base::Value* HttpStreamFactoryImpl::PipelineInfoToValue() const { return http_pipelined_host_pool_.PipelineInfoToValue(); } diff --git a/net/http/http_stream_factory_impl.h b/net/http/http_stream_factory_impl.h index 238b435..2542298 100644 --- a/net/http/http_stream_factory_impl.h +++ b/net/http/http_stream_factory_impl.h @@ -42,8 +42,6 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : const HttpRequestInfo& info, const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config) OVERRIDE; - virtual void AddTLSIntolerantServer(const HostPortPair& server) OVERRIDE; - virtual bool IsTLSIntolerantServer(const HostPortPair& server) const OVERRIDE; virtual base::Value* PipelineInfoToValue() const OVERRIDE; // HttpPipelinedHostPool::Delegate interface @@ -99,8 +97,6 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : HttpNetworkSession* const session_; - std::set<HostPortPair> tls_intolerant_servers_; - // All Requests are handed out to clients. By the time HttpStreamFactoryImpl // is destroyed, all Requests should be deleted (which should remove them from // |request_map_|. The Requests will delete the corresponding job. diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index 408c75e..bc4af30 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -1051,13 +1051,6 @@ bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() { void HttpStreamFactoryImpl::Job::InitSSLConfig( const HostPortPair& origin_server, SSLConfig* ssl_config) const { - if (stream_factory_->IsTLSIntolerantServer(origin_server)) { - LOG(WARNING) << "Falling back to SSLv3 because host is TLS intolerant: " - << origin_server.ToString(); - ssl_config->ssl3_fallback = true; - ssl_config->tls1_enabled = false; - } - if (proxy_info_.is_https() && ssl_config->send_client_cert) { // When connecting through an HTTPS proxy, disable TLS False Start so // that client authentication errors can be distinguished between those diff --git a/net/test/base_test_server.cc b/net/test/base_test_server.cc index eefd953..533cb9d 100644 --- a/net/test/base_test_server.cc +++ b/net/test/base_test_server.cc @@ -58,14 +58,16 @@ BaseTestServer::HTTPSOptions::HTTPSOptions() ocsp_status(OCSP_OK), request_client_certificate(false), bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY), - record_resume(false) {} + record_resume(false), + tls_intolerant(false) {} BaseTestServer::HTTPSOptions::HTTPSOptions( BaseTestServer::HTTPSOptions::ServerCertificate cert) : server_certificate(cert), request_client_certificate(false), bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY), - record_resume(false) {} + record_resume(false), + tls_intolerant(false) {} BaseTestServer::HTTPSOptions::~HTTPSOptions() {} @@ -375,6 +377,8 @@ bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const { arguments->Set("ssl-bulk-cipher", bulk_cipher_values.release()); if (https_options_.record_resume) arguments->Set("https-record-resume", base::Value::CreateNullValue()); + if (https_options_.tls_intolerant) + arguments->Set("tls-intolerant", base::Value::CreateNullValue()); } return true; } diff --git a/net/test/base_test_server.h b/net/test/base_test_server.h index fd04e55..f7d5654 100644 --- a/net/test/base_test_server.h +++ b/net/test/base_test_server.h @@ -126,6 +126,10 @@ class BaseTestServer { // causes it to log session cache actions and echo the log on // /ssl-session-cache. bool record_resume; + + // If true, the server will abort any TLS handshake in order to test + // SSLv3 fallback. + bool tls_intolerant; }; // Pass as the 'host' parameter during construction to server on 127.0.0.1 diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py index 6461997..9fc92ee 100755 --- a/net/tools/testserver/testserver.py +++ b/net/tools/testserver/testserver.py @@ -133,11 +133,13 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, def __init__(self, server_address, request_hander_class, pem_cert_and_key, ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers, - record_resume_info): + record_resume_info, tls_intolerant): self.cert_chain = tlslite.api.X509CertChain().parseChain(pem_cert_and_key) self.private_key = tlslite.api.parsePEMKey(pem_cert_and_key, private=True) self.ssl_client_auth = ssl_client_auth self.ssl_client_cas = [] + self.tls_intolerant = tls_intolerant + for ca_file in ssl_client_cas: s = open(ca_file).read() x509 = tlslite.api.X509() @@ -163,7 +165,8 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, sessionCache=self.session_cache, reqCert=self.ssl_client_auth, settings=self.ssl_handshake_settings, - reqCAs=self.ssl_client_cas) + reqCAs=self.ssl_client_cas, + tlsIntolerant=self.tls_intolerant) tlsConnection.ignoreAbruptClose = True return True except tlslite.api.TLSAbruptCloseError: @@ -2045,7 +2048,8 @@ def main(options, args): return server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key, options.ssl_client_auth, options.ssl_client_ca, - options.ssl_bulk_cipher, options.record_resume) + options.ssl_bulk_cipher, options.record_resume, + options.tls_intolerant) print 'HTTPS server started on %s:%d...' % (host, server.server_port) else: server = HTTPServer((host, port), TestPageHandler) @@ -2172,6 +2176,10 @@ if __name__ == '__main__': help='The type of OCSP response generated for the ' 'automatically generated certificate. One of ' '[ok,revoked,invalid]') + option_parser.add_option('', '--tls-intolerant', dest='tls_intolerant', + const=True, default=False, action='store_const', + help='If true, TLS connections will be aborted ' + ' in order to test SSLv3 fallback.') option_parser.add_option('', '--https-record-resume', dest='record_resume', const=True, default=False, action='store_const', help='Record resumption cache events rather than' diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index d825711..7126fa3 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -1726,6 +1726,29 @@ TEST_F(HTTPSCRLSetTest, ExpiredCRLSet) { EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } +TEST_F(HTTPSRequestTest, SSLv3Fallback) { + TestServer::HTTPSOptions https_options( + TestServer::HTTPSOptions::CERT_OK); + https_options.tls_intolerant = true; + TestServer test_server(https_options, + FilePath(FILE_PATH_LITERAL("net/data/ssl"))); + ASSERT_TRUE(test_server.Start()); + + TestDelegate d; + scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext(true)); + context->Init(); + d.set_allow_certificate_errors(true); + URLRequest r(test_server.GetURL(""), &d); + r.set_context(context.get()); + r.Start(); + + MessageLoop::current()->Run(); + + EXPECT_EQ(1, d.response_started_count()); + EXPECT_NE(0, d.bytes_received()); + EXPECT_TRUE(r.ssl_info().connection_status & SSL_CONNECTION_SSL3_FALLBACK); +} + // This tests that a load of www.google.com with a certificate error sets // the |certificate_errors_are_fatal| flag correctly. This flag will cause // the interstitial to be fatal. diff --git a/third_party/tlslite/README.chromium b/third_party/tlslite/README.chromium index ea99656..916fd36 100644 --- a/third_party/tlslite/README.chromium +++ b/third_party/tlslite/README.chromium @@ -30,3 +30,4 @@ Local Modifications: - patches/parse_chain.patch: tlslite/X509CertChain.py and tlslite/X509.py were updated to add a parseChain method, that can parse multiple certificates from a PEM string. +- patches/tls_intolerant.patch: allow TLSLite to simulate a TLS-intolerant server. diff --git a/third_party/tlslite/patches/tls_intolerant.patch b/third_party/tlslite/patches/tls_intolerant.patch new file mode 100644 index 0000000..506b4d3c --- /dev/null +++ b/third_party/tlslite/patches/tls_intolerant.patch @@ -0,0 +1,60 @@ +diff --git a/third_party/tlslite/tlslite/TLSConnection.py b/third_party/tlslite/tlslite/TLSConnection.py +index 7e38a23..02c7478 100644 +--- a/third_party/tlslite/tlslite/TLSConnection.py ++++ b/third_party/tlslite/tlslite/TLSConnection.py +@@ -932,7 +932,7 @@ class TLSConnection(TLSRecordLayer): + def handshakeServer(self, sharedKeyDB=None, verifierDB=None, + certChain=None, privateKey=None, reqCert=False, + sessionCache=None, settings=None, checker=None, +- reqCAs=None): ++ reqCAs=None, tlsIntolerant=False): + """Perform a handshake in the role of server. + + This function performs an SSL or TLS handshake. Depending on +@@ -1012,14 +1012,14 @@ class TLSConnection(TLSRecordLayer): + """ + for result in self.handshakeServerAsync(sharedKeyDB, verifierDB, + certChain, privateKey, reqCert, sessionCache, settings, +- checker, reqCAs): ++ checker, reqCAs, tlsIntolerant): + pass + + + def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None, + certChain=None, privateKey=None, reqCert=False, + sessionCache=None, settings=None, checker=None, +- reqCAs=None): ++ reqCAs=None, tlsIntolerant=False): + """Start a server handshake operation on the TLS connection. + + This function returns a generator which behaves similarly to +@@ -1036,14 +1036,15 @@ class TLSConnection(TLSRecordLayer): + verifierDB=verifierDB, certChain=certChain, + privateKey=privateKey, reqCert=reqCert, + sessionCache=sessionCache, settings=settings, +- reqCAs=reqCAs) ++ reqCAs=reqCAs, ++ tlsIntolerant=tlsIntolerant) + for result in self._handshakeWrapperAsync(handshaker, checker): + yield result + + + def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB, + certChain, privateKey, reqCert, sessionCache, +- settings, reqCAs): ++ settings, reqCAs, tlsIntolerant): + + self._handshakeStart(client=False) + +@@ -1111,6 +1112,11 @@ class TLSConnection(TLSRecordLayer): + "Too old version: %s" % str(clientHello.client_version)): + yield result + ++ if tlsIntolerant and clientHello.client_version > (3, 0): ++ for result in self._sendError(\ ++ AlertDescription.handshake_failure): ++ yield result ++ + #If client's version is too high, propose my highest version + elif clientHello.client_version > settings.maxVersion: + self.version = settings.maxVersion diff --git a/third_party/tlslite/tlslite/TLSConnection.py b/third_party/tlslite/tlslite/TLSConnection.py index 7e38a23..02c7478 100644 --- a/third_party/tlslite/tlslite/TLSConnection.py +++ b/third_party/tlslite/tlslite/TLSConnection.py @@ -932,7 +932,7 @@ class TLSConnection(TLSRecordLayer): def handshakeServer(self, sharedKeyDB=None, verifierDB=None, certChain=None, privateKey=None, reqCert=False, sessionCache=None, settings=None, checker=None, - reqCAs=None): + reqCAs=None, tlsIntolerant=False): """Perform a handshake in the role of server. This function performs an SSL or TLS handshake. Depending on @@ -1012,14 +1012,14 @@ class TLSConnection(TLSRecordLayer): """ for result in self.handshakeServerAsync(sharedKeyDB, verifierDB, certChain, privateKey, reqCert, sessionCache, settings, - checker, reqCAs): + checker, reqCAs, tlsIntolerant): pass def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None, certChain=None, privateKey=None, reqCert=False, sessionCache=None, settings=None, checker=None, - reqCAs=None): + reqCAs=None, tlsIntolerant=False): """Start a server handshake operation on the TLS connection. This function returns a generator which behaves similarly to @@ -1036,14 +1036,15 @@ class TLSConnection(TLSRecordLayer): verifierDB=verifierDB, certChain=certChain, privateKey=privateKey, reqCert=reqCert, sessionCache=sessionCache, settings=settings, - reqCAs=reqCAs) + reqCAs=reqCAs, + tlsIntolerant=tlsIntolerant) for result in self._handshakeWrapperAsync(handshaker, checker): yield result def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB, certChain, privateKey, reqCert, sessionCache, - settings, reqCAs): + settings, reqCAs, tlsIntolerant): self._handshakeStart(client=False) @@ -1111,6 +1112,11 @@ class TLSConnection(TLSRecordLayer): "Too old version: %s" % str(clientHello.client_version)): yield result + if tlsIntolerant and clientHello.client_version > (3, 0): + for result in self._sendError(\ + AlertDescription.handshake_failure): + yield result + #If client's version is too high, propose my highest version elif clientHello.client_version > settings.maxVersion: self.version = settings.maxVersion |