diff options
author | Ben Murdoch <benm@google.com> | 2010-11-18 18:32:45 +0000 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-11-18 18:38:07 +0000 |
commit | 513209b27ff55e2841eac0e4120199c23acce758 (patch) | |
tree | aeba30bb08c5f47c57003544e378a377c297eee6 /net/socket | |
parent | 164f7496de0fbee436b385a79ead9e3cb81a50c1 (diff) | |
download | external_chromium-513209b27ff55e2841eac0e4120199c23acce758.zip external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.gz external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.bz2 |
Merge Chromium at r65505: Initial merge by git.
Change-Id: I31d8f1d8cd33caaf7f47ffa7350aef42d5fbdb45
Diffstat (limited to 'net/socket')
43 files changed, 1196 insertions, 471 deletions
diff --git a/net/socket/client_socket.cc b/net/socket/client_socket.cc index 6f38eae..6b12841 100644 --- a/net/socket/client_socket.cc +++ b/net/socket/client_socket.cc @@ -58,6 +58,14 @@ ClientSocket::UseHistory::~UseHistory() { EmitPreconnectionHistograms(); } +void ClientSocket::UseHistory::Reset() { + EmitPreconnectionHistograms(); + was_ever_connected_ = false; + was_used_to_convey_data_ = false; + // omnibox_speculation_ and subresource_speculation_ values + // are intentionally preserved. +} + void ClientSocket::UseHistory::EmitPreconnectionHistograms() const { DCHECK(!subresource_speculation_ || !omnibox_speculation_); // 0 ==> non-speculative, never connected. diff --git a/net/socket/client_socket.h b/net/socket/client_socket.h index b4173c0..358716c 100644 --- a/net/socket/client_socket.h +++ b/net/socket/client_socket.h @@ -69,6 +69,10 @@ class ClientSocket : public Socket { // this call to the transport socket. virtual bool WasEverUsed() const = 0; + // Returns true if the underlying transport socket is using TCP FastOpen. + // TCP FastOpen is an experiment with sending data in the TCP SYN packet. + virtual bool UsingTCPFastOpen() const = 0; + protected: // The following class is only used to gather statistics about the history of // a socket. It is only instantiated and used in basic sockets, such as @@ -80,6 +84,10 @@ class ClientSocket : public Socket { UseHistory(); ~UseHistory(); + // Resets the state of UseHistory and emits histograms for the + // current state. + void Reset(); + void set_was_ever_connected(); void set_was_used_to_convey_data(); diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc index 3ac5a00..947a2fa 100644 --- a/net/socket/client_socket_factory.cc +++ b/net/socket/client_socket_factory.cc @@ -16,7 +16,6 @@ #elif defined(USE_OPENSSL) && defined(ANDROID) #include "net/socket/ssl_client_socket_openssl.h" #elif defined(OS_MACOSX) -#include "net/socket/ssl_client_socket_mac.h" #include "net/socket/ssl_client_socket_nss.h" #endif #include "net/socket/ssl_host_info.h" @@ -24,13 +23,16 @@ namespace net { +class DnsRRResolver; + namespace { SSLClientSocket* DefaultSSLClientSocketFactory( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { scoped_ptr<SSLHostInfo> shi(ssl_host_info); #if defined(OS_WIN) return new SSLClientSocketWin(transport_socket, hostname, ssl_config); @@ -38,16 +40,10 @@ SSLClientSocket* DefaultSSLClientSocketFactory( return new SSLClientSocketOpenSSL(transport_socket, hostname, ssl_config); #elif defined(USE_NSS) return new SSLClientSocketNSS(transport_socket, hostname, ssl_config, - shi.release()); + shi.release(), dnsrr_resolver); #elif defined(OS_MACOSX) - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // Mac OS X CDSA/CSSM yet (http://crbug.com/45369), so fall back on - // SSLClientSocketMac. - if (ssl_config.send_client_cert) - return new SSLClientSocketMac(transport_socket, hostname, ssl_config); - return new SSLClientSocketNSS(transport_socket, hostname, ssl_config, - shi.release()); + shi.release(), dnsrr_resolver); #else NOTIMPLEMENTED(); return NULL; @@ -69,8 +65,10 @@ class DefaultClientSocketFactory : public ClientSocketFactory { ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { - return g_ssl_factory(transport_socket, hostname, ssl_config, ssl_host_info); + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { + return g_ssl_factory(transport_socket, hostname, ssl_config, ssl_host_info, + dnsrr_resolver); } }; @@ -96,7 +94,7 @@ SSLClientSocket* ClientSocketFactory::CreateSSLClientSocket( ClientSocketHandle* socket_handle = new ClientSocketHandle(); socket_handle->set_socket(transport_socket); return CreateSSLClientSocket(socket_handle, hostname, ssl_config, - ssl_host_info); + ssl_host_info, NULL /* DnsRRResolver */); } } // namespace net diff --git a/net/socket/client_socket_factory.h b/net/socket/client_socket_factory.h index ad2cc54..4814b9c 100644 --- a/net/socket/client_socket_factory.h +++ b/net/socket/client_socket_factory.h @@ -15,6 +15,7 @@ namespace net { class AddressList; class ClientSocket; class ClientSocketHandle; +class DnsRRResolver; class SSLClientSocket; struct SSLConfig; class SSLHostInfo; @@ -24,7 +25,8 @@ typedef SSLClientSocket* (*SSLClientSocketFactory)( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info); + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver); // An interface used to instantiate ClientSocket objects. Used to facilitate // testing code with mock socket implementations. @@ -43,7 +45,8 @@ class ClientSocketFactory { ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) = 0; + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) = 0; // Deprecated function (http://crbug.com/37810) that takes a ClientSocket. virtual SSLClientSocket* CreateSSLClientSocket(ClientSocket* transport_socket, diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc index 6184905..29b5bd3 100644 --- a/net/socket/client_socket_handle.cc +++ b/net/socket/client_socket_handle.cc @@ -116,7 +116,8 @@ void ClientSocketHandle::HandleInitCompletion(int result) { DCHECK(socket_.get()); socket_->NetLog().BeginEvent( NetLog::TYPE_SOCKET_IN_USE, - new NetLogSourceParameter("source_dependency", requesting_source_)); + make_scoped_refptr(new NetLogSourceParameter( + "source_dependency", requesting_source_))); } } // namespace net diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc index 75abae6..2228729 100644 --- a/net/socket/client_socket_pool_base.cc +++ b/net/socket/client_socket_pool_base.cc @@ -87,9 +87,9 @@ void ConnectJob::UseForNormalRequest() { void ConnectJob::set_socket(ClientSocket* socket) { if (socket) { - net_log().AddEvent(NetLog::TYPE_CONNECT_JOB_SET_SOCKET, - new NetLogSourceParameter("source_dependency", - socket->NetLog().source())); + net_log().AddEvent(NetLog::TYPE_CONNECT_JOB_SET_SOCKET, make_scoped_refptr( + new NetLogSourceParameter("source_dependency", + socket->NetLog().source()))); } socket_.reset(socket); } @@ -110,7 +110,7 @@ void ConnectJob::ResetTimer(base::TimeDelta remaining_time) { void ConnectJob::LogConnectStart() { net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT, - new NetLogStringParameter("group_name", group_name_)); + make_scoped_refptr(new NetLogStringParameter("group_name", group_name_))); } void ConnectJob::LogConnectCompletion(int net_error) { @@ -239,7 +239,8 @@ void ClientSocketPoolBaseHelper::RequestSockets( request.net_log().BeginEvent( NetLog::TYPE_SOCKET_POOL_CONNECTING_N_SOCKETS, - new NetLogIntegerParameter("num_sockets", num_sockets)); + make_scoped_refptr(new NetLogIntegerParameter( + "num_sockets", num_sockets))); Group* group = GetOrCreateGroup(group_name); @@ -394,7 +395,8 @@ void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest( const NetLog::Source& connect_job_source, const Request* request) { request->net_log().AddEvent( NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB, - new NetLogSourceParameter("source_dependency", connect_job_source)); + make_scoped_refptr(new NetLogSourceParameter( + "source_dependency", connect_job_source))); } void ClientSocketPoolBaseHelper::CancelRequest( @@ -763,8 +765,8 @@ void ClientSocketPoolBaseHelper::OnConnectJobComplete( HandOutSocket(socket.release(), false /* unused socket */, r->handle(), base::TimeDelta(), group, r->net_log()); } - r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, - new NetLogIntegerParameter("net_error", result)); + r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, make_scoped_refptr( + new NetLogIntegerParameter("net_error", result))); InvokeUserCallbackLater(r->handle(), r->callback(), result); } else { RemoveConnectJob(job, group); @@ -849,13 +851,13 @@ void ClientSocketPoolBaseHelper::HandOutSocket( if (reused) { net_log.AddEvent( NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET, - new NetLogIntegerParameter( - "idle_ms", static_cast<int>(idle_time.InMilliseconds()))); + make_scoped_refptr(new NetLogIntegerParameter( + "idle_ms", static_cast<int>(idle_time.InMilliseconds())))); } net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET, - new NetLogSourceParameter( - "source_dependency", socket->NetLog().source())); + make_scoped_refptr(new NetLogSourceParameter( + "source_dependency", socket->NetLog().source()))); handed_out_socket_count_++; group->IncrementActiveSocketCount(); diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index 820b030..7b83162 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc @@ -81,6 +81,7 @@ class MockClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return was_used_to_convey_data_; } + virtual bool UsingTCPFastOpen() const { return false; } private: bool connected_; @@ -108,7 +109,8 @@ class MockClientSocketFactory : public ClientSocketFactory { ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { NOTIMPLEMENTED(); delete ssl_host_info; return NULL; @@ -1280,7 +1282,7 @@ class RequestSocketCallback : public CallbackRunner< Tuple1<int> > { } within_callback_ = true; TestCompletionCallback next_job_callback; - scoped_refptr<TestSocketParams> params = new TestSocketParams(); + scoped_refptr<TestSocketParams> params(new TestSocketParams()); int rv = handle_->Init("a", params, kDefaultPriority, @@ -2086,7 +2088,7 @@ class TestReleasingSocketRequest : public CallbackRunner< Tuple1<int> > { callback_.RunWithParams(params); if (reset_releasing_handle_) handle_.Reset(); - scoped_refptr<TestSocketParams> con_params = new TestSocketParams(); + scoped_refptr<TestSocketParams> con_params(new TestSocketParams()); EXPECT_EQ(expected_result_, handle2_.Init("a", con_params, kDefaultPriority, diff --git a/net/socket/deterministic_socket_data_unittest.cc b/net/socket/deterministic_socket_data_unittest.cc index 199dd0b..5e25aa0 100644 --- a/net/socket/deterministic_socket_data_unittest.cc +++ b/net/socket/deterministic_socket_data_unittest.cc @@ -131,7 +131,7 @@ void DeterministicSocketDataTest::AssertReadBufferEquals(const char* data, void DeterministicSocketDataTest::AssertSyncWriteEquals(const char* data, int len) { - scoped_refptr<IOBuffer> buf = new IOBuffer(len); + scoped_refptr<IOBuffer> buf(new IOBuffer(len)); memcpy(buf->data(), data, len); // Issue the write, which will complete immediately @@ -152,7 +152,7 @@ void DeterministicSocketDataTest::AssertAsyncWriteEquals(const char* data, void DeterministicSocketDataTest::AssertWriteReturns(const char* data, int len, int rv) { - scoped_refptr<IOBuffer> buf = new IOBuffer(len); + scoped_refptr<IOBuffer> buf(new IOBuffer(len)); memcpy(buf->data(), data, len); // Issue the read, which will complete asynchronously diff --git a/net/socket/dns_cert_provenance_check.cc b/net/socket/dns_cert_provenance_check.cc new file mode 100644 index 0000000..e83cb5e --- /dev/null +++ b/net/socket/dns_cert_provenance_check.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2010 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 "net/socket/dns_cert_provenance_check.h" + +#include <nspr.h> +#include <hasht.h> +#include <sechash.h> + +#include <string> + +#include "base/non_thread_safe.h" +#include "net/base/completion_callback.h" +#include "net/base/dns_util.h" +#include "net/base/dnsrr_resolver.h" +#include "net/base/net_log.h" +#include "net/base/net_errors.h" + +namespace net { + +namespace { + +class DNSCertProvenanceChecker : public NonThreadSafe { + public: + DNSCertProvenanceChecker(const std::string hostname, + DnsRRResolver* dnsrr_resolver, + const std::vector<base::StringPiece>& der_certs) + : hostname_(hostname), + dnsrr_resolver_(dnsrr_resolver), + der_certs_(der_certs.size()), + handle_(DnsRRResolver::kInvalidHandle), + ALLOW_THIS_IN_INITIALIZER_LIST(callback_( + this, &DNSCertProvenanceChecker::ResolutionComplete)) { + for (size_t i = 0; i < der_certs.size(); i++) + der_certs_[i] = der_certs[i].as_string(); + } + + void Start() { + DCHECK(CalledOnValidThread()); + + if (der_certs_.empty()) + return; + + uint8 fingerprint[SHA1_LENGTH]; + SECStatus rv = HASH_HashBuf( + HASH_AlgSHA1, fingerprint, (uint8*) der_certs_[0].data(), + der_certs_[0].size()); + DCHECK_EQ(SECSuccess, rv); + char fingerprint_hex[SHA1_LENGTH * 2 + 1]; + for (unsigned i = 0; i < sizeof(fingerprint); i++) { + static const char hextable[] = "0123456789abcdef"; + fingerprint_hex[i*2] = hextable[fingerprint[i] >> 4]; + fingerprint_hex[i*2 + 1] = hextable[fingerprint[i] & 15]; + } + fingerprint_hex[SHA1_LENGTH * 2] = 0; + + static const char kBaseCertName[] = ".certs.links.org"; + domain_.assign(fingerprint_hex); + domain_.append(kBaseCertName); + + handle_ = dnsrr_resolver_->Resolve( + domain_, kDNS_TXT, 0 /* flags */, &callback_, &response_, + 0 /* priority */, BoundNetLog()); + if (handle_ == DnsRRResolver::kInvalidHandle) { + LOG(ERROR) << "Failed to resolve " << domain_ << " for " << hostname_; + delete this; + } + } + + private: + void ResolutionComplete(int status) { + DCHECK(CalledOnValidThread()); + + if (status == ERR_NAME_NOT_RESOLVED || + (status == OK && response_.rrdatas.empty())) { + LOG(ERROR) << "FAILED" + << " hostname:" << hostname_ + << " domain:" << domain_; + } else if (status == OK) { + LOG(ERROR) << "GOOD" + << " hostname:" << hostname_ + << " resp:" << response_.rrdatas[0]; + } else { + LOG(ERROR) << "Unknown error " << status << " for " << domain_; + } + + delete this; + } + + const std::string hostname_; + std::string domain_; + DnsRRResolver* const dnsrr_resolver_; + std::vector<std::string> der_certs_; + RRResponse response_; + DnsRRResolver::Handle handle_; + CompletionCallbackImpl<DNSCertProvenanceChecker> callback_; +}; + +} // anonymous namespace + +void DoAsyncDNSCertProvenanceVerification( + const std::string& hostname, + DnsRRResolver* dnsrr_resolver, + const std::vector<base::StringPiece>& der_certs) { + DNSCertProvenanceChecker* c(new DNSCertProvenanceChecker( + hostname, dnsrr_resolver, der_certs)); + c->Start(); +} + +} // namespace net diff --git a/net/socket/dns_cert_provenance_check.h b/net/socket/dns_cert_provenance_check.h new file mode 100644 index 0000000..289cccf --- /dev/null +++ b/net/socket/dns_cert_provenance_check.h @@ -0,0 +1,26 @@ +// Copyright (c) 2010 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. + +#ifndef NET_SOCKET_DNS_CERT_PROVENANCE_CHECK_H +#define NET_SOCKET_DNS_CERT_PROVENANCE_CHECK_H + +#include <string> +#include <vector> + +#include "base/string_piece.h" + +namespace net { + +class DnsRRResolver; + +// DoAsyncDNSCertProvenanceVerification starts an asynchronous check for the +// given certificate chain. It must be run on the network thread. +void DoAsyncDNSCertProvenanceVerification( + const std::string& hostname, + DnsRRResolver* dnsrr_resolver, + const std::vector<base::StringPiece>& der_certs); + +} // namespace net + +#endif // NET_SOCKET_DNS_CERT_PROVENANCE_CHECK_H diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index 53bcf89..57aef05 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -513,6 +513,10 @@ bool MockSSLClientSocket::WasEverUsed() const { return transport_->socket()->WasEverUsed(); } +bool MockSSLClientSocket::UsingTCPFastOpen() const { + return transport_->socket()->UsingTCPFastOpen(); +} + int MockSSLClientSocket::Read(net::IOBuffer* buf, int buf_len, net::CompletionCallback* callback) { return transport_->socket()->Read(buf, buf_len, callback); @@ -1011,7 +1015,8 @@ SSLClientSocket* MockClientSocketFactory::CreateSSLClientSocket( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { MockSSLClientSocket* socket = new MockSSLClientSocket(transport_socket, hostname, ssl_config, ssl_host_info, mock_ssl_data_.GetNext()); @@ -1060,7 +1065,8 @@ SSLClientSocket* DeterministicMockClientSocketFactory::CreateSSLClientSocket( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { MockSSLClientSocket* socket = new MockSSLClientSocket(transport_socket, hostname, ssl_config, ssl_host_info, mock_ssl_data_.GetNext()); diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index a8e4537..349013e 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -534,7 +534,8 @@ class MockClientSocketFactory : public ClientSocketFactory { ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info); + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver); SocketDataProviderArray<SocketDataProvider>& mock_data() { return mock_data_; } @@ -609,6 +610,7 @@ class MockTCPClientSocket : public MockClientSocket { virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const { return IsConnected(); } virtual bool WasEverUsed() const { return was_used_to_convey_data_; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(net::IOBuffer* buf, int buf_len, @@ -654,6 +656,7 @@ class DeterministicMockTCPClientSocket : public MockClientSocket, virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const { return IsConnected(); } virtual bool WasEverUsed() const { return was_used_to_convey_data_; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Write(net::IOBuffer* buf, int buf_len, @@ -698,6 +701,7 @@ class MockSSLClientSocket : public MockClientSocket { virtual void Disconnect(); virtual bool IsConnected() const; virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(net::IOBuffer* buf, int buf_len, @@ -875,7 +879,8 @@ class DeterministicMockClientSocketFactory : public ClientSocketFactory { ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info); + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver); SocketDataProviderArray<DeterministicSocketData>& mock_data() { return mock_data_; diff --git a/net/socket/socks5_client_socket.cc b/net/socket/socks5_client_socket.cc index 42bb859..02e5b1f 100644 --- a/net/socket/socks5_client_socket.cc +++ b/net/socket/socks5_client_socket.cc @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/debug/trace_event.h" #include "base/format_macros.h" #include "base/string_util.h" #include "net/base/io_buffer.h" @@ -133,6 +134,14 @@ bool SOCKS5ClientSocket::WasEverUsed() const { return false; } +bool SOCKS5ClientSocket::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + // Read is called by the transport layer above to read. This can only be done // if the SOCKS handshake is complete. int SOCKS5ClientSocket::Read(IOBuffer* buf, int buf_len, @@ -303,13 +312,15 @@ int SOCKS5ClientSocket::DoGreetReadComplete(int result) { // Got the greet data. if (buffer_[0] != kSOCKS5Version) { - net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTED_VERSION, - new NetLogIntegerParameter("version", buffer_[0])); + net_log_.AddEvent( + NetLog::TYPE_SOCKS_UNEXPECTED_VERSION, + make_scoped_refptr(new NetLogIntegerParameter("version", buffer_[0]))); return ERR_SOCKS_CONNECTION_FAILED; } if (buffer_[1] != 0x00) { - net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTED_AUTH, - new NetLogIntegerParameter("method", buffer_[1])); + net_log_.AddEvent( + NetLog::TYPE_SOCKS_UNEXPECTED_AUTH, + make_scoped_refptr(new NetLogIntegerParameter("method", buffer_[1]))); return ERR_SOCKS_CONNECTION_FAILED; } @@ -412,13 +423,17 @@ int SOCKS5ClientSocket::DoHandshakeReadComplete(int result) { // and accordingly increase them if (bytes_received_ == kReadHeaderSize) { if (buffer_[0] != kSOCKS5Version || buffer_[2] != kNullByte) { - net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTED_VERSION, - new NetLogIntegerParameter("version", buffer_[0])); + net_log_.AddEvent( + NetLog::TYPE_SOCKS_UNEXPECTED_VERSION, + make_scoped_refptr( + new NetLogIntegerParameter("version", buffer_[0]))); return ERR_SOCKS_CONNECTION_FAILED; } if (buffer_[1] != 0x00) { - net_log_.AddEvent(NetLog::TYPE_SOCKS_SERVER_ERROR, - new NetLogIntegerParameter("error_code", buffer_[1])); + net_log_.AddEvent( + NetLog::TYPE_SOCKS_SERVER_ERROR, + make_scoped_refptr( + new NetLogIntegerParameter("error_code", buffer_[1]))); return ERR_SOCKS_CONNECTION_FAILED; } @@ -436,8 +451,10 @@ int SOCKS5ClientSocket::DoHandshakeReadComplete(int result) { else if (address_type == kEndPointResolvedIPv6) read_header_size += sizeof(struct in6_addr) - 1; else { - net_log_.AddEvent(NetLog::TYPE_SOCKS_UNKNOWN_ADDRESS_TYPE, - new NetLogIntegerParameter("address_type", buffer_[3])); + net_log_.AddEvent( + NetLog::TYPE_SOCKS_UNKNOWN_ADDRESS_TYPE, + make_scoped_refptr( + new NetLogIntegerParameter("address_type", buffer_[3]))); return ERR_SOCKS_CONNECTION_FAILED; } diff --git a/net/socket/socks5_client_socket.h b/net/socket/socks5_client_socket.h index 72e27db..5a918cc 100644 --- a/net/socket/socks5_client_socket.h +++ b/net/socket/socks5_client_socket.h @@ -59,6 +59,7 @@ class SOCKS5ClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/socks5_client_socket_unittest.cc b/net/socket/socks5_client_socket_unittest.cc index 43a441d..2152c86 100644 --- a/net/socket/socks5_client_socket_unittest.cc +++ b/net/socket/socks5_client_socket_unittest.cc @@ -141,7 +141,7 @@ TEST_F(SOCKS5ClientSocketTest, CompleteHandshake) { EXPECT_TRUE(LogContainsEndEvent(net_log_.entries(), -1, NetLog::TYPE_SOCKS5_CONNECT)); - scoped_refptr<IOBuffer> buffer = new IOBuffer(payload_write.size()); + scoped_refptr<IOBuffer> buffer(new IOBuffer(payload_write.size())); memcpy(buffer->data(), payload_write.data(), payload_write.size()); rv = user_sock_->Write(buffer, payload_write.size(), &callback_); EXPECT_EQ(ERR_IO_PENDING, rv); diff --git a/net/socket/socks_client_socket.cc b/net/socket/socks_client_socket.cc index 26cad34..4dad417 100644 --- a/net/socket/socks_client_socket.cc +++ b/net/socket/socks_client_socket.cc @@ -168,6 +168,15 @@ bool SOCKSClientSocket::WasEverUsed() const { return false; } +bool SOCKSClientSocket::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + + // Read is called by the transport layer above to read. This can only be done // if the SOCKS handshake is complete. int SOCKSClientSocket::Read(IOBuffer* buf, int buf_len, diff --git a/net/socket/socks_client_socket.h b/net/socket/socks_client_socket.h index e9e9695..86511cf 100644 --- a/net/socket/socks_client_socket.h +++ b/net/socket/socks_client_socket.h @@ -56,6 +56,7 @@ class SOCKSClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/socks_client_socket_unittest.cc b/net/socket/socks_client_socket_unittest.cc index 086b21d..11a88ae 100644 --- a/net/socket/socks_client_socket_unittest.cc +++ b/net/socket/socks_client_socket_unittest.cc @@ -153,7 +153,7 @@ TEST_F(SOCKSClientSocketTest, CompleteHandshake) { EXPECT_TRUE(LogContainsEndEvent( log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT)); - scoped_refptr<IOBuffer> buffer = new IOBuffer(payload_write.size()); + scoped_refptr<IOBuffer> buffer(new IOBuffer(payload_write.size())); memcpy(buffer->data(), payload_write.data(), payload_write.size()); rv = user_sock_->Write(buffer, payload_write.size(), &callback_); EXPECT_EQ(ERR_IO_PENDING, rv); diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc index 6a8270e..d6fca9b 100644 --- a/net/socket/ssl_client_socket_mac.cc +++ b/net/socket/ssl_client_socket_mac.cc @@ -614,6 +614,14 @@ bool SSLClientSocketMac::WasEverUsed() const { return false; } +bool SSLClientSocketMac::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(completed_handshake()); @@ -1124,8 +1132,8 @@ int SSLClientSocketMac::DidCompleteHandshake() { DCHECK(!server_cert_ || renegotiating_); VLOG(1) << "Handshake completed, next verify cert"; - scoped_refptr<X509Certificate> new_server_cert = - GetServerCert(ssl_context_); + scoped_refptr<X509Certificate> new_server_cert( + GetServerCert(ssl_context_)); if (!new_server_cert) return ERR_UNEXPECTED; diff --git a/net/socket/ssl_client_socket_mac.h b/net/socket/ssl_client_socket_mac.h index 00438fc..0763fd3 100644 --- a/net/socket/ssl_client_socket_mac.h +++ b/net/socket/ssl_client_socket_mac.h @@ -50,6 +50,7 @@ class SSLClientSocketMac : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_mac_factory.cc b/net/socket/ssl_client_socket_mac_factory.cc index 7f0c5ce..d10e10d 100644 --- a/net/socket/ssl_client_socket_mac_factory.cc +++ b/net/socket/ssl_client_socket_mac_factory.cc @@ -13,7 +13,8 @@ SSLClientSocket* SSLClientSocketMacFactory( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { delete ssl_host_info; return new SSLClientSocketMac(transport_socket, hostname, ssl_config); } diff --git a/net/socket/ssl_client_socket_mac_factory.h b/net/socket/ssl_client_socket_mac_factory.h index ca97b00..6f12883 100644 --- a/net/socket/ssl_client_socket_mac_factory.h +++ b/net/socket/ssl_client_socket_mac_factory.h @@ -10,6 +10,7 @@ namespace net { +class DnsRRResolver; class SSLHostInfo; // Creates SSLClientSocketMac objects. @@ -17,7 +18,8 @@ SSLClientSocket* SSLClientSocketMacFactory( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info); + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver); } // namespace net diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 136f138..a6aa458 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -50,6 +50,9 @@ #if defined(USE_SYSTEM_SSL) #include <dlfcn.h> #endif +#if defined(OS_MACOSX) +#include <Security/Security.h> +#endif #include <certdb.h> #include <hasht.h> #include <keyhi.h> @@ -60,6 +63,7 @@ #include <sechash.h> #include <ssl.h> #include <sslerr.h> +#include <sslproto.h> #include <limits> @@ -71,10 +75,12 @@ #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/stringprintf.h" +#include "base/thread_restrictions.h" #include "base/values.h" #include "net/base/address_list.h" #include "net/base/cert_status_flags.h" #include "net/base/cert_verifier.h" +#include "net/base/connection_type_histograms.h" #include "net/base/dns_util.h" #include "net/base/dnsrr_resolver.h" #include "net/base/dnssec_chain_verifier.h" @@ -87,6 +93,7 @@ #include "net/base/sys_addrinfo.h" #include "net/ocsp/nss_ocsp.h" #include "net/socket/client_socket_handle.h" +#include "net/socket/dns_cert_provenance_check.h" #include "net/socket/ssl_host_info.h" static const int kRecvBufferSize = 4096; @@ -176,6 +183,11 @@ class NSSSSLInitSingleton { // thread-safe, and the NSS SSL library will only ever be initialized once. // The NSS SSL library will be properly shut down on program exit. void EnsureNSSSSLInit() { + // Initializing SSL causes us to do blocking IO. + // Temporarily allow it until we fix + // http://code.google.com/p/chromium/issues/detail?id=59847 + base::ThreadRestrictions::ScopedAllowIO allow_io; + Singleton<NSSSSLInitSingleton>::get(); } @@ -206,6 +218,8 @@ int MapNSPRError(PRErrorCode err) { return ERR_INVALID_ARGUMENT; case PR_END_OF_FILE_ERROR: return ERR_CONNECTION_CLOSED; + case PR_NOT_IMPLEMENTED_ERROR: + return ERR_NOT_IMPLEMENTED; case SEC_ERROR_INVALID_ARGS: return ERR_INVALID_ARGUMENT; @@ -309,52 +323,13 @@ class SSLFailedNSSFunctionParams : public NetLog::EventParameters { void LogFailedNSSFunction(const BoundNetLog& net_log, const char* function, const char* param) { - net_log.AddEvent(NetLog::TYPE_SSL_NSS_ERROR, - new SSLFailedNSSFunctionParams(function, param)); + net_log.AddEvent( + NetLog::TYPE_SSL_NSS_ERROR, + make_scoped_refptr(new SSLFailedNSSFunctionParams(function, param))); } #if defined(OS_WIN) -// A certificate for COMODO EV SGC CA, issued by AddTrust External CA Root, -// causes CertGetCertificateChain to report CERT_TRUST_IS_NOT_VALID_FOR_USAGE. -// It seems to be caused by the szOID_APPLICATION_CERT_POLICIES extension in -// that certificate. -// -// This function is used in the workaround for http://crbug.com/43538 -bool IsProblematicComodoEVCACert(const CERTCertificate& cert) { - // Issuer: - // CN = AddTrust External CA Root - // OU = AddTrust External TTP Network - // O = AddTrust AB - // C = SE - static const uint8 kIssuer[] = { - 0x30, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x53, 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, 0x41, 0x64, 0x64, 0x54, - 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31, 0x26, 0x30, - 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64, - 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, - 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x41, 0x64, - 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52, - 0x6f, 0x6f, 0x74 - }; - - // Serial number: 79:0A:83:4D:48:40:6B:AB:6C:35:2A:D5:1F:42:83:FE. - static const uint8 kSerialNumber[] = { - 0x79, 0x0a, 0x83, 0x4d, 0x48, 0x40, 0x6b, 0xab, 0x6c, 0x35, - 0x2a, 0xd5, 0x1f, 0x42, 0x83, 0xfe - }; - - return cert.derIssuer.len == sizeof(kIssuer) && - memcmp(cert.derIssuer.data, kIssuer, cert.derIssuer.len) == 0 && - cert.serialNumber.len == sizeof(kSerialNumber) && - memcmp(cert.serialNumber.data, kSerialNumber, - cert.serialNumber.len) == 0; -} - // This callback is intended to be used with CertFindChainInStore. In addition // to filtering by extended/enhanced key usage, we do not show expired // certificates and require digital signature usage in the key usage @@ -391,17 +366,61 @@ BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context, #endif -} // namespace +// PeerCertificateChain is a helper object which extracts the certificate +// chain, as given by the server, from an NSS socket and performs the needed +// resource management. The first element of the chain is the leaf certificate +// and the other elements are in the order given by the server. +class PeerCertificateChain { + public: + explicit PeerCertificateChain(PRFileDesc* nss_fd) + : num_certs_(0), + certs_(NULL) { + SECStatus rv = SSL_PeerCertificateChain(nss_fd, NULL, &num_certs_); + DCHECK_EQ(rv, SECSuccess); -#if defined(OS_WIN) -// static -HCERTSTORE SSLClientSocketNSS::cert_store_ = NULL; -#endif + certs_ = new CERTCertificate*[num_certs_]; + const unsigned expected_num_certs = num_certs_; + rv = SSL_PeerCertificateChain(nss_fd, certs_, &num_certs_); + DCHECK_EQ(rv, SECSuccess); + DCHECK_EQ(num_certs_, expected_num_certs); + } + + ~PeerCertificateChain() { + for (unsigned i = 0; i < num_certs_; i++) + CERT_DestroyCertificate(certs_[i]); + delete[] certs_; + } + + unsigned size() const { return num_certs_; } + + CERTCertificate* operator[](unsigned i) { + DCHECK_LT(i, num_certs_); + return certs_[i]; + } + + std::vector<base::StringPiece> AsStringPieceVector() const { + std::vector<base::StringPiece> v(size()); + for (unsigned i = 0; i < size(); i++) { + v[i] = base::StringPiece( + reinterpret_cast<const char*>(certs_[i]->derCert.data), + certs_[i]->derCert.len); + } + + return v; + } + + private: + unsigned num_certs_; + CERTCertificate** certs_; +}; + +} // namespace SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_( this, &SSLClientSocketNSS::BufferSendComplete)), ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_( @@ -420,12 +439,15 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket, user_read_buf_len_(0), user_write_buf_len_(0), server_cert_nss_(NULL), + server_cert_verify_result_(NULL), + ssl_connection_status_(0), client_auth_cert_needed_(false), handshake_callback_called_(false), completed_handshake_(false), pseudo_connected_(false), eset_mitm_detected_(false), - netnanny_mitm_detected_(false), + predicted_cert_chain_correct_(false), + peername_initialized_(false), dnssec_provider_(NULL), next_handshake_state_(STATE_NONE), nss_fd_(NULL), @@ -433,7 +455,8 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket, net_log_(transport_socket->socket()->NetLog()), predicted_npn_status_(kNextProtoUnsupported), predicted_npn_proto_used_(false), - ssl_host_info_(ssl_host_info) { + ssl_host_info_(ssl_host_info), + dnsrr_resolver_(dnsrr_resolver) { EnterFunction(""); } @@ -475,6 +498,8 @@ void SSLClientSocketNSS::SaveSnapStartInfo() { NOTREACHED(); return; } + net_log_.AddEvent(NetLog::TYPE_SSL_SNAP_START, + new NetLogIntegerParameter("type", snap_start_type)); LOG(ERROR) << "Snap Start: " << snap_start_type << " " << hostname_; if (snap_start_type == SSL_SNAP_START_FULL || snap_start_type == SSL_SNAP_START_RESUME) { @@ -490,43 +515,32 @@ void SSLClientSocketNSS::SaveSnapStartInfo() { NOTREACHED(); return; } - // If the server doesn't support Snap Start then |hello_data_len| is zero. - if (!hello_data_len) - return; if (hello_data_len > std::numeric_limits<uint16>::max()) return; SSLHostInfo::State* state = ssl_host_info_->mutable_state(); state->server_hello = std::string(reinterpret_cast<const char *>(hello_data), hello_data_len); - state->npn_valid = true; - state->npn_status = GetNextProto(&state->npn_protocol); - - // TODO(wtc): CERT_GetCertChainFromCert might not return the same cert chain - // that the Certificate message actually contained. http://crbug.com/48854 - CERTCertList* cert_list = CERT_GetCertChainFromCert( - server_cert_nss_, PR_Now(), certUsageSSLCA); - if (!cert_list) - return; + if (hello_data_len > 0) { + state->npn_valid = true; + state->npn_status = GetNextProto(&state->npn_protocol); + } else { + state->npn_valid = false; + } - for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); - !CERT_LIST_END(node, cert_list); - node = CERT_LIST_NEXT(node)) { - if (node->cert->derCert.len > std::numeric_limits<uint16>::max()) { - CERT_DestroyCertList(cert_list); + state->certs.clear(); + PeerCertificateChain certs(nss_fd_); + for (unsigned i = 0; i < certs.size(); i++) { + if (certs[i]->derCert.len > std::numeric_limits<uint16>::max()) return; - } - if (node->cert->isRoot == PR_TRUE) - continue; + state->certs.push_back(std::string( - reinterpret_cast<char*>(node->cert->derCert.data), - node->cert->derCert.len)); + reinterpret_cast<char*>(certs[i]->derCert.data), + certs[i]->derCert.len)); } LOG(ERROR) << "Setting Snap Start info " << hostname_; ssl_host_info_->Persist(); - - CERT_DestroyCertList(cert_list); } static void DestroyCertificates(CERTCertificate** certs, unsigned len) { @@ -625,6 +639,16 @@ int SSLClientSocketNSS::Connect(CompletionCallback* callback) { return rv; } + // Attempt to initialize the peer name. In the case of TCP FastOpen, + // we don't have the peer yet. + if (!UsingTCPFastOpen()) { + rv = InitializeSSLPeerName(); + if (rv != OK) { + net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL); + return rv; + } + } + if (ssl_config_.snap_start_enabled && ssl_host_info_.get()) { GotoState(STATE_SNAP_START_LOAD_INFO); } else { @@ -655,28 +679,6 @@ int SSLClientSocketNSS::InitializeSSLOptions() { return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR error code. } - // Tell NSS who we're connected to - AddressList peer_address; - int err = transport_->socket()->GetPeerAddress(&peer_address); - if (err != OK) - return err; - - const struct addrinfo* ai = peer_address.head(); - - PRNetAddr peername; - memset(&peername, 0, sizeof(peername)); - DCHECK_LE(ai->ai_addrlen, sizeof(peername)); - size_t len = std::min(static_cast<size_t>(ai->ai_addrlen), sizeof(peername)); - memcpy(&peername, ai->ai_addr, len); - - // Adjust the address family field for BSD, whose sockaddr - // structure has a one-byte length and one-byte address family - // field at the beginning. PRNetAddr has a two-byte address - // family field at the beginning. - peername.raw.family = ai->ai_addr->sa_family; - - memio_SetPeerName(nss_fd_, &peername); - // Grab pointer to buffers nss_bufs_ = memio_GetSecret(nss_fd_); @@ -761,7 +763,7 @@ int SSLClientSocketNSS::InitializeSSLOptions() { // TODO(agl): check that SSL_ENABLE_SNAP_START actually does something in the // current NSS code. rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SNAP_START, - SSLConfigService::snap_start_enabled()); + ssl_config_.snap_start_enabled); if (rv != SECSuccess) VLOG(1) << "SSL_ENABLE_SNAP_START failed. Old system nss?"; #endif @@ -816,7 +818,12 @@ int SSLClientSocketNSS::InitializeSSLOptions() { return ERR_UNEXPECTED; } +#if defined(NSS_PLATFORM_CLIENT_AUTH) + rv = SSL_GetPlatformClientAuthDataHook(nss_fd_, PlatformClientAuthHandler, + this); +#else rv = SSL_GetClientAuthDataHook(nss_fd_, ClientAuthHandler, this); +#endif if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_GetClientAuthDataHook", ""); return ERR_UNEXPECTED; @@ -831,6 +838,36 @@ int SSLClientSocketNSS::InitializeSSLOptions() { // Tell SSL the hostname we're trying to connect to. SSL_SetURL(nss_fd_, hostname_.c_str()); + // Tell SSL we're a client; needed if not letting NSPR do socket I/O + SSL_ResetHandshake(nss_fd_, 0); + + return OK; +} + +int SSLClientSocketNSS::InitializeSSLPeerName() { + // Tell NSS who we're connected to + AddressList peer_address; + int err = transport_->socket()->GetPeerAddress(&peer_address); + if (err != OK) + return err; + + const struct addrinfo* ai = peer_address.head(); + + PRNetAddr peername; + memset(&peername, 0, sizeof(peername)); + DCHECK_LE(ai->ai_addrlen, sizeof(peername)); + size_t len = std::min(static_cast<size_t>(ai->ai_addrlen), + sizeof(peername)); + memcpy(&peername, ai->ai_addr, len); + + // Adjust the address family field for BSD, whose sockaddr + // structure has a one-byte length and one-byte address family + // field at the beginning. PRNetAddr has a two-byte address + // family field at the beginning. + peername.raw.family = ai->ai_addr->sa_family; + + memio_SetPeerName(nss_fd_, &peername); + // Set the peer ID for session reuse. This is necessary when we create an // SSL tunnel through a proxy -- GetPeerName returns the proxy's address // rather than the destination server's address in that case. @@ -838,13 +875,11 @@ int SSLClientSocketNSS::InitializeSSLOptions() { // used. std::string peer_id = base::StringPrintf("%s:%d", hostname_.c_str(), peer_address.GetPort()); - rv = SSL_SetSockPeerID(nss_fd_, const_cast<char*>(peer_id.c_str())); + SECStatus rv = SSL_SetSockPeerID(nss_fd_, const_cast<char*>(peer_id.c_str())); if (rv != SECSuccess) LogFailedNSSFunction(net_log_, "SSL_SetSockPeerID", peer_id.c_str()); - // Tell SSL we're a client; needed if not letting NSPR do socket I/O - SSL_ResetHandshake(nss_fd_, 0); - + peername_initialized_ = true; return OK; } @@ -885,11 +920,14 @@ void SSLClientSocketNSS::Disconnect() { CERT_DestroyCertificate(server_cert_nss_); server_cert_nss_ = NULL; } - server_cert_verify_result_.Reset(); + local_server_cert_verify_result_.Reset(); + server_cert_verify_result_ = NULL; + ssl_connection_status_ = 0; completed_handshake_ = false; pseudo_connected_ = false; eset_mitm_detected_ = false; - netnanny_mitm_detected_= false; + predicted_cert_chain_correct_ = false; + peername_initialized_ = false; nss_bufs_ = NULL; client_certs_.clear(); client_auth_cert_needed_ = false; @@ -955,6 +993,14 @@ bool SSLClientSocketNSS::WasEverUsed() const { return false; } +bool SSLClientSocketNSS::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int SSLClientSocketNSS::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { EnterFunction(buf_len); @@ -1044,157 +1090,110 @@ bool SSLClientSocketNSS::SetSendBufferSize(int32 size) { return transport_->socket()->SetSendBufferSize(size); } -#if defined(OS_WIN) -// static -X509Certificate::OSCertHandle SSLClientSocketNSS::CreateOSCert( - const SECItem& der_cert) { - // TODO(wtc): close cert_store_ at shutdown. - if (!cert_store_) - cert_store_ = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL); - - X509Certificate::OSCertHandle cert_handle = NULL; - BOOL ok = CertAddEncodedCertificateToStore( - cert_store_, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - der_cert.data, der_cert.len, CERT_STORE_ADD_USE_EXISTING, &cert_handle); - return ok ? cert_handle : NULL; -} -#elif defined(OS_MACOSX) -// static -X509Certificate::OSCertHandle SSLClientSocketNSS::CreateOSCert( - const SECItem& der_cert) { - return X509Certificate::CreateOSCertHandleFromBytes( - reinterpret_cast<char*>(der_cert.data), der_cert.len); -} -#endif - +// Sets server_cert_ and server_cert_nss_ if not yet set. +// Returns server_cert_. X509Certificate *SSLClientSocketNSS::UpdateServerCert() { - // We set the server_cert_ from HandshakeCallback(), but this handler - // does not necessarily get called if we are continuing a cached SSL - // session. + // We set the server_cert_ from HandshakeCallback(). if (server_cert_ == NULL) { server_cert_nss_ = SSL_PeerCertificate(nss_fd_); if (server_cert_nss_) { -#if defined(OS_MACOSX) || defined(OS_WIN) - // Get each of the intermediate certificates in the server's chain. - // These will be added to the server's X509Certificate object, making - // them available to X509Certificate::Verify() for chain building. - X509Certificate::OSCertHandles intermediate_ca_certs; - X509Certificate::OSCertHandle cert_handle = NULL; - CERTCertList* cert_list = CERT_GetCertChainFromCert( - server_cert_nss_, PR_Now(), certUsageSSLCA); - if (cert_list) { - for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); - !CERT_LIST_END(node, cert_list); - node = CERT_LIST_NEXT(node)) { - if (node->cert == server_cert_nss_) - continue; -#if defined(OS_WIN) - // Work around http://crbug.com/43538 by not importing the - // problematic COMODO EV SGC CA certificate. CryptoAPI will - // download a good certificate for that CA, issued by COMODO - // Certification Authority, using the AIA extension in the server - // certificate. - if (IsProblematicComodoEVCACert(*node->cert)) - continue; -#endif - cert_handle = CreateOSCert(node->cert->derCert); - DCHECK(cert_handle); - intermediate_ca_certs.push_back(cert_handle); - } - CERT_DestroyCertList(cert_list); - } - - // Finally create the X509Certificate object. - cert_handle = CreateOSCert(server_cert_nss_->derCert); - DCHECK(cert_handle); - server_cert_ = X509Certificate::CreateFromHandle( - cert_handle, - X509Certificate::SOURCE_FROM_NETWORK, - intermediate_ca_certs); - X509Certificate::FreeOSCertHandle(cert_handle); - for (size_t i = 0; i < intermediate_ca_certs.size(); ++i) - X509Certificate::FreeOSCertHandle(intermediate_ca_certs[i]); -#else - server_cert_ = X509Certificate::CreateFromHandle( - server_cert_nss_, - X509Certificate::SOURCE_FROM_NETWORK, - X509Certificate::OSCertHandles()); -#endif + PeerCertificateChain certs(nss_fd_); + server_cert_ = X509Certificate::CreateFromDERCertChain( + certs.AsStringPieceVector()); } } return server_cert_; } -// Log an informational message if the server does not support secure -// renegotiation (RFC 5746). -void SSLClientSocketNSS::CheckSecureRenegotiation() const { - // SSL_HandshakeNegotiatedExtension was added in NSS 3.12.6. - // Since SSL_MAX_EXTENSIONS was added at the same time, we can test - // SSL_MAX_EXTENSIONS for the presence of SSL_HandshakeNegotiatedExtension. -#if defined(SSL_MAX_EXTENSIONS) - PRBool received_renego_info; - if (SSL_HandshakeNegotiatedExtension(nss_fd_, ssl_renegotiation_info_xtn, - &received_renego_info) == SECSuccess && - !received_renego_info) { - VLOG(1) << "The server " << hostname_ - << " does not support the TLS renegotiation_info extension."; - } -#endif -} - -void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { - EnterFunction(""); - ssl_info->Reset(); - - if (!server_cert_) { - LOG(DFATAL) << "!server_cert_"; - return; - } - +// Sets ssl_connection_status_. +void SSLClientSocketNSS::UpdateConnectionStatus() { SSLChannelInfo channel_info; SECStatus ok = SSL_GetChannelInfo(nss_fd_, &channel_info, sizeof(channel_info)); if (ok == SECSuccess && channel_info.length == sizeof(channel_info) && channel_info.cipherSuite) { - SSLCipherSuiteInfo cipher_info; - ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, - &cipher_info, sizeof(cipher_info)); - if (ok == SECSuccess) { - ssl_info->security_bits = cipher_info.effectiveKeyBits; - } else { - ssl_info->security_bits = -1; - LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() - << " for cipherSuite " << channel_info.cipherSuite; - } - ssl_info->connection_status |= - (((int)channel_info.cipherSuite) & SSL_CONNECTION_CIPHERSUITE_MASK) << + ssl_connection_status_ |= + (static_cast<int>(channel_info.cipherSuite) & + SSL_CONNECTION_CIPHERSUITE_MASK) << SSL_CONNECTION_CIPHERSUITE_SHIFT; - ssl_info->connection_status |= - (((int)channel_info.compressionMethod) & + ssl_connection_status_ |= + (static_cast<int>(channel_info.compressionMethod) & SSL_CONNECTION_COMPRESSION_MASK) << SSL_CONNECTION_COMPRESSION_SHIFT; - UpdateServerCert(); + // NSS 3.12.x doesn't have version macros for TLS 1.1 and 1.2 (because NSS + // doesn't support them yet), so we use 0x0302 and 0x0303 directly. + int version = SSL_CONNECTION_VERSION_UNKNOWN; + if (channel_info.protocolVersion < SSL_LIBRARY_VERSION_3_0) { + // All versions less than SSL_LIBRARY_VERSION_3_0 are treated as SSL + // version 2. + version = SSL_CONNECTION_VERSION_SSL2; + } else if (channel_info.protocolVersion == SSL_LIBRARY_VERSION_3_0) { + version = SSL_CONNECTION_VERSION_SSL3; + } else if (channel_info.protocolVersion == SSL_LIBRARY_VERSION_3_1_TLS) { + version = SSL_CONNECTION_VERSION_TLS1; + } else if (channel_info.protocolVersion == 0x0302) { + version = SSL_CONNECTION_VERSION_TLS1_1; + } else if (channel_info.protocolVersion == 0x0303) { + version = SSL_CONNECTION_VERSION_TLS1_2; + } + ssl_connection_status_ |= + (version & SSL_CONNECTION_VERSION_MASK) << + SSL_CONNECTION_VERSION_SHIFT; } - ssl_info->cert_status = server_cert_verify_result_.cert_status; - DCHECK(server_cert_ != NULL); - ssl_info->cert = server_cert_; + // SSL_HandshakeNegotiatedExtension was added in NSS 3.12.6. + // Since SSL_MAX_EXTENSIONS was added at the same time, we can test + // SSL_MAX_EXTENSIONS for the presence of SSL_HandshakeNegotiatedExtension. +#if defined(SSL_MAX_EXTENSIONS) PRBool peer_supports_renego_ext; ok = SSL_HandshakeNegotiatedExtension(nss_fd_, ssl_renegotiation_info_xtn, &peer_supports_renego_ext); if (ok == SECSuccess) { - if (!peer_supports_renego_ext) - ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; + if (!peer_supports_renego_ext) { + ssl_connection_status_ |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; + // Log an informational message if the server does not support secure + // renegotiation (RFC 5746). + VLOG(1) << "The server " << hostname_ + << " does not support the TLS renegotiation_info extension."; + } UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported", - (int)peer_supports_renego_ext, 2); + peer_supports_renego_ext, 2); } +#endif if (ssl_config_.ssl3_fallback) - ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK; + ssl_connection_status_ |= SSL_CONNECTION_SSL3_FALLBACK; +} + +void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { + EnterFunction(""); + ssl_info->Reset(); + if (!server_cert_) { + LOG(DFATAL) << "!server_cert_"; + return; + } + + ssl_info->cert_status = server_cert_verify_result_->cert_status; + DCHECK(server_cert_ != NULL); + ssl_info->cert = server_cert_; + ssl_info->connection_status = ssl_connection_status_; + + PRUint16 cipher_suite = + SSLConnectionStatusToCipherSuite(ssl_connection_status_); + SSLCipherSuiteInfo cipher_info; + SECStatus ok = SSL_GetCipherSuiteInfo(cipher_suite, + &cipher_info, sizeof(cipher_info)); + if (ok == SECSuccess) { + ssl_info->security_bits = cipher_info.effectiveKeyBits; + } else { + ssl_info->security_bits = -1; + LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() + << " for cipherSuite " << cipher_suite; + } LeaveFunction(""); } @@ -1378,8 +1377,11 @@ static PRErrorCode MapErrorToNSS(int result) { case ERR_IO_PENDING: return PR_WOULD_BLOCK_ERROR; case ERR_ACCESS_DENIED: + case ERR_NETWORK_ACCESS_DENIED: // For connect, this could be mapped to PR_ADDRESS_NOT_SUPPORTED_ERROR. return PR_NO_ACCESS_RIGHTS_ERROR; + case ERR_NOT_IMPLEMENTED: + return PR_NOT_IMPLEMENTED_ERROR; case ERR_INTERNET_DISCONNECTED: // Equivalent to ENETDOWN. return PR_NETWORK_UNREACHABLE_ERROR; // Best approximation. case ERR_CONNECTION_TIMED_OUT: @@ -1437,7 +1439,7 @@ int SSLClientSocketNSS::BufferSend(void) { int rv = 0; if (len) { - scoped_refptr<IOBuffer> send_buffer = new IOBuffer(len); + scoped_refptr<IOBuffer> send_buffer(new IOBuffer(len)); memcpy(send_buffer->data(), buf1, len1); memcpy(send_buffer->data() + len1, buf2, len2); rv = transport_->socket()->Write(send_buffer, len, @@ -1455,6 +1457,11 @@ int SSLClientSocketNSS::BufferSend(void) { void SSLClientSocketNSS::BufferSendComplete(int result) { EnterFunction(result); + + // In the case of TCP FastOpen, connect is now finished. + if (!peername_initialized_ && UsingTCPFastOpen()) + InitializeSSLPeerName(); + memio_PutWriteResult(nss_bufs_, MapErrorToNSS(result)); transport_send_busy_ = false; OnSendComplete(result); @@ -1565,7 +1572,8 @@ int SSLClientSocketNSS::DoReadLoop(int result) { if (!nss_bufs_) { LOG(DFATAL) << "!nss_bufs_"; int rv = ERR_UNEXPECTED; - net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, new SSLErrorParams(rv, 0)); + net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, + make_scoped_refptr(new SSLErrorParams(rv, 0))); return rv; } @@ -1591,7 +1599,8 @@ int SSLClientSocketNSS::DoWriteLoop(int result) { if (!nss_bufs_) { LOG(DFATAL) << "!nss_bufs_"; int rv = ERR_UNEXPECTED; - net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, new SSLErrorParams(rv, 0)); + net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, + make_scoped_refptr(new SSLErrorParams(rv, 0))); return rv; } @@ -1624,16 +1633,10 @@ SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg, // different reads or not, depending on network conditions. PRBool false_start = 0; SECStatus rv = SSL_OptionGet(socket, SSL_ENABLE_FALSE_START, &false_start); - if (rv != SECSuccess) - NOTREACHED(); + DCHECK_EQ(SECSuccess, rv); + if (false_start) { SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); - if (!that->handshake_callback_called_) { - that->corked_ = true; - that->uncork_timer_.Start( - base::TimeDelta::FromMilliseconds(kCorkTimeoutMs), - that, &SSLClientSocketNSS::UncorkAfterTimeout); - } // ESET anti-virus is capable of intercepting HTTPS connections on Windows. // However, it is False Start intolerant and causes the connections to hang @@ -1646,12 +1649,24 @@ SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg, if (common_name) { if (strcmp(common_name, "ESET_RootSslCert") == 0) that->eset_mitm_detected_ = true; - if (strcmp(common_name, "ContentWatch Root Certificate Authority") == 0) - that->netnanny_mitm_detected_ = true; + if (strcmp(common_name, "ContentWatch Root Certificate Authority") == 0) { + // This is NetNanny. NetNanny are updating their product so we + // silently disable False Start for now. + rv = SSL_OptionSet(socket, SSL_ENABLE_FALSE_START, PR_FALSE); + DCHECK_EQ(SECSuccess, rv); + false_start = 0; + } PORT_Free(common_name); } CERT_DestroyCertificate(cert); } + + if (false_start && !that->handshake_callback_called_) { + that->corked_ = true; + that->uncork_timer_.Start( + base::TimeDelta::FromMilliseconds(kCorkTimeoutMs), + that, &SSLClientSocketNSS::UncorkAfterTimeout); + } } #endif @@ -1659,24 +1674,69 @@ SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg, return SECSuccess; } +#if defined(NSS_PLATFORM_CLIENT_AUTH) // static // NSS calls this if a client certificate is needed. -// Based on Mozilla's NSS_GetClientAuthData. -SECStatus SSLClientSocketNSS::ClientAuthHandler( +SECStatus SSLClientSocketNSS::PlatformClientAuthHandler( void* arg, PRFileDesc* socket, CERTDistNames* ca_names, - CERTCertificate** result_certificate, - SECKEYPrivateKey** result_private_key) { + CERTCertList** result_certs, + void** result_private_key) { SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); that->client_auth_cert_needed_ = !that->ssl_config_.send_client_cert; - #if defined(OS_WIN) if (that->ssl_config_.send_client_cert) { - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // CryptoAPI yet (http://crbug.com/37560), so client_cert must be NULL. - DCHECK(!that->ssl_config_.client_cert); + if (that->ssl_config_.client_cert) { + PCCERT_CONTEXT cert_context = + that->ssl_config_.client_cert->os_cert_handle(); + HCRYPTPROV provider = NULL; + DWORD key_spec = AT_KEYEXCHANGE; + BOOL must_free = FALSE; + BOOL acquired_key = CryptAcquireCertificatePrivateKey( + cert_context, + CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + NULL, &provider, &key_spec, &must_free); + if (acquired_key && provider) { + DCHECK_NE(key_spec, CERT_NCRYPT_KEY_SPEC); + + // The certificate cache may have been updated/used, in which case, + // duplicate the existing handle, since NSS will free it when no + // longer in use. + if (!must_free) + CryptContextAddRef(provider, NULL, 0); + + SECItem der_cert; + der_cert.type = siDERCertBuffer; + der_cert.data = cert_context->pbCertEncoded; + der_cert.len = cert_context->cbCertEncoded; + + // TODO(rsleevi): Error checking for NSS allocation errors. + *result_certs = CERT_NewCertList(); + CERTCertDBHandle* db_handle = CERT_GetDefaultCertDB(); + CERTCertificate* user_cert = CERT_NewTempCertificate( + db_handle, &der_cert, NULL, PR_FALSE, PR_TRUE); + CERT_AddCertToListTail(*result_certs, user_cert); + + // Add the intermediates. + X509Certificate::OSCertHandles intermediates = + that->ssl_config_.client_cert->GetIntermediateCertificates(); + for (X509Certificate::OSCertHandles::const_iterator it = + intermediates.begin(); it != intermediates.end(); ++it) { + der_cert.data = (*it)->pbCertEncoded; + der_cert.len = (*it)->cbCertEncoded; + + CERTCertificate* intermediate = CERT_NewTempCertificate( + db_handle, &der_cert, NULL, PR_FALSE, PR_TRUE); + CERT_AddCertToListTail(*result_certs, intermediate); + } + // TODO(wtc): |key_spec| should be passed along with |provider|. + *result_private_key = reinterpret_cast<void*>(provider); + return SECSuccess; + } + LOG(WARNING) << "Client cert found without private key"; + } // Send no client certificate. return SECFailure; } @@ -1708,10 +1768,6 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( PCCERT_CHAIN_CONTEXT chain_context = NULL; - // TODO(wtc): close cert_store_ at shutdown. - if (!cert_store_) - cert_store_ = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL); - for (;;) { // Find a certificate chain. chain_context = CertFindChainInStore(my_cert_store, @@ -1733,18 +1789,42 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( // Copy it to our own certificate store, so that we can close the "MY" // certificate store before returning from this function. PCCERT_CONTEXT cert_context2; - BOOL ok = CertAddCertificateContextToStore(cert_store_, cert_context, + BOOL ok = CertAddCertificateContextToStore(X509Certificate::cert_store(), + cert_context, CERT_STORE_ADD_USE_EXISTING, &cert_context2); if (!ok) { NOTREACHED(); continue; } + + // Copy the rest of the chain to our own store as well. Copying the chain + // stops gracefully if an error is encountered, with the partial chain + // being used as the intermediates, rather than failing to consider the + // client certificate. + net::X509Certificate::OSCertHandles intermediates; + for (DWORD i = 1; i < chain_context->rgpChain[0]->cElement; i++) { + PCCERT_CONTEXT intermediate_copy; + ok = CertAddCertificateContextToStore(X509Certificate::cert_store(), + chain_context->rgpChain[0]->rgpElement[i]->pCertContext, + CERT_STORE_ADD_USE_EXISTING, &intermediate_copy); + if (!ok) { + NOTREACHED(); + break; + } + intermediates.push_back(intermediate_copy); + } + scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( cert_context2, X509Certificate::SOURCE_LONE_CERT_IMPORT, - X509Certificate::OSCertHandles()); - X509Certificate::FreeOSCertHandle(cert_context2); + intermediates); that->client_certs_.push_back(cert); + + X509Certificate::FreeOSCertHandle(cert_context2); + for (net::X509Certificate::OSCertHandles::iterator it = + intermediates.begin(); it != intermediates.end(); ++it) { + net::X509Certificate::FreeOSCertHandle(*it); + } } BOOL ok = CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG); @@ -1755,9 +1835,63 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( return SECWouldBlock; #elif defined(OS_MACOSX) if (that->ssl_config_.send_client_cert) { - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // CDSA/CSSM yet (http://crbug.com/45369), so client_cert must be NULL. - DCHECK(!that->ssl_config_.client_cert); + if (that->ssl_config_.client_cert) { + OSStatus os_error = noErr; + SecIdentityRef identity = NULL; + SecKeyRef private_key = NULL; + CFArrayRef chain = + that->ssl_config_.client_cert->CreateClientCertificateChain(); + if (chain) { + identity = reinterpret_cast<SecIdentityRef>( + const_cast<void*>(CFArrayGetValueAtIndex(chain, 0))); + } + if (identity) + os_error = SecIdentityCopyPrivateKey(identity, &private_key); + + if (chain && identity && os_error == noErr) { + // TODO(rsleevi): Error checking for NSS allocation errors. + *result_certs = CERT_NewCertList(); + *result_private_key = reinterpret_cast<void*>(private_key); + + for (CFIndex i = 0; i < CFArrayGetCount(chain); ++i) { + CSSM_DATA cert_data; + SecCertificateRef cert_ref; + if (i == 0) { + cert_ref = that->ssl_config_.client_cert->os_cert_handle(); + } else { + cert_ref = reinterpret_cast<SecCertificateRef>( + const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); + } + os_error = SecCertificateGetData(cert_ref, &cert_data); + if (os_error != noErr) + break; + + SECItem der_cert; + der_cert.type = siDERCertBuffer; + der_cert.data = cert_data.Data; + der_cert.len = cert_data.Length; + CERTCertificate* nss_cert = CERT_NewTempCertificate( + CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); + CERT_AddCertToListTail(*result_certs, nss_cert); + } + } + if (os_error == noErr) { + CFRelease(chain); + return SECSuccess; + } + LOG(WARNING) << "Client cert found, but could not be used: " + << os_error; + if (*result_certs) { + CERT_DestroyCertList(*result_certs); + *result_certs = NULL; + } + if (*result_private_key) + *result_private_key = NULL; + if (private_key) + CFRelease(private_key); + if (chain) + CFRelease(chain); + } // Send no client certificate. return SECFailure; } @@ -1785,6 +1919,24 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED. return SECWouldBlock; #else + return SECFailure; +#endif +} + +#else // NSS_PLATFORM_CLIENT_AUTH + +// static +// NSS calls this if a client certificate is needed. +// Based on Mozilla's NSS_GetClientAuthData. +SECStatus SSLClientSocketNSS::ClientAuthHandler( + void* arg, + PRFileDesc* socket, + CERTDistNames* ca_names, + CERTCertificate** result_certificate, + SECKEYPrivateKey** result_private_key) { + SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); + + that->client_auth_cert_needed_ = !that->ssl_config_.send_client_cert; void* wincx = SSL_RevealPinArg(socket); // Second pass: a client certificate should have been selected. @@ -1838,8 +1990,8 @@ SECStatus SSLClientSocketNSS::ClientAuthHandler( // Tell NSS to suspend the client authentication. We will then abort the // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED. return SECWouldBlock; -#endif } +#endif // NSS_PLATFORM_CLIENT_AUTH // static // NSS calls this when handshake is completed. @@ -1852,22 +2004,32 @@ void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket, that->handshake_callback_called_ = true; that->UpdateServerCert(); - - that->CheckSecureRenegotiation(); + that->UpdateConnectionStatus(); } int SSLClientSocketNSS::DoSnapStartLoadInfo() { EnterFunction(""); int rv = ssl_host_info_->WaitForDataReady(&handshake_io_callback_); + GotoState(STATE_HANDSHAKE); if (rv == OK) { - if (LoadSnapStartInfo()) { - pseudo_connected_ = true; - GotoState(STATE_SNAP_START_WAIT_FOR_WRITE); - if (user_connect_callback_) - DoConnectCallback(OK); - } else { - GotoState(STATE_HANDSHAKE); + if (ssl_host_info_->WaitForCertVerification(NULL) == OK) { + if (LoadSnapStartInfo()) { + pseudo_connected_ = true; + GotoState(STATE_SNAP_START_WAIT_FOR_WRITE); + if (user_connect_callback_) + DoConnectCallback(OK); + } + } else if (!ssl_host_info_->state().server_hello.empty()) { + // A non-empty ServerHello suggests that we would have tried a Snap Start + // connection. + base::TimeTicks now = base::TimeTicks::Now(); + const base::TimeDelta duration = + now - ssl_host_info_->verification_start_time(); + UMA_HISTOGRAM_TIMES("Net.SSLSnapStartNeededVerificationInMs", duration); + VLOG(1) << "Cannot snap start because verification isn't ready. " + << "Wanted verification after " + << duration.InMilliseconds() << "ms"; } } else { DCHECK_EQ(ERR_IO_PENDING, rv); @@ -1909,7 +2071,6 @@ int SSLClientSocketNSS::DoSnapStartWaitForWrite() { nss_fd_, reinterpret_cast<const unsigned char*>(user_write_buf_->data()), user_write_buf_len_); DCHECK_EQ(SECSuccess, rv); - user_write_buf_ = NULL; GotoState(STATE_HANDSHAKE); LeaveFunction(""); @@ -1924,7 +2085,7 @@ int SSLClientSocketNSS::DoHandshake() { if (client_auth_cert_needed_) { net_error = ERR_SSL_CLIENT_AUTH_CERT_NEEDED; net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR, - new SSLErrorParams(net_error, 0)); + make_scoped_refptr(new SSLErrorParams(net_error, 0))); // If the handshake already succeeded (because the server requests but // doesn't require a client cert), we need to invalidate the SSL session // so that we won't try to resume the non-client-authenticated session in @@ -1937,9 +2098,27 @@ int SSLClientSocketNSS::DoHandshake() { if (handshake_callback_called_) { if (eset_mitm_detected_) { net_error = ERR_ESET_ANTI_VIRUS_SSL_INTERCEPTION; - } else if (netnanny_mitm_detected_) { - net_error = ERR_NETNANNY_SSL_INTERCEPTION; } else { + // We need to see if the predicted certificate chain (in + // |ssl_host_info_->state().certs) matches the actual certificate chain + // before we call SaveSnapStartInfo, as that will update + // |ssl_host_info_|. + if (ssl_host_info_.get() && !ssl_host_info_->state().certs.empty()) { + PeerCertificateChain certs(nss_fd_); + const SSLHostInfo::State& state = ssl_host_info_->state(); + predicted_cert_chain_correct_ = certs.size() == state.certs.size(); + if (predicted_cert_chain_correct_) { + for (unsigned i = 0; i < certs.size(); i++) { + if (certs[i]->derCert.len != state.certs[i].size() || + memcmp(certs[i]->derCert.data, state.certs[i].data(), + certs[i]->derCert.len) != 0) { + predicted_cert_chain_correct_ = false; + break; + } + } + } + } + SaveSnapStartInfo(); // SSL handshake is completed. It's possible that we mispredicted the // NPN agreed protocol. In this case, we've just sent a request in the @@ -1961,7 +2140,7 @@ int SSLClientSocketNSS::DoHandshake() { rv = SECFailure; net_error = ERR_SSL_PROTOCOL_ERROR; net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR, - new SSLErrorParams(net_error, 0)); + make_scoped_refptr(new SSLErrorParams(net_error, 0))); } } else { PRErrorCode prerr = PR_GetError(); @@ -1973,8 +2152,9 @@ int SSLClientSocketNSS::DoHandshake() { } else { LOG(ERROR) << "handshake failed; NSS error code " << prerr << ", net_error " << net_error; - net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR, - new SSLErrorParams(net_error, prerr)); + net_log_.AddEvent( + NetLog::TYPE_SSL_HANDSHAKE_ERROR, + make_scoped_refptr(new SSLErrorParams(net_error, prerr))); } } @@ -2135,10 +2315,19 @@ static DNSValidationResult CheckDNSSECChain( } int SSLClientSocketNSS::DoVerifyDNSSEC(int result) { +#if !defined(USE_OPENSSL) + if (ssl_config_.dns_cert_provenance_checking_enabled && dnsrr_resolver_) { + PeerCertificateChain certs(nss_fd_); + DoAsyncDNSCertProvenanceVerification( + hostname_, dnsrr_resolver_, certs.AsStringPieceVector()); + } +#endif + if (ssl_config_.dnssec_enabled) { DNSValidationResult r = CheckDNSSECChain(hostname_, server_cert_nss_); if (r == DNSVR_SUCCESS) { - server_cert_verify_result_.cert_status |= CERT_STATUS_IS_DNSSEC; + local_server_cert_verify_result_.cert_status |= CERT_STATUS_IS_DNSSEC; + server_cert_verify_result_ = &local_server_cert_verify_result_; GotoState(STATE_VERIFY_CERT_COMPLETE); return OK; } @@ -2183,13 +2372,15 @@ int SSLClientSocketNSS::DoVerifyDNSSECComplete(int result) { switch (r) { case DNSVR_FAILURE: GotoState(STATE_VERIFY_CERT_COMPLETE); - server_cert_verify_result_.cert_status |= CERT_STATUS_NOT_IN_DNS; + local_server_cert_verify_result_.cert_status |= CERT_STATUS_NOT_IN_DNS; + server_cert_verify_result_ = &local_server_cert_verify_result_; return ERR_CERT_NOT_IN_DNS; case DNSVR_CONTINUE: GotoState(STATE_VERIFY_CERT); break; case DNSVR_SUCCESS: - server_cert_verify_result_.cert_status |= CERT_STATUS_IS_DNSSEC; + local_server_cert_verify_result_.cert_status |= CERT_STATUS_IS_DNSSEC; + server_cert_verify_result_ = &local_server_cert_verify_result_; GotoState(STATE_VERIFY_CERT_COMPLETE); break; default: @@ -2202,16 +2393,35 @@ int SSLClientSocketNSS::DoVerifyDNSSECComplete(int result) { int SSLClientSocketNSS::DoVerifyCert(int result) { DCHECK(server_cert_); + GotoState(STATE_VERIFY_CERT_COMPLETE); - int flags = 0; + if (ssl_host_info_.get() && !ssl_host_info_->state().certs.empty() && + predicted_cert_chain_correct_) { + // If the SSLHostInfo had a prediction for the certificate chain of this + // server then it will have optimistically started a verification of that + // chain. So, if the prediction was correct, we should wait for that + // verification to finish rather than start our own. + net_log_.AddEvent(NetLog::TYPE_SSL_VERIFICATION_MERGED, NULL); + UMA_HISTOGRAM_ENUMERATION("Net.SSLVerificationMerged", 1 /* true */, 2); + base::TimeTicks now = base::TimeTicks::Now(); + UMA_HISTOGRAM_TIMES("Net.SSLVerificationMergedMsSaved", + now - ssl_host_info_->verification_start_time()); + server_cert_verify_result_ = &ssl_host_info_->cert_verify_result(); + return ssl_host_info_->WaitForCertVerification(&handshake_io_callback_); + } else { + UMA_HISTOGRAM_ENUMERATION("Net.SSLVerificationMerged", 0 /* false */, 2); + } + + int flags = 0; if (ssl_config_.rev_checking_enabled) flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED; if (ssl_config_.verify_ev_cert) flags |= X509Certificate::VERIFY_EV_CERT; verifier_.reset(new CertVerifier); + server_cert_verify_result_ = &local_server_cert_verify_result_; return verifier_->Verify(server_cert_, hostname_, flags, - &server_cert_verify_result_, + &local_server_cert_verify_result_, &handshake_io_callback_); } @@ -2220,45 +2430,15 @@ int SSLClientSocketNSS::DoVerifyCert(int result) { int SSLClientSocketNSS::DoVerifyCertComplete(int result) { verifier_.reset(); - // Using Snap Start disables certificate verification for now. - if (SSLConfigService::snap_start_enabled()) - result = OK; - - if (result == OK) { - // Remember the intermediate CA certs if the server sends them to us. - // - // We used to remember the intermediate CA certs in the NSS database - // persistently. However, NSS opens a connection to the SQLite database - // during NSS initialization and doesn't close the connection until NSS - // shuts down. If the file system where the database resides is gone, - // the database connection goes bad. What's worse, the connection won't - // recover when the file system comes back. Until this NSS or SQLite bug - // is fixed, we need to avoid using the NSS database for non-essential - // purposes. See https://bugzilla.mozilla.org/show_bug.cgi?id=508081 and - // http://crbug.com/15630 for more info. - CERTCertList* cert_list = CERT_GetCertChainFromCert( - server_cert_nss_, PR_Now(), certUsageSSLCA); - if (cert_list) { - for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); - !CERT_LIST_END(node, cert_list); - node = CERT_LIST_NEXT(node)) { - if (node->cert->slot || node->cert->isRoot || node->cert->isperm || - node->cert == server_cert_nss_) { - // Some certs we don't want to remember are: - // - found on a token. - // - the root cert. - // - already stored in perm db. - // - the server cert itself. - continue; - } - - // We have found a CA cert that we want to remember. - // TODO(wtc): Remember the intermediate CA certs in a std::set - // temporarily (http://crbug.com/15630). - } - CERT_DestroyCertList(cert_list); - } - } + // We used to remember the intermediate CA certs in the NSS database + // persistently. However, NSS opens a connection to the SQLite database + // during NSS initialization and doesn't close the connection until NSS + // shuts down. If the file system where the database resides is gone, + // the database connection goes bad. What's worse, the connection won't + // recover when the file system comes back. Until this NSS or SQLite bug + // is fixed, we need to avoid using the NSS database for non-essential + // purposes. See https://bugzilla.mozilla.org/show_bug.cgi?id=508081 and + // http://crbug.com/15630 for more info. // If we have been explicitly told to accept this certificate, override the // result of verifier_.Verify. @@ -2272,16 +2452,45 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) { result = OK; } + if (result == OK) + LogConnectionTypeMetrics(); + completed_handshake_ = true; // TODO(ukai): we may not need this call because it is now harmless to have a // session with a bad cert. InvalidateSessionIfBadCertificate(); - // Likewise, if we merged a Write call into the handshake we need to make the + // If we merged a Write call into the handshake we need to make the // callback now. if (user_write_callback_) { corked_ = false; - DoWriteCallback(user_write_buf_len_); + if (result != OK) { + DoWriteCallback(result); + } else { + SSLSnapStartResult snap_start_type; + SECStatus rv = SSL_GetSnapStartResult(nss_fd_, &snap_start_type); + DCHECK_EQ(rv, SECSuccess); + DCHECK_NE(snap_start_type, SSL_SNAP_START_NONE); + if (snap_start_type == SSL_SNAP_START_RECOVERY || + snap_start_type == SSL_SNAP_START_RESUME_RECOVERY) { + // If we mispredicted the server's handshake then Snap Start will have + // triggered a recovery mode. The misprediction could have been caused + // by the server having a different certificate so the application data + // wasn't resent. Now that we have verified the certificate, we need to + // resend the application data. + int bytes_written = DoPayloadWrite(); + if (bytes_written != ERR_IO_PENDING) + DoWriteCallback(bytes_written); + } else { + DoWriteCallback(user_write_buf_len_); + } + } + } + + if (user_read_callback_) { + int rv = DoReadLoop(OK); + if (rv != ERR_IO_PENDING) + DoReadCallback(rv); } // Exit DoHandshakeLoop and return the result to the caller to Connect. @@ -2300,7 +2509,7 @@ int SSLClientSocketNSS::DoPayloadRead() { LeaveFunction(""); rv = ERR_SSL_CLIENT_AUTH_CERT_NEEDED; net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, - new SSLErrorParams(rv, 0)); + make_scoped_refptr(new SSLErrorParams(rv, 0))); return rv; } if (rv >= 0) { @@ -2315,7 +2524,8 @@ int SSLClientSocketNSS::DoPayloadRead() { } LeaveFunction(""); rv = MapNSPRError(prerr); - net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, new SSLErrorParams(rv, prerr)); + net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, + make_scoped_refptr(new SSLErrorParams(rv, prerr))); return rv; } @@ -2336,8 +2546,40 @@ int SSLClientSocketNSS::DoPayloadWrite() { LeaveFunction(""); rv = MapNSPRError(prerr); net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, - new SSLErrorParams(rv, prerr)); + make_scoped_refptr(new SSLErrorParams(rv, prerr))); return rv; } +void SSLClientSocketNSS::LogConnectionTypeMetrics() const { + UpdateConnectionTypeHistograms(CONNECTION_SSL); + if (server_cert_verify_result_->has_md5) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5); + if (server_cert_verify_result_->has_md2) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2); + if (server_cert_verify_result_->has_md4) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD4); + if (server_cert_verify_result_->has_md5_ca) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5_CA); + if (server_cert_verify_result_->has_md2_ca) + UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2_CA); + int ssl_version = SSLConnectionStatusToVersion(ssl_connection_status_); + switch (ssl_version) { + case SSL_CONNECTION_VERSION_SSL2: + UpdateConnectionTypeHistograms(CONNECTION_SSL_SSL2); + break; + case SSL_CONNECTION_VERSION_SSL3: + UpdateConnectionTypeHistograms(CONNECTION_SSL_SSL3); + break; + case SSL_CONNECTION_VERSION_TLS1: + UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1); + break; + case SSL_CONNECTION_VERSION_TLS1_1: + UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1_1); + break; + case SSL_CONNECTION_VERSION_TLS1_2: + UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1_2); + break; + }; +} + } // namespace net diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 56d16b2..87f7b92 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -30,6 +30,7 @@ namespace net { class BoundNetLog; class CertVerifier; class ClientSocketHandle; +class DnsRRResolver; class SSLHostInfo; class X509Certificate; @@ -43,14 +44,15 @@ class SSLClientSocketNSS : public SSLClientSocket { SSLClientSocketNSS(ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info); + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver); ~SSLClientSocketNSS(); // SSLClientSocket methods: virtual void GetSSLInfo(SSLInfo* ssl_info); virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info); virtual NextProtoStatus GetNextProto(std::string* proto); - virtual void UseDNSSEC(DNSSECProvider*); + virtual void UseDNSSEC(DNSSECProvider* provider); // ClientSocket methods: virtual int Connect(CompletionCallback* callback); @@ -62,6 +64,7 @@ class SSLClientSocketNSS : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); @@ -73,13 +76,16 @@ class SSLClientSocketNSS : public SSLClientSocket { // Initializes NSS SSL options. Returns a net error code. int InitializeSSLOptions(); + // Initializes the socket peer name in SSL. Returns a net error code. + int InitializeSSLPeerName(); + void InvalidateSessionIfBadCertificate(); #if defined(OS_MACOSX) || defined(OS_WIN) // Creates an OS certificate from a DER-encoded certificate. static X509Certificate::OSCertHandle CreateOSCert(const SECItem& der_cert); #endif X509Certificate* UpdateServerCert(); - void CheckSecureRenegotiation() const; + void UpdateConnectionStatus(); void DoReadCallback(int result); void DoWriteCallback(int result); void DoConnectCallback(int result); @@ -101,6 +107,7 @@ class SSLClientSocketNSS : public SSLClientSocket { int DoVerifyCertComplete(int result); int DoPayloadRead(); int DoPayloadWrite(); + void LogConnectionTypeMetrics() const; int Init(); void SaveSnapStartInfo(); bool LoadSnapStartInfo(); @@ -118,11 +125,19 @@ class SSLClientSocketNSS : public SSLClientSocket { static SECStatus OwnAuthCertHandler(void* arg, PRFileDesc* socket, PRBool checksig, PRBool is_server); // NSS calls this when client authentication is requested. +#if defined(NSS_PLATFORM_CLIENT_AUTH) + static SECStatus PlatformClientAuthHandler(void* arg, + PRFileDesc* socket, + CERTDistNames* ca_names, + CERTCertList** result_certs, + void** result_private_key); +#else static SECStatus ClientAuthHandler(void* arg, PRFileDesc* socket, CERTDistNames* ca_names, CERTCertificate** result_certificate, SECKEYPrivateKey** result_private_key); +#endif // NSS calls this when handshake is completed. We pass 'this' as the second // argument. static void HandshakeCallback(PRFileDesc* socket, void* arg); @@ -161,7 +176,12 @@ class SSLClientSocketNSS : public SSLClientSocket { // converted into an X509Certificate object (server_cert_). scoped_refptr<X509Certificate> server_cert_; CERTCertificate* server_cert_nss_; - CertVerifyResult server_cert_verify_result_; + // |server_cert_verify_result_| points at the verification result, which may, + // or may not be, |&local_server_cert_verify_result_|, depending on whether + // we used an SSLHostInfo's verification. + const CertVerifyResult* server_cert_verify_result_; + CertVerifyResult local_server_cert_verify_result_; + int ssl_connection_status_; // Stores client authentication information between ClientAuthHandler and // GetSSLCertRequestInfo calls. @@ -184,9 +204,12 @@ class SSLClientSocketNSS : public SSLClientSocket { // HTTPS connections. bool eset_mitm_detected_; - // True iff we believe that the user has NetNanny intercepting our HTTPS - // connections. - bool netnanny_mitm_detected_; + // True iff |ssl_host_info_| contained a predicted certificate chain and + // that we found the prediction to be correct. + bool predicted_cert_chain_correct_; + + // True if the peer name has been initialized. + bool peername_initialized_; // This pointer is owned by the caller of UseDNSSEC. DNSSECProvider* dnssec_provider_; @@ -222,16 +245,7 @@ class SSLClientSocketNSS : public SSLClientSocket { bool predicted_npn_proto_used_; scoped_ptr<SSLHostInfo> ssl_host_info_; - -#if defined(OS_WIN) - // A CryptoAPI in-memory certificate store. We use it for two purposes: - // 1. Import server certificates into this store so that we can verify and - // display the certificates using CryptoAPI. - // 2. Copy client certificates from the "MY" system certificate store into - // this store so that we can close the system store when we finish - // searching for client certificates. - static HCERTSTORE cert_store_; -#endif + DnsRRResolver* const dnsrr_resolver_; }; } // namespace net diff --git a/net/socket/ssl_client_socket_nss_factory.cc b/net/socket/ssl_client_socket_nss_factory.cc index efa6e23..f4e8215 100644 --- a/net/socket/ssl_client_socket_nss_factory.cc +++ b/net/socket/ssl_client_socket_nss_factory.cc @@ -4,12 +4,8 @@ #include "net/socket/client_socket_factory.h" -#include "build/build_config.h" #include "net/socket/ssl_client_socket_nss.h" #include "net/socket/ssl_host_info.h" -#if defined(OS_WIN) -#include "net/socket/ssl_client_socket_win.h" -#endif // This file is only used on platforms where NSS is not the system SSL // library. When compiled, this file is the only object module that pulls @@ -22,18 +18,11 @@ SSLClientSocket* SSLClientSocketNSSFactory( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { scoped_ptr<SSLHostInfo> shi(ssl_host_info); - // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using - // CryptoAPI yet (http://crbug.com/37560), so we fall back on - // SSLClientSocketWin. -#if defined(OS_WIN) - if (ssl_config.send_client_cert) - return new SSLClientSocketWin(transport_socket, hostname, ssl_config); -#endif - return new SSLClientSocketNSS(transport_socket, hostname, ssl_config, - shi.release()); + shi.release(), dnsrr_resolver); } } // namespace net diff --git a/net/socket/ssl_client_socket_nss_factory.h b/net/socket/ssl_client_socket_nss_factory.h index d454bb9..29f9af4 100644 --- a/net/socket/ssl_client_socket_nss_factory.h +++ b/net/socket/ssl_client_socket_nss_factory.h @@ -10,6 +10,7 @@ namespace net { +class DnsRRResolver; class SSLHostInfo; // Creates SSLClientSocketNSS objects. @@ -17,7 +18,8 @@ SSLClientSocket* SSLClientSocketNSSFactory( ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info); + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver); } // namespace net diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index bbe3ee4..3aae457 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc @@ -631,6 +631,14 @@ bool SSLClientSocketOpenSSL::WasEverUsed() const { return false; } +bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) + return transport_->socket()->UsingTCPFastOpen(); + + NOTREACHED(); + return false; +} + // Socket methods int SSLClientSocketOpenSSL::Read(IOBuffer* buf, diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h index 20f321e..31d5c1c 100644 --- a/net/socket/ssl_client_socket_openssl.h +++ b/net/socket/ssl_client_socket_openssl.h @@ -51,6 +51,7 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc index 98e3b09..a7eea3a 100644 --- a/net/socket/ssl_client_socket_pool.cc +++ b/net/socket/ssl_client_socket_pool.cc @@ -193,7 +193,8 @@ int SSLConnectJob::DoTCPConnect() { if (ssl_host_info_factory_ && SSLConfigService::snap_start_enabled()) { ssl_host_info_.reset( - ssl_host_info_factory_->GetForHost(params_->hostname())); + ssl_host_info_factory_->GetForHost(params_->hostname(), + params_->ssl_config())); } if (ssl_host_info_.get()) { // This starts fetching the SSL host info from the disk cache for Snap @@ -284,7 +285,8 @@ int SSLConnectJob::DoSSLConnect() { ssl_socket_.reset(client_socket_factory_->CreateSSLClientSocket( transport_socket_handle_.release(), params_->hostname(), - params_->ssl_config(), ssl_host_info_.release())); + params_->ssl_config(), ssl_host_info_.release(), + dnsrr_resolver_)); return ssl_socket_->Connect(&callback_); } diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index bfcbe81..a32d5df 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc @@ -93,8 +93,9 @@ TEST_F(SSLClientSocketTest, Connect) { } TEST_F(SSLClientSocketTest, ConnectExpired) { - net::TestServer test_server(net::TestServer::TYPE_HTTPS_EXPIRED_CERTIFICATE, - FilePath()); + net::TestServer::HTTPSOptions https_options( + net::TestServer::HTTPSOptions::CERT_EXPIRED); + net::TestServer test_server(https_options, FilePath()); ASSERT_TRUE(test_server.Start()); net::AddressList addr; @@ -136,8 +137,9 @@ TEST_F(SSLClientSocketTest, ConnectExpired) { } TEST_F(SSLClientSocketTest, ConnectMismatched) { - net::TestServer test_server(net::TestServer::TYPE_HTTPS_MISMATCHED_HOSTNAME, - FilePath()); + net::TestServer::HTTPSOptions https_options( + net::TestServer::HTTPSOptions::CERT_MISMATCHED_NAME); + net::TestServer test_server(https_options, FilePath()); ASSERT_TRUE(test_server.Start()); net::AddressList addr; @@ -181,9 +183,11 @@ TEST_F(SSLClientSocketTest, ConnectMismatched) { // Attempt to connect to a page which requests a client certificate. It should // return an error code on connect. +// Flaky: http://crbug.com/54445 TEST_F(SSLClientSocketTest, FLAKY_ConnectClientAuthCertRequested) { - net::TestServer test_server(net::TestServer::TYPE_HTTPS_CLIENT_AUTH, - FilePath()); + net::TestServer::HTTPSOptions https_options; + https_options.request_client_certificate = true; + net::TestServer test_server(https_options, FilePath()); ASSERT_TRUE(test_server.Start()); net::AddressList addr; @@ -229,8 +233,9 @@ TEST_F(SSLClientSocketTest, FLAKY_ConnectClientAuthCertRequested) { // // TODO(davidben): Also test providing an actual certificate. TEST_F(SSLClientSocketTest, ConnectClientAuthSendNullCert) { - net::TestServer test_server(net::TestServer::TYPE_HTTPS_CLIENT_AUTH, - FilePath()); + net::TestServer::HTTPSOptions https_options; + https_options.request_client_certificate = true; + net::TestServer test_server(https_options, FilePath()); ASSERT_TRUE(test_server.Start()); net::AddressList addr; @@ -315,8 +320,8 @@ TEST_F(SSLClientSocketTest, Read) { EXPECT_TRUE(sock->IsConnected()); const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; - scoped_refptr<net::IOBuffer> request_buffer = - new net::IOBuffer(arraysize(request_text) - 1); + scoped_refptr<net::IOBuffer> request_buffer( + new net::IOBuffer(arraysize(request_text) - 1)); memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); rv = sock->Write(request_buffer, arraysize(request_text) - 1, &callback); @@ -326,7 +331,7 @@ TEST_F(SSLClientSocketTest, Read) { rv = callback.WaitForResult(); EXPECT_EQ(static_cast<int>(arraysize(request_text) - 1), rv); - scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(4096); + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(4096)); for (;;) { rv = sock->Read(buf, 4096, &callback); EXPECT_TRUE(rv >= 0 || rv == net::ERR_IO_PENDING); @@ -376,7 +381,7 @@ TEST_F(SSLClientSocketTest, Read_FullDuplex) { EXPECT_TRUE(sock->IsConnected()); // Issue a "hanging" Read first. - scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(4096); + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(4096)); rv = sock->Read(buf, 4096, &callback); // We haven't written the request, so there should be no response yet. ASSERT_EQ(net::ERR_IO_PENDING, rv); @@ -389,8 +394,8 @@ TEST_F(SSLClientSocketTest, Read_FullDuplex) { for (int i = 0; i < 3800; ++i) request_text.push_back('*'); request_text.append("\r\n\r\n"); - scoped_refptr<net::IOBuffer> request_buffer = - new net::StringIOBuffer(request_text); + scoped_refptr<net::IOBuffer> request_buffer( + new net::StringIOBuffer(request_text)); rv = sock->Write(request_buffer, request_text.size(), &callback2); EXPECT_TRUE(rv >= 0 || rv == net::ERR_IO_PENDING); @@ -433,8 +438,8 @@ TEST_F(SSLClientSocketTest, Read_SmallChunks) { } const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; - scoped_refptr<net::IOBuffer> request_buffer = - new net::IOBuffer(arraysize(request_text) - 1); + scoped_refptr<net::IOBuffer> request_buffer( + new net::IOBuffer(arraysize(request_text) - 1)); memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); rv = sock->Write(request_buffer, arraysize(request_text) - 1, &callback); @@ -444,7 +449,7 @@ TEST_F(SSLClientSocketTest, Read_SmallChunks) { rv = callback.WaitForResult(); EXPECT_EQ(static_cast<int>(arraysize(request_text) - 1), rv); - scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(1); + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(1)); for (;;) { rv = sock->Read(buf, 1, &callback); EXPECT_TRUE(rv >= 0 || rv == net::ERR_IO_PENDING); @@ -487,8 +492,8 @@ TEST_F(SSLClientSocketTest, Read_Interrupted) { } const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; - scoped_refptr<net::IOBuffer> request_buffer = - new net::IOBuffer(arraysize(request_text) - 1); + scoped_refptr<net::IOBuffer> request_buffer( + new net::IOBuffer(arraysize(request_text) - 1)); memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); rv = sock->Write(request_buffer, arraysize(request_text) - 1, &callback); @@ -499,7 +504,7 @@ TEST_F(SSLClientSocketTest, Read_Interrupted) { EXPECT_EQ(static_cast<int>(arraysize(request_text) - 1), rv); // Do a partial read and then exit. This test should not crash! - scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(512); + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(512)); rv = sock->Read(buf, 512, &callback); EXPECT_TRUE(rv > 0 || rv == net::ERR_IO_PENDING); diff --git a/net/socket/ssl_client_socket_win.cc b/net/socket/ssl_client_socket_win.cc index 7e8c29e..eead7ed 100644 --- a/net/socket/ssl_client_socket_win.cc +++ b/net/socket/ssl_client_socket_win.cc @@ -705,6 +705,14 @@ bool SSLClientSocketWin::WasEverUsed() const { return false; } +bool SSLClientSocketWin::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int SSLClientSocketWin::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(completed_handshake()); diff --git a/net/socket/ssl_client_socket_win.h b/net/socket/ssl_client_socket_win.h index 38cdb32..4f96e80 100644 --- a/net/socket/ssl_client_socket_win.h +++ b/net/socket/ssl_client_socket_win.h @@ -54,6 +54,7 @@ class SSLClientSocketWin : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_host_info.cc b/net/socket/ssl_host_info.cc index cc29545..4f6c1bb 100644 --- a/net/socket/ssl_host_info.cc +++ b/net/socket/ssl_host_info.cc @@ -4,6 +4,11 @@ #include "net/socket/ssl_host_info.h" +#include "base/metrics/histogram.h" +#include "base/string_piece.h" +#include "net/base/cert_verifier.h" +#include "net/base/ssl_config_service.h" +#include "net/base/x509_certificate.h" #include "net/socket/ssl_client_socket.h" #ifdef ANDROID // the android platform build system use a fixed include path relative to the @@ -15,7 +20,25 @@ namespace net { -SSLHostInfo::SSLHostInfo() { +SSLHostInfo::State::State() + : npn_valid(false), + npn_status(SSLClientSocket::kNextProtoUnsupported) { +} + +SSLHostInfo::State::~State() {} + +SSLHostInfo::SSLHostInfo( + const std::string& hostname, + const SSLConfig& ssl_config) + : hostname_(hostname), + cert_verification_complete_(false), + cert_parsing_failed_(false), + cert_verification_callback_(NULL), + rev_checking_enabled_(ssl_config.rev_checking_enabled), + verify_ev_cert_(ssl_config.verify_ev_cert), + callback_(new CancelableCompletionCallback<SSLHostInfo>( + ALLOW_THIS_IN_INITIALIZER_LIST(this), + &SSLHostInfo::VerifyCallback)) { state_.npn_valid = false; } @@ -66,6 +89,7 @@ bool SSLHostInfo::Parse(const std::string& data) { state->certs.clear(); state->server_hello.clear(); state->npn_valid = false; + cert_verification_complete_ = false; if (!proto.ParseFromString(data)) return false; @@ -80,6 +104,30 @@ bool SSLHostInfo::Parse(const std::string& data) { state->npn_protocol = proto.npn_protocol(); } + if (state->certs.size() > 0) { + std::vector<base::StringPiece> der_certs(state->certs.size()); + for (size_t i = 0; i < state->certs.size(); i++) + der_certs[i] = state->certs[i]; + cert_ = X509Certificate::CreateFromDERCertChain(der_certs); + if (cert_.get()) { + int flags = 0; + if (verify_ev_cert_) + flags |= X509Certificate::VERIFY_EV_CERT; + if (rev_checking_enabled_) + flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED; + verifier_.reset(new CertVerifier); + VLOG(1) << "Kicking off verification for " << hostname_; + verification_start_time_ = base::TimeTicks::Now(); + if (verifier_->Verify(cert_.get(), hostname_, flags, + &cert_verify_result_, callback_) == OK) { + VerifyCallback(OK); + } + } else { + cert_parsing_failed_ = true; + DCHECK(!cert_verification_callback_); + } + } + return true; } @@ -101,6 +149,35 @@ std::string SSLHostInfo::Serialize() const { return proto.SerializeAsString(); } +const CertVerifyResult& SSLHostInfo::cert_verify_result() const { + return cert_verify_result_; +} + +int SSLHostInfo::WaitForCertVerification(CompletionCallback* callback) { + if (cert_verification_complete_) + return cert_verification_result_; + DCHECK(!cert_parsing_failed_); + DCHECK(!cert_verification_callback_); + DCHECK(!state_.certs.empty()); + cert_verification_callback_ = callback; + return ERR_IO_PENDING; +} + +void SSLHostInfo::VerifyCallback(int rv) { + DCHECK(!verification_start_time_.is_null()); + base::TimeTicks now = base::TimeTicks::Now(); + const base::TimeDelta duration = now - verification_start_time(); + UMA_HISTOGRAM_TIMES("Net.SSLHostInfoVerificationTimeMs", duration); + VLOG(1) << "Verification took " << duration.InMilliseconds() << "ms"; + cert_verification_complete_ = true; + cert_verification_result_ = rv; + if (cert_verification_callback_) { + CompletionCallback* callback = cert_verification_callback_; + cert_verification_callback_ = NULL; + callback->Run(rv); + } +} + SSLHostInfoFactory::~SSLHostInfoFactory() {} } // namespace net diff --git a/net/socket/ssl_host_info.h b/net/socket/ssl_host_info.h index 8065b47..5f515fb 100644 --- a/net/socket/ssl_host_info.h +++ b/net/socket/ssl_host_info.h @@ -9,18 +9,25 @@ #include <vector> #include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "net/base/cert_verify_result.h" #include "net/base/completion_callback.h" #include "net/socket/ssl_client_socket.h" namespace net { +class CertVerifier; +class X509Certificate; +struct SSLConfig; + // SSLHostInfo is an interface for fetching information about an SSL server. // This information may be stored on disk so does not include keys or session // information etc. Primarily it's intended for caching the server's // certificates. class SSLHostInfo { public: - SSLHostInfo(); + SSLHostInfo(const std::string& hostname, const SSLConfig& ssl_config); virtual ~SSLHostInfo(); // Start will commence the lookup. This must be called before any other @@ -48,6 +55,9 @@ class SSLHostInfo { virtual void Persist() = 0; struct State { + State(); + ~State(); + // certs is a vector of DER encoded X.509 certificates, as the server // returned them and in the same order. std::vector<std::string> certs; @@ -59,6 +69,9 @@ class SSLHostInfo { // these members contain the NPN result of a connection to the server. SSLClientSocket::NextProtoStatus npn_status; std::string npn_protocol; + + private: + DISALLOW_COPY_AND_ASSIGN(State); }; // Once the data is ready, it can be read using the following members. These @@ -66,6 +79,21 @@ class SSLHostInfo { const State& state() const; State* mutable_state(); + // If |cert_valid()| returns true, then this contains the result of verifying + // the certificate. + const CertVerifyResult& cert_verify_result() const; + + // WaitForCertVerification returns ERR_IO_PENDING if the certificate chain in + // |state().certs| is still being validated and arranges for the given + // callback to be called when the verification completes. If the verification has + // already finished then WaitForCertVerification returns the result of that + // verification. + int WaitForCertVerification(CompletionCallback* callback); + + base::TimeTicks verification_start_time() const { + return verification_start_time_; + } + protected: // Parse parses an opaque blob of data and fills out the public member fields // of this object. It returns true iff the parse was successful. The public @@ -73,6 +101,25 @@ class SSLHostInfo { bool Parse(const std::string& data); std::string Serialize() const; State state_; + + private: + // This is the callback function which the CertVerifier calls via |callback_|. + void VerifyCallback(int rv); + + // This is the hostname that we'll validate the certificates against. + const std::string hostname_; + bool cert_verification_complete_; + bool cert_parsing_failed_; + int cert_verification_result_; + CompletionCallback* cert_verification_callback_; + // These two members are taken from the SSLConfig. + bool rev_checking_enabled_; + bool verify_ev_cert_; + base::TimeTicks verification_start_time_; + CertVerifyResult cert_verify_result_; + scoped_ptr<CertVerifier> verifier_; + scoped_refptr<X509Certificate> cert_; + scoped_refptr<CancelableCompletionCallback<SSLHostInfo> > callback_; }; class SSLHostInfoFactory { @@ -81,7 +128,8 @@ class SSLHostInfoFactory { // GetForHost returns a fresh, allocated SSLHostInfo for the given hostname // or NULL on failure. - virtual SSLHostInfo* GetForHost(const std::string& hostname) = 0; + virtual SSLHostInfo* GetForHost(const std::string& hostname, + const SSLConfig& ssl_config) = 0; }; } // namespace net diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc new file mode 100644 index 0000000..d3d4e20 --- /dev/null +++ b/net/socket/tcp_client_socket.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2006-2009 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 "net/socket/tcp_client_socket.h" + +namespace net { + +static bool g_tcp_fastopen_enabled = false; + +void set_tcp_fastopen_enabled(bool value) { + g_tcp_fastopen_enabled = value; +} + +bool is_tcp_fastopen_enabled() { + return g_tcp_fastopen_enabled; +} + +} // namespace net diff --git a/net/socket/tcp_client_socket.h b/net/socket/tcp_client_socket.h index 6139b5b..13d2dfb 100644 --- a/net/socket/tcp_client_socket.h +++ b/net/socket/tcp_client_socket.h @@ -23,6 +23,13 @@ typedef TCPClientSocketWin TCPClientSocket; typedef TCPClientSocketLibevent TCPClientSocket; #endif +// Enable/disable experimental TCP FastOpen option. +// Not thread safe. Must be called during initialization/startup only. +void set_tcp_fastopen_enabled(bool value); + +// Check if the TCP FastOpen option is enabled. +bool is_tcp_fastopen_enabled(); + } // namespace net #endif // NET_SOCKET_TCP_CLIENT_SOCKET_H_ diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc index aedbf8a..1bbe827 100644 --- a/net/socket/tcp_client_socket_libevent.cc +++ b/net/socket/tcp_client_socket_libevent.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/socket/tcp_client_socket_libevent.h" +#include "net/socket/tcp_client_socket.h" #include <errno.h> #include <fcntl.h> @@ -86,6 +86,8 @@ int MapPosixError(int os_error) { int MapConnectError(int os_error) { switch (os_error) { + case EACCES: + return ERR_NETWORK_ACCESS_DENIED; case ETIMEDOUT: return ERR_CONNECTION_TIMED_OUT; default: { @@ -120,11 +122,17 @@ TCPClientSocketLibevent::TCPClientSocketLibevent( write_callback_(NULL), next_connect_state_(CONNECT_STATE_NONE), connect_os_error_(0), - net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) { + net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), + previously_disconnected_(false), + use_tcp_fastopen_(false), + tcp_fastopen_connected_(false) { scoped_refptr<NetLog::EventParameters> params; if (source.is_valid()) params = new NetLogSourceParameter("source_dependency", source); net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, params); + + if (is_tcp_fastopen_enabled()) + use_tcp_fastopen_ = true; } TCPClientSocketLibevent::~TCPClientSocketLibevent() { @@ -144,8 +152,9 @@ int TCPClientSocketLibevent::Connect(CompletionCallback* callback) { DCHECK(!waiting_connect()); - net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT, - new AddressListNetLogParam(addresses_)); + net_log_.BeginEvent( + NetLog::TYPE_TCP_CONNECT, + make_scoped_refptr(new AddressListNetLogParam(addresses_))); // We will try to connect to each address in addresses_. Start with the // first one in the list. @@ -194,9 +203,14 @@ int TCPClientSocketLibevent::DoConnect() { DCHECK_EQ(0, connect_os_error_); + if (previously_disconnected_) { + use_history_.Reset(); + previously_disconnected_ = false; + } + net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, - new NetLogStringParameter( - "address", NetAddressToStringWithPort(current_ai_))); + make_scoped_refptr(new NetLogStringParameter( + "address", NetAddressToStringWithPort(current_ai_)))); next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; @@ -206,9 +220,15 @@ int TCPClientSocketLibevent::DoConnect() { return MapPosixError(connect_os_error_); // Connect the socket. - if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr, - static_cast<int>(current_ai_->ai_addrlen)))) { - // Connected without waiting! + if (!use_tcp_fastopen_) { + if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr, + static_cast<int>(current_ai_->ai_addrlen)))) { + // Connected without waiting! + return OK; + } + } else { + // With TCP FastOpen, we pretend that the socket is connected. + DCHECK(!tcp_fastopen_connected_); return OK; } @@ -278,6 +298,7 @@ void TCPClientSocketLibevent::DoDisconnect() { if (HANDLE_EINTR(close(socket_)) < 0) PLOG(ERROR) << "close"; socket_ = kInvalidSocket; + previously_disconnected_ = true; } bool TCPClientSocketLibevent::IsConnected() const { @@ -365,7 +386,7 @@ int TCPClientSocketLibevent::Write(IOBuffer* buf, DCHECK(callback); DCHECK_GT(buf_len, 0); - int nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len)); + int nwrite = InternalWrite(buf, buf_len); if (nwrite >= 0) { static base::StatsCounter write_bytes("tcp.write_bytes"); write_bytes.Add(nwrite); @@ -391,6 +412,38 @@ int TCPClientSocketLibevent::Write(IOBuffer* buf, return ERR_IO_PENDING; } +int TCPClientSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) { + int nwrite; + if (use_tcp_fastopen_ && !tcp_fastopen_connected_) { + // We have a limited amount of data to send in the SYN packet. + int kMaxFastOpenSendLength = 1420; + + buf_len = std::min(kMaxFastOpenSendLength, buf_len); + + int flags = 0x20000000; // Magic flag to enable TCP_FASTOPEN + nwrite = HANDLE_EINTR(sendto(socket_, + buf->data(), + buf_len, + flags, + current_ai_->ai_addr, + static_cast<int>(current_ai_->ai_addrlen))); + tcp_fastopen_connected_ = true; + + if (nwrite < 0) { + // Non-blocking mode is returning EINPROGRESS rather than EAGAIN. + if (errno == EINPROGRESS) + errno = EAGAIN; + + // Unlike "normal" nonblocking sockets, the data is already queued, + // so tell the app that we've consumed it. + return buf_len; + } + } else { + nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len)); + } + return nwrite; +} + bool TCPClientSocketLibevent::SetReceiveBufferSize(int32 size) { DCHECK(CalledOnValidThread()); int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF, @@ -553,4 +606,8 @@ bool TCPClientSocketLibevent::WasEverUsed() const { return use_history_.was_used_to_convey_data(); } +bool TCPClientSocketLibevent::UsingTCPFastOpen() const { + return use_tcp_fastopen_; +} + } // namespace net diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h index 980e4cd..890d0c2 100644 --- a/net/socket/tcp_client_socket_libevent.h +++ b/net/socket/tcp_client_socket_libevent.h @@ -43,6 +43,7 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: // Multiple outstanding requests are not supported. @@ -127,6 +128,9 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { // Helper to add a TCP_CONNECT (end) event to the NetLog. void LogConnectCompletion(int net_error); + // Internal function to write to a socket. + int InternalWrite(IOBuffer* buf, int buf_len); + int socket_; // The list of addresses we should try in order to establish a connection. @@ -165,10 +169,19 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { BoundNetLog net_log_; + // This socket was previously disconnected and has not been re-connected. + bool previously_disconnected_; + // Record of connectivity and transmissions, for use in speculative connection // histograms. UseHistory use_history_; + // Enables experimental TCP FastOpen option. + bool use_tcp_fastopen_; + + // True when TCP FastOpen is in use and we have done the connect. + bool tcp_fastopen_connected_; + DISALLOW_COPY_AND_ASSIGN(TCPClientSocketLibevent); }; diff --git a/net/socket/tcp_client_socket_pool.cc b/net/socket/tcp_client_socket_pool.cc index 5f1f43f..8ec9cac 100644 --- a/net/socket/tcp_client_socket_pool.cc +++ b/net/socket/tcp_client_socket_pool.cc @@ -222,9 +222,9 @@ int TCPClientSocketPool::RequestSocket( // TODO(eroman): Split out the host and port parameters. net_log.AddEvent( NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET, - new NetLogStringParameter( + make_scoped_refptr(new NetLogStringParameter( "host_and_port", - casted_params->get()->destination().host_port_pair().ToString())); + casted_params->get()->destination().host_port_pair().ToString()))); } return base_.RequestSocket(group_name, *casted_params, priority, handle, @@ -243,9 +243,9 @@ void TCPClientSocketPool::RequestSockets( // TODO(eroman): Split out the host and port parameters. net_log.AddEvent( NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS, - new NetLogStringParameter( + make_scoped_refptr(new NetLogStringParameter( "host_and_port", - casted_params->get()->destination().host_port_pair().ToString())); + casted_params->get()->destination().host_port_pair().ToString()))); } base_.RequestSockets(group_name, *casted_params, num_sockets, net_log); diff --git a/net/socket/tcp_client_socket_pool_unittest.cc b/net/socket/tcp_client_socket_pool_unittest.cc index 80de0aa..e53e264 100644 --- a/net/socket/tcp_client_socket_pool_unittest.cc +++ b/net/socket/tcp_client_socket_pool_unittest.cc @@ -54,6 +54,7 @@ class MockClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, @@ -99,6 +100,7 @@ class MockFailingClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, @@ -157,6 +159,7 @@ class MockPendingClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, @@ -247,7 +250,8 @@ class MockClientSocketFactory : public ClientSocketFactory { ClientSocketHandle* transport_socket, const std::string& hostname, const SSLConfig& ssl_config, - SSLHostInfo* ssl_host_info) { + SSLHostInfo* ssl_host_info, + DnsRRResolver* dnsrr_resolver) { NOTIMPLEMENTED(); delete ssl_host_info; return NULL; @@ -294,8 +298,8 @@ class TCPClientSocketPoolTest : public testing::Test { } int StartRequest(const std::string& group_name, RequestPriority priority) { - scoped_refptr<TCPSocketParams> params = new TCPSocketParams( - HostPortPair("www.google.com", 80), MEDIUM, GURL(), false); + scoped_refptr<TCPSocketParams> params(new TCPSocketParams( + HostPortPair("www.google.com", 80), MEDIUM, GURL(), false)); return test_base_.StartRequestUsingPool( &pool_, group_name, priority, params); } @@ -343,8 +347,8 @@ TEST_F(TCPClientSocketPoolTest, InitHostResolutionFailure) { host_resolver_->rules()->AddSimulatedFailure("unresolvable.host.name"); TestCompletionCallback callback; ClientSocketHandle handle; - scoped_refptr<TCPSocketParams> dest = new TCPSocketParams( - "unresolvable.host.name", 80, kDefaultPriority, GURL(), false); + scoped_refptr<TCPSocketParams> dest(new TCPSocketParams( + "unresolvable.host.name", 80, kDefaultPriority, GURL(), false)); EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", dest, kDefaultPriority, &callback, &pool_, BoundNetLog())); @@ -602,8 +606,8 @@ class RequestSocketCallback : public CallbackRunner< Tuple1<int> > { MessageLoop::current()->RunAllPending(); } within_callback_ = true; - scoped_refptr<TCPSocketParams> dest = new TCPSocketParams( - HostPortPair("www.google.com", 80), LOWEST, GURL(), false); + scoped_refptr<TCPSocketParams> dest(new TCPSocketParams( + HostPortPair("www.google.com", 80), LOWEST, GURL(), false)); int rv = handle_->Init("a", dest, LOWEST, this, pool_, BoundNetLog()); EXPECT_EQ(OK, rv); } @@ -623,8 +627,8 @@ class RequestSocketCallback : public CallbackRunner< Tuple1<int> > { TEST_F(TCPClientSocketPoolTest, RequestTwice) { ClientSocketHandle handle; RequestSocketCallback callback(&handle, &pool_); - scoped_refptr<TCPSocketParams> dest = new TCPSocketParams( - HostPortPair("www.google.com", 80), LOWEST, GURL(), false); + scoped_refptr<TCPSocketParams> dest(new TCPSocketParams( + HostPortPair("www.google.com", 80), LOWEST, GURL(), false)); int rv = handle.Init("a", dest, LOWEST, &callback, &pool_, BoundNetLog()); ASSERT_EQ(ERR_IO_PENDING, rv); diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc index fdd966c..64d78f3 100644 --- a/net/socket/tcp_client_socket_unittest.cc +++ b/net/socket/tcp_client_socket_unittest.cc @@ -93,7 +93,7 @@ void TCPClientSocketTest::SetUp() { AddressList addr; scoped_ptr<HostResolver> resolver( CreateSystemHostResolver(HostResolver::kDefaultParallelism, - NULL)); + NULL, NULL)); HostResolver::RequestInfo info(HostPortPair("localhost", listen_port_)); int rv = resolver->Resolve(info, &addr, NULL, NULL, BoundNetLog()); CHECK_EQ(rv, OK); @@ -138,8 +138,8 @@ TEST_F(TCPClientSocketTest, Read) { } const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; - scoped_refptr<IOBuffer> request_buffer = - new IOBuffer(arraysize(request_text) - 1); + scoped_refptr<IOBuffer> request_buffer( + new IOBuffer(arraysize(request_text) - 1)); memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); rv = sock_->Write(request_buffer, arraysize(request_text) - 1, &callback); @@ -150,7 +150,7 @@ TEST_F(TCPClientSocketTest, Read) { EXPECT_EQ(rv, static_cast<int>(arraysize(request_text) - 1)); } - scoped_refptr<IOBuffer> buf = new IOBuffer(4096); + scoped_refptr<IOBuffer> buf(new IOBuffer(4096)); uint32 bytes_read = 0; while (bytes_read < arraysize(kServerReply) - 1) { rv = sock_->Read(buf, 4096, &callback); @@ -183,8 +183,8 @@ TEST_F(TCPClientSocketTest, Read_SmallChunks) { } const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; - scoped_refptr<IOBuffer> request_buffer = - new IOBuffer(arraysize(request_text) - 1); + scoped_refptr<IOBuffer> request_buffer( + new IOBuffer(arraysize(request_text) - 1)); memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); rv = sock_->Write(request_buffer, arraysize(request_text) - 1, &callback); @@ -195,7 +195,7 @@ TEST_F(TCPClientSocketTest, Read_SmallChunks) { EXPECT_EQ(rv, static_cast<int>(arraysize(request_text) - 1)); } - scoped_refptr<IOBuffer> buf = new IOBuffer(1); + scoped_refptr<IOBuffer> buf(new IOBuffer(1)); uint32 bytes_read = 0; while (bytes_read < arraysize(kServerReply) - 1) { rv = sock_->Read(buf, 1, &callback); @@ -228,8 +228,8 @@ TEST_F(TCPClientSocketTest, Read_Interrupted) { } const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; - scoped_refptr<IOBuffer> request_buffer = - new IOBuffer(arraysize(request_text) - 1); + scoped_refptr<IOBuffer> request_buffer( + new IOBuffer(arraysize(request_text) - 1)); memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); rv = sock_->Write(request_buffer, arraysize(request_text) - 1, &callback); @@ -241,7 +241,7 @@ TEST_F(TCPClientSocketTest, Read_Interrupted) { } // Do a partial read and then exit. This test should not crash! - scoped_refptr<IOBuffer> buf = new IOBuffer(16); + scoped_refptr<IOBuffer> buf(new IOBuffer(16)); rv = sock_->Read(buf, 16, &callback); EXPECT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); @@ -263,13 +263,13 @@ TEST_F(TCPClientSocketTest, DISABLED_FullDuplex_ReadFirst) { // Read first. There's no data, so it should return ERR_IO_PENDING. const int kBufLen = 4096; - scoped_refptr<IOBuffer> buf = new IOBuffer(kBufLen); + scoped_refptr<IOBuffer> buf(new IOBuffer(kBufLen)); rv = sock_->Read(buf, kBufLen, &callback); EXPECT_EQ(ERR_IO_PENDING, rv); PauseServerReads(); const int kWriteBufLen = 64 * 1024; - scoped_refptr<IOBuffer> request_buffer = new IOBuffer(kWriteBufLen); + scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kWriteBufLen)); char* request_data = request_buffer->data(); memset(request_data, 'A', kWriteBufLen); TestCompletionCallback write_callback; @@ -305,7 +305,7 @@ TEST_F(TCPClientSocketTest, DISABLED_FullDuplex_WriteFirst) { PauseServerReads(); const int kWriteBufLen = 64 * 1024; - scoped_refptr<IOBuffer> request_buffer = new IOBuffer(kWriteBufLen); + scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kWriteBufLen)); char* request_data = request_buffer->data(); memset(request_data, 'A', kWriteBufLen); TestCompletionCallback write_callback; @@ -322,7 +322,7 @@ TEST_F(TCPClientSocketTest, DISABLED_FullDuplex_WriteFirst) { // Read() to block on ERR_IO_PENDING too. const int kBufLen = 4096; - scoped_refptr<IOBuffer> buf = new IOBuffer(kBufLen); + scoped_refptr<IOBuffer> buf(new IOBuffer(kBufLen)); while (true) { rv = sock_->Read(buf, kBufLen, &callback); ASSERT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc index 9143b81..b3b79a0 100644 --- a/net/socket/tcp_client_socket_win.cc +++ b/net/socket/tcp_client_socket_win.cc @@ -64,8 +64,6 @@ int MapWinsockError(int os_error) { // There are numerous Winsock error codes, but these are the ones we thus far // find interesting. switch (os_error) { - // connect fails with WSAEACCES when Windows Firewall blocks the - // connection. case WSAEACCES: return ERR_ACCESS_DENIED; case WSAENETDOWN: @@ -104,6 +102,10 @@ int MapWinsockError(int os_error) { int MapConnectError(int os_error) { switch (os_error) { + // connect fails with WSAEACCES when Windows Firewall blocks the + // connection. + case WSAEACCES: + return ERR_NETWORK_ACCESS_DENIED; case WSAETIMEDOUT: return ERR_CONNECTION_TIMED_OUT; default: { @@ -290,8 +292,8 @@ TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses, write_callback_(NULL), next_connect_state_(CONNECT_STATE_NONE), connect_os_error_(0), - net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) { - + net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), + previously_disconnected_(false) { scoped_refptr<NetLog::EventParameters> params; if (source.is_valid()) params = new NetLogSourceParameter("source_dependency", source); @@ -364,6 +366,11 @@ int TCPClientSocketWin::DoConnect() { DCHECK(ai); DCHECK_EQ(0, connect_os_error_); + if (previously_disconnected_) { + use_history_.Reset(); + previously_disconnected_ = false; + } + net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, new NetLogStringParameter( "address", NetAddressToStringWithPort(current_ai_))); @@ -478,6 +485,8 @@ void TCPClientSocketWin::DoDisconnect() { core_->Detach(); core_ = NULL; + + previously_disconnected_ = true; } bool TCPClientSocketWin::IsConnected() const { @@ -536,6 +545,11 @@ bool TCPClientSocketWin::WasEverUsed() const { return use_history_.was_used_to_convey_data(); } +bool TCPClientSocketWin::UsingTCPFastOpen() const { + // Not supported on windows. + return false; +} + int TCPClientSocketWin::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h index be25157..cd6b066 100644 --- a/net/socket/tcp_client_socket_win.h +++ b/net/socket/tcp_client_socket_win.h @@ -40,6 +40,7 @@ class TCPClientSocketWin : public ClientSocket, NonThreadSafe { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: // Multiple outstanding requests are not supported. @@ -117,6 +118,9 @@ class TCPClientSocketWin : public ClientSocket, NonThreadSafe { BoundNetLog net_log_; + // This socket was previously disconnected and has not been re-connected. + bool previously_disconnected_; + // Record of connectivity and transmissions, for use in speculative connection // histograms. UseHistory use_history_; |