LCOV - code coverage report
Current view: top level - security/certverifier - NSSCertDBTrustDomain.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 28 604 4.6 %
Date: 2017-07-14 16:53:18 Functions: 3 36 8.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "NSSCertDBTrustDomain.h"
       8             : 
       9             : #include <stdint.h>
      10             : 
      11             : #include "ExtendedValidation.h"
      12             : #include "NSSErrorsService.h"
      13             : #include "OCSPRequestor.h"
      14             : #include "OCSPVerificationTrustDomain.h"
      15             : #include "PublicKeyPinningService.h"
      16             : #include "cert.h"
      17             : #include "certdb.h"
      18             : #include "mozilla/Assertions.h"
      19             : #include "mozilla/Casting.h"
      20             : #include "mozilla/Move.h"
      21             : #include "mozilla/PodOperations.h"
      22             : #include "mozilla/TimeStamp.h"
      23             : #include "mozilla/Unused.h"
      24             : #include "nsNSSCertificate.h"
      25             : #include "nsServiceManagerUtils.h"
      26             : #include "nss.h"
      27             : #include "pk11pub.h"
      28             : #include "pkix/Result.h"
      29             : #include "pkix/pkix.h"
      30             : #include "pkix/pkixnss.h"
      31             : #include "prerror.h"
      32             : #include "secerr.h"
      33             : 
      34             : #include "CNNICHashWhitelist.inc"
      35             : #include "StartComAndWoSignData.inc"
      36             : 
      37             : using namespace mozilla;
      38             : using namespace mozilla::pkix;
      39             : 
      40             : extern LazyLogModule gCertVerifierLog;
      41             : 
      42             : static const uint64_t ServerFailureDelaySeconds = 5 * 60;
      43             : 
      44             : namespace mozilla { namespace psm {
      45             : 
      46           0 : NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
      47             :                                            OCSPFetching ocspFetching,
      48             :                                            OCSPCache& ocspCache,
      49             :              /*optional but shouldn't be*/ void* pinArg,
      50             :                                            CertVerifier::OcspGetConfig ocspGETConfig,
      51             :                                            TimeDuration ocspTimeoutSoft,
      52             :                                            TimeDuration ocspTimeoutHard,
      53             :                                            uint32_t certShortLifetimeInDays,
      54             :                                            CertVerifier::PinningMode pinningMode,
      55             :                                            unsigned int minRSABits,
      56             :                                            ValidityCheckingMode validityCheckingMode,
      57             :                                            CertVerifier::SHA1Mode sha1Mode,
      58             :                                            NetscapeStepUpPolicy netscapeStepUpPolicy,
      59             :                                            const OriginAttributes& originAttributes,
      60             :                                            UniqueCERTCertList& builtChain,
      61             :                               /*optional*/ UniqueCERTCertList* peerCertChain,
      62             :                               /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
      63           0 :                               /*optional*/ const char* hostname)
      64             :   : mCertDBTrustType(certDBTrustType)
      65             :   , mOCSPFetching(ocspFetching)
      66             :   , mOCSPCache(ocspCache)
      67             :   , mPinArg(pinArg)
      68             :   , mOCSPGetConfig(ocspGETConfig)
      69             :   , mOCSPTimeoutSoft(ocspTimeoutSoft)
      70             :   , mOCSPTimeoutHard(ocspTimeoutHard)
      71             :   , mCertShortLifetimeInDays(certShortLifetimeInDays)
      72             :   , mPinningMode(pinningMode)
      73             :   , mMinRSABits(minRSABits)
      74             :   , mValidityCheckingMode(validityCheckingMode)
      75             :   , mSHA1Mode(sha1Mode)
      76             :   , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
      77             :   , mOriginAttributes(originAttributes)
      78             :   , mBuiltChain(builtChain)
      79             :   , mPeerCertChain(peerCertChain)
      80             :   , mPinningTelemetryInfo(pinningTelemetryInfo)
      81             :   , mHostname(hostname)
      82             :   , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
      83             :   , mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
      84             :   , mSCTListFromCertificate()
      85           0 :   , mSCTListFromOCSPStapling()
      86             : {
      87           0 : }
      88             : 
      89             : // If useRoots is true, we only use root certificates in the candidate list.
      90             : // If useRoots is false, we only use non-root certificates in the list.
      91             : static Result
      92           0 : FindIssuerInner(const UniqueCERTCertList& candidates, bool useRoots,
      93             :                 Input encodedIssuerName, TrustDomain::IssuerChecker& checker,
      94             :                 /*out*/ bool& keepGoing)
      95             : {
      96           0 :   keepGoing = true;
      97           0 :   for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
      98           0 :        !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
      99           0 :     bool candidateIsRoot = !!n->cert->isRoot;
     100           0 :     if (candidateIsRoot != useRoots) {
     101           0 :       continue;
     102             :     }
     103           0 :     Input certDER;
     104           0 :     Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
     105           0 :     if (rv != Success) {
     106           0 :       continue; // probably too big
     107             :     }
     108             : 
     109             :     const SECItem encodedIssuerNameItem = {
     110             :       siBuffer,
     111           0 :       const_cast<unsigned char*>(encodedIssuerName.UnsafeGetData()),
     112           0 :       encodedIssuerName.GetLength()
     113           0 :     };
     114           0 :     ScopedAutoSECItem nameConstraints;
     115             :     SECStatus srv = CERT_GetImposedNameConstraints(&encodedIssuerNameItem,
     116           0 :                                                    &nameConstraints);
     117           0 :     if (srv != SECSuccess) {
     118           0 :       if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
     119           0 :         return Result::FATAL_ERROR_LIBRARY_FAILURE;
     120             :       }
     121             : 
     122             :       // If no imposed name constraints were found, continue without them
     123           0 :       rv = checker.Check(certDER, nullptr, keepGoing);
     124             :     } else {
     125             :       // Otherwise apply the constraints
     126           0 :       Input nameConstraintsInput;
     127           0 :       if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len)
     128             :             != Success) {
     129           0 :         return Result::FATAL_ERROR_LIBRARY_FAILURE;
     130             :       }
     131           0 :       rv = checker.Check(certDER, &nameConstraintsInput, keepGoing);
     132             :     }
     133           0 :     if (rv != Success) {
     134           0 :       return rv;
     135             :     }
     136           0 :     if (!keepGoing) {
     137           0 :       break;
     138             :     }
     139             :   }
     140             : 
     141           0 :   return Success;
     142             : }
     143             : 
     144             : // Remove from newCandidates any CERTCertificates in alreadyTried.
     145             : // alreadyTried is likely to be small or empty.
     146             : static void
     147           0 : RemoveCandidatesAlreadyTried(UniqueCERTCertList& newCandidates,
     148             :                              const UniqueCERTCertList& alreadyTried)
     149             : {
     150           0 :   for (const CERTCertListNode* triedNode = CERT_LIST_HEAD(alreadyTried);
     151           0 :        !CERT_LIST_END(triedNode, alreadyTried);
     152           0 :        triedNode = CERT_LIST_NEXT(triedNode)) {
     153           0 :     CERTCertListNode* newNode = CERT_LIST_HEAD(newCandidates);
     154           0 :     while (!CERT_LIST_END(newNode, newCandidates)) {
     155           0 :       CERTCertListNode* savedNode = CERT_LIST_NEXT(newNode);
     156           0 :       if (CERT_CompareCerts(triedNode->cert, newNode->cert)) {
     157           0 :         CERT_RemoveCertListNode(newNode);
     158             :       }
     159           0 :       newNode = savedNode;
     160             :     }
     161             :   }
     162           0 : }
     163             : 
     164             : // Add to matchingCandidates any CERTCertificates from candidatesIn that have a
     165             : // DER-encoded subject name equal to the given subject name.
     166             : static Result
     167           0 : AddMatchingCandidates(UniqueCERTCertList& matchingCandidates,
     168             :                       const UniqueCERTCertList& candidatesIn,
     169             :                       Input subjectName)
     170             : {
     171           0 :   for (const CERTCertListNode* node = CERT_LIST_HEAD(candidatesIn);
     172           0 :        !CERT_LIST_END(node, candidatesIn); node = CERT_LIST_NEXT(node)) {
     173           0 :     Input candidateSubjectName;
     174           0 :     Result rv = candidateSubjectName.Init(node->cert->derSubject.data,
     175           0 :                                           node->cert->derSubject.len);
     176           0 :     if (rv != Success) {
     177           0 :       continue; // probably just too big - continue processing other candidates
     178             :     }
     179           0 :     if (InputsAreEqual(candidateSubjectName, subjectName)) {
     180           0 :       UniqueCERTCertificate certDuplicate(CERT_DupCertificate(node->cert));
     181           0 :       if (!certDuplicate) {
     182           0 :         return Result::FATAL_ERROR_NO_MEMORY;
     183             :       }
     184           0 :       SECStatus srv = CERT_AddCertToListTail(matchingCandidates.get(),
     185           0 :                                              certDuplicate.get());
     186           0 :       if (srv != SECSuccess) {
     187           0 :         return MapPRErrorCodeToResult(PR_GetError());
     188             :       }
     189             :       // matchingCandidates now owns certDuplicate
     190           0 :       Unused << certDuplicate.release();
     191             :     }
     192             :   }
     193           0 :   return Success;
     194             : }
     195             : 
     196             : Result
     197           0 : NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
     198             :                                  IssuerChecker& checker, Time)
     199             : {
     200             :   // If the peer certificate chain was specified, try to use it before falling
     201             :   // back to CERT_CreateSubjectCertList.
     202             :   bool keepGoing;
     203           0 :   UniqueCERTCertList peerCertChainCandidates(CERT_NewCertList());
     204           0 :   if (!peerCertChainCandidates) {
     205           0 :     return Result::FATAL_ERROR_NO_MEMORY;
     206             :   }
     207           0 :   if (mPeerCertChain) {
     208             :     // Build a candidate list that consists only of certificates with a subject
     209             :     // matching the issuer we're looking for.
     210           0 :     Result rv = AddMatchingCandidates(peerCertChainCandidates, *mPeerCertChain,
     211           0 :                                       encodedIssuerName);
     212           0 :     if (rv != Success) {
     213           0 :       return rv;
     214             :     }
     215             :     rv = FindIssuerInner(peerCertChainCandidates, true, encodedIssuerName,
     216           0 :                          checker, keepGoing);
     217           0 :     if (rv != Success) {
     218           0 :       return rv;
     219             :     }
     220           0 :     if (keepGoing) {
     221             :       rv = FindIssuerInner(peerCertChainCandidates, false, encodedIssuerName,
     222           0 :                            checker, keepGoing);
     223           0 :       if (rv != Success) {
     224           0 :         return rv;
     225             :       }
     226             :     }
     227           0 :     if (!keepGoing) {
     228           0 :       return Success;
     229             :     }
     230             :   }
     231             :   // TODO: NSS seems to be ambiguous between "no potential issuers found" and
     232             :   // "there was an error trying to retrieve the potential issuers."
     233           0 :   SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
     234             :   UniqueCERTCertList
     235             :     candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
     236             :                                           &encodedIssuerNameItem, 0,
     237           0 :                                           false));
     238           0 :   if (candidates) {
     239           0 :     RemoveCandidatesAlreadyTried(candidates, peerCertChainCandidates);
     240             :     // First, try all the root certs; then try all the non-root certs.
     241             :     Result rv = FindIssuerInner(candidates, true, encodedIssuerName, checker,
     242           0 :                                 keepGoing);
     243           0 :     if (rv != Success) {
     244           0 :       return rv;
     245             :     }
     246           0 :     if (keepGoing) {
     247             :       rv = FindIssuerInner(candidates, false, encodedIssuerName, checker,
     248           0 :                            keepGoing);
     249           0 :       if (rv != Success) {
     250           0 :         return rv;
     251             :       }
     252             :     }
     253             :   }
     254             : 
     255           0 :   return Success;
     256             : }
     257             : 
     258             : Result
     259           0 : NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
     260             :                                    const CertPolicyId& policy,
     261             :                                    Input candidateCertDER,
     262             :                                    /*out*/ TrustLevel& trustLevel)
     263             : {
     264             :   // XXX: This would be cleaner and more efficient if we could get the trust
     265             :   // information without constructing a CERTCertificate here, but NSS doesn't
     266             :   // expose it in any other easy-to-use fashion. The use of
     267             :   // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
     268             :   // performance problem because NSS will just find the existing
     269             :   // CERTCertificate in its in-memory cache and return it.
     270           0 :   SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
     271             :   UniqueCERTCertificate candidateCert(
     272             :     CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
     273           0 :                             nullptr, false, true));
     274           0 :   if (!candidateCert) {
     275           0 :     return MapPRErrorCodeToResult(PR_GetError());
     276             :   }
     277             : 
     278             :   // Check the certificate against the OneCRL cert blocklist
     279           0 :   if (!mCertBlocklist) {
     280           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
     281             :   }
     282             : 
     283             :   // The certificate blocklist currently only applies to TLS server
     284             :   // certificates.
     285           0 :   if (mCertDBTrustType == trustSSL) {
     286             :     bool isCertRevoked;
     287           0 :     nsresult nsrv = mCertBlocklist->IsCertRevoked(
     288           0 :                       candidateCert->derIssuer.data,
     289           0 :                       candidateCert->derIssuer.len,
     290           0 :                       candidateCert->serialNumber.data,
     291           0 :                       candidateCert->serialNumber.len,
     292           0 :                       candidateCert->derSubject.data,
     293           0 :                       candidateCert->derSubject.len,
     294           0 :                       candidateCert->derPublicKey.data,
     295           0 :                       candidateCert->derPublicKey.len,
     296           0 :                       &isCertRevoked);
     297           0 :     if (NS_FAILED(nsrv)) {
     298           0 :       return Result::FATAL_ERROR_LIBRARY_FAILURE;
     299             :     }
     300             : 
     301           0 :     if (isCertRevoked) {
     302           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     303             :              ("NSSCertDBTrustDomain: certificate is in blocklist"));
     304           0 :       return Result::ERROR_REVOKED_CERTIFICATE;
     305             :     }
     306             :   }
     307             : 
     308             :   // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
     309             :   // SECSuccess means that there is a trust record and SECFailure means there
     310             :   // is not a trust record. I looked at NSS's internal uses of
     311             :   // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
     312             :   // "We have a trust record."
     313             :   CERTCertTrust trust;
     314           0 :   if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
     315           0 :     uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
     316             : 
     317             :     // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
     318             :     // because we can have active distrust for either type of cert. Note that
     319             :     // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
     320             :     // relevant trust bit isn't set then that means the cert must be considered
     321             :     // distrusted.
     322             :     uint32_t relevantTrustBit =
     323           0 :       endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA
     324           0 :                                                : CERTDB_TRUSTED;
     325           0 :     if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
     326             :             == CERTDB_TERMINAL_RECORD) {
     327           0 :       trustLevel = TrustLevel::ActivelyDistrusted;
     328           0 :       return Success;
     329             :     }
     330             : 
     331             :     // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
     332             :     // needed to consider end-entity certs to be their own trust anchors since
     333             :     // Gecko implemented nsICertOverrideService.
     334             :     // Of course, for this to work as expected, we need to make sure we're
     335             :     // inquiring about the trust of a CA and not an end-entity. If an end-entity
     336             :     // has the CERTDB_TRUSTED_CA bit set, Gecko does not consider it to be a
     337             :     // trust anchor; it must inherit its trust.
     338           0 :     if (flags & CERTDB_TRUSTED_CA && endEntityOrCA == EndEntityOrCA::MustBeCA) {
     339           0 :       if (policy.IsAnyPolicy()) {
     340           0 :         trustLevel = TrustLevel::TrustAnchor;
     341           0 :         return Success;
     342             :       }
     343           0 :       if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
     344           0 :         trustLevel = TrustLevel::TrustAnchor;
     345           0 :         return Success;
     346             :       }
     347             :     }
     348             :   }
     349             : 
     350           0 :   trustLevel = TrustLevel::InheritsTrust;
     351           0 :   return Success;
     352             : }
     353             : 
     354             : Result
     355           0 : NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
     356             :                                 /*out*/ uint8_t* digestBuf, size_t digestBufLen)
     357             : {
     358           0 :   return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
     359             : }
     360             : 
     361             : TimeDuration
     362           0 : NSSCertDBTrustDomain::GetOCSPTimeout() const
     363             : {
     364           0 :   switch (mOCSPFetching) {
     365             :     case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
     366           0 :       return mOCSPTimeoutSoft;
     367             :     case NSSCertDBTrustDomain::FetchOCSPForEV:
     368             :     case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
     369           0 :       return mOCSPTimeoutHard;
     370             :     // The rest of these are error cases. Assert in debug builds, but return
     371             :     // the soft timeout value in release builds.
     372             :     case NSSCertDBTrustDomain::NeverFetchOCSP:
     373             :     case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
     374           0 :       MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
     375             :       break;
     376             :   }
     377             : 
     378           0 :   MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
     379             :   return mOCSPTimeoutSoft;
     380             : }
     381             : 
     382             : // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
     383             : // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
     384             : // Success with url == nullptr when an OCSP URI was not found, and Success with
     385             : // url != nullptr when an OCSP URI was found. The output url will be owned
     386             : // by the arena.
     387             : static Result
     388           0 : GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
     389             :                                    Input aiaExtension,
     390             :                                    /*out*/ char const*& url)
     391             : {
     392           0 :   MOZ_ASSERT(arena.get());
     393           0 :   if (!arena.get()) {
     394           0 :     return Result::FATAL_ERROR_INVALID_ARGS;
     395             :   }
     396             : 
     397           0 :   url = nullptr;
     398           0 :   SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
     399             :   CERTAuthInfoAccess** aia =
     400           0 :     CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
     401           0 :   if (!aia) {
     402           0 :     return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
     403             :   }
     404           0 :   for (size_t i = 0; aia[i]; ++i) {
     405           0 :     if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
     406             :       // NSS chooses the **last** OCSP URL; we choose the **first**
     407           0 :       CERTGeneralName* current = aia[i]->location;
     408           0 :       if (!current) {
     409           0 :         continue;
     410             :       }
     411           0 :       do {
     412           0 :         if (current->type == certURI) {
     413           0 :           const SECItem& location = current->name.other;
     414             :           // (location.len + 1) must be small enough to fit into a uint32_t,
     415             :           // but we limit it to a smaller bound to reduce OOM risk.
     416           0 :           if (location.len > 1024 || memchr(location.data, 0, location.len)) {
     417             :             // Reject embedded nulls. (NSS doesn't do this)
     418           0 :             return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
     419             :           }
     420             :           // Copy the non-null-terminated SECItem into a null-terminated string.
     421             :           char* nullTerminatedURL(
     422           0 :             static_cast<char*>(PORT_ArenaAlloc(arena.get(), location.len + 1)));
     423           0 :           if (!nullTerminatedURL) {
     424           0 :             return Result::FATAL_ERROR_NO_MEMORY;
     425             :           }
     426           0 :           memcpy(nullTerminatedURL, location.data, location.len);
     427           0 :           nullTerminatedURL[location.len] = 0;
     428           0 :           url = nullTerminatedURL;
     429           0 :           return Success;
     430             :         }
     431           0 :         current = CERT_GetNextGeneralName(current);
     432           0 :       } while (current != aia[i]->location);
     433             :     }
     434             :   }
     435             : 
     436           0 :   return Success;
     437             : }
     438             : 
     439             : Result
     440           0 : NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
     441             :                                       const CertID& certID, Time time,
     442             :                                       Duration validityDuration,
     443             :                          /*optional*/ const Input* stapledOCSPResponse,
     444             :                          /*optional*/ const Input* aiaExtension)
     445             : {
     446             :   // Actively distrusted certificates will have already been blocked by
     447             :   // GetCertTrust.
     448             : 
     449             :   // TODO: need to verify that IsRevoked isn't called for trust anchors AND
     450             :   // that that fact is documented in mozillapkix.
     451             : 
     452           0 :   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     453             :          ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
     454             : 
     455             :   // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
     456             :   // Since this affects EV there is no reason why DV should be more strict
     457             :   // so all intermediatates are allowed to have OCSP responses up to one year
     458             :   // old.
     459           0 :   uint16_t maxOCSPLifetimeInDays = 10;
     460           0 :   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
     461           0 :     maxOCSPLifetimeInDays = 365;
     462             :   }
     463             : 
     464             :   // If we have a stapled OCSP response then the verification of that response
     465             :   // determines the result unless the OCSP response is expired. We make an
     466             :   // exception for expired responses because some servers, nginx in particular,
     467             :   // are known to serve expired responses due to bugs.
     468             :   // We keep track of the result of verifying the stapled response but don't
     469             :   // immediately return failure if the response has expired.
     470             :   //
     471             :   // We only set the OCSP stapling status if we're validating the end-entity
     472             :   // certificate. Non-end-entity certificates would always be
     473             :   // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
     474           0 :   Result stapledOCSPResponseResult = Success;
     475           0 :   if (stapledOCSPResponse) {
     476           0 :     MOZ_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
     477             :     bool expired;
     478             :     stapledOCSPResponseResult =
     479           0 :       VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
     480             :                                              maxOCSPLifetimeInDays,
     481             :                                              *stapledOCSPResponse,
     482           0 :                                              ResponseWasStapled, expired);
     483           0 :     if (stapledOCSPResponseResult == Success) {
     484             :       // stapled OCSP response present and good
     485           0 :       mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
     486           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     487             :              ("NSSCertDBTrustDomain: stapled OCSP response: good"));
     488           0 :       return Success;
     489             :     }
     490           0 :     if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
     491             :         expired) {
     492             :       // stapled OCSP response present but expired
     493           0 :       mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
     494           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     495             :              ("NSSCertDBTrustDomain: expired stapled OCSP response"));
     496             :     } else {
     497             :       // stapled OCSP response present but invalid for some reason
     498           0 :       mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
     499           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     500             :              ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
     501           0 :       return stapledOCSPResponseResult;
     502             :     }
     503           0 :   } else if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
     504             :     // no stapled OCSP response
     505           0 :     mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
     506           0 :     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     507             :            ("NSSCertDBTrustDomain: no stapled OCSP response"));
     508             :   }
     509             : 
     510           0 :   Result cachedResponseResult = Success;
     511           0 :   Time cachedResponseValidThrough(Time::uninitialized);
     512           0 :   bool cachedResponsePresent = mOCSPCache.Get(certID, mOriginAttributes,
     513             :                                               cachedResponseResult,
     514           0 :                                               cachedResponseValidThrough);
     515           0 :   if (cachedResponsePresent) {
     516           0 :     if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
     517           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     518             :              ("NSSCertDBTrustDomain: cached OCSP response: good"));
     519           0 :       return Success;
     520             :     }
     521             :     // If we have a cached revoked response, use it.
     522           0 :     if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
     523           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     524             :              ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
     525           0 :       return Result::ERROR_REVOKED_CERTIFICATE;
     526             :     }
     527             :     // The cached response may indicate an unknown certificate or it may be
     528             :     // expired. Don't return with either of these statuses yet - we may be
     529             :     // able to fetch a more recent one.
     530           0 :     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     531             :            ("NSSCertDBTrustDomain: cached OCSP response: error %d",
     532             :             static_cast<int>(cachedResponseResult)));
     533             :     // When a good cached response has expired, it is more convenient
     534             :     // to convert that to an error code and just deal with
     535             :     // cachedResponseResult from here on out.
     536           0 :     if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
     537           0 :       cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
     538             :     }
     539             :     // We may have a cached indication of server failure. Ignore it if
     540             :     // it has expired.
     541           0 :     if (cachedResponseResult != Success &&
     542           0 :         cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
     543           0 :         cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
     544           0 :         cachedResponseValidThrough < time) {
     545           0 :       cachedResponseResult = Success;
     546           0 :       cachedResponsePresent = false;
     547             :     }
     548             :   } else {
     549           0 :     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     550             :            ("NSSCertDBTrustDomain: no cached OCSP response"));
     551             :   }
     552             :   // At this point, if and only if cachedErrorResult is Success, there was no
     553             :   // cached response.
     554           0 :   MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
     555             :              (cachedResponsePresent && cachedResponseResult != Success));
     556             : 
     557             :   // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
     558             :   bool blocklistIsFresh;
     559           0 :   nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
     560           0 :   if (NS_FAILED(nsrv)) {
     561           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
     562             :   }
     563             : 
     564             :   // TODO: We still need to handle the fallback for expired responses. But,
     565             :   // if/when we disable OCSP fetching by default, it would be ambiguous whether
     566             :   // security.OCSP.enable==0 means "I want the default" or "I really never want
     567             :   // you to ever fetch OCSP."
     568             : 
     569           0 :   Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
     570             : 
     571           0 :   if ((mOCSPFetching == NeverFetchOCSP) ||
     572           0 :       (validityDuration < shortLifetime) ||
     573           0 :       (endEntityOrCA == EndEntityOrCA::MustBeCA &&
     574           0 :        (mOCSPFetching == FetchOCSPForDVHardFail ||
     575           0 :         mOCSPFetching == FetchOCSPForDVSoftFail ||
     576             :         blocklistIsFresh))) {
     577             :     // We're not going to be doing any fetching, so if there was a cached
     578             :     // "unknown" response, say so.
     579           0 :     if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
     580           0 :       return Result::ERROR_OCSP_UNKNOWN_CERT;
     581             :     }
     582             :     // If we're doing hard-fail, we want to know if we have a cached response
     583             :     // that has expired.
     584           0 :     if (mOCSPFetching == FetchOCSPForDVHardFail &&
     585           0 :         cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
     586           0 :       return Result::ERROR_OCSP_OLD_RESPONSE;
     587             :     }
     588             : 
     589           0 :     return Success;
     590             :   }
     591             : 
     592           0 :   if (mOCSPFetching == LocalOnlyOCSPForEV) {
     593           0 :     if (cachedResponseResult != Success) {
     594           0 :       return cachedResponseResult;
     595             :     }
     596           0 :     return Result::ERROR_OCSP_UNKNOWN_CERT;
     597             :   }
     598             : 
     599           0 :   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     600           0 :   if (!arena) {
     601           0 :     return Result::FATAL_ERROR_NO_MEMORY;
     602             :   }
     603             : 
     604             :   Result rv;
     605           0 :   const char* url = nullptr; // owned by the arena
     606             : 
     607           0 :   if (aiaExtension) {
     608           0 :     rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, url);
     609           0 :     if (rv != Success) {
     610           0 :       return rv;
     611             :     }
     612             :   }
     613             : 
     614           0 :   if (!url) {
     615           0 :     if (mOCSPFetching == FetchOCSPForEV ||
     616           0 :         cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
     617           0 :       return Result::ERROR_OCSP_UNKNOWN_CERT;
     618             :     }
     619           0 :     if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
     620           0 :       return Result::ERROR_OCSP_OLD_RESPONSE;
     621             :     }
     622           0 :     if (stapledOCSPResponseResult != Success) {
     623           0 :       return stapledOCSPResponseResult;
     624             :     }
     625             : 
     626             :     // Nothing to do if we don't have an OCSP responder URI for the cert; just
     627             :     // assume it is good. Note that this is the confusing, but intended,
     628             :     // interpretation of "strict" revocation checking in the face of a
     629             :     // certificate that lacks an OCSP responder URI.
     630           0 :     return Success;
     631             :   }
     632             : 
     633             :   // Only request a response if we didn't have a cached indication of failure
     634             :   // (don't keep requesting responses from a failing server).
     635           0 :   Input response;
     636             :   bool attemptedRequest;
     637           0 :   if (cachedResponseResult == Success ||
     638           0 :       cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
     639           0 :       cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
     640             :     uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
     641             :     size_t ocspRequestLength;
     642           0 :     rv = CreateEncodedOCSPRequest(*this, certID, ocspRequest,
     643           0 :                                   ocspRequestLength);
     644           0 :     if (rv != Success) {
     645           0 :       return rv;
     646             :     }
     647             :     SECItem ocspRequestItem = {
     648             :       siBuffer,
     649             :       ocspRequest,
     650             :       static_cast<unsigned int>(ocspRequestLength)
     651           0 :     };
     652             :     // Owned by arena
     653           0 :     SECItem* responseSECItem = nullptr;
     654             :     Result tempRV =
     655           0 :       DoOCSPRequest(arena, url, mOriginAttributes, &ocspRequestItem,
     656             :                     GetOCSPTimeout(),
     657           0 :                     mOCSPGetConfig == CertVerifier::ocspGetEnabled,
     658           0 :                     responseSECItem);
     659           0 :     MOZ_ASSERT((tempRV != Success) || responseSECItem);
     660           0 :     if (tempRV != Success) {
     661           0 :       rv = tempRV;
     662           0 :     } else if (response.Init(responseSECItem->data, responseSECItem->len)
     663             :                  != Success) {
     664           0 :       rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
     665             :     }
     666           0 :     attemptedRequest = true;
     667             :   } else {
     668           0 :     rv = cachedResponseResult;
     669           0 :     attemptedRequest = false;
     670             :   }
     671             : 
     672           0 :   if (response.GetLength() == 0) {
     673           0 :     Result error = rv;
     674           0 :     if (attemptedRequest) {
     675           0 :       Time timeout(time);
     676           0 :       if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
     677           0 :         return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
     678             :       }
     679           0 :       rv = mOCSPCache.Put(certID, mOriginAttributes, error, time, timeout);
     680           0 :       if (rv != Success) {
     681           0 :         return rv;
     682             :       }
     683             :     }
     684           0 :     if (mOCSPFetching != FetchOCSPForDVSoftFail) {
     685           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     686             :              ("NSSCertDBTrustDomain: returning SECFailure after "
     687             :               "OCSP request failure"));
     688           0 :       return error;
     689             :     }
     690           0 :     if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
     691           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     692             :              ("NSSCertDBTrustDomain: returning SECFailure from cached "
     693             :               "response after OCSP request failure"));
     694           0 :       return cachedResponseResult;
     695             :     }
     696           0 :     if (stapledOCSPResponseResult != Success) {
     697           0 :       MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     698             :              ("NSSCertDBTrustDomain: returning SECFailure from expired "
     699             :               "stapled response after OCSP request failure"));
     700           0 :       return stapledOCSPResponseResult;
     701             :     }
     702             : 
     703           0 :     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     704             :            ("NSSCertDBTrustDomain: returning SECSuccess after "
     705             :             "OCSP request failure"));
     706           0 :     return Success; // Soft fail -> success :(
     707             :   }
     708             : 
     709             :   // If the response from the network has expired but indicates a revoked
     710             :   // or unknown certificate, PR_GetError() will return the appropriate error.
     711             :   // We actually ignore expired here.
     712             :   bool expired;
     713           0 :   rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
     714             :                                               maxOCSPLifetimeInDays,
     715             :                                               response, ResponseIsFromNetwork,
     716           0 :                                               expired);
     717           0 :   if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
     718           0 :     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     719             :       ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
     720           0 :     return rv;
     721             :   }
     722             : 
     723           0 :   if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
     724             :       rv == Result::ERROR_REVOKED_CERTIFICATE) {
     725           0 :     return rv;
     726             :   }
     727           0 :   if (stapledOCSPResponseResult != Success) {
     728           0 :     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     729             :            ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
     730             :             "response after OCSP request verification failure"));
     731           0 :     return stapledOCSPResponseResult;
     732             :   }
     733             : 
     734           0 :   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     735             :          ("NSSCertDBTrustDomain: end of CheckRevocation"));
     736             : 
     737           0 :   return Success; // Soft fail -> success :(
     738             : }
     739             : 
     740             : Result
     741           0 : NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
     742             :   const CertID& certID, Time time, uint16_t maxLifetimeInDays,
     743             :   Input encodedResponse, EncodedResponseSource responseSource,
     744             :   /*out*/ bool& expired)
     745             : {
     746           0 :   Time thisUpdate(Time::uninitialized);
     747           0 :   Time validThrough(Time::uninitialized);
     748             : 
     749             :   // We use a try and fallback approach which first mandates good signature
     750             :   // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
     751             :   // OCSP response signing certificate was issued with a SHA-1 signature,
     752             :   // verification initially fails. We cache the failure and then re-use that
     753             :   // result even when doing fallback (i.e. when weak signature digest algorithms
     754             :   // should succeed). To address this we use an OCSPVerificationTrustDomain
     755             :   // here, rather than using *this, to ensure verification succeeds for all
     756             :   // allowed signature digest algorithms.
     757           0 :   OCSPVerificationTrustDomain trustDomain(*this);
     758           0 :   Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
     759             :                                         maxLifetimeInDays, encodedResponse,
     760           0 :                                         expired, &thisUpdate, &validThrough);
     761             :   // If a response was stapled and expired, we don't want to cache it. Return
     762             :   // early to simplify the logic here.
     763           0 :   if (responseSource == ResponseWasStapled && expired) {
     764           0 :     MOZ_ASSERT(rv != Success);
     765           0 :     return rv;
     766             :   }
     767             :   // validThrough is only trustworthy if the response successfully verifies
     768             :   // or it indicates a revoked or unknown certificate.
     769             :   // If this isn't the case, store an indication of failure (to prevent
     770             :   // repeatedly requesting a response from a failing server).
     771           0 :   if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
     772             :       rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
     773           0 :     validThrough = time;
     774           0 :     if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
     775           0 :       return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
     776             :     }
     777             :   }
     778           0 :   if (responseSource == ResponseIsFromNetwork ||
     779           0 :       rv == Success ||
     780           0 :       rv == Result::ERROR_REVOKED_CERTIFICATE ||
     781             :       rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
     782           0 :     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     783             :            ("NSSCertDBTrustDomain: caching OCSP response"));
     784           0 :     Result putRV = mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate,
     785           0 :                                   validThrough);
     786           0 :     if (putRV != Success) {
     787           0 :       return putRV;
     788             :     }
     789             :   }
     790             : 
     791           0 :   return rv;
     792             : }
     793             : 
     794             : static const uint8_t CNNIC_ROOT_CA_SUBJECT_DATA[] =
     795             :   "\x30\x32\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x0E\x30"
     796             :   "\x0C\x06\x03\x55\x04\x0A\x13\x05\x43\x4E\x4E\x49\x43\x31\x13\x30\x11\x06"
     797             :   "\x03\x55\x04\x03\x13\x0A\x43\x4E\x4E\x49\x43\x20\x52\x4F\x4F\x54";
     798             : 
     799             : static const uint8_t CNNIC_EV_ROOT_CA_SUBJECT_DATA[] =
     800             :   "\x30\x81\x8A\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x32"
     801             :   "\x30\x30\x06\x03\x55\x04\x0A\x0C\x29\x43\x68\x69\x6E\x61\x20\x49\x6E\x74"
     802             :   "\x65\x72\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F"
     803             :   "\x72\x6D\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x31\x47\x30\x45"
     804             :   "\x06\x03\x55\x04\x03\x0C\x3E\x43\x68\x69\x6E\x61\x20\x49\x6E\x74\x65\x72"
     805             :   "\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F\x72\x6D"
     806             :   "\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x20\x45\x56\x20\x43\x65"
     807             :   "\x72\x74\x69\x66\x69\x63\x61\x74\x65\x73\x20\x52\x6F\x6F\x74";
     808             : 
     809             : class WhitelistedCNNICHashBinarySearchComparator
     810             : {
     811             : public:
     812           0 :   explicit WhitelistedCNNICHashBinarySearchComparator(const uint8_t* aTarget,
     813             :                                                       size_t aTargetLength)
     814           0 :     : mTarget(aTarget)
     815             :   {
     816           0 :     MOZ_ASSERT(aTargetLength == CNNIC_WHITELIST_HASH_LEN,
     817             :                "Hashes should be of the same length.");
     818           0 :   }
     819             : 
     820           0 :   int operator()(const WhitelistedCNNICHash val) const {
     821           0 :     return memcmp(mTarget, val.hash, CNNIC_WHITELIST_HASH_LEN);
     822             :   }
     823             : 
     824             : private:
     825             :   const uint8_t* mTarget;
     826             : };
     827             : 
     828             : static bool
     829           0 : CertIsStartComOrWoSign(const CERTCertificate* cert)
     830             : {
     831           0 :   for (const DataAndLength& dn : StartComAndWoSignDNs) {
     832           0 :     if (cert->derSubject.len == dn.len &&
     833           0 :         PodEqual(cert->derSubject.data, dn.data, dn.len)) {
     834           0 :       return true;
     835             :     }
     836             :   }
     837           0 :   return false;
     838             : }
     839             : 
     840             : // If a certificate in the given chain appears to have been issued by one of
     841             : // seven roots operated by StartCom and WoSign that are not trusted to issue new
     842             : // certificates, verify that the end-entity has a notBefore date before 21
     843             : // October 2016. If the value of notBefore is after this time, the chain is not
     844             : // valid.
     845             : // (NB: While there are seven distinct roots being checked for, two of them
     846             : // share distinguished names, resulting in six distinct distinguished names to
     847             : // actually look for.)
     848             : static Result
     849           0 : CheckForStartComOrWoSign(const UniqueCERTCertList& certChain)
     850             : {
     851           0 :   if (CERT_LIST_EMPTY(certChain)) {
     852           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
     853             :   }
     854           0 :   const CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certChain);
     855           0 :   if (!endEntityNode || !endEntityNode->cert) {
     856           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
     857             :   }
     858             :   PRTime notBefore;
     859             :   PRTime notAfter;
     860           0 :   if (CERT_GetCertTimes(endEntityNode->cert, &notBefore, &notAfter)
     861             :         != SECSuccess) {
     862           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
     863             :   }
     864             :   // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
     865             :   // (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
     866             :   static const PRTime OCTOBER_21_2016 = 1477008000000000;
     867           0 :   if (notBefore <= OCTOBER_21_2016) {
     868           0 :     return Success;
     869             :   }
     870             : 
     871           0 :   for (const CERTCertListNode* node = CERT_LIST_HEAD(certChain);
     872           0 :        !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
     873           0 :     if (!node || !node->cert) {
     874           0 :       return Result::FATAL_ERROR_LIBRARY_FAILURE;
     875             :     }
     876           0 :     if (CertIsStartComOrWoSign(node->cert)) {
     877           0 :       return Result::ERROR_REVOKED_CERTIFICATE;
     878             :     }
     879             :   }
     880           0 :   return Success;
     881             : }
     882             : 
     883             : // python DottedOIDToCode.py sGlobalSignEVPolicyBytes 1.3.6.1.4.1.4146.1.1
     884             : static const uint8_t sGlobalSignEVPolicyBytes[] = {
     885             :   0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0, 0x32, 0x01, 0x01
     886             : };
     887             : 
     888             : static const CertPolicyId sGlobalSignEVPolicy = {
     889             :   sizeof(sGlobalSignEVPolicyBytes),
     890             :   // It's unfortunate, but there isn't a nice way to do this.
     891             :   // Just make sure these bytes match sGlobalSignEVPolicyBytes.
     892             :   { 0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0, 0x32, 0x01, 0x01 }
     893             : };
     894             : 
     895             : static const unsigned char sGlobalSignRootCAR2SubjectBytes[] = {
     896             :   0x30, 0x4c, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x17,
     897             :   0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x52, 0x6f,
     898             :   0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x52, 0x32, 0x31, 0x13, 0x30,
     899             :   0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61,
     900             :   0x6c, 0x53, 0x69, 0x67, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
     901             :   0x03, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e,
     902             : };
     903             : 
     904             : static const unsigned char sGlobalSignRootCAR2SPKIBytes[] = {
     905             :   0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
     906             :   0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
     907             :   0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa6, 0xcf, 0x24, 0x0e, 0xbe, 0x2e,
     908             :   0x6f, 0x28, 0x99, 0x45, 0x42, 0xc4, 0xab, 0x3e, 0x21, 0x54, 0x9b, 0x0b, 0xd3,
     909             :   0x7f, 0x84, 0x70, 0xfa, 0x12, 0xb3, 0xcb, 0xbf, 0x87, 0x5f, 0xc6, 0x7f, 0x86,
     910             :   0xd3, 0xb2, 0x30, 0x5c, 0xd6, 0xfd, 0xad, 0xf1, 0x7b, 0xdc, 0xe5, 0xf8, 0x60,
     911             :   0x96, 0x09, 0x92, 0x10, 0xf5, 0xd0, 0x53, 0xde, 0xfb, 0x7b, 0x7e, 0x73, 0x88,
     912             :   0xac, 0x52, 0x88, 0x7b, 0x4a, 0xa6, 0xca, 0x49, 0xa6, 0x5e, 0xa8, 0xa7, 0x8c,
     913             :   0x5a, 0x11, 0xbc, 0x7a, 0x82, 0xeb, 0xbe, 0x8c, 0xe9, 0xb3, 0xac, 0x96, 0x25,
     914             :   0x07, 0x97, 0x4a, 0x99, 0x2a, 0x07, 0x2f, 0xb4, 0x1e, 0x77, 0xbf, 0x8a, 0x0f,
     915             :   0xb5, 0x02, 0x7c, 0x1b, 0x96, 0xb8, 0xc5, 0xb9, 0x3a, 0x2c, 0xbc, 0xd6, 0x12,
     916             :   0xb9, 0xeb, 0x59, 0x7d, 0xe2, 0xd0, 0x06, 0x86, 0x5f, 0x5e, 0x49, 0x6a, 0xb5,
     917             :   0x39, 0x5e, 0x88, 0x34, 0xec, 0xbc, 0x78, 0x0c, 0x08, 0x98, 0x84, 0x6c, 0xa8,
     918             :   0xcd, 0x4b, 0xb4, 0xa0, 0x7d, 0x0c, 0x79, 0x4d, 0xf0, 0xb8, 0x2d, 0xcb, 0x21,
     919             :   0xca, 0xd5, 0x6c, 0x5b, 0x7d, 0xe1, 0xa0, 0x29, 0x84, 0xa1, 0xf9, 0xd3, 0x94,
     920             :   0x49, 0xcb, 0x24, 0x62, 0x91, 0x20, 0xbc, 0xdd, 0x0b, 0xd5, 0xd9, 0xcc, 0xf9,
     921             :   0xea, 0x27, 0x0a, 0x2b, 0x73, 0x91, 0xc6, 0x9d, 0x1b, 0xac, 0xc8, 0xcb, 0xe8,
     922             :   0xe0, 0xa0, 0xf4, 0x2f, 0x90, 0x8b, 0x4d, 0xfb, 0xb0, 0x36, 0x1b, 0xf6, 0x19,
     923             :   0x7a, 0x85, 0xe0, 0x6d, 0xf2, 0x61, 0x13, 0x88, 0x5c, 0x9f, 0xe0, 0x93, 0x0a,
     924             :   0x51, 0x97, 0x8a, 0x5a, 0xce, 0xaf, 0xab, 0xd5, 0xf7, 0xaa, 0x09, 0xaa, 0x60,
     925             :   0xbd, 0xdc, 0xd9, 0x5f, 0xdf, 0x72, 0xa9, 0x60, 0x13, 0x5e, 0x00, 0x01, 0xc9,
     926             :   0x4a, 0xfa, 0x3f, 0xa4, 0xea, 0x07, 0x03, 0x21, 0x02, 0x8e, 0x82, 0xca, 0x03,
     927             :   0xc2, 0x9b, 0x8f, 0x02, 0x03, 0x01, 0x00, 0x01,
     928             : };
     929             : 
     930             : static const unsigned char sGlobalSignExtendedValidationCASHA256G2SubjectBytes[] = {
     931             :   0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
     932             :   0x42, 0x45, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10,
     933             :   0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76,
     934             :   0x2d, 0x73, 0x61, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
     935             :   0x2f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x45,
     936             :   0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64,
     937             :   0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x53, 0x48,
     938             :   0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32,
     939             : };
     940             : 
     941             : static const unsigned char sGlobalSignExtendedValidationCASHA256G2SPKIBytes[] = {
     942             :   0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
     943             :   0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
     944             :   0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0xea, 0xa1, 0xd2, 0xc3, 0x49,
     945             :   0xe5, 0xf7, 0x1c, 0x5d, 0xaf, 0xc3, 0x92, 0x42, 0xaf, 0x8a, 0x3c, 0xdc, 0xef,
     946             :   0x4c, 0xe6, 0x2f, 0x5f, 0x0c, 0x2b, 0x9f, 0x8a, 0x50, 0x30, 0x66, 0xef, 0x4e,
     947             :   0xc8, 0x4f, 0x21, 0x4a, 0xf6, 0xe7, 0xf2, 0x4e, 0x1b, 0x8c, 0x53, 0x57, 0xb0,
     948             :   0x9e, 0xc8, 0x5b, 0xf7, 0xb8, 0x46, 0x55, 0xb3, 0x1a, 0xed, 0xc2, 0x6a, 0xfe,
     949             :   0xf4, 0x1b, 0xec, 0x48, 0x46, 0x0e, 0x8f, 0xe0, 0xfb, 0xe0, 0x91, 0x19, 0xdf,
     950             :   0x99, 0x18, 0x6f, 0x2e, 0x51, 0xaf, 0xda, 0xf6, 0x9a, 0xca, 0x64, 0x6f, 0x99,
     951             :   0x54, 0x10, 0x74, 0xea, 0x3c, 0xc8, 0xaa, 0x80, 0x4d, 0x43, 0x37, 0xfb, 0xc8,
     952             :   0xa4, 0x7f, 0x05, 0x9d, 0x37, 0x92, 0xbd, 0x98, 0x00, 0x35, 0x5a, 0xaf, 0xbb,
     953             :   0x5b, 0x74, 0x15, 0x0e, 0xbc, 0xbc, 0xc6, 0xe9, 0xb7, 0x86, 0xe7, 0xee, 0xae,
     954             :   0x4d, 0x4b, 0x04, 0x4c, 0x2b, 0xa0, 0xb4, 0x65, 0x48, 0xb8, 0xc3, 0x3a, 0xcd,
     955             :   0x75, 0xbb, 0x37, 0xc9, 0x4a, 0xc0, 0x01, 0x11, 0xd9, 0xbf, 0x3f, 0x15, 0x86,
     956             :   0x60, 0x19, 0x6b, 0x34, 0x20, 0x46, 0xf5, 0x86, 0x66, 0x0f, 0x24, 0xf4, 0xcc,
     957             :   0x62, 0x9f, 0x9f, 0x9e, 0x1d, 0xfd, 0x10, 0xa4, 0x99, 0x5e, 0xf0, 0x41, 0xeb,
     958             :   0xb0, 0x94, 0xff, 0x2c, 0xb3, 0x36, 0xd6, 0xeb, 0x1d, 0xa7, 0x17, 0x5f, 0xdf,
     959             :   0xce, 0x6a, 0x77, 0xc7, 0x9a, 0xc4, 0x32, 0x63, 0xa7, 0x06, 0xad, 0xf3, 0x12,
     960             :   0x1b, 0x9d, 0x30, 0x72, 0x59, 0x0b, 0xeb, 0x72, 0xeb, 0x2a, 0xd2, 0x77, 0x7b,
     961             :   0x91, 0x77, 0xdb, 0x00, 0xfc, 0xd8, 0x6f, 0xf5, 0x2f, 0xd8, 0x7a, 0xc5, 0x0c,
     962             :   0x3a, 0xa0, 0x7b, 0x5e, 0x90, 0xf3, 0x9d, 0x84, 0x59, 0xc8, 0x01, 0xd9, 0x91,
     963             :   0x37, 0x56, 0xe5, 0x3a, 0x53, 0x93, 0xad, 0x60, 0x49, 0x27, 0x25, 0xd9, 0xe1,
     964             :   0xda, 0x82, 0xd7, 0x02, 0x03, 0x01, 0x00, 0x01,
     965             : };
     966             : 
     967             : template<size_t T, size_t R>
     968             : static bool
     969           0 : CertMatchesStaticData(const CERTCertificate* cert,
     970             :                       const unsigned char (&subject)[T],
     971             :                       const unsigned char (&spki)[R]) {
     972           0 :   MOZ_ASSERT(cert);
     973           0 :   if (!cert) {
     974           0 :     return false;
     975             :   }
     976           0 :   return cert->derSubject.len == T &&
     977           0 :          mozilla::PodEqual(cert->derSubject.data, subject, T) &&
     978           0 :          cert->derPublicKey.len == R &&
     979           0 :          mozilla::PodEqual(cert->derPublicKey.data, spki, R);
     980             : }
     981             : 
     982             : Result
     983           0 : NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
     984             :                                    const CertPolicyId& requiredPolicy)
     985             : {
     986           0 :   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
     987             :          ("NSSCertDBTrustDomain: IsChainValid"));
     988             : 
     989           0 :   UniqueCERTCertList certList;
     990             :   SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray,
     991           0 :                                                             certList);
     992           0 :   if (srv != SECSuccess) {
     993           0 :     return MapPRErrorCodeToResult(PR_GetError());
     994             :   }
     995           0 :   if (CERT_LIST_EMPTY(certList)) {
     996           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
     997             :   }
     998             : 
     999           0 :   Result rv = CheckForStartComOrWoSign(certList);
    1000           0 :   if (rv != Success) {
    1001           0 :     return rv;
    1002             :   }
    1003             : 
    1004             :   // If the certificate appears to have been issued by a CNNIC root, only allow
    1005             :   // it if it is on the whitelist.
    1006           0 :   CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
    1007           0 :   if (!rootNode) {
    1008           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1009             :   }
    1010           0 :   CERTCertificate* root = rootNode->cert;
    1011           0 :   if (!root) {
    1012           0 :     return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1013             :   }
    1014           0 :   if ((root->derSubject.len == sizeof(CNNIC_ROOT_CA_SUBJECT_DATA) - 1 &&
    1015           0 :        memcmp(root->derSubject.data, CNNIC_ROOT_CA_SUBJECT_DATA,
    1016           0 :               root->derSubject.len) == 0) ||
    1017           0 :       (root->derSubject.len == sizeof(CNNIC_EV_ROOT_CA_SUBJECT_DATA) - 1 &&
    1018           0 :        memcmp(root->derSubject.data, CNNIC_EV_ROOT_CA_SUBJECT_DATA,
    1019           0 :               root->derSubject.len) == 0)) {
    1020           0 :     CERTCertListNode* certNode = CERT_LIST_HEAD(certList);
    1021           0 :     if (!certNode) {
    1022           0 :       return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1023             :     }
    1024           0 :     CERTCertificate* cert = certNode->cert;
    1025           0 :     if (!cert) {
    1026           0 :       return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1027             :     }
    1028           0 :     Digest digest;
    1029           0 :     nsresult nsrv = digest.DigestBuf(SEC_OID_SHA256, cert->derCert.data,
    1030           0 :                                      cert->derCert.len);
    1031           0 :     if (NS_FAILED(nsrv)) {
    1032           0 :       return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1033             :     }
    1034             :     const uint8_t* certHash(
    1035           0 :       BitwiseCast<uint8_t*, unsigned char*>(digest.get().data));
    1036           0 :     size_t certHashLen = digest.get().len;
    1037             :     size_t unused;
    1038           0 :     if (!mozilla::BinarySearchIf(WhitelistedCNNICHashes, 0,
    1039             :                                  ArrayLength(WhitelistedCNNICHashes),
    1040           0 :                                  WhitelistedCNNICHashBinarySearchComparator(
    1041             :                                    certHash, certHashLen),
    1042             :                                  &unused)) {
    1043           0 :       return Result::ERROR_REVOKED_CERTIFICATE;
    1044             :     }
    1045             :   }
    1046             : 
    1047           0 :   bool isBuiltInRoot = false;
    1048           0 :   rv = IsCertBuiltInRoot(root, isBuiltInRoot);
    1049           0 :   if (rv != Success) {
    1050           0 :     return rv;
    1051             :   }
    1052             :   bool skipPinningChecksBecauseOfMITMMode =
    1053           0 :     (!isBuiltInRoot && mPinningMode == CertVerifier::pinningAllowUserCAMITM);
    1054             :   // If mHostname isn't set, we're not verifying in the context of a TLS
    1055             :   // handshake, so don't verify HPKP in those cases.
    1056           0 :   if (mHostname && (mPinningMode != CertVerifier::pinningDisabled) &&
    1057           0 :       !skipPinningChecksBecauseOfMITMMode) {
    1058             :     bool enforceTestMode =
    1059           0 :       (mPinningMode == CertVerifier::pinningEnforceTestMode);
    1060             :     bool chainHasValidPins;
    1061           0 :     nsresult nsrv = PublicKeyPinningService::ChainHasValidPins(
    1062             :       certList, mHostname, time, enforceTestMode, mOriginAttributes,
    1063           0 :       chainHasValidPins, mPinningTelemetryInfo);
    1064           0 :     if (NS_FAILED(nsrv)) {
    1065           0 :       return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1066             :     }
    1067           0 :     if (!chainHasValidPins) {
    1068           0 :       return Result::ERROR_KEY_PINNING_FAILURE;
    1069             :     }
    1070             :   }
    1071             : 
    1072             :   // See bug 1349762. If the root is "GlobalSign Root CA - R2", don't consider
    1073             :   // the end-entity valid for EV unless the
    1074             :   // "GlobalSign Extended Validation CA - SHA256 - G2" intermediate is in the
    1075             :   // chain as well. It should be possible to remove this workaround after
    1076             :   // January 2019 as per bug 1349727 comment 17.
    1077           0 :   if (requiredPolicy == sGlobalSignEVPolicy &&
    1078           0 :       CertMatchesStaticData(root, sGlobalSignRootCAR2SubjectBytes,
    1079             :                             sGlobalSignRootCAR2SPKIBytes)) {
    1080           0 :     bool foundRequiredIntermediate = false;
    1081           0 :     for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
    1082           0 :          !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) {
    1083           0 :       if (CertMatchesStaticData(
    1084           0 :             node->cert,
    1085             :             sGlobalSignExtendedValidationCASHA256G2SubjectBytes,
    1086             :             sGlobalSignExtendedValidationCASHA256G2SPKIBytes)) {
    1087           0 :         foundRequiredIntermediate = true;
    1088           0 :         break;
    1089             :       }
    1090             :     }
    1091           0 :     if (!foundRequiredIntermediate) {
    1092           0 :       return Result::ERROR_POLICY_VALIDATION_FAILED;
    1093             :     }
    1094             :   }
    1095             : 
    1096           0 :   mBuiltChain = Move(certList);
    1097             : 
    1098           0 :   return Success;
    1099             : }
    1100             : 
    1101             : Result
    1102           0 : NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
    1103             :                                                     EndEntityOrCA endEntityOrCA,
    1104             :                                                     Time notBefore)
    1105             : {
    1106             :   // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
    1107           0 :   static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
    1108             : 
    1109           0 :   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    1110             :           ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
    1111           0 :   if (aAlg == DigestAlgorithm::sha1) {
    1112           0 :     switch (mSHA1Mode) {
    1113             :       case CertVerifier::SHA1Mode::Forbidden:
    1114           0 :         MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
    1115           0 :         return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
    1116             :       case CertVerifier::SHA1Mode::ImportedRootOrBefore2016:
    1117           0 :         if (JANUARY_FIRST_2016 <= notBefore) {
    1118           0 :           MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Post-2015 SHA-1 certificate rejected"));
    1119           0 :           return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
    1120             :         }
    1121           0 :         break;
    1122             :       case CertVerifier::SHA1Mode::Allowed:
    1123             :       // Enforcing that the resulting chain uses an imported root is only
    1124             :       // possible at a higher level. This is done in CertVerifier::VerifyCert.
    1125             :       case CertVerifier::SHA1Mode::ImportedRoot:
    1126             :       default:
    1127           0 :         break;
    1128             :       // MSVC warns unless we explicitly handle this now-unused option.
    1129             :       case CertVerifier::SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
    1130           0 :         MOZ_ASSERT_UNREACHABLE("unexpected SHA1Mode type");
    1131             :         return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1132             :     }
    1133             :   }
    1134             : 
    1135           0 :   return Success;
    1136             : }
    1137             : 
    1138             : Result
    1139           0 : NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
    1140             :   EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
    1141             : {
    1142           0 :   if (modulusSizeInBits < mMinRSABits) {
    1143           0 :     return Result::ERROR_INADEQUATE_KEY_SIZE;
    1144             :   }
    1145           0 :   return Success;
    1146             : }
    1147             : 
    1148             : Result
    1149           0 : NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
    1150             :   const SignedDigest& signedDigest,
    1151             :   Input subjectPublicKeyInfo)
    1152             : {
    1153           0 :   return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
    1154           0 :                                        mPinArg);
    1155             : }
    1156             : 
    1157             : Result
    1158           0 : NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
    1159             :   EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve)
    1160             : {
    1161           0 :   switch (curve) {
    1162             :     case NamedCurve::secp256r1: // fall through
    1163             :     case NamedCurve::secp384r1: // fall through
    1164             :     case NamedCurve::secp521r1:
    1165           0 :       return Success;
    1166             :   }
    1167             : 
    1168           0 :   return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
    1169             : }
    1170             : 
    1171             : Result
    1172           0 : NSSCertDBTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
    1173             :                                               Input subjectPublicKeyInfo)
    1174             : {
    1175           0 :   return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
    1176           0 :                                     mPinArg);
    1177             : }
    1178             : 
    1179             : Result
    1180           0 : NSSCertDBTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
    1181             :                                                 EndEntityOrCA endEntityOrCA,
    1182             :                                                 KeyPurposeId keyPurpose)
    1183             : {
    1184           0 :   if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
    1185           0 :     return Success;
    1186             :   }
    1187           0 :   if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
    1188           0 :     return Success;
    1189             :   }
    1190             : 
    1191             :   Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
    1192           0 :                                         Time::ONE_DAY_IN_SECONDS);
    1193           0 :   Duration maxValidityDuration(UINT64_MAX);
    1194           0 :   Duration validityDuration(notBefore, notAfter);
    1195             : 
    1196           0 :   switch (mValidityCheckingMode) {
    1197             :     case ValidityCheckingMode::CheckingOff:
    1198           0 :       return Success;
    1199             :     case ValidityCheckingMode::CheckForEV:
    1200             :       // The EV Guidelines say the maximum is 27 months, but we use a slightly
    1201             :       // higher limit here to (hopefully) minimize compatibility breakage.
    1202           0 :       maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
    1203           0 :       break;
    1204             :     default:
    1205           0 :       MOZ_ASSERT_UNREACHABLE("We're not handling every ValidityCheckingMode type");
    1206             :   }
    1207             : 
    1208           0 :   if (validityDuration > maxValidityDuration) {
    1209           0 :     return Result::ERROR_VALIDITY_TOO_LONG;
    1210             :   }
    1211             : 
    1212           0 :   return Success;
    1213             : }
    1214             : 
    1215             : Result
    1216           0 : NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
    1217             :                                                       /*out*/ bool& matches)
    1218             : {
    1219             :   // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
    1220           0 :   static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
    1221             :   // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
    1222           0 :   static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
    1223             : 
    1224           0 :   switch (mNetscapeStepUpPolicy) {
    1225             :     case NetscapeStepUpPolicy::AlwaysMatch:
    1226           0 :       matches = true;
    1227           0 :       return Success;
    1228             :     case NetscapeStepUpPolicy::MatchBefore23August2016:
    1229           0 :       matches = notBefore < AUGUST_23_2016;
    1230           0 :       return Success;
    1231             :     case NetscapeStepUpPolicy::MatchBefore23August2015:
    1232           0 :       matches = notBefore < AUGUST_23_2015;
    1233           0 :       return Success;
    1234             :     case NetscapeStepUpPolicy::NeverMatch:
    1235           0 :       matches = false;
    1236           0 :       return Success;
    1237             :     default:
    1238           0 :       MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
    1239             :   }
    1240             :   return Result::FATAL_ERROR_LIBRARY_FAILURE;
    1241             : }
    1242             : 
    1243             : void
    1244           0 : NSSCertDBTrustDomain::ResetAccumulatedState()
    1245             : {
    1246           0 :   mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
    1247           0 :   mSCTListFromOCSPStapling = nullptr;
    1248           0 :   mSCTListFromCertificate = nullptr;
    1249           0 : }
    1250             : 
    1251             : static Input
    1252           0 : SECItemToInput(const UniqueSECItem& item)
    1253             : {
    1254           0 :   Input result;
    1255           0 :   if (item) {
    1256           0 :     MOZ_ASSERT(item->type == siBuffer);
    1257           0 :     Result rv = result.Init(item->data, item->len);
    1258             :     // As used here, |item| originally comes from an Input,
    1259             :     // so there should be no issues converting it back.
    1260           0 :     MOZ_ASSERT(rv == Success);
    1261             :     Unused << rv; // suppresses warnings in release builds
    1262             :   }
    1263           0 :   return result;
    1264             : }
    1265             : 
    1266             : Input
    1267           0 : NSSCertDBTrustDomain::GetSCTListFromCertificate() const
    1268             : {
    1269           0 :   return SECItemToInput(mSCTListFromCertificate);
    1270             : }
    1271             : 
    1272             : Input
    1273           0 : NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const
    1274             : {
    1275           0 :   return SECItemToInput(mSCTListFromOCSPStapling);
    1276             : }
    1277             : 
    1278             : void
    1279           0 : NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
    1280             :                                              Input extensionData)
    1281             : {
    1282           0 :   UniqueSECItem* out = nullptr;
    1283           0 :   switch (extension) {
    1284             :     case AuxiliaryExtension::EmbeddedSCTList:
    1285           0 :       out = &mSCTListFromCertificate;
    1286           0 :       break;
    1287             :     case AuxiliaryExtension::SCTListFromOCSPResponse:
    1288           0 :       out = &mSCTListFromOCSPStapling;
    1289           0 :       break;
    1290             :     default:
    1291           0 :       MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
    1292             :   }
    1293           0 :   if (out) {
    1294           0 :     SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
    1295           0 :     out->reset(SECITEM_DupItem(&extensionDataItem));
    1296             :   }
    1297           0 : }
    1298             : 
    1299             : SECStatus
    1300           1 : InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules)
    1301             : {
    1302             :   // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
    1303             :   // module by NSS_Initialize because we will load it in InstallLoadableRoots
    1304             :   // later.  It also allows us to work around a bug in the system NSS in
    1305             :   // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
    1306             :   // "/usr/lib/nss/libnssckbi.so".
    1307           1 :   uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
    1308           1 :   if (readOnly) {
    1309           0 :     flags |= NSS_INIT_READONLY;
    1310             :   }
    1311           1 :   if (!loadPKCS11Modules) {
    1312           0 :     flags |= NSS_INIT_NOMODDB;
    1313             :   }
    1314           1 :   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    1315             :           ("InitializeNSS(%s, %d, %d)", dir, readOnly, loadPKCS11Modules));
    1316           1 :   return ::NSS_Initialize(dir, "", "", SECMOD_DB, flags);
    1317             : }
    1318             : 
    1319             : void
    1320           2 : DisableMD5()
    1321             : {
    1322             :   NSS_SetAlgorithmPolicy(SEC_OID_MD5,
    1323           2 :     0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
    1324             :   NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
    1325           2 :     0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
    1326             :   NSS_SetAlgorithmPolicy(SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC,
    1327           2 :     0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
    1328           2 : }
    1329             : 
    1330             : bool
    1331           1 : LoadLoadableRoots(const nsCString& dir, const nsCString& modNameUTF8)
    1332             : {
    1333             :   UniquePRLibraryName fullLibraryPath(
    1334           2 :     PR_GetLibraryName(dir.IsEmpty() ? nullptr : dir.get(), "nssckbi"));
    1335           1 :   if (!fullLibraryPath) {
    1336           0 :     return false;
    1337             :   }
    1338             : 
    1339             :   // Escape the \ and " characters.
    1340           2 :   nsAutoCString escapedFullLibraryPath(fullLibraryPath.get());
    1341           1 :   escapedFullLibraryPath.ReplaceSubstring("\\", "\\\\");
    1342           1 :   escapedFullLibraryPath.ReplaceSubstring("\"", "\\\"");
    1343           1 :   if (escapedFullLibraryPath.IsEmpty()) {
    1344           0 :     return false;
    1345             :   }
    1346             : 
    1347             :   // If a module exists with the same name, make a best effort attempt to delete
    1348             :   // it. Note that it isn't possible to delete the internal module, so checking
    1349             :   // the return value would be detrimental in that case.
    1350             :   int unusedModType;
    1351           1 :   Unused << SECMOD_DeleteModule(modNameUTF8.get(), &unusedModType);
    1352             : 
    1353           2 :   nsAutoCString pkcs11ModuleSpec;
    1354           1 :   pkcs11ModuleSpec.AppendPrintf("name=\"%s\" library=\"%s\"", modNameUTF8.get(),
    1355           1 :                                 escapedFullLibraryPath.get());
    1356           1 :   if (pkcs11ModuleSpec.IsEmpty()) {
    1357           0 :     return false;
    1358             :   }
    1359             : 
    1360             :   UniqueSECMODModule rootsModule(
    1361           1 :     SECMOD_LoadUserModule(const_cast<char*>(pkcs11ModuleSpec.get()), nullptr,
    1362           2 :                           false));
    1363           1 :   if (!rootsModule) {
    1364           0 :     return false;
    1365             :   }
    1366             : 
    1367           1 :   if (!rootsModule->loaded) {
    1368           0 :     return false;
    1369             :   }
    1370             : 
    1371           1 :   return true;
    1372             : }
    1373             : 
    1374             : void
    1375           0 : UnloadLoadableRoots(const char* modNameUTF8)
    1376             : {
    1377           0 :   MOZ_ASSERT(modNameUTF8);
    1378           0 :   UniqueSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
    1379             : 
    1380           0 :   if (rootsModule) {
    1381           0 :     SECMOD_UnloadUserModule(rootsModule.get());
    1382             :   }
    1383           0 : }
    1384             : 
    1385             : nsresult
    1386           0 : DefaultServerNicknameForCert(const CERTCertificate* cert,
    1387             :                      /*out*/ nsCString& nickname)
    1388             : {
    1389           0 :   MOZ_ASSERT(cert);
    1390           0 :   NS_ENSURE_ARG_POINTER(cert);
    1391             : 
    1392           0 :   UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
    1393           0 :   if (!baseName) {
    1394           0 :     baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
    1395             :   }
    1396           0 :   if (!baseName) {
    1397           0 :     baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
    1398             :   }
    1399           0 :   if (!baseName) {
    1400           0 :     baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
    1401             :   }
    1402           0 :   if (!baseName) {
    1403           0 :     baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
    1404             :   }
    1405           0 :   if (!baseName) {
    1406           0 :     baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
    1407             :   }
    1408           0 :   if (!baseName) {
    1409           0 :     return NS_ERROR_FAILURE;
    1410             :   }
    1411             : 
    1412             :   // This function is only used in contexts where a failure to find a suitable
    1413             :   // nickname does not block the overall task from succeeding.
    1414             :   // As such, we use an arbitrary limit to prevent this nickname searching
    1415             :   // process from taking forever.
    1416             :   static const uint32_t ARBITRARY_LIMIT = 500;
    1417           0 :   for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
    1418           0 :     nickname = baseName.get();
    1419           0 :     if (count != 1) {
    1420           0 :       nickname.AppendPrintf(" #%u", count);
    1421             :     }
    1422           0 :     if (nickname.IsEmpty()) {
    1423           0 :       return NS_ERROR_FAILURE;
    1424             :     }
    1425             : 
    1426           0 :     bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
    1427           0 :                                              cert->dbhandle);
    1428           0 :     if (!conflict) {
    1429           0 :       return NS_OK;
    1430             :     }
    1431             :   }
    1432             : 
    1433           0 :   return NS_ERROR_FAILURE;
    1434             : }
    1435             : 
    1436             : /**
    1437             :  * Given a list of certificates representing a verified certificate path from an
    1438             :  * end-entity certificate to a trust anchor, imports the intermediate
    1439             :  * certificates into the permanent certificate database. This is an attempt to
    1440             :  * cope with misconfigured servers that don't include the appropriate
    1441             :  * intermediate certificates in the TLS handshake.
    1442             :  *
    1443             :  * @param certList the verified certificate list
    1444             :  */
    1445             : void
    1446           0 : SaveIntermediateCerts(const UniqueCERTCertList& certList)
    1447             : {
    1448           0 :   if (!certList) {
    1449           0 :     return;
    1450             :   }
    1451             : 
    1452           0 :   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    1453           0 :   if (!slot) {
    1454           0 :     return;
    1455             :   }
    1456             : 
    1457           0 :   bool isEndEntity = true;
    1458           0 :   for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
    1459           0 :         !CERT_LIST_END(node, certList);
    1460           0 :         node = CERT_LIST_NEXT(node)) {
    1461           0 :     if (isEndEntity) {
    1462             :       // Skip the end-entity; we only want to store intermediates
    1463           0 :       isEndEntity = false;
    1464           0 :       continue;
    1465             :     }
    1466             : 
    1467           0 :     if (node->cert->slot) {
    1468             :       // This cert was found on a token; no need to remember it in the permanent
    1469             :       // database.
    1470           0 :       continue;
    1471             :     }
    1472             : 
    1473           0 :     if (node->cert->isperm) {
    1474             :       // We don't need to remember certs already stored in perm db.
    1475           0 :       continue;
    1476             :     }
    1477             : 
    1478             :     // No need to save the trust anchor - it's either already a permanent
    1479             :     // certificate or it's the Microsoft Family Safety root or an enterprise
    1480             :     // root temporarily imported via the child mode or enterprise root features.
    1481             :     // We don't want to import these because they're intended to be temporary
    1482             :     // (and because importing them happens to reset their trust settings, which
    1483             :     // breaks these features).
    1484           0 :     if (node == CERT_LIST_TAIL(certList)) {
    1485           0 :       continue;
    1486             :     }
    1487             : 
    1488           0 :     nsAutoCString nickname;
    1489           0 :     nsresult rv = DefaultServerNicknameForCert(node->cert, nickname);
    1490           0 :     if (NS_FAILED(rv)) {
    1491           0 :       continue;
    1492             :     }
    1493             : 
    1494             :     // As mentioned in the documentation of this function, we're importing only
    1495             :     // to cope with misconfigured servers. As such, we ignore the return value
    1496             :     // below, since it doesn't really matter if the import fails.
    1497           0 :     Unused << PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
    1498             :                               nickname.get(), false);
    1499             :   }
    1500             : }
    1501             : 
    1502             : } } // namespace mozilla::psm

Generated by: LCOV version 1.13