summaryrefslogtreecommitdiffstats
path: root/net/socket/ssl_client_socket_nss.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/socket/ssl_client_socket_nss.cc')
-rw-r--r--net/socket/ssl_client_socket_nss.cc882
1 files changed, 562 insertions, 320 deletions
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