Index: net/quic/crypto/proof_verifier_chromium.cc |
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc |
index 443c0e352ea5336987ff7142e1609d4dd99c108b..5f70199a5058af1ebecaa2a03411d3006171e858 100644 |
--- a/net/quic/crypto/proof_verifier_chromium.cc |
+++ b/net/quic/crypto/proof_verifier_chromium.cc |
@@ -9,6 +9,7 @@ |
#include "base/callback_helpers.h" |
#include "base/compiler_specific.h" |
#include "base/logging.h" |
+#include "base/metrics/histogram.h" |
#include "base/stl_util.h" |
#include "base/strings/stringprintf.h" |
#include "crypto/signature_verifier.h" |
@@ -21,6 +22,7 @@ |
#include "net/cert/single_request_cert_verifier.h" |
#include "net/cert/x509_certificate.h" |
#include "net/cert/x509_util.h" |
+#include "net/http/transport_security_state.h" |
#include "net/quic/crypto/crypto_protocol.h" |
#include "net/ssl/ssl_config_service.h" |
@@ -44,6 +46,7 @@ class ProofVerifierChromium::Job { |
public: |
Job(ProofVerifierChromium* proof_verifier, |
CertVerifier* cert_verifier, |
+ TransportSecurityState* transport_security_state, |
const BoundNetLog& net_log); |
// Starts the proof verification. If |QUIC_PENDING| is returned, then |
@@ -78,6 +81,8 @@ class ProofVerifierChromium::Job { |
// The underlying verifier used for verifying certificates. |
scoped_ptr<SingleRequestCertVerifier> verifier_; |
+ TransportSecurityState* transport_security_state_; |
+ |
// |hostname| specifies the hostname for which |certs| is a valid chain. |
std::string hostname_; |
@@ -95,11 +100,14 @@ class ProofVerifierChromium::Job { |
DISALLOW_COPY_AND_ASSIGN(Job); |
}; |
-ProofVerifierChromium::Job::Job(ProofVerifierChromium* proof_verifier, |
- CertVerifier* cert_verifier, |
- const BoundNetLog& net_log) |
+ProofVerifierChromium::Job::Job( |
+ ProofVerifierChromium* proof_verifier, |
+ CertVerifier* cert_verifier, |
+ TransportSecurityState* transport_security_state, |
+ const BoundNetLog& net_log) |
: proof_verifier_(proof_verifier), |
verifier_(new SingleRequestCertVerifier(cert_verifier)), |
+ transport_security_state_(transport_security_state), |
next_state_(STATE_NONE), |
net_log_(net_log) { |
} |
@@ -228,6 +236,59 @@ int ProofVerifierChromium::Job::DoVerifyCert(int result) { |
int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { |
verifier_.reset(); |
+#if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS) |
wtc
2014/08/01 21:46:36
Adam: I didn't have time to test this patch yester
|
+ // TODO(wtc): The following code was copied from ssl_client_socket_nss.cc. |
+ // Convert it to a new function that can be called by both files. These |
+ // variables simulate the arguments to the new function. |
+ const CertVerifyResult& cert_verify_result = |
+ verify_details_->cert_verify_result; |
+ bool sni_available = true; |
+ const std::string& host = hostname_; |
+ TransportSecurityState* transport_security_state = transport_security_state_; |
+ std::string* pinning_failure_log = &verify_details_->pinning_failure_log; |
+ |
+ // Take care of any mandates for public key pinning. |
+ // |
+ // Pinning is only enabled for official builds to make sure that others don't |
+ // end up with pins that cannot be easily updated. |
+ // |
+ // TODO(agl): We might have an issue here where a request for foo.example.com |
+ // merges into a SPDY connection to www.example.com, and gets a different |
+ // certificate. |
+ |
+ // Perform pin validation if, and only if, all these conditions obtain: |
+ // |
+ // * a TransportSecurityState object is available; |
+ // * the server's certificate chain is valid (or suffers from only a minor |
+ // error); |
+ // * the server's certificate chain chains up to a known root (i.e. not a |
+ // user-installed trust anchor); and |
+ // * the build is recent (very old builds should fail open so that users |
+ // have some chance to recover). |
+ // |
+ const CertStatus cert_status = cert_verify_result.cert_status; |
+ if (transport_security_state && |
+ (result == OK || |
+ (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) && |
+ cert_verify_result.is_issued_by_known_root && |
+ TransportSecurityState::IsBuildTimely()) { |
+ if (transport_security_state->HasPublicKeyPins(host, sni_available)) { |
+ if (!transport_security_state->CheckPublicKeyPins( |
+ host, |
+ sni_available, |
+ cert_verify_result.public_key_hashes, |
+ pinning_failure_log)) { |
+ LOG(ERROR) << *pinning_failure_log; |
+ result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; |
+ UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false); |
+ TransportSecurityState::ReportUMAOnPinFailure(host); |
+ } else { |
+ UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", true); |
+ } |
+ } |
+ } |
+#endif |
+ |
if (result != OK) { |
error_details_ = StringPrintf("Failed to verify certificate chain: %s", |
ErrorToString(result)); |
@@ -315,8 +376,12 @@ bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data, |
return true; |
} |
-ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier) |
- : cert_verifier_(cert_verifier) {} |
+ProofVerifierChromium::ProofVerifierChromium( |
+ CertVerifier* cert_verifier, |
+ TransportSecurityState* transport_security_state) |
+ : cert_verifier_(cert_verifier), |
+ transport_security_state_(transport_security_state) { |
+} |
ProofVerifierChromium::~ProofVerifierChromium() { |
STLDeleteElements(&active_jobs_); |
@@ -337,7 +402,10 @@ QuicAsyncStatus ProofVerifierChromium::VerifyProof( |
} |
const ProofVerifyContextChromium* chromium_context = |
reinterpret_cast<const ProofVerifyContextChromium*>(verify_context); |
- scoped_ptr<Job> job(new Job(this, cert_verifier_, chromium_context->net_log)); |
+ scoped_ptr<Job> job(new Job(this, |
+ cert_verifier_, |
+ transport_security_state_, |
+ chromium_context->net_log)); |
QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs, |
signature, error_details, |
verify_details, callback); |