Extend ProofVerifierChromium and ProofVerifyDetailsChromium.

Original code review: https://codereview.chromium.org/436753002/

[email protected],[email protected]
BUG=399457

Review URL: https://codereview.chromium.org/439133002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287275 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc
index 443c0e352..5f70199 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 @@
  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 @@
   // 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 @@
   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::DoVerifyCertComplete(int result) {
   verifier_.reset();
 
+#if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS)
+  // 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 @@
   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 @@
   }
   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);