| Index: net/socket/ssl_client_socket_nss.cc
|
| diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
|
| index b46ebbdfd3e03497c5733d16bd510e16121da252..b0721766ca763544f3811735ef7041d60bb44951 100644
|
| --- a/net/socket/ssl_client_socket_nss.cc
|
| +++ b/net/socket/ssl_client_socket_nss.cc
|
| @@ -106,6 +106,7 @@
|
| #include "net/socket/client_socket_handle.h"
|
| #include "net/socket/nss_ssl_util.h"
|
| #include "net/socket/ssl_error_params.h"
|
| +#include "net/socket/ssl_host_info.h"
|
| #include "net/ssl/ssl_cert_request_info.h"
|
| #include "net/ssl/ssl_connection_status_flags.h"
|
| #include "net/ssl/ssl_info.h"
|
| @@ -413,6 +414,7 @@ struct HandshakeState {
|
| channel_id_sent = false;
|
| server_cert_chain.Reset(NULL);
|
| server_cert = NULL;
|
| + predicted_cert_chain_correct = false;
|
| sct_list_from_tls_extension.clear();
|
| stapled_ocsp_response.clear();
|
| resumed_handshake = false;
|
| @@ -444,6 +446,10 @@ struct HandshakeState {
|
| // always be non-NULL.
|
| PeerCertificateChain server_cert_chain;
|
| scoped_refptr<X509Certificate> server_cert;
|
| + // True if we predicted a certificate chain (via
|
| + // Core::SetPredictedCertificates) and that prediction matched what the
|
| + // server sent.
|
| + bool predicted_cert_chain_correct;
|
| // SignedCertificateTimestampList received via TLS extension (RFC 6962).
|
| std::string sct_list_from_tls_extension;
|
| // Stapled OCSP response received.
|
| @@ -1653,6 +1659,26 @@ void SSLClientSocketNSS::Core::HandshakeSucceeded() {
|
| UpdateConnectionStatus();
|
| UpdateNextProto();
|
|
|
| + // We need to see if the predicted certificate chain (from
|
| + // SetPredictedCertificates) matches the actual certificate chain.
|
| + nss_handshake_state_.predicted_cert_chain_correct = false;
|
| + if (!predicted_certs_.empty()) {
|
| + PeerCertificateChain& certs = nss_handshake_state_.server_cert_chain;
|
| + nss_handshake_state_.predicted_cert_chain_correct =
|
| + certs.size() == predicted_certs_.size();
|
| +
|
| + if (nss_handshake_state_.predicted_cert_chain_correct) {
|
| + for (unsigned i = 0; i < certs.size(); i++) {
|
| + if (certs[i]->derCert.len != predicted_certs_[i].size() ||
|
| + memcmp(certs[i]->derCert.data, predicted_certs_[i].data(),
|
| + certs[i]->derCert.len) != 0) {
|
| + nss_handshake_state_.predicted_cert_chain_correct = false;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| // Update the network task runners view of the handshake state whenever
|
| // a handshake has completed.
|
| PostOrRunCallback(
|
| @@ -2398,7 +2424,8 @@ void SSLClientSocketNSS::Core::UpdateStapledOCSPResponse() {
|
|
|
| // TODO(agl): figure out how to plumb an OCSP response into the Mac
|
| // system library and update IsOCSPStaplingSupported for Mac.
|
| - if (IsOCSPStaplingSupported()) {
|
| + if (!nss_handshake_state_.predicted_cert_chain_correct &&
|
| + IsOCSPStaplingSupported()) {
|
| #if defined(OS_WIN)
|
| if (nss_handshake_state_.server_cert) {
|
| CRYPT_DATA_BLOB ocsp_response_blob;
|
| @@ -2769,11 +2796,13 @@ SSLClientSocketNSS::SSLClientSocketNSS(
|
| scoped_ptr<ClientSocketHandle> transport_socket,
|
| const HostPortPair& host_and_port,
|
| const SSLConfig& ssl_config,
|
| + SSLHostInfo* ssl_host_info,
|
| const SSLClientSocketContext& context)
|
| : nss_task_runner_(nss_task_runner),
|
| transport_(transport_socket.Pass()),
|
| host_and_port_(host_and_port),
|
| ssl_config_(ssl_config),
|
| + server_cert_verify_result_(NULL),
|
| cert_verifier_(context.cert_verifier),
|
| cert_transparency_verifier_(context.cert_transparency_verifier),
|
| server_bound_cert_service_(context.server_bound_cert_service),
|
| @@ -2782,6 +2811,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(
|
| next_handshake_state_(STATE_NONE),
|
| nss_fd_(NULL),
|
| net_log_(transport_->socket()->NetLog()),
|
| + ssl_host_info_(ssl_host_info),
|
| transport_security_state_(context.transport_security_state),
|
| valid_thread_id_(base::kInvalidThreadId) {
|
| EnterFunction("");
|
| @@ -2813,16 +2843,16 @@ bool SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
|
| return false;
|
| }
|
|
|
| - ssl_info->cert_status = server_cert_verify_result_.cert_status;
|
| - ssl_info->cert = server_cert_verify_result_.verified_cert;
|
| + ssl_info->cert_status = server_cert_verify_result_->cert_status;
|
| + ssl_info->cert = server_cert_verify_result_->verified_cert;
|
|
|
| AddSCTInfoToSSLInfo(ssl_info);
|
|
|
| ssl_info->connection_status =
|
| core_->state().ssl_connection_status;
|
| - ssl_info->public_key_hashes = server_cert_verify_result_.public_key_hashes;
|
| + ssl_info->public_key_hashes = server_cert_verify_result_->public_key_hashes;
|
| ssl_info->is_issued_by_known_root =
|
| - server_cert_verify_result_.is_issued_by_known_root;
|
| + server_cert_verify_result_->is_issued_by_known_root;
|
| ssl_info->client_cert_sent =
|
| ssl_config_.send_client_cert && ssl_config_.client_cert.get();
|
| ssl_info->channel_id_sent = WasChannelIDSent();
|
| @@ -2932,7 +2962,11 @@ int SSLClientSocketNSS::Connect(const CompletionCallback& callback) {
|
| return rv;
|
| }
|
|
|
| - GotoState(STATE_HANDSHAKE);
|
| + if (ssl_host_info_.get()) {
|
| + GotoState(STATE_LOAD_SSL_HOST_INFO);
|
| + } else {
|
| + GotoState(STATE_HANDSHAKE);
|
| + }
|
|
|
| rv = DoHandshakeLoop(OK);
|
| if (rv == ERR_IO_PENDING) {
|
| @@ -2957,7 +2991,8 @@ void SSLClientSocketNSS::Disconnect() {
|
|
|
| // Reset object state.
|
| user_connect_callback_.Reset();
|
| - server_cert_verify_result_.Reset();
|
| + local_server_cert_verify_result_.Reset();
|
| + server_cert_verify_result_ = NULL;
|
| completed_handshake_ = false;
|
| start_cert_verification_time_ = base::TimeTicks();
|
| InitCore();
|
| @@ -3286,6 +3321,35 @@ void SSLClientSocketNSS::OnHandshakeIOComplete(int result) {
|
| LeaveFunction("");
|
| }
|
|
|
| +void SSLClientSocketNSS::LoadSSLHostInfo() {
|
| + const SSLHostInfo::State& state(ssl_host_info_->state());
|
| +
|
| + if (state.certs.empty())
|
| + return;
|
| +
|
| + /* TODO(rtenneti): Implement the following */
|
| + // const std::vector<std::string>& certs_in = state.certs;
|
| + // core_->SetPredictedCertificates(certs_in);
|
| +}
|
| +
|
| +int SSLClientSocketNSS::DoLoadSSLHostInfo() {
|
| + EnterFunction("");
|
| + int rv = ssl_host_info_->WaitForDataReady(
|
| + base::Bind(&SSLClientSocketNSS::OnHandshakeIOComplete,
|
| + base::Unretained(this)));
|
| + GotoState(STATE_HANDSHAKE);
|
| +
|
| + if (rv == OK) {
|
| + LoadSSLHostInfo();
|
| + } else {
|
| + DCHECK_EQ(ERR_IO_PENDING, rv);
|
| + GotoState(STATE_LOAD_SSL_HOST_INFO);
|
| + }
|
| +
|
| + LeaveFunction("");
|
| + return rv;
|
| +}
|
| +
|
| int SSLClientSocketNSS::DoHandshakeLoop(int last_io_result) {
|
| EnterFunction(last_io_result);
|
| int rv = last_io_result;
|
| @@ -3298,6 +3362,10 @@ int SSLClientSocketNSS::DoHandshakeLoop(int last_io_result) {
|
| State state = next_handshake_state_;
|
| GotoState(STATE_NONE);
|
| switch (state) {
|
| + case STATE_LOAD_SSL_HOST_INFO:
|
| + DCHECK(rv == OK || rv == ERR_IO_PENDING);
|
| + rv = DoLoadSSLHostInfo();
|
| + break;
|
| case STATE_HANDSHAKE:
|
| rv = DoHandshake();
|
| break;
|
| @@ -3337,6 +3405,7 @@ int SSLClientSocketNSS::DoHandshakeComplete(int result) {
|
| EnterFunction(result);
|
|
|
| if (result == OK) {
|
| + SaveSSLHostInfo();
|
| // SSL handshake is completed. Let's verify the certificate.
|
| GotoState(STATE_VERIFY_CERT);
|
| // Done!
|
| @@ -3367,22 +3436,46 @@ int SSLClientSocketNSS::DoVerifyCert(int result) {
|
| if (ssl_config_.IsAllowedBadCert(der_cert, &cert_status)) {
|
| DCHECK(start_cert_verification_time_.is_null());
|
| VLOG(1) << "Received an expected bad cert with status: " << cert_status;
|
| - server_cert_verify_result_.Reset();
|
| - server_cert_verify_result_.cert_status = cert_status;
|
| - server_cert_verify_result_.verified_cert = core_->state().server_cert;
|
| + server_cert_verify_result_ = &local_server_cert_verify_result_;
|
| + local_server_cert_verify_result_.Reset();
|
| + local_server_cert_verify_result_.cert_status = cert_status;
|
| + local_server_cert_verify_result_.verified_cert =
|
| + core_->state().server_cert;
|
| return OK;
|
| }
|
|
|
| // We may have failed to create X509Certificate object if we are
|
| // running inside sandbox.
|
| if (!core_->state().server_cert.get()) {
|
| - server_cert_verify_result_.Reset();
|
| - server_cert_verify_result_.cert_status = CERT_STATUS_INVALID;
|
| + server_cert_verify_result_ = &local_server_cert_verify_result_;
|
| + local_server_cert_verify_result_.Reset();
|
| + local_server_cert_verify_result_.cert_status = CERT_STATUS_INVALID;
|
| return ERR_CERT_INVALID;
|
| }
|
|
|
| start_cert_verification_time_ = base::TimeTicks::Now();
|
|
|
| + if (ssl_host_info_.get() && !ssl_host_info_->state().certs.empty() &&
|
| + core_->state().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);
|
| + UMA_HISTOGRAM_ENUMERATION("Net.SSLVerificationMerged", 1 /* true */, 2);
|
| + base::TimeTicks end_time = ssl_host_info_->verification_end_time();
|
| + if (end_time.is_null())
|
| + end_time = base::TimeTicks::Now();
|
| + UMA_HISTOGRAM_TIMES("Net.SSLVerificationMergedMsSaved",
|
| + end_time - ssl_host_info_->verification_start_time());
|
| + server_cert_verify_result_ = &ssl_host_info_->cert_verify_result();
|
| + return ssl_host_info_->WaitForCertVerification(
|
| + base::Bind(&SSLClientSocketNSS::OnHandshakeIOComplete,
|
| + base::Unretained(this)));
|
| + } else {
|
| + UMA_HISTOGRAM_ENUMERATION("Net.SSLVerificationMerged", 0 /* false */, 2);
|
| + }
|
| +
|
| int flags = 0;
|
| if (ssl_config_.rev_checking_enabled)
|
| flags |= CertVerifier::VERIFY_REV_CHECKING_ENABLED;
|
| @@ -3393,12 +3486,13 @@ int SSLClientSocketNSS::DoVerifyCert(int result) {
|
| if (ssl_config_.rev_checking_required_local_anchors)
|
| flags |= CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
|
| verifier_.reset(new SingleRequestCertVerifier(cert_verifier_));
|
| + server_cert_verify_result_ = &local_server_cert_verify_result_;
|
| return verifier_->Verify(
|
| core_->state().server_cert.get(),
|
| host_and_port_.host(),
|
| flags,
|
| SSLConfigService::GetCRLSet().get(),
|
| - &server_cert_verify_result_,
|
| + &local_server_cert_verify_result_,
|
| base::Bind(&SSLClientSocketNSS::OnHandshakeIOComplete,
|
| base::Unretained(this)),
|
| net_log_);
|
| @@ -3454,11 +3548,11 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) {
|
| // * the build is recent (very old builds should fail open so that users
|
| // have some chance to recover).
|
| //
|
| - const CertStatus cert_status = server_cert_verify_result_.cert_status;
|
| + const CertStatus cert_status = server_cert_verify_result_->cert_status;
|
| if (transport_security_state_ &&
|
| (result == OK ||
|
| (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) &&
|
| - server_cert_verify_result_.is_issued_by_known_root &&
|
| + server_cert_verify_result_->is_issued_by_known_root &&
|
| TransportSecurityState::IsBuildTimely()) {
|
| bool sni_available =
|
| ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 ||
|
| @@ -3470,7 +3564,7 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) {
|
| &domain_state) &&
|
| domain_state.HasPublicKeyPins()) {
|
| if (!domain_state.CheckPublicKeyPins(
|
| - server_cert_verify_result_.public_key_hashes)) {
|
| + server_cert_verify_result_->public_key_hashes)) {
|
| result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
|
| UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false);
|
| TransportSecurityState::ReportUMAOnPinFailure(host);
|
| @@ -3505,7 +3599,7 @@ void SSLClientSocketNSS::VerifyCT() {
|
| // gets all the data it needs for SCT verification and does not do any
|
| // external communication.
|
| int result = cert_transparency_verifier_->Verify(
|
| - server_cert_verify_result_.verified_cert,
|
| + server_cert_verify_result_->verified_cert,
|
| core_->state().stapled_ocsp_response,
|
| core_->state().sct_list_from_tls_extension,
|
| &ct_verify_result_,
|
| @@ -3543,6 +3637,35 @@ void SSLClientSocketNSS::LogConnectionTypeMetrics() const {
|
| };
|
| }
|
|
|
| +// SaveSSLHostInfo saves the certificate chain of the connection so that we can
|
| +// start verification faster in the future.
|
| +void SSLClientSocketNSS::SaveSSLHostInfo() {
|
| + 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(net::CompletionCallback()) != OK)
|
| + return;
|
| +
|
| + SSLHostInfo::State* state = ssl_host_info_->mutable_state();
|
| +
|
| + state->certs.clear();
|
| + const PeerCertificateChain& certs = core_->state().server_cert_chain;
|
| + for (unsigned i = 0; i < certs.size(); i++) {
|
| + if (certs[i] == NULL ||
|
| + 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();
|
| +}
|
| +
|
| void SSLClientSocketNSS::EnsureThreadIdAssigned() const {
|
| base::AutoLock auto_lock(lock_);
|
| if (valid_thread_id_ != base::kInvalidThreadId)
|
|
|