bug 1071308 - (2/2) remove libpkix-style chain validation callback from CertVerifier r=cviecco
authorDavid Keeler <dkeeler@mozilla.com>
Thu, 25 Sep 2014 11:18:56 -0700
changeset 207509 4f90b7fb1918462222c557100342cdd627e2f3f3
parent 207508 9dc5491eb546b9d334fd305488d50891e2749773
child 207510 0e92508994019308a549b841ee0ec32321f93e72
push id27556
push userryanvm@gmail.com
push dateFri, 26 Sep 2014 20:54:36 +0000
treeherdermozilla-central@16ff803b47a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscviecco
bugs1071308
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1071308 - (2/2) remove libpkix-style chain validation callback from CertVerifier r=cviecco
security/apps/AppTrustDomain.cpp
security/apps/AppTrustDomain.h
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/pkix/include/pkix/pkixtypes.h
security/pkix/lib/pkixbuild.cpp
security/pkix/test/gtest/pkixbuild_tests.cpp
security/pkix/test/gtest/pkixcert_extension_tests.cpp
security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -233,17 +233,17 @@ AppTrustDomain::CheckRevocation(EndEntit
                                 /*optional*/ const Input*)
 {
   // We don't currently do revocation checking. If we need to distrust an Apps
   // certificate, we will use the active distrust mechanism.
   return Success;
 }
 
 Result
-AppTrustDomain::IsChainValid(const DERArray& certChain)
+AppTrustDomain::IsChainValid(const DERArray& certChain, Time time)
 {
   SECStatus srv = ConstructCERTCertListFromReversedDERArray(certChain,
                                                             mCertChain);
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
   return Success;
 }
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -31,18 +31,18 @@ public:
   virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
                             IssuerChecker& checker,
                             mozilla::pkix::Time time) MOZ_OVERRIDE;
   virtual Result CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                  const mozilla::pkix::CertID& certID,
                                  mozilla::pkix::Time time,
                     /*optional*/ const mozilla::pkix::Input* stapledOCSPresponse,
                     /*optional*/ const mozilla::pkix::Input* aiaExtension);
-  virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain)
-                              MOZ_OVERRIDE;
+  virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
+                              mozilla::pkix::Time time) MOZ_OVERRIDE;
   virtual Result CheckPublicKey(mozilla::pkix::Input subjectPublicKeyInfo)
                                 MOZ_OVERRIDE;
   virtual Result VerifySignedData(
            const mozilla::pkix::SignedDataWithSignature& signedData,
            mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE;
   virtual Result DigestBuf(mozilla::pkix::Input item,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) MOZ_OVERRIDE;
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -76,88 +76,55 @@ IsCertBuiltInRoot(CERTCertificate* cert,
     if (strcmp("Builtin Object Token", token) == 0) {
       result = true;
       return SECSuccess;
     }
   }
   return SECSuccess;
 }
 
-struct ChainValidationCallbackState
+Result
+CertListContainsExpectedKeys(const CERTCertList* certList,
+                             const char* hostname, Time time,
+                             CertVerifier::PinningMode pinningMode)
 {
-  const char* hostname;
-  const CertVerifier::PinningMode pinningMode;
-  const SECCertificateUsage usage;
-  const Time time;
-};
-
-SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
-                                  PRBool* chainOK)
-{
-  ChainValidationCallbackState* callbackState =
-    reinterpret_cast<ChainValidationCallbackState*>(state);
-
-  *chainOK = PR_FALSE;
+  if (pinningMode == CertVerifier::pinningDisabled) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("Pinning is disabled; not checking keys."));
+    return Success;
+  }
 
-  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
-         ("verifycert: Inside the Callback \n"));
+  if (!certList) {
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
 
-  // On sanity failure we fail closed.
-  if (!certList) {
-    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
-           ("verifycert: Short circuit, callback, sanity check failed \n"));
-    PR_SetError(PR_INVALID_STATE_ERROR, 0);
-    return SECFailure;
-  }
-  if (!callbackState) {
-    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
-           ("verifycert: Short circuit, callback, no state! \n"));
-    PR_SetError(PR_INVALID_STATE_ERROR, 0);
-    return SECFailure;
+  CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
+  if (CERT_LIST_END(rootNode, certList)) {
+    return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
-  if (callbackState->usage != certificateUsageSSLServer ||
-      callbackState->pinningMode == CertVerifier::pinningDisabled) {
-    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
-           ("verifycert: Callback shortcut pel=%d \n",
-            callbackState->pinningMode));
-    *chainOK = PR_TRUE;
-    return SECSuccess;
+  bool isBuiltInRoot = false;
+  SECStatus srv = IsCertBuiltInRoot(rootNode->cert, isBuiltInRoot);
+  if (srv != SECSuccess) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+  // If desired, the user can enable "allow user CA MITM mode", in which
+  // case key pinning is not enforced for certificates that chain to trust
+  // anchors that are not in Mozilla's root program
+  if (!isBuiltInRoot && pinningMode == CertVerifier::pinningAllowUserCAMITM) {
+    return Success;
   }
 
-  for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
-       !CERT_LIST_END(node, certList);
-       node = CERT_LIST_NEXT(node)) {
-    CERTCertificate* currentCert = node->cert;
-    if (CERT_LIST_END(CERT_LIST_NEXT(node), certList)) {
-      bool isBuiltInRoot = false;
-      SECStatus srv = IsCertBuiltInRoot(currentCert, isBuiltInRoot);
-      if (srv != SECSuccess) {
-        PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Is BuiltInRoot failure"));
-        return srv;
-      }
-      // If desired, the user can enable "allow user CA MITM mode", in which
-      // case key pinning is not enforced for certificates that chain to trust
-      // anchors that are not in Mozilla's root program
-      if (!isBuiltInRoot &&
-          (callbackState->pinningMode ==
-             CertVerifier::pinningAllowUserCAMITM)) {
-        *chainOK = PR_TRUE;
-        return SECSuccess;
-      }
-    }
+  bool enforceTestMode = (pinningMode == CertVerifier::pinningEnforceTestMode);
+  if (PublicKeyPinningService::ChainHasValidPins(certList, hostname, time,
+                                                 enforceTestMode)) {
+    return Success;
   }
 
-  bool enforceTestMode = (callbackState->pinningMode ==
-                          CertVerifier::pinningEnforceTestMode);
-  *chainOK = PublicKeyPinningService::
-    ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
-                      enforceTestMode);
-
-  return SECSuccess;
+  return Result::ERROR_KEY_PINNING_FAILURE;
 }
 
 static Result
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, Input certDER,
                              Time time, KeyUsage ku1, KeyUsage ku2,
                              KeyUsage ku3, KeyPurposeId eku,
                              const CertPolicyId& requiredPolicy,
                              const Input* stapledOCSPResponse)
@@ -211,23 +178,16 @@ CertVerifier::VerifyCert(CERTCertificate
 
   Input certDER;
   rv = certDER.Init(cert->derCert.data, cert->derCert.len);
   if (rv != Success) {
     PR_SetError(MapResultToPRErrorCode(rv), 0);
     return SECFailure;
   }
 
-  ChainValidationCallbackState callbackState = {
-    hostname, mPinningMode, usage, time
-  };
-  CERTChainVerifyCallback callbackContainer;
-  callbackContainer.isChainValid = chainValidationCallback;
-  callbackContainer.isChainValidArg = &callbackState;
-
   NSSCertDBTrustDomain::OCSPFetching ocspFetching
     = !mOCSPDownloadEnabled ||
       (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
     : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
                                 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
 
   ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled
                                                   : ocsp_get_disabled;
@@ -245,18 +205,18 @@ CertVerifier::VerifyCert(CERTCertificate
     stapledOCSPResponse = &stapledOCSPResponseInput;
   }
 
   switch (usage) {
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig, nullptr,
-                                       builtChain);
+                                       pinArg, ocspGETConfig, pinningDisabled,
+                                       nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_clientAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
@@ -271,18 +231,18 @@ CertVerifier::VerifyCert(CERTCertificate
       SECOidTag evPolicyOidTag;
       SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
       if (srv == SECSuccess) {
         NSSCertDBTrustDomain
           trustDomain(trustSSL,
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
-                      mOCSPCache, pinArg, ocspGETConfig,
-                      &callbackContainer, builtChain);
+                      mOCSPCache, pinArg, ocspGETConfig, mPinningMode,
+                      hostname, builtChain);
         rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                           KeyUsage::digitalSignature,// (EC)DHE
                                           KeyUsage::keyEncipherment, // RSA
                                           KeyUsage::keyAgreement,    // (EC)DH
                                           KeyPurposeId::id_kp_serverAuth,
                                           evPolicy, stapledOCSPResponse);
         if (rv == Success) {
           if (evOidPolicy) {
@@ -295,58 +255,58 @@ CertVerifier::VerifyCert(CERTCertificate
 
       if (flags & FLAG_MUST_BE_EV) {
         rv = Result::ERROR_POLICY_VALIDATION_FAILED;
         break;
       }
 
       // Now try non-EV.
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig, &callbackContainer,
-                                       builtChain);
+                                       pinArg, ocspGETConfig, mPinningMode,
+                                       hostname, builtChain);
       rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                         KeyUsage::digitalSignature, // (EC)DHE
                                         KeyUsage::keyEncipherment, // RSA
                                         KeyUsage::keyAgreement, // (EC)DH
                                         KeyPurposeId::id_kp_serverAuth,
                                         CertPolicyId::anyPolicy,
                                         stapledOCSPResponse);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig, nullptr,
-                                       builtChain);
+                                       pinArg, ocspGETConfig, pinningDisabled,
+                                       nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
                           KeyPurposeId::id_kp_serverAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig, nullptr,
-                                       builtChain);
+                                       pinArg, ocspGETConfig, pinningDisabled,
+                                       nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageEmailRecipient: {
       // TODO: The higher level S/MIME processing should pass in which key
       // usage it is trying to verify for, and base its algorithm choices
       // based on the result of the verification(s).
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig, nullptr,
-                                       builtChain);
+                                       pinArg, ocspGETConfig, pinningDisabled,
+                                       nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::keyEncipherment, // RSA
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
         rv = BuildCertChain(trustDomain, certDER, time,
                             EndEntityOrCA::MustBeEndEntity,
@@ -355,17 +315,17 @@ CertVerifier::VerifyCert(CERTCertificate
                             CertPolicyId::anyPolicy, stapledOCSPResponse);
       }
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
-                                       nullptr, builtChain);
+                                       pinningDisabled, nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_codeSigning,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
@@ -383,67 +343,67 @@ CertVerifier::VerifyCert(CERTCertificate
         eku = KeyPurposeId::anyExtendedKeyUsage;
       } else {
         endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
         keyUsage = KeyUsage::digitalSignature;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg,
-                                    ocspGETConfig, nullptr, builtChain);
+                                    ocspGETConfig, pinningDisabled, nullptr,
+                                    builtChain);
       rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse);
       if (rv == Result::ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
-                                        pinArg, ocspGETConfig, nullptr,
-                                        builtChain);
+                                        pinArg, ocspGETConfig, pinningDisabled,
+                                        nullptr, builtChain);
         rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA,
                             keyUsage, eku, CertPolicyId::anyPolicy,
                             stapledOCSPResponse);
         if (rv == Result::ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   ocspFetching, mOCSPCache,
                                                   pinArg, ocspGETConfig,
-                                                  nullptr, builtChain);
+                                                  pinningDisabled, nullptr,
+                                                  builtChain);
           rv = BuildCertChain(objectSigningTrust, certDER, time,
                               endEntityOrCA, keyUsage, eku,
                               CertPolicyId::anyPolicy, stapledOCSPResponse);
         }
       }
 
       break;
     }
 
     default:
       rv = Result::FATAL_ERROR_INVALID_ARGS;
   }
 
   if (rv != Success) {
-    if (rv != Result::ERROR_KEY_PINNING_FAILURE) {
+    if (rv != Result::ERROR_KEY_PINNING_FAILURE &&
+        usage == certificateUsageSSLServer) {
       ScopedCERTCertificate certCopy(CERT_DupCertificate(cert));
       if (!certCopy) {
         return SECFailure;
       }
       ScopedCERTCertList certList(CERT_NewCertList());
       if (!certList) {
         return SECFailure;
       }
       SECStatus srv = CERT_AddCertToListTail(certList.get(), certCopy.get());
       if (srv != SECSuccess) {
         return SECFailure;
       }
       certCopy.forget(); // now owned by certList
-      PRBool chainOK = false;
-      srv = chainValidationCallback(&callbackState, certList, &chainOK);
-      if (srv != SECSuccess) {
-        return SECFailure;
-      }
-      if (!chainOK) {
-        rv = Result::ERROR_KEY_PINNING_FAILURE;
+      Result pinningResult = CertListContainsExpectedKeys(certList, hostname,
+                                                          time, mPinningMode);
+      if (pinningResult != Success) {
+        rv = pinningResult;
       }
     }
     PR_SetError(MapResultToPRErrorCode(rv), 0);
     return SECFailure;
   }
 
   return SECSuccess;
 }
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -74,12 +74,15 @@ public:
   const PinningMode mPinningMode;
 
 private:
   OCSPCache mOCSPCache;
 };
 
 void InitCertVerifierLog();
 SECStatus IsCertBuiltInRoot(CERTCertificate* cert, bool& result);
+mozilla::pkix::Result CertListContainsExpectedKeys(
+  const CERTCertList* certList, const char* hostname, mozilla::pkix::Time time,
+  CertVerifier::PinningMode pinningMode);
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__CertVerifier_h
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -47,24 +47,26 @@ typedef ScopedPtr<SECMODModule, SECMOD_D
 
 } // unnamed namespace
 
 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
                                            OCSPFetching ocspFetching,
                                            OCSPCache& ocspCache,
              /*optional but shouldn't be*/ void* pinArg,
                                            CertVerifier::ocsp_get_config ocspGETConfig,
-                              /*optional*/ CERTChainVerifyCallback* checkChainCallback,
+                                           CertVerifier::PinningMode pinningMode,
+                              /*optional*/ const char* hostname,
                               /*optional*/ ScopedCERTCertList* builtChain)
   : mCertDBTrustType(certDBTrustType)
   , mOCSPFetching(ocspFetching)
   , mOCSPCache(ocspCache)
   , mPinArg(pinArg)
   , mOCSPGetConfig(ocspGETConfig)
-  , mCheckChainCallback(checkChainCallback)
+  , mPinningMode(pinningMode)
+  , mHostname(hostname)
   , mBuiltChain(builtChain)
 {
 }
 
 // E=igca@sgdn.pm.gouv.fr,CN=IGC/A,OU=DCSSI,O=PM/SGDN,L=Paris,ST=France,C=FR
 static const uint8_t ANSSI_SUBJECT_DATA[] =
                        "\x30\x81\x85\x31\x0B\x30\x09\x06\x03\x55\x04"
                        "\x06\x13\x02\x46\x52\x31\x0F\x30\x0D\x06\x03"
@@ -628,47 +630,32 @@ NSSCertDBTrustDomain::VerifyAndMaybeCach
       return putRV;
     }
   }
 
   return rv;
 }
 
 Result
-NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray)
+NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time)
 {
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
-      ("NSSCertDBTrustDomain: Top of IsChainValid mCheckChainCallback=%p",
-       mCheckChainCallback));
-
-  if (!mBuiltChain && !mCheckChainCallback) {
-    // No need to create a CERTCertList, and nothing else to do.
-    return Success;
-  }
+         ("NSSCertDBTrustDomain: IsChainValid"));
 
   ScopedCERTCertList certList;
   SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray,
                                                             certList);
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
 
-  if (mCheckChainCallback) {
-    if (!mCheckChainCallback->isChainValid) {
-      return Result::FATAL_ERROR_INVALID_ARGS;
-    }
-    PRBool chainOK;
-    srv = (mCheckChainCallback->isChainValid)(
-            mCheckChainCallback->isChainValidArg, certList.get(), &chainOK);
-    if (srv != SECSuccess) {
-      return MapPRErrorCodeToResult(PR_GetError());
-    }
-    if (!chainOK) {
-      return Result::ERROR_KEY_PINNING_FAILURE;
-    }
+  Result result = CertListContainsExpectedKeys(certList, mHostname, time,
+                                               mPinningMode);
+  if (result != Success) {
+    return result;
   }
 
   if (mBuiltChain) {
     *mBuiltChain = certList.forget();
   }
 
   return Success;
 }
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -48,18 +48,19 @@ public:
     FetchOCSPForDVSoftFail = 1,
     FetchOCSPForDVHardFail = 2,
     FetchOCSPForEV = 3,
     LocalOnlyOCSPForEV = 4,
   };
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
                        OCSPCache& ocspCache, void* pinArg,
                        CertVerifier::ocsp_get_config ocspGETConfig,
-          /*optional*/ CERTChainVerifyCallback* checkChainCallback = nullptr,
-          /*optional*/ ScopedCERTCertList* builtChain = nullptr);
+                       CertVerifier::PinningMode pinningMode,
+          /*optional*/ const char* hostname = nullptr,
+      /*optional out*/ ScopedCERTCertList* builtChain = nullptr);
 
   virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
                             IssuerChecker& checker,
                             mozilla::pkix::Time time) MOZ_OVERRIDE;
 
   virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                               const mozilla::pkix::CertPolicyId& policy,
                               mozilla::pkix::Input candidateCertDER,
@@ -81,33 +82,34 @@ public:
   virtual Result CheckRevocation(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    const mozilla::pkix::CertID& certID,
                    mozilla::pkix::Time time,
       /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
       /*optional*/ const mozilla::pkix::Input* aiaExtension)
                    MOZ_OVERRIDE;
 
-  virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain)
-                              MOZ_OVERRIDE;
+  virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
+                              mozilla::pkix::Time time) MOZ_OVERRIDE;
 
 private:
   enum EncodedResponseSource {
     ResponseIsFromNetwork = 1,
     ResponseWasStapled = 2
   };
   Result VerifyAndMaybeCacheEncodedOCSPResponse(
     const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
     uint16_t maxLifetimeInDays, mozilla::pkix::Input encodedResponse,
     EncodedResponseSource responseSource, /*out*/ bool& expired);
 
   const SECTrustType mCertDBTrustType;
   const OCSPFetching mOCSPFetching;
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
   const CertVerifier::ocsp_get_config mOCSPGetConfig;
-  CERTChainVerifyCallback* mCheckChainCallback; // non-owning!
+  CertVerifier::PinningMode mPinningMode;
+  const char* mHostname; // non-owning - only used for pinning checks
   ScopedCERTCertList* mBuiltChain; // non-owning
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -277,17 +277,17 @@ public:
   // Keep in mind, in particular, that if the application saves a copy of the
   // certificate chain the last invocation of IsChainValid during a validation,
   // it is still possible for BuildCertChain to fail (return SECFailure), in
   // which case the application must not assume anything about the validity of
   // the last certificate chain passed to IsChainValid; especially, it would be
   // very wrong to assume that the certificate chain is valid.
   //
   // certChain.GetDER(0) is the trust anchor.
-  virtual Result IsChainValid(const DERArray& certChain) = 0;
+  virtual Result IsChainValid(const DERArray& certChain, Time time) = 0;
 
   // issuerCertToDup is only non-const so CERT_DupCertificate can be called on
   // it.
   virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA,
                                  const CertID& certID, Time time,
                     /*optional*/ const Input* stapledOCSPresponse,
                     /*optional*/ const Input* aiaExtension) = 0;
 
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -241,17 +241,17 @@ BuildForward(TrustDomain& trustDomain,
       rv = chain.Append(cert->GetDER());
       if (rv != Success) {
         return NotReached("NonOwningDERArray::SetItem failed.", rv);
       }
     }
 
     // This must be done here, after the chain is built but before any
     // revocation checks have been done.
-    return trustDomain.IsChainValid(chain);
+    return trustDomain.IsChainValid(chain, time);
   }
 
   if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) {
     // Avoid stack overflows and poor performance by limiting cert chain
     // length.
     static const unsigned int MAX_SUBCA_COUNT = 6;
     static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ ==
                   NonOwningDERArray::MAX_LENGTH,
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -160,17 +160,17 @@ private:
 
   virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                  /*optional*/ const Input*,
                                  /*optional*/ const Input*)
   {
     return Success;
   }
 
-  virtual Result IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&, Time)
   {
     return Success;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
                                   Input subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp
@@ -82,17 +82,17 @@ private:
 
   virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                  /*optional*/ const Input*,
                                  /*optional*/ const Input*)
   {
     return Success;
   }
 
-  virtual Result IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&, Time)
   {
     return Success;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
                                   Input subjectPublicKeyInfo)
   {
     return TestVerifySignedData(signedData, subjectPublicKeyInfo);
--- a/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
+++ b/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
@@ -48,17 +48,17 @@ private:
 
   virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                  const Input*, const Input*)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&, Time)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature&,
                                   Input)
   {
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -58,17 +58,17 @@ public:
   {
     // TODO: I guess mozilla::pkix should support revocation of designated
     // OCSP responder eventually, but we don't now, so this function should
     // never get called.
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&, Time)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
                                   Input subjectPublicKeyInfo)
   {