diff options
Diffstat (limited to 'net/socket')
-rw-r--r-- | net/socket/client_socket_factory.cc | 2 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base_unittest.cc | 1 | ||||
-rw-r--r-- | net/socket/socket_test_util.cc | 1 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_mac_factory.cc | 1 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 193 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.h | 2 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss_factory.cc | 1 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_pool.cc | 2 | ||||
-rw-r--r-- | net/socket/ssl_host_info.cc | 100 | ||||
-rw-r--r-- | net/socket/ssl_host_info.h | 89 | ||||
-rw-r--r-- | net/socket/ssl_host_info.proto | 34 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_pool_unittest.cc | 1 |
12 files changed, 270 insertions, 157 deletions
diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc index a31af007..90bcdc5 100644 --- a/net/socket/client_socket_factory.cc +++ b/net/socket/client_socket_factory.cc @@ -6,7 +6,6 @@ #include "base/singleton.h" #include "build/build_config.h" -#include "net/base/ssl_host_info.h" #include "net/socket/client_socket_handle.h" #if defined(OS_WIN) #include "net/socket/ssl_client_socket_win.h" @@ -18,6 +17,7 @@ #include "net/socket/ssl_client_socket_mac.h" #include "net/socket/ssl_client_socket_nss.h" #endif +#include "net/socket/ssl_host_info.h" #include "net/socket/tcp_client_socket.h" namespace net { diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index 1f99b3d..820b030 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc @@ -23,6 +23,7 @@ #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_histograms.h" #include "net/socket/socket_test_util.h" +#include "net/socket/ssl_host_info.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index 9854cc5..53bcf89 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -21,6 +21,7 @@ #include "net/http/http_response_headers.h" #include "net/socket/client_socket_pool_histograms.h" #include "net/socket/socket.h" +#include "net/socket/ssl_host_info.h" #include "testing/gtest/include/gtest/gtest.h" #define NET_TRACE(level, s) DLOG(level) << s << __FUNCTION__ << "() " diff --git a/net/socket/ssl_client_socket_mac_factory.cc b/net/socket/ssl_client_socket_mac_factory.cc index bc26261..7f0c5ce 100644 --- a/net/socket/ssl_client_socket_mac_factory.cc +++ b/net/socket/ssl_client_socket_mac_factory.cc @@ -5,6 +5,7 @@ #include "net/socket/client_socket_factory.h" #include "net/socket/ssl_client_socket_mac.h" +#include "net/socket/ssl_host_info.h" namespace net { diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 95458fa..136f138 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -83,11 +83,11 @@ #include "net/base/net_log.h" #include "net/base/ssl_cert_request_info.h" #include "net/base/ssl_connection_status_flags.h" -#include "net/base/ssl_host_info.h" #include "net/base/ssl_info.h" #include "net/base/sys_addrinfo.h" #include "net/ocsp/nss_ocsp.h" #include "net/socket/client_socket_handle.h" +#include "net/socket/ssl_host_info.h" static const int kRecvBufferSize = 4096; @@ -461,15 +461,9 @@ int SSLClientSocketNSS::Init() { return OK; } -// This is a version number of the Snap Start information saved by -// |SaveSnapStartInfo| and loaded by |LoadSnapStartInfo|. Since the information -// can be saved on disk we might have version skew in the future. Any data with -// a different version is ignored by |LoadSnapStartInfo|. -static const uint8 kSnapStartInfoVersion = 0; - -// SaveSnapStartInfo serialises the information needed to perform a Snap Start -// with this server in the future (if any) and tells -// |ssl_host_info_| to preserve it. +// SaveSnapStartInfo extracts the information needed to perform a Snap Start +// with this server in the future (if any) and tells |ssl_host_info_| to +// preserve it. void SSLClientSocketNSS::SaveSnapStartInfo() { if (!ssl_host_info_.get()) return; @@ -501,32 +495,12 @@ void SSLClientSocketNSS::SaveSnapStartInfo() { 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); - // The format of the saved info looks like: - // struct Cert { - // uint16 length - // opaque certificate[length]; - // } - // - // uint8 version (kSnapStartInfoVersion) - // uint8 npn_status - // uint8 npn_proto_len - // uint8 npn_proto[npn_proto_len] - // uint16 hello_data_len - // opaque hello_data[hello_data_len] - // uint8 num_certs; - // Cert[num_certs]; - - std::string npn_proto; - NextProtoStatus npn_status = GetNextProto(&npn_proto); - - unsigned num_certs = 0; - unsigned len = 3; - DCHECK_LT(npn_proto.size(), 256u); - len += npn_proto.size(); - len += 2; // for hello_data_len - len += hello_data_len; - len++; // for |num_certs| + 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 @@ -535,67 +509,22 @@ void SSLClientSocketNSS::SaveSnapStartInfo() { if (!cert_list) return; - unsigned last_cert_len = 0; - bool last_cert_is_root = false; for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) { - num_certs++; if (node->cert->derCert.len > std::numeric_limits<uint16>::max()) { CERT_DestroyCertList(cert_list); return; } - last_cert_len = node->cert->derCert.len; - len += 2 + last_cert_len; - last_cert_is_root = node->cert->isRoot == PR_TRUE; - } - - if (num_certs == 0 || num_certs > std::numeric_limits<uint8>::max()) { - CERT_DestroyCertList(cert_list); - return; - } - - if (num_certs > 1 && last_cert_is_root) { - // The cert list included the root certificate, which we don't want to - // save. (Since we need to predict the server's certificates we don't want - // to predict the root cert because the server won't send it to us. We - // could implement this logic either here, or in the code which loads the - // certificates. But, by doing it here, we save a little disk space). - // - // Note that, when the TODO above (http://crbug.com/48854) is handled, this - // point will be moot. - len -= 2 + last_cert_len; - num_certs--; - } - - std::vector<uint8> data(len); - unsigned j = 0; - data[j++] = kSnapStartInfoVersion; - data[j++] = static_cast<uint8>(npn_status); - data[j++] = static_cast<uint8>(npn_proto.size()); - memcpy(&data[j], npn_proto.data(), npn_proto.size()); - j += npn_proto.size(); - data[j++] = hello_data_len >> 8; - data[j++] = hello_data_len; - memcpy(&data[j], hello_data, hello_data_len); - j += hello_data_len; - data[j++] = num_certs; - - unsigned i = 0; - for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); - i < num_certs; - node = CERT_LIST_NEXT(node), i++) { - data[j++] = node->cert->derCert.len >> 8; - data[j++] = node->cert->derCert.len; - memcpy(&data[j], node->cert->derCert.data, node->cert->derCert.len); - j += node->cert->derCert.len; + if (node->cert->isRoot == PR_TRUE) + continue; + state->certs.push_back(std::string( + reinterpret_cast<char*>(node->cert->derCert.data), + node->cert->derCert.len)); } - DCHECK_EQ(j, len); - - LOG(ERROR) << "Setting Snap Start info " << hostname_ << " " << len; - ssl_host_info_->Set(std::string( - reinterpret_cast<const char *>(&data[0]), len)); + LOG(ERROR) << "Setting Snap Start info " << hostname_; + ssl_host_info_->Persist(); CERT_DestroyCertList(cert_list); } @@ -609,69 +538,29 @@ static void DestroyCertificates(CERTCertificate** certs, unsigned len) { // by |SaveSnapStartInfo|, and sets the predicted certificates and ServerHello // data on the NSS socket. Returns true on success. If this function returns // false, the caller should try a normal TLS handshake. -bool SSLClientSocketNSS::LoadSnapStartInfo(const std::string& info) { - const unsigned char* data = - reinterpret_cast<const unsigned char*>(info.data()); - SECStatus rv; - - // See the comment in |SaveSnapStartInfo| for the format of the data. - if (info.size() < 3 || - data[0] != kSnapStartInfoVersion) { - return false; - } +bool SSLClientSocketNSS::LoadSnapStartInfo() { + const SSLHostInfo::State& state(ssl_host_info_->state()); - unsigned j = 1; - const uint8 npn_status = data[j++]; - const uint8 npn_proto_len = data[j++]; - if (static_cast<unsigned>(npn_proto_len) + j > info.size()) { - NOTREACHED(); + if (state.server_hello.empty() || + state.certs.empty() || + !state.npn_valid) { return false; } - const std::string npn_proto(info.substr(j, npn_proto_len)); - j += npn_proto_len; - if (j + 2 > info.size()) { - NOTREACHED(); - return false; - } - uint16 hello_data_len = static_cast<uint16>(data[j]) << 8 | - static_cast<uint16>(data[j+1]); - j += 2; - if (static_cast<unsigned>(hello_data_len) + j > info.size()) { - NOTREACHED(); - return false; - } - - rv = SSL_SetPredictedServerHelloData(nss_fd_, &data[j], hello_data_len); + SECStatus rv; + rv = SSL_SetPredictedServerHelloData( + nss_fd_, + reinterpret_cast<const uint8*>(state.server_hello.data()), + state.server_hello.size()); DCHECK_EQ(SECSuccess, rv); - j += hello_data_len; - - if (j + 1 > info.size()) { - NOTREACHED(); - return false; - } - unsigned num_certs = data[j++]; - scoped_array<CERTCertificate*> certs(new CERTCertificate*[num_certs]); - for (unsigned i = 0; i < num_certs; i++) { - if (j + 2 > info.size()) { - DestroyCertificates(&certs[0], i); - NOTREACHED(); - // It's harmless to call only SSL_SetPredictedServerHelloData. - return false; - } - uint16 cert_len = static_cast<uint16>(data[j]) << 8 | - static_cast<uint16>(data[j+1]); - j += 2; - if (static_cast<unsigned>(cert_len) + j > info.size()) { - DestroyCertificates(&certs[0], i); - NOTREACHED(); - return false; - } + const std::vector<std::string>& certs_in = state.certs; + scoped_array<CERTCertificate*> certs(new CERTCertificate*[certs_in.size()]); + for (size_t i = 0; i < certs_in.size(); i++) { SECItem derCert; - derCert.data = const_cast<uint8*>(data + j); - derCert.len = cert_len; - j += cert_len; + derCert.data = + const_cast<uint8*>(reinterpret_cast<const uint8*>(certs_in[i].data())); + derCert.len = certs_in[i].size(); certs[i] = CERT_NewTempCertificate( CERT_GetDefaultCertDB(), &derCert, NULL /* no nickname given */, PR_FALSE /* not permanent */, PR_TRUE /* copy DER data */); @@ -682,16 +571,14 @@ bool SSLClientSocketNSS::LoadSnapStartInfo(const std::string& info) { } } - rv = SSL_SetPredictedPeerCertificates(nss_fd_, certs.get(), num_certs); - DestroyCertificates(&certs[0], num_certs); + rv = SSL_SetPredictedPeerCertificates(nss_fd_, certs.get(), certs_in.size()); + DestroyCertificates(&certs[0], certs_in.size()); DCHECK_EQ(SECSuccess, rv); - predicted_npn_status_ = static_cast<NextProtoStatus>(npn_status); - predicted_npn_proto_ = npn_proto; - - // We ignore any trailing data that might be in |info|. - if (j != info.size()) - LOG(WARNING) << "Trailing data found in SSLHostInfo"; + if (state.npn_valid) { + predicted_npn_status_ = state.npn_status; + predicted_npn_proto_ = state.npn_protocol; + } return true; } @@ -1974,9 +1861,7 @@ int SSLClientSocketNSS::DoSnapStartLoadInfo() { int rv = ssl_host_info_->WaitForDataReady(&handshake_io_callback_); if (rv == OK) { - LOG(ERROR) << "SSL host info size " << hostname_ << " " - << ssl_host_info_->data().size(); - if (LoadSnapStartInfo(ssl_host_info_->data())) { + if (LoadSnapStartInfo()) { pseudo_connected_ = true; GotoState(STATE_SNAP_START_WAIT_FOR_WRITE); if (user_connect_callback_) diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 8518084..56d16b2 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -103,7 +103,7 @@ class SSLClientSocketNSS : public SSLClientSocket { int DoPayloadWrite(); int Init(); void SaveSnapStartInfo(); - bool LoadSnapStartInfo(const std::string& info); + bool LoadSnapStartInfo(); bool IsNPNProtocolMispredicted(); void UncorkAfterTimeout(); diff --git a/net/socket/ssl_client_socket_nss_factory.cc b/net/socket/ssl_client_socket_nss_factory.cc index b5488d9..efa6e23 100644 --- a/net/socket/ssl_client_socket_nss_factory.cc +++ b/net/socket/ssl_client_socket_nss_factory.cc @@ -6,6 +6,7 @@ #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 diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc index 048ff4b..98e3b09 100644 --- a/net/socket/ssl_client_socket_pool.cc +++ b/net/socket/ssl_client_socket_pool.cc @@ -8,13 +8,13 @@ #include "base/values.h" #include "net/base/net_errors.h" #include "net/base/ssl_cert_request_info.h" -#include "net/base/ssl_host_info.h" #include "net/http/http_proxy_client_socket.h" #include "net/http/http_proxy_client_socket_pool.h" #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_handle.h" #include "net/socket/socks_client_socket_pool.h" #include "net/socket/ssl_client_socket.h" +#include "net/socket/ssl_host_info.h" #include "net/socket/tcp_client_socket_pool.h" namespace net { diff --git a/net/socket/ssl_host_info.cc b/net/socket/ssl_host_info.cc new file mode 100644 index 0000000..4b5bb02 --- /dev/null +++ b/net/socket/ssl_host_info.cc @@ -0,0 +1,100 @@ +// 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/ssl_host_info.h" + +#include "net/socket/ssl_client_socket.h" +#include "net/socket/ssl_host_info.pb.h" + +namespace net { + +SSLHostInfo::SSLHostInfo() { + state_.npn_valid = false; +} + +SSLHostInfo::~SSLHostInfo() {} + +// This array and the next two functions serve to map between the internal NPN +// status enum (which might change across versions) and the protocol buffer +// based enum (which will not). +static const struct { + SSLClientSocket::NextProtoStatus npn_status; + SSLHostInfoProto::NextProtoStatus proto_status; +} kNPNStatusMapping[] = { + { SSLClientSocket::kNextProtoUnsupported, SSLHostInfoProto::UNSUPPORTED }, + { SSLClientSocket::kNextProtoNegotiated, SSLHostInfoProto::NEGOTIATED }, + { SSLClientSocket::kNextProtoNoOverlap, SSLHostInfoProto::NO_OVERLAP }, +}; + +static SSLClientSocket::NextProtoStatus NPNStatusFromProtoStatus( + SSLHostInfoProto::NextProtoStatus proto_status) { + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kNPNStatusMapping) - 1; i++) { + if (kNPNStatusMapping[i].proto_status == proto_status) + return kNPNStatusMapping[i].npn_status; + } + return kNPNStatusMapping[ARRAYSIZE_UNSAFE(kNPNStatusMapping) - 1].npn_status; +} + +static SSLHostInfoProto::NextProtoStatus ProtoStatusFromNPNStatus( + SSLClientSocket::NextProtoStatus npn_status) { + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kNPNStatusMapping) - 1; i++) { + if (kNPNStatusMapping[i].npn_status == npn_status) + return kNPNStatusMapping[i].proto_status; + } + return kNPNStatusMapping[ARRAYSIZE_UNSAFE(kNPNStatusMapping)-1].proto_status; +} + +const SSLHostInfo::State& SSLHostInfo::state() const { + return state_; +} + +SSLHostInfo::State* SSLHostInfo::mutable_state() { + return &state_; +} + +bool SSLHostInfo::Parse(const std::string& data) { + SSLHostInfoProto proto; + State* state = mutable_state(); + + state->certs.clear(); + state->server_hello.clear(); + state->npn_valid = false; + + if (!proto.ParseFromString(data)) + return false; + + for (int i = 0; i < proto.certificate_der_size(); i++) + state->certs.push_back(proto.certificate_der(i)); + if (proto.has_server_hello()) + state->server_hello = proto.server_hello(); + if (proto.has_npn_status() && proto.has_npn_protocol()) { + state->npn_valid = true; + state->npn_status = NPNStatusFromProtoStatus(proto.npn_status()); + state->npn_protocol = proto.npn_protocol(); + } + + return true; +} + +std::string SSLHostInfo::Serialize() const { + SSLHostInfoProto proto; + + for (std::vector<std::string>::const_iterator + i = state_.certs.begin(); i != state_.certs.end(); i++) { + proto.add_certificate_der(*i); + } + if (!state_.server_hello.empty()) + proto.set_server_hello(state_.server_hello); + + if (state_.npn_valid) { + proto.set_npn_status(ProtoStatusFromNPNStatus(state_.npn_status)); + proto.set_npn_protocol(state_.npn_protocol); + } + + return proto.SerializeAsString(); +} + +SSLHostInfoFactory::~SSLHostInfoFactory() {} + +} // namespace net diff --git a/net/socket/ssl_host_info.h b/net/socket/ssl_host_info.h new file mode 100644 index 0000000..8065b47 --- /dev/null +++ b/net/socket/ssl_host_info.h @@ -0,0 +1,89 @@ +// 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_SSL_HOST_INFO_H +#define NET_SOCKET_SSL_HOST_INFO_H + +#include <string> +#include <vector> + +#include "base/ref_counted.h" +#include "net/base/completion_callback.h" +#include "net/socket/ssl_client_socket.h" + +namespace net { + +// 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(); + virtual ~SSLHostInfo(); + + // Start will commence the lookup. This must be called before any other + // methods. By opportunistically calling this early, it may be possible to + // overlap this object's lookup and reduce latency. + virtual void Start() = 0; + + // WaitForDataReady returns OK if the fetch of the requested data has + // completed. Otherwise it returns ERR_IO_PENDING and will call |callback| on + // the current thread when ready. + // + // Only a single callback can be outstanding at a given time and, in the + // event that WaitForDataReady returns OK, it's the caller's responsibility + // to delete |callback|. + // + // |callback| may be NULL, in which case ERR_IO_PENDING may still be returned + // but, obviously, a callback will never be made. + virtual int WaitForDataReady(CompletionCallback* callback) = 0; + + // Persist allows for the host information to be updated for future users. + // This is a fire and forget operation: the caller may drop its reference + // from this object and the store operation will still complete. This can + // only be called once WaitForDataReady has returned OK or called its + // callback. + virtual void Persist() = 0; + + struct 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; + // server_hello contains the bytes of the ServerHello message (or may be + // empty if the server doesn't support Snap Start.) + std::string server_hello; + // npn_valid is true iff |npn_status| and |npn_protocol| is successful. + bool npn_valid; + // these members contain the NPN result of a connection to the server. + SSLClientSocket::NextProtoStatus npn_status; + std::string npn_protocol; + }; + + // Once the data is ready, it can be read using the following members. These + // members can then be updated before calling |Persist|. + const State& state() const; + State* mutable_state(); + + 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 + // member fields will be set to something sane in any case. + bool Parse(const std::string& data); + std::string Serialize() const; + State state_; +}; + +class SSLHostInfoFactory { + public: + virtual ~SSLHostInfoFactory(); + + // GetForHost returns a fresh, allocated SSLHostInfo for the given hostname + // or NULL on failure. + virtual SSLHostInfo* GetForHost(const std::string& hostname) = 0; +}; + +} // namespace net + +#endif // NET_SOCKET_SSL_HOST_INFO_H diff --git a/net/socket/ssl_host_info.proto b/net/socket/ssl_host_info.proto new file mode 100644 index 0000000..d74a872 --- /dev/null +++ b/net/socket/ssl_host_info.proto @@ -0,0 +1,34 @@ +// 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package net; + +// SSLHostInfoProto contains information which we store about a given HTTPS +// server. +message SSLHostInfoProto { + // The certificate chain, as returned by the server, as a series of DER + // encoded X.509 certificates. + repeated bytes certificate_der = 1; + // The contents of the server's ServerHello message. Needed in order to + // predict its response in the case of Snap Start. + optional bytes server_hello = 2; + + // This is a mirror of SSLClientSocket::NextProtoStatus. We use this in order + // to be robust against changes to that enum, which isn't required to be + // stable across versions. See the comments there for details. + enum NextProtoStatus { + UNSUPPORTED = 0; + NEGOTIATED = 1; + NO_OVERLAP = 2; + } + + // When doing Snap Start, we also need to know what protocol we expect the + // server to negotiate. + optional NextProtoStatus npn_status = 3; + optional bytes npn_protocol = 4; +}; diff --git a/net/socket/tcp_client_socket_pool_unittest.cc b/net/socket/tcp_client_socket_pool_unittest.cc index 32ac9f8..80de0aa 100644 --- a/net/socket/tcp_client_socket_pool_unittest.cc +++ b/net/socket/tcp_client_socket_pool_unittest.cc @@ -15,6 +15,7 @@ #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_histograms.h" #include "net/socket/socket_test_util.h" +#include "net/socket/ssl_host_info.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { |