summaryrefslogtreecommitdiffstats
path: root/net/socket/ssl_client_socket_nss.cc
diff options
context:
space:
mode:
authorerg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-26 22:47:11 +0000
committererg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-26 22:47:11 +0000
commitd100e44f64d4abb2cc244cb61bb736c602146767 (patch)
treebfdd81d5424b2335e8543044dd726b0d30666663 /net/socket/ssl_client_socket_nss.cc
parent5d8054efc1e1f26ea806e46869df5e0a84e41a4c (diff)
downloadchromium_src-d100e44f64d4abb2cc244cb61bb736c602146767.zip
chromium_src-d100e44f64d4abb2cc244cb61bb736c602146767.tar.gz
chromium_src-d100e44f64d4abb2cc244cb61bb736c602146767.tar.bz2
More net/ method ordering.
BUG=68682 TEST=compiles Review URL: http://codereview.chromium.org/6339012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@72710 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket/ssl_client_socket_nss.cc')
-rw-r--r--net/socket/ssl_client_socket_nss.cc2239
1 files changed, 1119 insertions, 1120 deletions
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index d006d58..a71af2c 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -279,6 +279,162 @@ class PeerCertificateChain {
CERTCertificate** certs_;
};
+void DestroyCertificates(CERTCertificate** certs, unsigned len) {
+ for (unsigned i = 0; i < len; i++)
+ CERT_DestroyCertificate(certs[i]);
+}
+
+// DNSValidationResult enumerates the possible outcomes from processing a
+// set of DNS records.
+enum DNSValidationResult {
+ DNSVR_SUCCESS, // the cert is immediately acceptable.
+ DNSVR_FAILURE, // the cert is unconditionally rejected.
+ DNSVR_CONTINUE, // perform CA validation as usual.
+};
+
+// VerifyTXTRecords processes the RRDATA for a number of DNS TXT records and
+// checks them against the given certificate.
+// dnssec: if true then the TXT records are DNSSEC validated. In this case,
+// DNSVR_SUCCESS may be returned.
+// server_cert_nss: the certificate to validate
+// rrdatas: the TXT records for the current domain.
+DNSValidationResult VerifyTXTRecords(
+ bool dnssec,
+ CERTCertificate* server_cert_nss,
+ const std::vector<base::StringPiece>& rrdatas) {
+ bool found_well_formed_record = false;
+ bool matched_record = false;
+
+ for (std::vector<base::StringPiece>::const_iterator
+ i = rrdatas.begin(); i != rrdatas.end(); ++i) {
+ std::map<std::string, std::string> m(
+ DNSSECChainVerifier::ParseTLSTXTRecord(*i));
+ if (m.empty())
+ continue;
+
+ std::map<std::string, std::string>::const_iterator j;
+ j = m.find("v");
+ if (j == m.end() || j->second != "tls1")
+ continue;
+
+ j = m.find("ha");
+
+ HASH_HashType hash_algorithm;
+ unsigned hash_length;
+ if (j == m.end() || j->second == "sha1") {
+ hash_algorithm = HASH_AlgSHA1;
+ hash_length = SHA1_LENGTH;
+ } else if (j->second == "sha256") {
+ hash_algorithm = HASH_AlgSHA256;
+ hash_length = SHA256_LENGTH;
+ } else {
+ continue;
+ }
+
+ j = m.find("h");
+ if (j == m.end())
+ continue;
+
+ std::vector<uint8> given_hash;
+ if (!base::HexStringToBytes(j->second, &given_hash))
+ continue;
+
+ if (given_hash.size() != hash_length)
+ continue;
+
+ uint8 calculated_hash[SHA256_LENGTH]; // SHA256 is the largest.
+ SECStatus rv;
+
+ j = m.find("hr");
+ if (j == m.end() || j->second == "pubkey") {
+ rv = HASH_HashBuf(hash_algorithm, calculated_hash,
+ server_cert_nss->derPublicKey.data,
+ server_cert_nss->derPublicKey.len);
+ } else if (j->second == "cert") {
+ rv = HASH_HashBuf(hash_algorithm, calculated_hash,
+ server_cert_nss->derCert.data,
+ server_cert_nss->derCert.len);
+ } else {
+ continue;
+ }
+
+ if (rv != SECSuccess)
+ NOTREACHED();
+
+ found_well_formed_record = true;
+
+ if (memcmp(calculated_hash, &given_hash[0], hash_length) == 0) {
+ matched_record = true;
+ if (dnssec)
+ return DNSVR_SUCCESS;
+ }
+ }
+
+ if (found_well_formed_record && !matched_record)
+ return DNSVR_FAILURE;
+
+ return DNSVR_CONTINUE;
+}
+
+// CheckDNSSECChain tries to validate a DNSSEC chain embedded in
+// |server_cert_nss_|. It returns true iff a chain is found that proves the
+// value of a TXT record that contains a valid public key fingerprint.
+DNSValidationResult CheckDNSSECChain(
+ const std::string& hostname,
+ CERTCertificate* server_cert_nss) {
+ if (!server_cert_nss)
+ return DNSVR_CONTINUE;
+
+ // CERT_FindCertExtensionByOID isn't exported so we have to install an OID,
+ // get a tag for it and find the extension by using that tag.
+ static SECOidTag dnssec_chain_tag;
+ static bool dnssec_chain_tag_valid;
+ if (!dnssec_chain_tag_valid) {
+ // It's harmless if multiple threads enter this block concurrently.
+ static const uint8 kDNSSECChainOID[] =
+ // 1.3.6.1.4.1.11129.2.1.4
+ // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
+ // certificateExtensions.dnssecEmbeddedChain)
+ {0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x04};
+ SECOidData oid_data;
+ memset(&oid_data, 0, sizeof(oid_data));
+ oid_data.oid.data = const_cast<uint8*>(kDNSSECChainOID);
+ oid_data.oid.len = sizeof(kDNSSECChainOID);
+ oid_data.desc = "DNSSEC chain";
+ oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
+ dnssec_chain_tag = SECOID_AddEntry(&oid_data);
+ DCHECK_NE(SEC_OID_UNKNOWN, dnssec_chain_tag);
+ dnssec_chain_tag_valid = true;
+ }
+
+ SECItem dnssec_embedded_chain;
+ SECStatus rv = CERT_FindCertExtension(server_cert_nss,
+ dnssec_chain_tag, &dnssec_embedded_chain);
+ if (rv != SECSuccess)
+ return DNSVR_CONTINUE;
+
+ base::StringPiece chain(
+ reinterpret_cast<char*>(dnssec_embedded_chain.data),
+ dnssec_embedded_chain.len);
+ std::string dns_hostname;
+ if (!DNSDomainFromDot(hostname, &dns_hostname))
+ return DNSVR_CONTINUE;
+ DNSSECChainVerifier verifier(dns_hostname, chain);
+ DNSSECChainVerifier::Error err = verifier.Verify();
+ if (err != DNSSECChainVerifier::OK) {
+ LOG(ERROR) << "DNSSEC chain verification failed: " << err;
+ return DNSVR_CONTINUE;
+ }
+
+ if (verifier.rrtype() != kDNS_TXT)
+ return DNSVR_CONTINUE;
+
+ DNSValidationResult r = VerifyTXTRecords(
+ true /* DNSSEC verified */, server_cert_nss, verifier.rrdatas());
+ SECITEM_FreeItem(&dnssec_embedded_chain, PR_FALSE);
+ return r;
+}
+
} // namespace
SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket,
@@ -333,156 +489,94 @@ SSLClientSocketNSS::~SSLClientSocketNSS() {
LeaveFunction("");
}
-int SSLClientSocketNSS::Init() {
- EnterFunction("");
- // Initialize the NSS SSL library in a threadsafe way. This also
- // initializes the NSS base library.
- EnsureNSSSSLInit();
- if (!NSS_IsInitialized())
- return ERR_UNEXPECTED;
-#if !defined(OS_MACOSX) && !defined(OS_WIN)
- // We must call EnsureOCSPInit() here, on the IO thread, to get the IO loop
- // by MessageLoopForIO::current().
- // X509Certificate::Verify() runs on a worker thread of CertVerifier.
- EnsureOCSPInit();
-#endif
-
- LeaveFunction("");
- return OK;
+// static
+void SSLClientSocketNSS::ClearSessionCache() {
+ SSL_ClearSessionCache();
}
-// 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;
-
- // If the SSLHostInfo hasn't managed to load from disk yet then we can't save
- // anything.
- if (ssl_host_info_->WaitForDataReady(NULL) != OK)
- return;
+void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
+ EnterFunction("");
+ ssl_info->Reset();
- SECStatus rv;
- SSLSnapStartResult snap_start_type;
- rv = SSL_GetSnapStartResult(nss_fd_, &snap_start_type);
- if (rv != SECSuccess) {
- NOTREACHED();
- return;
- }
- net_log_.AddEvent(NetLog::TYPE_SSL_SNAP_START,
- new NetLogIntegerParameter("type", snap_start_type));
- if (snap_start_type == SSL_SNAP_START_FULL ||
- snap_start_type == SSL_SNAP_START_RESUME) {
- // If we did a successful Snap Start then our information was correct and
- // there's no point saving it again.
+ if (!server_cert_) {
+ LOG(DFATAL) << "!server_cert_";
return;
}
- const unsigned char* hello_data;
- unsigned hello_data_len;
- rv = SSL_GetPredictedServerHelloData(nss_fd_, &hello_data, &hello_data_len);
- if (rv != SECSuccess) {
- NOTREACHED();
- return;
- }
- if (hello_data_len > std::numeric_limits<uint16>::max())
- return;
- SSLHostInfo::State* state = ssl_host_info_->mutable_state();
+ 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_;
- if (hello_data_len > 0) {
- 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);
+ 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 {
- state->server_hello.clear();
- state->npn_valid = false;
- }
-
- 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;
-
- state->certs.push_back(std::string(
- reinterpret_cast<char*>(certs[i]->derCert.data),
- certs[i]->derCert.len));
+ ssl_info->security_bits = -1;
+ LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError()
+ << " for cipherSuite " << cipher_suite;
}
-
- ssl_host_info_->Persist();
+ LeaveFunction("");
}
-static void DestroyCertificates(CERTCertificate** certs, unsigned len) {
- for (unsigned i = 0; i < len; i++)
- CERT_DestroyCertificate(certs[i]);
+void SSLClientSocketNSS::GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) {
+ EnterFunction("");
+ // TODO(rch): switch SSLCertRequestInfo.host_and_port to a HostPortPair
+ cert_request_info->host_and_port = host_and_port_.ToString();
+ cert_request_info->client_certs = client_certs_;
+ LeaveFunction(cert_request_info->client_certs.size());
}
-// LoadSnapStartInfo parses |info|, which contains data previously serialised
-// 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 SSLHostInfo::State& state(ssl_host_info_->state());
-
- if (state.server_hello.empty() ||
- state.certs.empty() ||
- !state.npn_valid) {
- return false;
+SSLClientSocket::NextProtoStatus
+SSLClientSocketNSS::GetNextProto(std::string* proto) {
+#if defined(SSL_NEXT_PROTO_NEGOTIATED)
+ if (!handshake_callback_called_) {
+ DCHECK(pseudo_connected_);
+ predicted_npn_proto_used_ = true;
+ *proto = predicted_npn_proto_;
+ return predicted_npn_status_;
}
- SECStatus rv;
- rv = SSL_SetPredictedServerHelloData(
- nss_fd_,
- reinterpret_cast<const uint8*>(state.server_hello.data()),
- state.server_hello.size());
- DCHECK_EQ(SECSuccess, rv);
-
- 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*>(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 */);
- if (!certs[i]) {
- DestroyCertificates(&certs[0], i);
- NOTREACHED();
- return false;
- }
+ unsigned char buf[255];
+ int state;
+ unsigned len;
+ SECStatus rv = SSL_GetNextProto(nss_fd_, &state, buf, &len, sizeof(buf));
+ if (rv != SECSuccess) {
+ NOTREACHED() << "Error return from SSL_GetNextProto: " << rv;
+ proto->clear();
+ return kNextProtoUnsupported;
}
-
- rv = SSL_SetPredictedPeerCertificates(nss_fd_, certs.get(), certs_in.size());
- DestroyCertificates(&certs[0], certs_in.size());
- DCHECK_EQ(SECSuccess, rv);
-
- if (state.npn_valid) {
- predicted_npn_status_ = state.npn_status;
- predicted_npn_proto_ = state.npn_protocol;
+ // We don't check for truncation because sizeof(buf) is large enough to hold
+ // the maximum protocol size.
+ switch (state) {
+ case SSL_NEXT_PROTO_NO_SUPPORT:
+ proto->clear();
+ return kNextProtoUnsupported;
+ case SSL_NEXT_PROTO_NEGOTIATED:
+ *proto = std::string(reinterpret_cast<char*>(buf), len);
+ return kNextProtoNegotiated;
+ case SSL_NEXT_PROTO_NO_OVERLAP:
+ *proto = std::string(reinterpret_cast<char*>(buf), len);
+ return kNextProtoNoOverlap;
+ default:
+ NOTREACHED() << "Unknown status from SSL_GetNextProto: " << state;
+ proto->clear();
+ return kNextProtoUnsupported;
}
-
- return true;
-}
-
-bool SSLClientSocketNSS::IsNPNProtocolMispredicted() {
- DCHECK(handshake_callback_called_);
- if (!predicted_npn_proto_used_)
- return false;
- std::string npn_proto;
- GetNextProto(&npn_proto);
- return predicted_npn_proto_ != npn_proto;
+#else
+ // No NPN support in the libssl that we are building with.
+ proto->clear();
+ return kNextProtoUnsupported;
+#endif
}
-void SSLClientSocketNSS::UncorkAfterTimeout() {
- corked_ = false;
- int nsent;
- do {
- nsent = BufferSend();
- } while (nsent > 0);
+void SSLClientSocketNSS::UseDNSSEC(DNSSECProvider* provider) {
+ dnssec_provider_ = provider;
}
int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
@@ -542,6 +636,228 @@ int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
return rv > OK ? OK : rv;
}
+void SSLClientSocketNSS::Disconnect() {
+ EnterFunction("");
+
+ // TODO(wtc): Send SSL close_notify alert.
+ if (nss_fd_ != NULL) {
+ PR_Close(nss_fd_);
+ nss_fd_ = NULL;
+ }
+
+ // Shut down anything that may call us back (through buffer_send_callback_,
+ // buffer_recv_callback, or handshake_io_callback_).
+ verifier_.reset();
+ transport_->socket()->Disconnect();
+
+ // Reset object state
+ transport_send_busy_ = false;
+ transport_recv_busy_ = false;
+ user_connect_callback_ = NULL;
+ user_read_callback_ = NULL;
+ user_write_callback_ = NULL;
+ user_read_buf_ = NULL;
+ user_read_buf_len_ = 0;
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+ server_cert_ = NULL;
+ if (server_cert_nss_) {
+ CERT_DestroyCertificate(server_cert_nss_);
+ server_cert_nss_ = NULL;
+ }
+ 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;
+ start_cert_verification_time_ = base::TimeTicks();
+ predicted_cert_chain_correct_ = false;
+ peername_initialized_ = false;
+ nss_bufs_ = NULL;
+ client_certs_.clear();
+ client_auth_cert_needed_ = false;
+
+ LeaveFunction("");
+}
+
+bool SSLClientSocketNSS::IsConnected() const {
+ // Ideally, we should also check if we have received the close_notify alert
+ // message from the server, and return false in that case. We're not doing
+ // that, so this function may return a false positive. Since the upper
+ // layer (HttpNetworkTransaction) needs to handle a persistent connection
+ // closed by the server when we send a request anyway, a false positive in
+ // exchange for simpler code is a good trade-off.
+ EnterFunction("");
+ bool ret = (pseudo_connected_ || completed_handshake_) &&
+ transport_->socket()->IsConnected();
+ LeaveFunction("");
+ return ret;
+}
+
+bool SSLClientSocketNSS::IsConnectedAndIdle() const {
+ // Unlike IsConnected, this method doesn't return a false positive.
+ //
+ // Strictly speaking, we should check if we have received the close_notify
+ // alert message from the server, and return false in that case. Although
+ // the close_notify alert message means EOF in the SSL layer, it is just
+ // bytes to the transport layer below, so
+ // transport_->socket()->IsConnectedAndIdle() returns the desired false
+ // when we receive close_notify.
+ EnterFunction("");
+ bool ret = (pseudo_connected_ || completed_handshake_) &&
+ transport_->socket()->IsConnectedAndIdle();
+ LeaveFunction("");
+ return ret;
+}
+
+int SSLClientSocketNSS::GetPeerAddress(AddressList* address) const {
+ return transport_->socket()->GetPeerAddress(address);
+}
+
+const BoundNetLog& SSLClientSocketNSS::NetLog() const {
+ return net_log_;
+}
+
+void SSLClientSocketNSS::SetSubresourceSpeculation() {
+ if (transport_.get() && transport_->socket()) {
+ transport_->socket()->SetSubresourceSpeculation();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void SSLClientSocketNSS::SetOmniboxSpeculation() {
+ if (transport_.get() && transport_->socket()) {
+ transport_->socket()->SetOmniboxSpeculation();
+ } else {
+ NOTREACHED();
+ }
+}
+
+bool SSLClientSocketNSS::WasEverUsed() const {
+ if (transport_.get() && transport_->socket()) {
+ return transport_->socket()->WasEverUsed();
+ }
+ NOTREACHED();
+ 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);
+ DCHECK(!user_read_callback_);
+ DCHECK(!user_connect_callback_);
+ DCHECK(!user_read_buf_);
+ DCHECK(nss_bufs_);
+
+ user_read_buf_ = buf;
+ user_read_buf_len_ = buf_len;
+
+ if (!completed_handshake_) {
+ // In this case we have lied about being connected in order to merge the
+ // first Write into a Snap Start handshake. We'll leave the read hanging
+ // until the handshake has completed.
+ DCHECK(pseudo_connected_);
+
+ user_read_callback_ = callback;
+ LeaveFunction(ERR_IO_PENDING);
+ return ERR_IO_PENDING;
+ }
+
+ int rv = DoReadLoop(OK);
+
+ if (rv == ERR_IO_PENDING) {
+ user_read_callback_ = callback;
+ } else {
+ user_read_buf_ = NULL;
+ user_read_buf_len_ = 0;
+ }
+ LeaveFunction(rv);
+ return rv;
+}
+
+int SSLClientSocketNSS::Write(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ EnterFunction(buf_len);
+ if (!pseudo_connected_) {
+ DCHECK(completed_handshake_);
+ DCHECK(next_handshake_state_ == STATE_NONE);
+ DCHECK(!user_connect_callback_);
+ }
+ DCHECK(!user_write_callback_);
+ DCHECK(!user_write_buf_);
+ DCHECK(nss_bufs_);
+
+ user_write_buf_ = buf;
+ user_write_buf_len_ = buf_len;
+
+ if (next_handshake_state_ == STATE_SNAP_START_WAIT_FOR_WRITE) {
+ // We lied about being connected and we have been waiting for this write in
+ // order to merge it into the Snap Start handshake. We'll leave the write
+ // pending until the handshake completes.
+ DCHECK(pseudo_connected_);
+ int rv = DoHandshakeLoop(OK);
+ if (rv == ERR_IO_PENDING) {
+ user_write_callback_ = callback;
+ } else {
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+ }
+ if (rv != OK)
+ return rv;
+ }
+
+ if (corked_) {
+ corked_ = false;
+ uncork_timer_.Reset();
+ }
+ int rv = DoWriteLoop(OK);
+
+ if (rv == ERR_IO_PENDING) {
+ user_write_callback_ = callback;
+ } else {
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+ }
+ LeaveFunction(rv);
+ return rv;
+}
+
+bool SSLClientSocketNSS::SetReceiveBufferSize(int32 size) {
+ return transport_->socket()->SetReceiveBufferSize(size);
+}
+
+bool SSLClientSocketNSS::SetSendBufferSize(int32 size) {
+ return transport_->socket()->SetSendBufferSize(size);
+}
+
+int SSLClientSocketNSS::Init() {
+ EnterFunction("");
+ // Initialize the NSS SSL library in a threadsafe way. This also
+ // initializes the NSS base library.
+ EnsureNSSSSLInit();
+ if (!NSS_IsInitialized())
+ return ERR_UNEXPECTED;
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
+ // We must call EnsureOCSPInit() here, on the IO thread, to get the IO loop
+ // by MessageLoopForIO::current().
+ // X509Certificate::Verify() runs on a worker thread of CertVerifier.
+ EnsureOCSPInit();
+#endif
+
+ LeaveFunction("");
+ return OK;
+}
+
int SSLClientSocketNSS::InitializeSSLOptions() {
// Transport connected, now hook it up to nss
// TODO(port): specify rx and tx buffer sizes separately
@@ -764,214 +1080,6 @@ int SSLClientSocketNSS::InitializeSSLPeerName() {
return OK;
}
-void SSLClientSocketNSS::Disconnect() {
- EnterFunction("");
-
- // TODO(wtc): Send SSL close_notify alert.
- if (nss_fd_ != NULL) {
- PR_Close(nss_fd_);
- nss_fd_ = NULL;
- }
-
- // Shut down anything that may call us back (through buffer_send_callback_,
- // buffer_recv_callback, or handshake_io_callback_).
- verifier_.reset();
- transport_->socket()->Disconnect();
-
- // Reset object state
- transport_send_busy_ = false;
- transport_recv_busy_ = false;
- user_connect_callback_ = NULL;
- user_read_callback_ = NULL;
- user_write_callback_ = NULL;
- user_read_buf_ = NULL;
- user_read_buf_len_ = 0;
- user_write_buf_ = NULL;
- user_write_buf_len_ = 0;
- server_cert_ = NULL;
- if (server_cert_nss_) {
- CERT_DestroyCertificate(server_cert_nss_);
- server_cert_nss_ = NULL;
- }
- 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;
- start_cert_verification_time_ = base::TimeTicks();
- predicted_cert_chain_correct_ = false;
- peername_initialized_ = false;
- nss_bufs_ = NULL;
- client_certs_.clear();
- client_auth_cert_needed_ = false;
-
- LeaveFunction("");
-}
-
-bool SSLClientSocketNSS::IsConnected() const {
- // Ideally, we should also check if we have received the close_notify alert
- // message from the server, and return false in that case. We're not doing
- // that, so this function may return a false positive. Since the upper
- // layer (HttpNetworkTransaction) needs to handle a persistent connection
- // closed by the server when we send a request anyway, a false positive in
- // exchange for simpler code is a good trade-off.
- EnterFunction("");
- bool ret = (pseudo_connected_ || completed_handshake_) &&
- transport_->socket()->IsConnected();
- LeaveFunction("");
- return ret;
-}
-
-bool SSLClientSocketNSS::IsConnectedAndIdle() const {
- // Unlike IsConnected, this method doesn't return a false positive.
- //
- // Strictly speaking, we should check if we have received the close_notify
- // alert message from the server, and return false in that case. Although
- // the close_notify alert message means EOF in the SSL layer, it is just
- // bytes to the transport layer below, so
- // transport_->socket()->IsConnectedAndIdle() returns the desired false
- // when we receive close_notify.
- EnterFunction("");
- bool ret = (pseudo_connected_ || completed_handshake_) &&
- transport_->socket()->IsConnectedAndIdle();
- LeaveFunction("");
- return ret;
-}
-
-int SSLClientSocketNSS::GetPeerAddress(AddressList* address) const {
- return transport_->socket()->GetPeerAddress(address);
-}
-
-const BoundNetLog& SSLClientSocketNSS::NetLog() const {
- return net_log_;
-}
-
-void SSLClientSocketNSS::SetSubresourceSpeculation() {
- if (transport_.get() && transport_->socket()) {
- transport_->socket()->SetSubresourceSpeculation();
- } else {
- NOTREACHED();
- }
-}
-
-void SSLClientSocketNSS::SetOmniboxSpeculation() {
- if (transport_.get() && transport_->socket()) {
- transport_->socket()->SetOmniboxSpeculation();
- } else {
- NOTREACHED();
- }
-}
-
-bool SSLClientSocketNSS::WasEverUsed() const {
- if (transport_.get() && transport_->socket()) {
- return transport_->socket()->WasEverUsed();
- }
- NOTREACHED();
- 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);
- DCHECK(!user_read_callback_);
- DCHECK(!user_connect_callback_);
- DCHECK(!user_read_buf_);
- DCHECK(nss_bufs_);
-
- user_read_buf_ = buf;
- user_read_buf_len_ = buf_len;
-
- if (!completed_handshake_) {
- // In this case we have lied about being connected in order to merge the
- // first Write into a Snap Start handshake. We'll leave the read hanging
- // until the handshake has completed.
- DCHECK(pseudo_connected_);
-
- user_read_callback_ = callback;
- LeaveFunction(ERR_IO_PENDING);
- return ERR_IO_PENDING;
- }
-
- int rv = DoReadLoop(OK);
-
- if (rv == ERR_IO_PENDING) {
- user_read_callback_ = callback;
- } else {
- user_read_buf_ = NULL;
- user_read_buf_len_ = 0;
- }
- LeaveFunction(rv);
- return rv;
-}
-
-int SSLClientSocketNSS::Write(IOBuffer* buf, int buf_len,
- CompletionCallback* callback) {
- EnterFunction(buf_len);
- if (!pseudo_connected_) {
- DCHECK(completed_handshake_);
- DCHECK(next_handshake_state_ == STATE_NONE);
- DCHECK(!user_connect_callback_);
- }
- DCHECK(!user_write_callback_);
- DCHECK(!user_write_buf_);
- DCHECK(nss_bufs_);
-
- user_write_buf_ = buf;
- user_write_buf_len_ = buf_len;
-
- if (next_handshake_state_ == STATE_SNAP_START_WAIT_FOR_WRITE) {
- // We lied about being connected and we have been waiting for this write in
- // order to merge it into the Snap Start handshake. We'll leave the write
- // pending until the handshake completes.
- DCHECK(pseudo_connected_);
- int rv = DoHandshakeLoop(OK);
- if (rv == ERR_IO_PENDING) {
- user_write_callback_ = callback;
- } else {
- user_write_buf_ = NULL;
- user_write_buf_len_ = 0;
- }
- if (rv != OK)
- return rv;
- }
-
- if (corked_) {
- corked_ = false;
- uncork_timer_.Reset();
- }
- int rv = DoWriteLoop(OK);
-
- if (rv == ERR_IO_PENDING) {
- user_write_callback_ = callback;
- } else {
- user_write_buf_ = NULL;
- user_write_buf_len_ = 0;
- }
- LeaveFunction(rv);
- return rv;
-}
-
-bool SSLClientSocketNSS::SetReceiveBufferSize(int32 size) {
- return transport_->socket()->SetReceiveBufferSize(size);
-}
-
-bool SSLClientSocketNSS::SetSendBufferSize(int32 size) {
- return transport_->socket()->SetSendBufferSize(size);
-}
-
-// static
-void SSLClientSocketNSS::ClearSessionCache() {
- SSL_ClearSessionCache();
-}
// Sets server_cert_ and server_cert_nss_ if not yet set.
// Returns server_cert_.
@@ -1051,91 +1159,6 @@ void SSLClientSocketNSS::UpdateConnectionStatus() {
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("");
-}
-
-void SSLClientSocketNSS::GetSSLCertRequestInfo(
- SSLCertRequestInfo* cert_request_info) {
- EnterFunction("");
- // TODO(rch): switch SSLCertRequestInfo.host_and_port to a HostPortPair
- cert_request_info->host_and_port = host_and_port_.ToString();
- cert_request_info->client_certs = client_certs_;
- LeaveFunction(cert_request_info->client_certs.size());
-}
-
-SSLClientSocket::NextProtoStatus
-SSLClientSocketNSS::GetNextProto(std::string* proto) {
-#if defined(SSL_NEXT_PROTO_NEGOTIATED)
- if (!handshake_callback_called_) {
- DCHECK(pseudo_connected_);
- predicted_npn_proto_used_ = true;
- *proto = predicted_npn_proto_;
- return predicted_npn_status_;
- }
-
- unsigned char buf[255];
- int state;
- unsigned len;
- SECStatus rv = SSL_GetNextProto(nss_fd_, &state, buf, &len, sizeof(buf));
- if (rv != SECSuccess) {
- NOTREACHED() << "Error return from SSL_GetNextProto: " << rv;
- proto->clear();
- return kNextProtoUnsupported;
- }
- // We don't check for truncation because sizeof(buf) is large enough to hold
- // the maximum protocol size.
- switch (state) {
- case SSL_NEXT_PROTO_NO_SUPPORT:
- proto->clear();
- return kNextProtoUnsupported;
- case SSL_NEXT_PROTO_NEGOTIATED:
- *proto = std::string(reinterpret_cast<char*>(buf), len);
- return kNextProtoNegotiated;
- case SSL_NEXT_PROTO_NO_OVERLAP:
- *proto = std::string(reinterpret_cast<char*>(buf), len);
- return kNextProtoNoOverlap;
- default:
- NOTREACHED() << "Unknown status from SSL_GetNextProto: " << state;
- proto->clear();
- return kNextProtoUnsupported;
- }
-#else
- // No NPN support in the libssl that we are building with.
- proto->clear();
- return kNextProtoUnsupported;
-#endif
-}
-
-void SSLClientSocketNSS::UseDNSSEC(DNSSECProvider* provider) {
- dnssec_provider_ = provider;
-}
-
void SSLClientSocketNSS::DoReadCallback(int rv) {
EnterFunction(rv);
DCHECK(rv != ERR_IO_PENDING);
@@ -1250,109 +1273,6 @@ void SSLClientSocketNSS::OnRecvComplete(int result) {
LeaveFunction("");
}
-// Do network I/O between the given buffer and the given socket.
-// Return true if some I/O performed, false otherwise (error or ERR_IO_PENDING)
-bool SSLClientSocketNSS::DoTransportIO() {
- EnterFunction("");
- bool network_moved = false;
- if (nss_bufs_ != NULL) {
- int nsent = BufferSend();
- int nreceived = BufferRecv();
- network_moved = (nsent > 0 || nreceived >= 0);
- }
- LeaveFunction(network_moved);
- return network_moved;
-}
-
-// Return 0 for EOF,
-// > 0 for bytes transferred immediately,
-// < 0 for error (or the non-error ERR_IO_PENDING).
-int SSLClientSocketNSS::BufferSend(void) {
- if (transport_send_busy_)
- return ERR_IO_PENDING;
-
- EnterFunction("");
- const char* buf1;
- const char* buf2;
- unsigned int len1, len2;
- memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2);
- const unsigned int len = len1 + len2;
-
- if (corked_ && len < kRecvBufferSize / 2)
- return 0;
-
- int rv = 0;
- if (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,
- &buffer_send_callback_);
- if (rv == ERR_IO_PENDING) {
- transport_send_busy_ = true;
- } else {
- memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv));
- }
- }
-
- LeaveFunction(rv);
- return rv;
-}
-
-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);
- LeaveFunction("");
-}
-
-
-int SSLClientSocketNSS::BufferRecv(void) {
- if (transport_recv_busy_) return ERR_IO_PENDING;
-
- char *buf;
- int nb = memio_GetReadParams(nss_bufs_, &buf);
- EnterFunction(nb);
- int rv;
- if (!nb) {
- // buffer too full to read into, so no I/O possible at moment
- rv = ERR_IO_PENDING;
- } else {
- recv_buffer_ = new IOBuffer(nb);
- rv = transport_->socket()->Read(recv_buffer_, nb, &buffer_recv_callback_);
- if (rv == ERR_IO_PENDING) {
- transport_recv_busy_ = true;
- } else {
- if (rv > 0)
- memcpy(buf, recv_buffer_->data(), rv);
- memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv));
- recv_buffer_ = NULL;
- }
- }
- LeaveFunction(rv);
- return rv;
-}
-
-void SSLClientSocketNSS::BufferRecvComplete(int result) {
- EnterFunction(result);
- if (result > 0) {
- char *buf;
- memio_GetReadParams(nss_bufs_, &buf);
- memcpy(buf, recv_buffer_->data(), result);
- }
- recv_buffer_ = NULL;
- memio_PutReadResult(nss_bufs_, MapErrorToNSS(result));
- transport_recv_busy_ = false;
- OnRecvComplete(result);
- LeaveFunction("");
-}
-
int SSLClientSocketNSS::DoHandshakeLoop(int last_io_result) {
EnterFunction(last_io_result);
bool network_moved;
@@ -1459,447 +1379,6 @@ int SSLClientSocketNSS::DoWriteLoop(int result) {
return rv;
}
-// static
-// NSS calls this if an incoming certificate needs to be verified.
-// Do nothing but return SECSuccess.
-// This is called only in full handshake mode.
-// Peer certificate is retrieved in HandshakeCallback() later, which is called
-// in full handshake mode or in resumption handshake mode.
-SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg,
- PRFileDesc* socket,
- PRBool checksig,
- PRBool is_server) {
-#ifdef SSL_ENABLE_FALSE_START
- // In the event that we are False Starting this connection, we wish to send
- // out the Finished message and first application data record in the same
- // packet. This prevents non-determinism when talking to False Start
- // intolerant servers which, otherwise, might see the two messages in
- // different reads or not, depending on network conditions.
- PRBool false_start = 0;
- SECStatus rv = SSL_OptionGet(socket, SSL_ENABLE_FALSE_START, &false_start);
- DCHECK_EQ(SECSuccess, rv);
-
- if (false_start) {
- SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
-
- // ESET anti-virus is capable of intercepting HTTPS connections on Windows.
- // However, it is False Start intolerant and causes the connections to hang
- // forever. We detect ESET by the issuer of the leaf certificate and set a
- // flag to return a specific error, giving the user instructions for
- // reconfiguring ESET.
- CERTCertificate* cert = SSL_PeerCertificate(that->nss_fd_);
- if (cert) {
- char* common_name = CERT_GetCommonName(&cert->issuer);
- if (common_name) {
- if (strcmp(common_name, "ESET_RootSslCert") == 0)
- that->eset_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
-
- // Tell NSS to not verify the certificate.
- return SECSuccess;
-}
-
-#if defined(NSS_PLATFORM_CLIENT_AUTH)
-// static
-// NSS calls this if a client certificate is needed.
-SECStatus SSLClientSocketNSS::PlatformClientAuthHandler(
- void* arg,
- PRFileDesc* socket,
- CERTDistNames* ca_names,
- 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) {
- if (that->ssl_config_.client_cert) {
- PCCERT_CONTEXT cert_context =
- that->ssl_config_.client_cert->os_cert_handle();
- if (VLOG_IS_ON(1)) {
- do {
- DWORD size_needed = 0;
- BOOL got_info = CertGetCertificateContextProperty(
- cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size_needed);
- if (!got_info) {
- VLOG(1) << "Failed to get key prov info size " << GetLastError();
- break;
- }
- std::vector<BYTE> raw_info(size_needed);
- got_info = CertGetCertificateContextProperty(
- cert_context, CERT_KEY_PROV_INFO_PROP_ID, &raw_info[0],
- &size_needed);
- if (!got_info) {
- VLOG(1) << "Failed to get key prov info " << GetLastError();
- break;
- }
- PCRYPT_KEY_PROV_INFO info =
- reinterpret_cast<PCRYPT_KEY_PROV_INFO>(&raw_info[0]);
- VLOG(1) << "Container Name: " << info->pwszContainerName
- << "\nProvider Name: " << info->pwszProvName
- << "\nProvider Type: " << info->dwProvType
- << "\nFlags: " << info->dwFlags
- << "\nProvider Param Count: " << info->cProvParam
- << "\nKey Specifier: " << info->dwKeySpec;
- } while (false);
-
- do {
- DWORD size_needed = 0;
- BOOL got_identifier = CertGetCertificateContextProperty(
- cert_context, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size_needed);
- if (!got_identifier) {
- VLOG(1) << "Failed to get key identifier size "
- << GetLastError();
- break;
- }
- std::vector<BYTE> raw_id(size_needed);
- got_identifier = CertGetCertificateContextProperty(
- cert_context, CERT_KEY_IDENTIFIER_PROP_ID, &raw_id[0],
- &size_needed);
- if (!got_identifier) {
- VLOG(1) << "Failed to get key identifier " << GetLastError();
- break;
- }
- VLOG(1) << "Key Identifier: " << base::HexEncode(&raw_id[0],
- size_needed);
- } while (false);
- }
- 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;
- }
-
- that->client_certs_.clear();
-
- std::vector<CERT_NAME_BLOB> issuer_list(ca_names->nnames);
- for (int i = 0; i < ca_names->nnames; ++i) {
- issuer_list[i].cbData = ca_names->names[i].len;
- issuer_list[i].pbData = ca_names->names[i].data;
- }
-
- // Client certificates of the user are in the "MY" system certificate store.
- HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
- if (!my_cert_store) {
- LOG(ERROR) << "Could not open the \"MY\" system certificate store: "
- << GetLastError();
- return SECFailure;
- }
-
- // Enumerate the client certificates.
- CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para;
- memset(&find_by_issuer_para, 0, sizeof(find_by_issuer_para));
- find_by_issuer_para.cbSize = sizeof(find_by_issuer_para);
- find_by_issuer_para.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
- find_by_issuer_para.cIssuer = ca_names->nnames;
- find_by_issuer_para.rgIssuer = ca_names->nnames ? &issuer_list[0] : NULL;
- find_by_issuer_para.pfnFindCallback = ClientCertFindCallback;
-
- PCCERT_CHAIN_CONTEXT chain_context = NULL;
-
- for (;;) {
- // Find a certificate chain.
- chain_context = CertFindChainInStore(my_cert_store,
- X509_ASN_ENCODING,
- 0,
- CERT_CHAIN_FIND_BY_ISSUER,
- &find_by_issuer_para,
- chain_context);
- if (!chain_context) {
- DWORD err = GetLastError();
- if (err != CRYPT_E_NOT_FOUND)
- DLOG(ERROR) << "CertFindChainInStore failed: " << err;
- break;
- }
-
- // Get the leaf certificate.
- PCCERT_CONTEXT cert_context =
- chain_context->rgpChain[0]->rgpElement[0]->pCertContext;
- // 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(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,
- 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);
- DCHECK(ok);
-
- // Tell NSS to suspend the client authentication. We will then abort the
- // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED.
- return SECWouldBlock;
-#elif defined(OS_MACOSX)
- if (that->ssl_config_.send_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;
- }
-
- that->client_certs_.clear();
-
- // First, get the cert issuer names allowed by the server.
- std::vector<CertPrincipal> valid_issuers;
- int n = ca_names->nnames;
- for (int i = 0; i < n; i++) {
- // Parse each name into a CertPrincipal object.
- CertPrincipal p;
- if (p.ParseDistinguishedName(ca_names->names[i].data,
- ca_names->names[i].len)) {
- valid_issuers.push_back(p);
- }
- }
-
- // Now get the available client certs whose issuers are allowed by the server.
- X509Certificate::GetSSLClientCertificates(that->host_and_port_.host(),
- valid_issuers,
- &that->client_certs_);
-
- // Tell NSS to suspend the client authentication. We will then abort the
- // 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.
- if (that->ssl_config_.send_client_cert) {
- if (that->ssl_config_.client_cert) {
- CERTCertificate* cert = CERT_DupCertificate(
- that->ssl_config_.client_cert->os_cert_handle());
- SECKEYPrivateKey* privkey = PK11_FindKeyByAnyCert(cert, wincx);
- if (privkey) {
- // TODO(jsorianopastor): We should wait for server certificate
- // verification before sending our credentials. See
- // http://crbug.com/13934.
- *result_certificate = cert;
- *result_private_key = privkey;
- return SECSuccess;
- }
- LOG(WARNING) << "Client cert found without private key";
- }
- // Send no client certificate.
- return SECFailure;
- }
-
- // Iterate over all client certificates.
- CERTCertList* client_certs = CERT_FindUserCertsByUsage(
- CERT_GetDefaultCertDB(), certUsageSSLClient,
- PR_FALSE, PR_FALSE, wincx);
- if (client_certs) {
- for (CERTCertListNode* node = CERT_LIST_HEAD(client_certs);
- !CERT_LIST_END(node, client_certs);
- node = CERT_LIST_NEXT(node)) {
- // Only offer unexpired certificates.
- if (CERT_CheckCertValidTimes(node->cert, PR_Now(), PR_TRUE) !=
- secCertTimeValid)
- continue;
- // Filter by issuer.
- //
- // TODO(davidben): This does a binary comparison of the DER-encoded
- // issuers. We should match according to RFC 5280 sec. 7.1. We should find
- // an appropriate NSS function or add one if needbe.
- if (ca_names->nnames &&
- NSS_CmpCertChainWCANames(node->cert, ca_names) != SECSuccess)
- continue;
- X509Certificate* x509_cert = X509Certificate::CreateFromHandle(
- node->cert, X509Certificate::SOURCE_LONE_CERT_IMPORT,
- net::X509Certificate::OSCertHandles());
- that->client_certs_.push_back(x509_cert);
- }
- CERT_DestroyCertList(client_certs);
- }
-
- // Tell NSS to suspend the client authentication. We will then abort the
- // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED.
- return SECWouldBlock;
-}
-#endif // NSS_PLATFORM_CLIENT_AUTH
-
-// static
-// NSS calls this when handshake is completed.
-// After the SSL handshake is finished, use CertVerifier to verify
-// the saved server certificate.
-void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket,
- void* arg) {
- SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
-
- that->handshake_callback_called_ = true;
-
- that->UpdateServerCert();
- that->UpdateConnectionStatus();
-}
-
int SSLClientSocketNSS::DoSnapStartLoadInfo() {
EnterFunction("");
int rv = ssl_host_info_->WaitForDataReady(&handshake_io_callback_);
@@ -2083,158 +1562,6 @@ int SSLClientSocketNSS::DoHandshake() {
return net_error;
}
-// DNSValidationResult enumerates the possible outcomes from processing a
-// set of DNS records.
-enum DNSValidationResult {
- DNSVR_SUCCESS, // the cert is immediately acceptable.
- DNSVR_FAILURE, // the cert is unconditionally rejected.
- DNSVR_CONTINUE, // perform CA validation as usual.
-};
-
-// VerifyTXTRecords processes the RRDATA for a number of DNS TXT records and
-// checks them against the given certificate.
-// dnssec: if true then the TXT records are DNSSEC validated. In this case,
-// DNSVR_SUCCESS may be returned.
-// server_cert_nss: the certificate to validate
-// rrdatas: the TXT records for the current domain.
-static DNSValidationResult VerifyTXTRecords(
- bool dnssec,
- CERTCertificate* server_cert_nss,
- const std::vector<base::StringPiece>& rrdatas) {
- bool found_well_formed_record = false;
- bool matched_record = false;
-
- for (std::vector<base::StringPiece>::const_iterator
- i = rrdatas.begin(); i != rrdatas.end(); ++i) {
- std::map<std::string, std::string> m(
- DNSSECChainVerifier::ParseTLSTXTRecord(*i));
- if (m.empty())
- continue;
-
- std::map<std::string, std::string>::const_iterator j;
- j = m.find("v");
- if (j == m.end() || j->second != "tls1")
- continue;
-
- j = m.find("ha");
-
- HASH_HashType hash_algorithm;
- unsigned hash_length;
- if (j == m.end() || j->second == "sha1") {
- hash_algorithm = HASH_AlgSHA1;
- hash_length = SHA1_LENGTH;
- } else if (j->second == "sha256") {
- hash_algorithm = HASH_AlgSHA256;
- hash_length = SHA256_LENGTH;
- } else {
- continue;
- }
-
- j = m.find("h");
- if (j == m.end())
- continue;
-
- std::vector<uint8> given_hash;
- if (!base::HexStringToBytes(j->second, &given_hash))
- continue;
-
- if (given_hash.size() != hash_length)
- continue;
-
- uint8 calculated_hash[SHA256_LENGTH]; // SHA256 is the largest.
- SECStatus rv;
-
- j = m.find("hr");
- if (j == m.end() || j->second == "pubkey") {
- rv = HASH_HashBuf(hash_algorithm, calculated_hash,
- server_cert_nss->derPublicKey.data,
- server_cert_nss->derPublicKey.len);
- } else if (j->second == "cert") {
- rv = HASH_HashBuf(hash_algorithm, calculated_hash,
- server_cert_nss->derCert.data,
- server_cert_nss->derCert.len);
- } else {
- continue;
- }
-
- if (rv != SECSuccess)
- NOTREACHED();
-
- found_well_formed_record = true;
-
- if (memcmp(calculated_hash, &given_hash[0], hash_length) == 0) {
- matched_record = true;
- if (dnssec)
- return DNSVR_SUCCESS;
- }
- }
-
- if (found_well_formed_record && !matched_record)
- return DNSVR_FAILURE;
-
- return DNSVR_CONTINUE;
-}
-
-
-// CheckDNSSECChain tries to validate a DNSSEC chain embedded in
-// |server_cert_nss_|. It returns true iff a chain is found that proves the
-// value of a TXT record that contains a valid public key fingerprint.
-static DNSValidationResult CheckDNSSECChain(
- const std::string& hostname,
- CERTCertificate* server_cert_nss) {
- if (!server_cert_nss)
- return DNSVR_CONTINUE;
-
- // CERT_FindCertExtensionByOID isn't exported so we have to install an OID,
- // get a tag for it and find the extension by using that tag.
- static SECOidTag dnssec_chain_tag;
- static bool dnssec_chain_tag_valid;
- if (!dnssec_chain_tag_valid) {
- // It's harmless if multiple threads enter this block concurrently.
- static const uint8 kDNSSECChainOID[] =
- // 1.3.6.1.4.1.11129.2.1.4
- // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
- // certificateExtensions.dnssecEmbeddedChain)
- {0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x04};
- SECOidData oid_data;
- memset(&oid_data, 0, sizeof(oid_data));
- oid_data.oid.data = const_cast<uint8*>(kDNSSECChainOID);
- oid_data.oid.len = sizeof(kDNSSECChainOID);
- oid_data.desc = "DNSSEC chain";
- oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
- dnssec_chain_tag = SECOID_AddEntry(&oid_data);
- DCHECK_NE(SEC_OID_UNKNOWN, dnssec_chain_tag);
- dnssec_chain_tag_valid = true;
- }
-
- SECItem dnssec_embedded_chain;
- SECStatus rv = CERT_FindCertExtension(server_cert_nss,
- dnssec_chain_tag, &dnssec_embedded_chain);
- if (rv != SECSuccess)
- return DNSVR_CONTINUE;
-
- base::StringPiece chain(
- reinterpret_cast<char*>(dnssec_embedded_chain.data),
- dnssec_embedded_chain.len);
- std::string dns_hostname;
- if (!DNSDomainFromDot(hostname, &dns_hostname))
- return DNSVR_CONTINUE;
- DNSSECChainVerifier verifier(dns_hostname, chain);
- DNSSECChainVerifier::Error err = verifier.Verify();
- if (err != DNSSECChainVerifier::OK) {
- LOG(ERROR) << "DNSSEC chain verification failed: " << err;
- return DNSVR_CONTINUE;
- }
-
- if (verifier.rrtype() != kDNS_TXT)
- return DNSVR_CONTINUE;
-
- DNSValidationResult r = VerifyTXTRecords(
- true /* DNSSEC verified */, server_cert_nss, verifier.rrdatas());
- SECITEM_FreeItem(&dnssec_embedded_chain, PR_FALSE);
- return r;
-}
-
int SSLClientSocketNSS::DoVerifyDNSSEC(int result) {
if (ssl_config_.dns_cert_provenance_checking_enabled &&
dns_cert_checker_) {
@@ -2515,4 +1842,676 @@ void SSLClientSocketNSS::LogConnectionTypeMetrics() const {
};
}
+// 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;
+
+ // If the SSLHostInfo hasn't managed to load from disk yet then we can't save
+ // anything.
+ if (ssl_host_info_->WaitForDataReady(NULL) != OK)
+ return;
+
+ SECStatus rv;
+ SSLSnapStartResult snap_start_type;
+ rv = SSL_GetSnapStartResult(nss_fd_, &snap_start_type);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return;
+ }
+ net_log_.AddEvent(NetLog::TYPE_SSL_SNAP_START,
+ new NetLogIntegerParameter("type", snap_start_type));
+ if (snap_start_type == SSL_SNAP_START_FULL ||
+ snap_start_type == SSL_SNAP_START_RESUME) {
+ // If we did a successful Snap Start then our information was correct and
+ // there's no point saving it again.
+ return;
+ }
+
+ const unsigned char* hello_data;
+ unsigned hello_data_len;
+ rv = SSL_GetPredictedServerHelloData(nss_fd_, &hello_data, &hello_data_len);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return;
+ }
+ if (hello_data_len > std::numeric_limits<uint16>::max())
+ return;
+ SSLHostInfo::State* state = ssl_host_info_->mutable_state();
+
+ if (hello_data_len > 0) {
+ 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);
+ } else {
+ state->server_hello.clear();
+ state->npn_valid = false;
+ }
+
+ 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;
+
+ state->certs.push_back(std::string(
+ reinterpret_cast<char*>(certs[i]->derCert.data),
+ certs[i]->derCert.len));
+ }
+
+ ssl_host_info_->Persist();
+}
+
+// LoadSnapStartInfo parses |info|, which contains data previously serialised
+// 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 SSLHostInfo::State& state(ssl_host_info_->state());
+
+ if (state.server_hello.empty() ||
+ state.certs.empty() ||
+ !state.npn_valid) {
+ return false;
+ }
+
+ SECStatus rv;
+ rv = SSL_SetPredictedServerHelloData(
+ nss_fd_,
+ reinterpret_cast<const uint8*>(state.server_hello.data()),
+ state.server_hello.size());
+ DCHECK_EQ(SECSuccess, rv);
+
+ 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*>(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 */);
+ if (!certs[i]) {
+ DestroyCertificates(&certs[0], i);
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ rv = SSL_SetPredictedPeerCertificates(nss_fd_, certs.get(), certs_in.size());
+ DestroyCertificates(&certs[0], certs_in.size());
+ DCHECK_EQ(SECSuccess, rv);
+
+ if (state.npn_valid) {
+ predicted_npn_status_ = state.npn_status;
+ predicted_npn_proto_ = state.npn_protocol;
+ }
+
+ return true;
+}
+
+bool SSLClientSocketNSS::IsNPNProtocolMispredicted() {
+ DCHECK(handshake_callback_called_);
+ if (!predicted_npn_proto_used_)
+ return false;
+ std::string npn_proto;
+ GetNextProto(&npn_proto);
+ return predicted_npn_proto_ != npn_proto;
+}
+
+void SSLClientSocketNSS::UncorkAfterTimeout() {
+ corked_ = false;
+ int nsent;
+ do {
+ nsent = BufferSend();
+ } while (nsent > 0);
+}
+
+// Do network I/O between the given buffer and the given socket.
+// Return true if some I/O performed, false otherwise (error or ERR_IO_PENDING)
+bool SSLClientSocketNSS::DoTransportIO() {
+ EnterFunction("");
+ bool network_moved = false;
+ if (nss_bufs_ != NULL) {
+ int nsent = BufferSend();
+ int nreceived = BufferRecv();
+ network_moved = (nsent > 0 || nreceived >= 0);
+ }
+ LeaveFunction(network_moved);
+ return network_moved;
+}
+
+// Return 0 for EOF,
+// > 0 for bytes transferred immediately,
+// < 0 for error (or the non-error ERR_IO_PENDING).
+int SSLClientSocketNSS::BufferSend(void) {
+ if (transport_send_busy_)
+ return ERR_IO_PENDING;
+
+ EnterFunction("");
+ const char* buf1;
+ const char* buf2;
+ unsigned int len1, len2;
+ memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2);
+ const unsigned int len = len1 + len2;
+
+ if (corked_ && len < kRecvBufferSize / 2)
+ return 0;
+
+ int rv = 0;
+ if (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,
+ &buffer_send_callback_);
+ if (rv == ERR_IO_PENDING) {
+ transport_send_busy_ = true;
+ } else {
+ memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv));
+ }
+ }
+
+ LeaveFunction(rv);
+ return rv;
+}
+
+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);
+ LeaveFunction("");
+}
+
+int SSLClientSocketNSS::BufferRecv(void) {
+ if (transport_recv_busy_) return ERR_IO_PENDING;
+
+ char *buf;
+ int nb = memio_GetReadParams(nss_bufs_, &buf);
+ EnterFunction(nb);
+ int rv;
+ if (!nb) {
+ // buffer too full to read into, so no I/O possible at moment
+ rv = ERR_IO_PENDING;
+ } else {
+ recv_buffer_ = new IOBuffer(nb);
+ rv = transport_->socket()->Read(recv_buffer_, nb, &buffer_recv_callback_);
+ if (rv == ERR_IO_PENDING) {
+ transport_recv_busy_ = true;
+ } else {
+ if (rv > 0)
+ memcpy(buf, recv_buffer_->data(), rv);
+ memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv));
+ recv_buffer_ = NULL;
+ }
+ }
+ LeaveFunction(rv);
+ return rv;
+}
+
+void SSLClientSocketNSS::BufferRecvComplete(int result) {
+ EnterFunction(result);
+ if (result > 0) {
+ char *buf;
+ memio_GetReadParams(nss_bufs_, &buf);
+ memcpy(buf, recv_buffer_->data(), result);
+ }
+ recv_buffer_ = NULL;
+ memio_PutReadResult(nss_bufs_, MapErrorToNSS(result));
+ transport_recv_busy_ = false;
+ OnRecvComplete(result);
+ LeaveFunction("");
+}
+
+// static
+// NSS calls this if an incoming certificate needs to be verified.
+// Do nothing but return SECSuccess.
+// This is called only in full handshake mode.
+// Peer certificate is retrieved in HandshakeCallback() later, which is called
+// in full handshake mode or in resumption handshake mode.
+SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg,
+ PRFileDesc* socket,
+ PRBool checksig,
+ PRBool is_server) {
+#ifdef SSL_ENABLE_FALSE_START
+ // In the event that we are False Starting this connection, we wish to send
+ // out the Finished message and first application data record in the same
+ // packet. This prevents non-determinism when talking to False Start
+ // intolerant servers which, otherwise, might see the two messages in
+ // different reads or not, depending on network conditions.
+ PRBool false_start = 0;
+ SECStatus rv = SSL_OptionGet(socket, SSL_ENABLE_FALSE_START, &false_start);
+ DCHECK_EQ(SECSuccess, rv);
+
+ if (false_start) {
+ SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
+
+ // ESET anti-virus is capable of intercepting HTTPS connections on Windows.
+ // However, it is False Start intolerant and causes the connections to hang
+ // forever. We detect ESET by the issuer of the leaf certificate and set a
+ // flag to return a specific error, giving the user instructions for
+ // reconfiguring ESET.
+ CERTCertificate* cert = SSL_PeerCertificate(that->nss_fd_);
+ if (cert) {
+ char* common_name = CERT_GetCommonName(&cert->issuer);
+ if (common_name) {
+ if (strcmp(common_name, "ESET_RootSslCert") == 0)
+ that->eset_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
+
+ // Tell NSS to not verify the certificate.
+ return SECSuccess;
+}
+
+#if defined(NSS_PLATFORM_CLIENT_AUTH)
+// static
+// NSS calls this if a client certificate is needed.
+SECStatus SSLClientSocketNSS::PlatformClientAuthHandler(
+ void* arg,
+ PRFileDesc* socket,
+ CERTDistNames* ca_names,
+ 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) {
+ if (that->ssl_config_.client_cert) {
+ PCCERT_CONTEXT cert_context =
+ that->ssl_config_.client_cert->os_cert_handle();
+ if (VLOG_IS_ON(1)) {
+ do {
+ DWORD size_needed = 0;
+ BOOL got_info = CertGetCertificateContextProperty(
+ cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size_needed);
+ if (!got_info) {
+ VLOG(1) << "Failed to get key prov info size " << GetLastError();
+ break;
+ }
+ std::vector<BYTE> raw_info(size_needed);
+ got_info = CertGetCertificateContextProperty(
+ cert_context, CERT_KEY_PROV_INFO_PROP_ID, &raw_info[0],
+ &size_needed);
+ if (!got_info) {
+ VLOG(1) << "Failed to get key prov info " << GetLastError();
+ break;
+ }
+ PCRYPT_KEY_PROV_INFO info =
+ reinterpret_cast<PCRYPT_KEY_PROV_INFO>(&raw_info[0]);
+ VLOG(1) << "Container Name: " << info->pwszContainerName
+ << "\nProvider Name: " << info->pwszProvName
+ << "\nProvider Type: " << info->dwProvType
+ << "\nFlags: " << info->dwFlags
+ << "\nProvider Param Count: " << info->cProvParam
+ << "\nKey Specifier: " << info->dwKeySpec;
+ } while (false);
+
+ do {
+ DWORD size_needed = 0;
+ BOOL got_identifier = CertGetCertificateContextProperty(
+ cert_context, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size_needed);
+ if (!got_identifier) {
+ VLOG(1) << "Failed to get key identifier size "
+ << GetLastError();
+ break;
+ }
+ std::vector<BYTE> raw_id(size_needed);
+ got_identifier = CertGetCertificateContextProperty(
+ cert_context, CERT_KEY_IDENTIFIER_PROP_ID, &raw_id[0],
+ &size_needed);
+ if (!got_identifier) {
+ VLOG(1) << "Failed to get key identifier " << GetLastError();
+ break;
+ }
+ VLOG(1) << "Key Identifier: " << base::HexEncode(&raw_id[0],
+ size_needed);
+ } while (false);
+ }
+ 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;
+ }
+
+ that->client_certs_.clear();
+
+ std::vector<CERT_NAME_BLOB> issuer_list(ca_names->nnames);
+ for (int i = 0; i < ca_names->nnames; ++i) {
+ issuer_list[i].cbData = ca_names->names[i].len;
+ issuer_list[i].pbData = ca_names->names[i].data;
+ }
+
+ // Client certificates of the user are in the "MY" system certificate store.
+ HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
+ if (!my_cert_store) {
+ LOG(ERROR) << "Could not open the \"MY\" system certificate store: "
+ << GetLastError();
+ return SECFailure;
+ }
+
+ // Enumerate the client certificates.
+ CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para;
+ memset(&find_by_issuer_para, 0, sizeof(find_by_issuer_para));
+ find_by_issuer_para.cbSize = sizeof(find_by_issuer_para);
+ find_by_issuer_para.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
+ find_by_issuer_para.cIssuer = ca_names->nnames;
+ find_by_issuer_para.rgIssuer = ca_names->nnames ? &issuer_list[0] : NULL;
+ find_by_issuer_para.pfnFindCallback = ClientCertFindCallback;
+
+ PCCERT_CHAIN_CONTEXT chain_context = NULL;
+
+ for (;;) {
+ // Find a certificate chain.
+ chain_context = CertFindChainInStore(my_cert_store,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_CHAIN_FIND_BY_ISSUER,
+ &find_by_issuer_para,
+ chain_context);
+ if (!chain_context) {
+ DWORD err = GetLastError();
+ if (err != CRYPT_E_NOT_FOUND)
+ DLOG(ERROR) << "CertFindChainInStore failed: " << err;
+ break;
+ }
+
+ // Get the leaf certificate.
+ PCCERT_CONTEXT cert_context =
+ chain_context->rgpChain[0]->rgpElement[0]->pCertContext;
+ // 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(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,
+ 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);
+ DCHECK(ok);
+
+ // Tell NSS to suspend the client authentication. We will then abort the
+ // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED.
+ return SECWouldBlock;
+#elif defined(OS_MACOSX)
+ if (that->ssl_config_.send_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;
+ }
+
+ that->client_certs_.clear();
+
+ // First, get the cert issuer names allowed by the server.
+ std::vector<CertPrincipal> valid_issuers;
+ int n = ca_names->nnames;
+ for (int i = 0; i < n; i++) {
+ // Parse each name into a CertPrincipal object.
+ CertPrincipal p;
+ if (p.ParseDistinguishedName(ca_names->names[i].data,
+ ca_names->names[i].len)) {
+ valid_issuers.push_back(p);
+ }
+ }
+
+ // Now get the available client certs whose issuers are allowed by the server.
+ X509Certificate::GetSSLClientCertificates(that->host_and_port_.host(),
+ valid_issuers,
+ &that->client_certs_);
+
+ // Tell NSS to suspend the client authentication. We will then abort the
+ // 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.
+ if (that->ssl_config_.send_client_cert) {
+ if (that->ssl_config_.client_cert) {
+ CERTCertificate* cert = CERT_DupCertificate(
+ that->ssl_config_.client_cert->os_cert_handle());
+ SECKEYPrivateKey* privkey = PK11_FindKeyByAnyCert(cert, wincx);
+ if (privkey) {
+ // TODO(jsorianopastor): We should wait for server certificate
+ // verification before sending our credentials. See
+ // http://crbug.com/13934.
+ *result_certificate = cert;
+ *result_private_key = privkey;
+ return SECSuccess;
+ }
+ LOG(WARNING) << "Client cert found without private key";
+ }
+ // Send no client certificate.
+ return SECFailure;
+ }
+
+ // Iterate over all client certificates.
+ CERTCertList* client_certs = CERT_FindUserCertsByUsage(
+ CERT_GetDefaultCertDB(), certUsageSSLClient,
+ PR_FALSE, PR_FALSE, wincx);
+ if (client_certs) {
+ for (CERTCertListNode* node = CERT_LIST_HEAD(client_certs);
+ !CERT_LIST_END(node, client_certs);
+ node = CERT_LIST_NEXT(node)) {
+ // Only offer unexpired certificates.
+ if (CERT_CheckCertValidTimes(node->cert, PR_Now(), PR_TRUE) !=
+ secCertTimeValid)
+ continue;
+ // Filter by issuer.
+ //
+ // TODO(davidben): This does a binary comparison of the DER-encoded
+ // issuers. We should match according to RFC 5280 sec. 7.1. We should find
+ // an appropriate NSS function or add one if needbe.
+ if (ca_names->nnames &&
+ NSS_CmpCertChainWCANames(node->cert, ca_names) != SECSuccess)
+ continue;
+ X509Certificate* x509_cert = X509Certificate::CreateFromHandle(
+ node->cert, X509Certificate::SOURCE_LONE_CERT_IMPORT,
+ net::X509Certificate::OSCertHandles());
+ that->client_certs_.push_back(x509_cert);
+ }
+ CERT_DestroyCertList(client_certs);
+ }
+
+ // Tell NSS to suspend the client authentication. We will then abort the
+ // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED.
+ return SECWouldBlock;
+}
+#endif // NSS_PLATFORM_CLIENT_AUTH
+
+// static
+// NSS calls this when handshake is completed.
+// After the SSL handshake is finished, use CertVerifier to verify
+// the saved server certificate.
+void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket,
+ void* arg) {
+ SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
+
+ that->handshake_callback_called_ = true;
+
+ that->UpdateServerCert();
+ that->UpdateConnectionStatus();
+}
+
} // namespace net