tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NSSCertDBTrustDomain.cpp (68726B)


      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 #include <utility>
     11 
     12 #include "CRLiteTimestamp.h"
     13 #include "ExtendedValidation.h"
     14 #include "MultiLogCTVerifier.h"
     15 #include "NSSErrorsService.h"
     16 #include "PKCS11ModuleDB.h"
     17 #include "PublicKeyPinningService.h"
     18 #include "cert.h"
     19 #include "cert_storage/src/cert_storage.h"
     20 #include "certdb.h"
     21 #include "mozilla/AppShutdown.h"
     22 #include "mozilla/Assertions.h"
     23 #include "mozilla/ClearOnShutdown.h"
     24 #include "mozilla/Logging.h"
     25 #include "mozilla/Services.h"
     26 #include "mozilla/StaticPrefs_security.h"
     27 #include "mozilla/SyncRunnable.h"
     28 #include "mozilla/TimeStamp.h"
     29 #include "mozilla/glean/SecurityCertverifierMetrics.h"
     30 #include "mozpkix/Result.h"
     31 #include "mozpkix/pkix.h"
     32 #include "mozpkix/pkixcheck.h"
     33 #include "mozpkix/pkixnss.h"
     34 #include "mozpkix/pkixutil.h"
     35 #include "nsCRTGlue.h"
     36 #include "nsIObserverService.h"
     37 #include "nsNSSCallbacks.h"
     38 #include "nsNSSCertificate.h"
     39 #include "nsNSSCertificateDB.h"
     40 #include "nsNSSComponent.h"
     41 #include "nsNSSIOLayer.h"
     42 #include "nsNetCID.h"
     43 #include "nsPrintfCString.h"
     44 #include "nsServiceManagerUtils.h"
     45 #include "nsThreadUtils.h"
     46 #include "nss.h"
     47 #include "pk11pub.h"
     48 #include "prerror.h"
     49 #include "secder.h"
     50 #include "secerr.h"
     51 
     52 using namespace mozilla;
     53 using namespace mozilla::ct;
     54 using namespace mozilla::pkix;
     55 
     56 extern LazyLogModule gCertVerifierLog;
     57 
     58 static const uint64_t ServerFailureDelaySeconds = 5 * 60;
     59 
     60 namespace mozilla {
     61 namespace psm {
     62 
     63 NSSCertDBTrustDomain::NSSCertDBTrustDomain(
     64    SECTrustType certDBTrustType, RevocationCheckMode ocspFetching,
     65    OCSPCache& ocspCache, SignatureCache* signatureCache,
     66    TrustCache* trustCache, /*optional but shouldn't be*/ void* pinArg,
     67    TimeDuration ocspTimeoutSoft, TimeDuration ocspTimeoutHard,
     68    uint32_t certShortLifetimeInDays, unsigned int minRSABits,
     69    CRLiteMode crliteMode, const OriginAttributes& originAttributes,
     70    const nsTArray<Input>& thirdPartyRootInputs,
     71    const nsTArray<Input>& thirdPartyIntermediateInputs,
     72    const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
     73    const mozilla::pkix::Input& encodedSCTsFromTLS,
     74    const UniquePtr<mozilla::ct::MultiLogCTVerifier>& ctVerifier,
     75    /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
     76    /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
     77    /*optional*/ const char* hostname)
     78    : mCertDBTrustType(certDBTrustType),
     79      mOCSPFetching(ocspFetching),
     80      mOCSPCache(ocspCache),
     81      mSignatureCache(signatureCache),
     82      mTrustCache(trustCache),
     83      mPinArg(pinArg),
     84      mOCSPTimeoutSoft(ocspTimeoutSoft),
     85      mOCSPTimeoutHard(ocspTimeoutHard),
     86      mCertShortLifetimeInDays(certShortLifetimeInDays),
     87      mMinRSABits(minRSABits),
     88      mCRLiteMode(crliteMode),
     89      mOriginAttributes(originAttributes),
     90      mThirdPartyRootInputs(thirdPartyRootInputs),
     91      mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs),
     92      mExtraCertificates(extraCertificates),
     93      mEncodedSCTsFromTLS(encodedSCTsFromTLS),
     94      mCTVerifier(ctVerifier),
     95      mBuiltChain(builtChain),
     96      mIsBuiltChainRootBuiltInRoot(false),
     97      mPinningTelemetryInfo(pinningTelemetryInfo),
     98      mHostname(hostname),
     99      mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
    100      mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED),
    101      mBuiltInRootsModule(SECMOD_FindModule(kRootModuleName.get())),
    102      mOCSPFetchStatus(OCSPFetchStatus::NotFetched) {}
    103 
    104 static void FindRootsWithSubject(UniqueSECMODModule& rootsModule,
    105                                 SECItem subject,
    106                                 /*out*/ nsTArray<nsTArray<uint8_t>>& roots) {
    107  MOZ_ASSERT(rootsModule);
    108  AutoSECMODListReadLock lock;
    109  for (int slotIndex = 0; slotIndex < rootsModule->slotCount; slotIndex++) {
    110    CERTCertificateList* rawResults = nullptr;
    111    if (PK11_FindRawCertsWithSubject(rootsModule->slots[slotIndex], &subject,
    112                                     &rawResults) != SECSuccess) {
    113      continue;
    114    }
    115    // rawResults == nullptr means we didn't find any matching certificates
    116    if (!rawResults) {
    117      continue;
    118    }
    119    UniqueCERTCertificateList results(rawResults);
    120    for (int certIndex = 0; certIndex < results->len; certIndex++) {
    121      nsTArray<uint8_t> root;
    122      root.AppendElements(results->certs[certIndex].data,
    123                          results->certs[certIndex].len);
    124      roots.AppendElement(std::move(root));
    125    }
    126  }
    127 }
    128 
    129 // A self-signed issuer certificate should never be necessary in order to build
    130 // a trusted certificate chain unless it is a trust anchor. This is because if
    131 // it were necessary, there would exist another certificate with the same
    132 // subject and public key that is also a valid issing certificate. Given this
    133 // certificate, it is possible to build another chain using just it instead of
    134 // it and the self-signed certificate. This is only true as long as the
    135 // certificate extensions we support are restrictive rather than additive in
    136 // terms of the rest of the chain (for example, we don't support policy mapping
    137 // and we ignore any SCT information in intermediates).
    138 bool NSSCertDBTrustDomain::ShouldSkipSelfSignedNonTrustAnchor(Input certDER) {
    139  BackCert cert(certDER, EndEntityOrCA::MustBeCA, nullptr);
    140  if (cert.Init() != Success) {
    141    return false;  // turn any failures into "don't skip trying this cert"
    142  }
    143  // If subject != issuer, this isn't a self-signed cert.
    144  if (!InputsAreEqual(cert.GetSubject(), cert.GetIssuer())) {
    145    return false;
    146  }
    147  TrustLevel trust;
    148  if (GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy, certDER,
    149                   trust) != Success) {
    150    return false;
    151  }
    152  // If the trust for this certificate is anything other than "inherit", we want
    153  // to process it like normal.
    154  if (trust != TrustLevel::InheritsTrust) {
    155    return false;
    156  }
    157  if (VerifySignedData(*this, cert.GetSignedData(),
    158                       cert.GetSubjectPublicKeyInfo()) != Success) {
    159    return false;
    160  }
    161  // This is a self-signed, non-trust-anchor certificate, so we shouldn't use it
    162  // for path building. See bug 1056341.
    163  return true;
    164 }
    165 
    166 Result NSSCertDBTrustDomain::CheckCandidates(
    167    IssuerChecker& checker, nsTArray<IssuerCandidateWithSource>& candidates,
    168    Input* nameConstraintsInputPtr, bool& keepGoing) {
    169  for (const auto& candidate : candidates) {
    170    // Stop path building if the program is shutting down.
    171    if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    172      keepGoing = false;
    173      return Success;
    174    }
    175    if (ShouldSkipSelfSignedNonTrustAnchor(candidate.mDER)) {
    176      continue;
    177    }
    178    Result rv =
    179        checker.Check(candidate.mDER, nameConstraintsInputPtr, keepGoing);
    180    if (rv != Success) {
    181      return rv;
    182    }
    183    if (!keepGoing) {
    184      mIssuerSources += candidate.mIssuerSource;
    185      return Success;
    186    }
    187 
    188    ResetCandidateBuiltChainState();
    189  }
    190 
    191  return Success;
    192 }
    193 
    194 Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
    195                                        IssuerChecker& checker, Time) {
    196  SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
    197  // Handle imposed name constraints, if any.
    198  ScopedAutoSECItem nameConstraints;
    199  Input nameConstraintsInput;
    200  Input* nameConstraintsInputPtr = nullptr;
    201  SECStatus srv =
    202      CERT_GetImposedNameConstraints(&encodedIssuerNameItem, &nameConstraints);
    203  if (srv == SECSuccess) {
    204    if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len) !=
    205        Success) {
    206      return Result::FATAL_ERROR_LIBRARY_FAILURE;
    207    }
    208    nameConstraintsInputPtr = &nameConstraintsInput;
    209  } else if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
    210    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    211  }
    212 
    213  // First try all relevant certificates known to Gecko, which avoids calling
    214  // CERT_CreateSubjectCertList, because that can be expensive.
    215  nsTArray<IssuerCandidateWithSource> geckoRootCandidates;
    216  nsTArray<IssuerCandidateWithSource> geckoIntermediateCandidates;
    217 
    218  // We might not have this module if e.g. we're on a Linux distribution that
    219  // does something unexpected.
    220  nsTArray<nsTArray<uint8_t>> builtInRoots;
    221  if (mBuiltInRootsModule) {
    222    FindRootsWithSubject(mBuiltInRootsModule, encodedIssuerNameItem,
    223                         builtInRoots);
    224    for (const auto& root : builtInRoots) {
    225      Input rootInput;
    226      Result rv = rootInput.Init(root.Elements(), root.Length());
    227      if (rv != Success) {
    228        continue;  // probably too big
    229      }
    230      geckoRootCandidates.AppendElement(IssuerCandidateWithSource{
    231          rootInput, IssuerSource::BuiltInRootsModule});
    232    }
    233  } else {
    234    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    235            ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module"));
    236  }
    237 
    238  if (mExtraCertificates.isSome()) {
    239    for (const auto& extraCert : *mExtraCertificates) {
    240      Input certInput;
    241      Result rv = certInput.Init(extraCert.Elements(), extraCert.Length());
    242      if (rv != Success) {
    243        continue;
    244      }
    245      BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
    246      rv = cert.Init();
    247      if (rv != Success) {
    248        continue;
    249      }
    250      // Filter out certificates that can't be issuers we're looking for because
    251      // the subject distinguished name doesn't match. This prevents
    252      // mozilla::pkix from accumulating spurious errors during path building.
    253      if (!InputsAreEqual(encodedIssuerName, cert.GetSubject())) {
    254        continue;
    255      }
    256      // We assume that extra certificates (presumably from the TLS handshake)
    257      // are intermediates, since sending trust anchors would be superfluous.
    258      geckoIntermediateCandidates.AppendElement(
    259          IssuerCandidateWithSource{certInput, IssuerSource::TLSHandshake});
    260    }
    261  }
    262 
    263  for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
    264    BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr);
    265    Result rv = root.Init();
    266    if (rv != Success) {
    267      continue;
    268    }
    269    // Filter out 3rd party roots that can't be issuers we're looking for
    270    // because the subject distinguished name doesn't match. This prevents
    271    // mozilla::pkix from accumulating spurious errors during path building.
    272    if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) {
    273      continue;
    274    }
    275    geckoRootCandidates.AppendElement(IssuerCandidateWithSource{
    276        thirdPartyRootInput, IssuerSource::ThirdPartyCertificates});
    277  }
    278 
    279  for (const auto& thirdPartyIntermediateInput :
    280       mThirdPartyIntermediateInputs) {
    281    BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA,
    282                          nullptr);
    283    Result rv = intermediate.Init();
    284    if (rv != Success) {
    285      continue;
    286    }
    287    // Filter out 3rd party intermediates that can't be issuers we're looking
    288    // for because the subject distinguished name doesn't match. This prevents
    289    // mozilla::pkix from accumulating spurious errors during path building.
    290    if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) {
    291      continue;
    292    }
    293    geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{
    294        thirdPartyIntermediateInput, IssuerSource::ThirdPartyCertificates});
    295  }
    296 
    297  if (!mCertStorage) {
    298    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    299  }
    300  nsTArray<uint8_t> subject;
    301  subject.AppendElements(encodedIssuerName.UnsafeGetData(),
    302                         encodedIssuerName.GetLength());
    303  nsTArray<nsTArray<uint8_t>> certs;
    304  nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
    305  if (NS_FAILED(rv)) {
    306    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    307  }
    308  for (auto& cert : certs) {
    309    Input certDER;
    310    Result rv = certDER.Init(cert.Elements(), cert.Length());
    311    if (rv != Success) {
    312      continue;  // probably too big
    313    }
    314    // Currently we're only expecting intermediate certificates in cert storage.
    315    geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{
    316        std::move(certDER), IssuerSource::PreloadedIntermediates});
    317  }
    318 
    319  // Try all root certs first and then all (presumably) intermediates.
    320  geckoRootCandidates.AppendElements(std::move(geckoIntermediateCandidates));
    321 
    322  bool keepGoing = true;
    323  Result result = CheckCandidates(checker, geckoRootCandidates,
    324                                  nameConstraintsInputPtr, keepGoing);
    325  if (result != Success) {
    326    return result;
    327  }
    328  if (!keepGoing) {
    329    return Success;
    330  }
    331 
    332  // Synchronously dispatch a task to the socket thread to find
    333  // CERTCertificates with the given subject. This involves querying NSS
    334  // structures and databases, so it should be done on the socket thread.
    335  nsTArray<nsTArray<uint8_t>> nssRootCandidates;
    336  nsTArray<nsTArray<uint8_t>> nssIntermediateCandidates;
    337  RefPtr<Runnable> getCandidatesTask =
    338      NS_NewRunnableFunction("NSSCertDBTrustDomain::FindIssuer", [&]() {
    339        if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    340          return;
    341        }
    342        // NSS seems not to differentiate between "no potential issuers found"
    343        // and "there was an error trying to retrieve the potential issuers." We
    344        // assume there was no error if CERT_CreateSubjectCertList returns
    345        // nullptr.
    346        UniqueCERTCertList candidates(
    347            CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
    348                                       &encodedIssuerNameItem, 0, false));
    349        if (candidates) {
    350          for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
    351               !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
    352            nsTArray<uint8_t> candidate;
    353            candidate.AppendElements(n->cert->derCert.data,
    354                                     n->cert->derCert.len);
    355            if (n->cert->isRoot) {
    356              nssRootCandidates.AppendElement(std::move(candidate));
    357            } else {
    358              nssIntermediateCandidates.AppendElement(std::move(candidate));
    359            }
    360          }
    361        }
    362      });
    363  nsCOMPtr<nsIEventTarget> socketThread(
    364      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
    365  if (!socketThread) {
    366    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    367  }
    368  rv = SyncRunnable::DispatchToThread(socketThread, getCandidatesTask);
    369  if (NS_FAILED(rv)) {
    370    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    371  }
    372 
    373  nsTArray<IssuerCandidateWithSource> nssCandidates;
    374  for (const auto& rootCandidate : nssRootCandidates) {
    375    Input certDER;
    376    Result rv = certDER.Init(rootCandidate.Elements(), rootCandidate.Length());
    377    if (rv != Success) {
    378      continue;  // probably too big
    379    }
    380    nssCandidates.AppendElement(
    381        IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB});
    382  }
    383  for (const auto& intermediateCandidate : nssIntermediateCandidates) {
    384    Input certDER;
    385    Result rv = certDER.Init(intermediateCandidate.Elements(),
    386                             intermediateCandidate.Length());
    387    if (rv != Success) {
    388      continue;  // probably too big
    389    }
    390    nssCandidates.AppendElement(
    391        IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB});
    392  }
    393 
    394  return CheckCandidates(checker, nssCandidates, nameConstraintsInputPtr,
    395                         keepGoing);
    396 }
    397 
    398 void HashTrustParams(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy,
    399                     Input certDER, SECTrustType trustType,
    400                     /*out*/ Maybe<nsTArray<uint8_t>>& sha512Hash) {
    401  sha512Hash.reset();
    402  Digest digest;
    403  if (NS_FAILED(digest.Begin(SEC_OID_SHA512))) {
    404    return;
    405  }
    406  if (NS_FAILED(digest.Update(reinterpret_cast<const uint8_t*>(&endEntityOrCA),
    407                              sizeof(endEntityOrCA)))) {
    408    return;
    409  }
    410  if (NS_FAILED(
    411          digest.Update(reinterpret_cast<const uint8_t*>(&policy.numBytes),
    412                        sizeof(policy.numBytes)))) {
    413    return;
    414  }
    415  if (NS_FAILED(digest.Update(policy.bytes, policy.numBytes))) {
    416    return;
    417  }
    418  if (NS_FAILED(digest.Update(certDER.UnsafeGetData(), certDER.GetLength()))) {
    419    return;
    420  }
    421  if (NS_FAILED(digest.Update(reinterpret_cast<const uint8_t*>(&trustType),
    422                              sizeof(trustType)))) {
    423    return;
    424  }
    425  nsTArray<uint8_t> result;
    426  if (NS_FAILED(digest.End(result))) {
    427    return;
    428  }
    429  sha512Hash.emplace(std::move(result));
    430 }
    431 
    432 Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
    433                                          const CertPolicyId& policy,
    434                                          Input candidateCertDER,
    435                                          /*out*/ TrustLevel& trustLevel) {
    436  // Check the certificate against the OneCRL cert blocklist
    437  if (!mCertStorage) {
    438    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    439  }
    440 
    441  // The certificate blocklist currently only applies to TLS server
    442  // certificates.
    443  if (mCertDBTrustType == trustSSL) {
    444    int16_t revocationState;
    445 
    446    nsTArray<uint8_t> issuerBytes;
    447    nsTArray<uint8_t> serialBytes;
    448    nsTArray<uint8_t> subjectBytes;
    449    nsTArray<uint8_t> pubKeyBytes;
    450 
    451    Result result =
    452        BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes,
    453                                   serialBytes, subjectBytes, pubKeyBytes);
    454    if (result != Success) {
    455      return result;
    456    }
    457 
    458    nsresult nsrv = mCertStorage->GetRevocationState(
    459        issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
    460    if (NS_FAILED(nsrv)) {
    461      return Result::FATAL_ERROR_LIBRARY_FAILURE;
    462    }
    463 
    464    if (revocationState == nsICertStorage::STATE_ENFORCE) {
    465      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    466              ("NSSCertDBTrustDomain: certificate is in blocklist"));
    467      mozilla::glean::cert_verifier::cert_revocation_mechanisms.Get("OneCRL"_ns)
    468          .Add(1);
    469      return Result::ERROR_REVOKED_CERTIFICATE;
    470    }
    471  }
    472 
    473  // This may be a third-party root.
    474  for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
    475    if (InputsAreEqual(candidateCertDER, thirdPartyRootInput)) {
    476      trustLevel = TrustLevel::TrustAnchor;
    477      return Success;
    478    }
    479  }
    480 
    481  // This may be a third-party intermediate.
    482  for (const auto& thirdPartyIntermediateInput :
    483       mThirdPartyIntermediateInputs) {
    484    if (InputsAreEqual(candidateCertDER, thirdPartyIntermediateInput)) {
    485      trustLevel = TrustLevel::InheritsTrust;
    486      return Success;
    487    }
    488  }
    489 
    490  mozilla::glean::cert_trust_cache::total.Add(1);
    491  Maybe<nsTArray<uint8_t>> sha512Hash;
    492  HashTrustParams(endEntityOrCA, policy, candidateCertDER, mCertDBTrustType,
    493                  sha512Hash);
    494  uint8_t cachedTrust = 0;
    495  if (sha512Hash.isSome() &&
    496      trust_cache_get(mTrustCache, sha512Hash.ref().Elements(), &cachedTrust)) {
    497    mozilla::glean::cert_trust_cache::hits.AddToNumerator(1);
    498    trustLevel = static_cast<TrustLevel>(cachedTrust);
    499    return Success;
    500  }
    501 
    502  // Synchronously dispatch a task to the socket thread to construct a
    503  // CERTCertificate and get its trust from NSS. This involves querying NSS
    504  // structures and databases, so it should be done on the socket thread.
    505  Result result = Result::FATAL_ERROR_LIBRARY_FAILURE;
    506  RefPtr<Runnable> getTrustTask =
    507      NS_NewRunnableFunction("NSSCertDBTrustDomain::GetCertTrust", [&]() {
    508        if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    509          result = Result::FATAL_ERROR_LIBRARY_FAILURE;
    510          return;
    511        }
    512        // This would be cleaner and more efficient if we could get the trust
    513        // information without constructing a CERTCertificate here, but NSS
    514        // doesn't expose it in any other easy-to-use fashion. The use of
    515        // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
    516        // performance problem for certificates already known to NSS because NSS
    517        // will just find the existing CERTCertificate in its in-memory cache
    518        // and return it. For certificates not already in NSS (namely
    519        // third-party roots and intermediates), we want to avoid calling
    520        // CERT_NewTempCertificate repeatedly, so we've already checked if the
    521        // candidate certificate is a third-party certificate, above.
    522        SECItem candidateCertDERSECItem =
    523            UnsafeMapInputToSECItem(candidateCertDER);
    524 
    525        UniqueCERTCertificate candidateCert(CERT_NewTempCertificate(
    526            CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false,
    527            true));
    528        if (!candidateCert) {
    529          result = MapPRErrorCodeToResult(PR_GetError());
    530          return;
    531        }
    532        // NB: CERT_GetCertTrust seems to be abusing SECStatus as a boolean,
    533        // where SECSuccess means that there is a trust record and SECFailure
    534        // means there is not a trust record. I looked at NSS's internal uses of
    535        // CERT_GetCertTrust, and all that code uses the result as a boolean
    536        // meaning "We have a trust record."
    537 
    538        CERTCertTrust trust;
    539        if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
    540          uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
    541 
    542          // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
    543          // because we can have active distrust for either type of cert. Note
    544          // that CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so
    545          // if the relevant trust bit isn't set then that means the cert must
    546          // be considered distrusted.
    547          uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
    548                                          ? CERTDB_TRUSTED_CA
    549                                          : CERTDB_TRUSTED;
    550          if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) ==
    551              CERTDB_TERMINAL_RECORD) {
    552            trustLevel = TrustLevel::ActivelyDistrusted;
    553            result = Success;
    554            return;
    555          }
    556 
    557          // For TRUST, we use the CERTDB_TRUSTED_CA bit.
    558          if (flags & CERTDB_TRUSTED_CA) {
    559            if (policy.IsAnyPolicy()) {
    560              trustLevel = TrustLevel::TrustAnchor;
    561              result = Success;
    562              return;
    563            }
    564 
    565            nsTArray<uint8_t> certBytes(candidateCert->derCert.data,
    566                                        candidateCert->derCert.len);
    567            if (CertIsAuthoritativeForEVPolicy(certBytes, policy)) {
    568              trustLevel = TrustLevel::TrustAnchor;
    569              result = Success;
    570              return;
    571            }
    572          }
    573        }
    574        trustLevel = TrustLevel::InheritsTrust;
    575        result = Success;
    576      });
    577  nsCOMPtr<nsIEventTarget> socketThread(
    578      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
    579  if (!socketThread) {
    580    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    581  }
    582  nsresult rv = SyncRunnable::DispatchToThread(socketThread, getTrustTask);
    583  if (NS_FAILED(rv)) {
    584    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    585  }
    586  if (result == Success && sha512Hash.isSome()) {
    587    uint8_t trust = static_cast<uint8_t>(trustLevel);
    588    trust_cache_insert(mTrustCache, sha512Hash.ref().Elements(), trust);
    589  }
    590  return result;
    591 }
    592 
    593 Result NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
    594                                       /*out*/ uint8_t* digestBuf,
    595                                       size_t digestBufLen) {
    596  return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
    597 }
    598 
    599 TimeDuration NSSCertDBTrustDomain::GetOCSPTimeout() const {
    600  switch (mOCSPFetching) {
    601    case NSSCertDBTrustDomain::RevocationCheckMayFetch:
    602      return mOCSPTimeoutSoft;
    603    case NSSCertDBTrustDomain::RevocationCheckRequired:
    604      return mOCSPTimeoutHard;
    605    // Reaching this case is an error. Assert in debug builds, but return the
    606    // soft timeout value in release builds.
    607    case NSSCertDBTrustDomain::RevocationCheckLocalOnly:
    608      MOZ_ASSERT_UNREACHABLE(
    609          "we should never see this RevocationCheckMode type here");
    610      break;
    611  }
    612 
    613  MOZ_ASSERT_UNREACHABLE("we're not handling every RevocationCheckMode type");
    614  return mOCSPTimeoutSoft;
    615 }
    616 
    617 // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
    618 // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
    619 // Success with result.IsVoid() == true when an OCSP URI was not found, and
    620 // Success with result.IsVoid() == false when an OCSP URI was found.
    621 static Result GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
    622                                                 Input aiaExtension,
    623                                                 /*out*/ nsCString& result) {
    624  MOZ_ASSERT(arena.get());
    625  if (!arena.get()) {
    626    return Result::FATAL_ERROR_INVALID_ARGS;
    627  }
    628 
    629  result.Assign(VoidCString());
    630  SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
    631  CERTAuthInfoAccess** aia =
    632      CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
    633  if (!aia) {
    634    return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
    635  }
    636  for (size_t i = 0; aia[i]; ++i) {
    637    if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
    638      // NSS chooses the **last** OCSP URL; we choose the **first**
    639      CERTGeneralName* current = aia[i]->location;
    640      if (!current) {
    641        continue;
    642      }
    643      do {
    644        if (current->type == certURI) {
    645          const SECItem& location = current->name.other;
    646          // (location.len + 1) must be small enough to fit into a uint32_t,
    647          // but we limit it to a smaller bound to reduce OOM risk.
    648          if (location.len > 1024 || memchr(location.data, 0, location.len)) {
    649            // Reject embedded nulls. (NSS doesn't do this)
    650            return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
    651          }
    652          result.Assign(nsDependentCSubstring(
    653              reinterpret_cast<const char*>(location.data), location.len));
    654          return Success;
    655        }
    656        current = CERT_GetNextGeneralName(current);
    657      } while (current != aia[i]->location);
    658    }
    659  }
    660 
    661  return Success;
    662 }
    663 
    664 NS_IMPL_ISUPPORTS(CRLiteTimestamp, nsICRLiteTimestamp)
    665 
    666 NS_IMETHODIMP
    667 CRLiteTimestamp::GetLogID(nsTArray<uint8_t>& aLogID) {
    668  aLogID.Clear();
    669  aLogID.AppendElements(mLogID);
    670  return NS_OK;
    671 }
    672 
    673 NS_IMETHODIMP
    674 CRLiteTimestamp::GetTimestamp(uint64_t* aTimestamp) {
    675  *aTimestamp = mTimestamp;
    676  return NS_OK;
    677 }
    678 
    679 Result NSSCertDBTrustDomain::CheckCRLite(
    680    const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
    681    const nsTArray<uint8_t>& serialNumberBytes,
    682    const nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps,
    683    /*out*/ bool& filterCoversCertificate) {
    684  filterCoversCertificate = false;
    685  int16_t crliteRevocationState;
    686  nsresult rv = mCertStorage->GetCRLiteRevocationState(
    687      issuerSubjectPublicKeyInfoBytes, serialNumberBytes, timestamps,
    688      &crliteRevocationState);
    689  if (NS_FAILED(rv)) {
    690    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    691            ("NSSCertDBTrustDomain::CheckCRLite: CRLite call failed"));
    692    return Result::FATAL_ERROR_LIBRARY_FAILURE;
    693  }
    694  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    695          ("NSSCertDBTrustDomain::CheckCRLite: CRLite check returned "
    696           "state=%hd",
    697           crliteRevocationState));
    698 
    699  switch (crliteRevocationState) {
    700    case nsICertStorage::STATE_ENFORCE:
    701      filterCoversCertificate = true;
    702      mozilla::glean::cert_verifier::crlite_status.Get("revoked_in_filter"_ns)
    703          .Add(1);
    704      return Result::ERROR_REVOKED_CERTIFICATE;
    705    case nsICertStorage::STATE_UNSET:
    706      filterCoversCertificate = true;
    707      mozilla::glean::cert_verifier::crlite_status.Get("not_revoked"_ns).Add(1);
    708      return Success;
    709    case nsICertStorage::STATE_NOT_ENROLLED:
    710      filterCoversCertificate = false;
    711      mozilla::glean::cert_verifier::crlite_status.Get("not_enrolled"_ns)
    712          .Add(1);
    713      return Success;
    714    case nsICertStorage::STATE_NOT_COVERED:
    715      filterCoversCertificate = false;
    716      mozilla::glean::cert_verifier::crlite_status.Get("not_covered"_ns).Add(1);
    717      return Success;
    718    case nsICertStorage::STATE_NO_FILTER:
    719      filterCoversCertificate = false;
    720      mozilla::glean::cert_verifier::crlite_status.Get("no_filter"_ns).Add(1);
    721      return Success;
    722    default:
    723      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    724              ("NSSCertDBTrustDomain::CheckCRLite: Unknown CRLite revocation "
    725               "state"));
    726      return Result::FATAL_ERROR_LIBRARY_FAILURE;
    727  }
    728 }
    729 
    730 Result NSSCertDBTrustDomain::CheckRevocation(
    731    EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
    732    Duration validityDuration,
    733    /*optional*/ const Input* stapledOCSPResponse,
    734    /*optional*/ const Input* aiaExtension) {
    735  // Actively distrusted certificates will have already been blocked by
    736  // GetCertTrust.
    737 
    738  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    739          ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
    740 
    741  // None of the revocation methods in this function are consulted for CA
    742  // certificates. Revocation for CAs is handled by GetCertTrust.
    743  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
    744    return Success;
    745  }
    746 
    747  bool crliteCoversCertificate = false;
    748  Result crliteResult = Success;
    749  if (mCRLiteMode != CRLiteMode::Disabled) {
    750    crliteResult =
    751        CheckRevocationByCRLite(certID, time, crliteCoversCertificate);
    752 
    753    // If CheckCRLite returned an error other than "revoked certificate",
    754    // propagate that error.
    755    if (crliteResult != Success &&
    756        crliteResult != Result::ERROR_REVOKED_CERTIFICATE) {
    757      return crliteResult;
    758    }
    759 
    760    if (crliteCoversCertificate) {
    761      mozilla::glean::cert_verifier::cert_revocation_mechanisms.Get("CRLite"_ns)
    762          .Add(1);
    763      if (mCRLiteMode == CRLiteMode::Enforce) {
    764        return crliteResult;
    765      }
    766    }
    767  }
    768 
    769  nsCString aiaLocation(VoidCString());
    770  if (aiaExtension) {
    771    UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
    772    if (!arena) {
    773      return Result::FATAL_ERROR_NO_MEMORY;
    774    }
    775    Result rv =
    776        GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation);
    777    if (rv != Success) {
    778      return rv;
    779    }
    780  }
    781 
    782  Result ocspResult = CheckRevocationByOCSP(certID, time, validityDuration,
    783                                            aiaLocation, stapledOCSPResponse);
    784 
    785  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    786          ("NSSCertDBTrustDomain: end of CheckRevocation"));
    787 
    788  return ocspResult;
    789 }
    790 
    791 Result NSSCertDBTrustDomain::CheckRevocationByCRLite(
    792    const CertID& certID, Time time, /*out*/ bool& crliteCoversCertificate) {
    793  crliteCoversCertificate = false;
    794  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    795          ("NSSCertDBTrustDomain::CheckRevocation: checking CRLite"));
    796 
    797  nsTArray<uint8_t> issuerSubjectPublicKeyInfoBytes;
    798  issuerSubjectPublicKeyInfoBytes.AppendElements(
    799      certID.issuerSubjectPublicKeyInfo.UnsafeGetData(),
    800      certID.issuerSubjectPublicKeyInfo.GetLength());
    801  nsTArray<uint8_t> serialNumberBytes;
    802  serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(),
    803                                   certID.serialNumber.GetLength());
    804 
    805  nsTArray<RefPtr<nsICRLiteTimestamp>> timestamps;
    806 
    807  // CRLite relies on timestamps from SCTs, so we should check signatures on
    808  // SCTs before calling into CRLite. However, the risk of using unverified
    809  // timestamps (particularly from embedded SCTs) is marginal, and if CT is
    810  // disabled we will pass unverified timestamps from embedded SCTs to CRLite.
    811  if (GetCertificateTransparencyMode() ==
    812      CertVerifier::CertificateTransparencyMode::Disabled) {
    813    size_t decodingErrors;
    814    std::vector<SignedCertificateTimestamp> decodedSCTsFromExtension;
    815    DecodeSCTs(GetSCTListFromCertificate(), decodedSCTsFromExtension,
    816               decodingErrors);
    817    (void)decodingErrors;
    818    for (const auto& sct : decodedSCTsFromExtension) {
    819      timestamps.AppendElement(new CRLiteTimestamp(sct));
    820    }
    821 
    822    return CheckCRLite(issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
    823                       timestamps, crliteCoversCertificate);
    824  }
    825 
    826  // When CT is enabled, we verify the signatures on all available SCTs and
    827  // cache the verification result in the trust domain so that it can be used
    828  // for CT policy enforcement. The verification result only depends on the end
    829  // entity certificate and the issuer SPKI, so it is path-independent and we
    830  // only need to compute it once.
    831  if (mCTVerifyResult.isNothing()) {
    832    MOZ_ASSERT(mBuiltChain.Length() > 0);
    833 
    834    CTVerifyResult ctVerifyResult;
    835    Input leafCertificate;
    836    const nsTArray<uint8_t>& endEntityBytes = mBuiltChain.ElementAt(0);
    837    Result rv = leafCertificate.Init(endEntityBytes.Elements(),
    838                                     endEntityBytes.Length());
    839    if (rv != Success) {
    840      return rv;
    841    }
    842 
    843    Input encodedSCTsFromOCSP;  // empty since we haven't done OCSP yet.
    844    rv = mCTVerifier->Verify(leafCertificate, certID.issuerSubjectPublicKeyInfo,
    845                             GetSCTListFromCertificate(), encodedSCTsFromOCSP,
    846                             mEncodedSCTsFromTLS, time, GetDistrustAfterTime(),
    847                             ctVerifyResult);
    848    if (rv != Success) {
    849      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    850              ("SCT verification failed with fatal error %" PRId32 "\n",
    851               static_cast<uint32_t>(rv)));
    852      return rv;
    853    }
    854 
    855    mCTVerifyResult.emplace(std::move(ctVerifyResult));
    856  }
    857 
    858  if (mCTVerifyResult.isSome()) {
    859    for (const auto& sct : mCTVerifyResult->verifiedScts) {
    860      timestamps.AppendElement(new CRLiteTimestamp(sct));
    861    }
    862  }
    863 
    864  return CheckCRLite(issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
    865                     timestamps, crliteCoversCertificate);
    866 }
    867 
    868 Result NSSCertDBTrustDomain::CheckRevocationByOCSP(
    869    const CertID& certID, Time time, Duration validityDuration,
    870    const nsCString& aiaLocation,
    871    /*optional*/ const Input* stapledOCSPResponse) {
    872  const uint16_t maxOCSPLifetimeInDays = 10;
    873  // If we have a stapled OCSP response then the verification of that response
    874  // determines the result unless the OCSP response is expired. We make an
    875  // exception for expired responses because some servers, nginx in particular,
    876  // are known to serve expired responses due to bugs.
    877  // We keep track of the result of verifying the stapled response but don't
    878  // immediately return failure if the response has expired.
    879  Result stapledOCSPResponseResult = Success;
    880  if (stapledOCSPResponse) {
    881    bool expired;
    882    stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse(
    883        certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse,
    884        ResponseWasStapled, expired);
    885    mozilla::glean::cert_verifier::cert_revocation_mechanisms
    886        .Get("StapledOCSP"_ns)
    887        .Add(1);
    888    if (stapledOCSPResponseResult == Success) {
    889      // stapled OCSP response present and good
    890      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
    891      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    892              ("NSSCertDBTrustDomain: stapled OCSP response: good"));
    893      return Success;
    894    }
    895    if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
    896        expired) {
    897      // stapled OCSP response present but expired
    898      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
    899      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    900              ("NSSCertDBTrustDomain: expired stapled OCSP response"));
    901    } else if (stapledOCSPResponseResult ==
    902                   Result::ERROR_OCSP_TRY_SERVER_LATER ||
    903               stapledOCSPResponseResult ==
    904                   Result::ERROR_OCSP_INVALID_SIGNING_CERT ||
    905               stapledOCSPResponseResult ==
    906                   Result::ERROR_OCSP_RESPONSE_FOR_CERT_MISSING) {
    907      // Stapled OCSP response present but invalid for a small number of reasons
    908      // CAs/servers commonly get wrong. This will be treated similarly to an
    909      // expired stapled response.
    910      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
    911      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    912              ("NSSCertDBTrustDomain: stapled OCSP response: "
    913               "failure (allowed for compatibility)"));
    914    } else {
    915      // stapled OCSP response present but invalid for some reason
    916      mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
    917      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    918              ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
    919      return stapledOCSPResponseResult;
    920    }
    921  } else {
    922    // no stapled OCSP response
    923    mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
    924    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    925            ("NSSCertDBTrustDomain: no stapled OCSP response"));
    926  }
    927 
    928  Result cachedResponseResult = Success;
    929  Time cachedResponseValidThrough(Time::uninitialized);
    930  bool cachedResponsePresent =
    931      mOCSPCache.Get(certID, mOriginAttributes, cachedResponseResult,
    932                     cachedResponseValidThrough);
    933  if (cachedResponsePresent) {
    934    mozilla::glean::cert_verifier::cert_revocation_mechanisms
    935        .Get("CachedOCSP"_ns)
    936        .Add(1);
    937    if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
    938      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    939              ("NSSCertDBTrustDomain: cached OCSP response: good"));
    940      return Success;
    941    }
    942    // If we have a cached revoked response, use it.
    943    if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
    944      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    945              ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
    946      return Result::ERROR_REVOKED_CERTIFICATE;
    947    }
    948    // The cached response may indicate an unknown certificate or it may be
    949    // expired. Don't return with either of these statuses yet - we may be
    950    // able to fetch a more recent one.
    951    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    952            ("NSSCertDBTrustDomain: cached OCSP response: error %d",
    953             static_cast<int>(cachedResponseResult)));
    954    // When a good cached response has expired, it is more convenient
    955    // to convert that to an error code and just deal with
    956    // cachedResponseResult from here on out.
    957    if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
    958      cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
    959    }
    960    // We may have a cached indication of server failure. Ignore it if
    961    // it has expired.
    962    if (cachedResponseResult != Success &&
    963        cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
    964        cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
    965        cachedResponseValidThrough < time) {
    966      cachedResponseResult = Success;
    967      cachedResponsePresent = false;
    968    }
    969  } else {
    970    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
    971            ("NSSCertDBTrustDomain: no cached OCSP response"));
    972  }
    973  // At this point, if and only if cachedErrorResult is Success, there was no
    974  // cached response.
    975  MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
    976             (cachedResponsePresent && cachedResponseResult != Success));
    977 
    978  // TODO: We still need to handle the fallback for invalid stapled responses.
    979  // But, if/when we disable OCSP fetching by default, it would be ambiguous
    980  // whether security.OCSP.enable==0 means "I want the default" or "I really
    981  // never want you to ever fetch OCSP."
    982  // Additionally, this doesn't properly handle OCSP-must-staple when OCSP
    983  // fetching is disabled.
    984  Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
    985  if (validityDuration < shortLifetime) {
    986    mozilla::glean::cert_verifier::cert_revocation_mechanisms
    987        .Get("ShortValidity"_ns)
    988        .Add(1);
    989  }
    990  if ((mOCSPFetching == RevocationCheckLocalOnly) ||
    991      (validityDuration < shortLifetime)) {
    992    // We're not going to be doing any fetching, so if there was a cached
    993    // "unknown" response, say so.
    994    if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
    995      return Result::ERROR_OCSP_UNKNOWN_CERT;
    996    }
    997    // If we're doing hard-fail, we want to know if we have a cached response
    998    // that has expired.
    999    if (mOCSPFetching == RevocationCheckRequired &&
   1000        cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
   1001      return Result::ERROR_OCSP_OLD_RESPONSE;
   1002    }
   1003 
   1004    return Success;
   1005  }
   1006 
   1007  // There are a few situations where the user's CRLite data may not cover a
   1008  // certificate that chains to our root store, e.g.
   1009  //  1) the user has not yet downloaded CRLite filters, or
   1010  //  2) the user's CRLite filters are out-of-date, or
   1011  //  3) the certificate has been in CT for < 1 MMD interval.
   1012  // If we're configured to enforce CRLite and we're configured to tolerate OCSP
   1013  // soft failures, then it's reasonable to skip the synchronous OCSP request
   1014  // here. In effect, we're choosing to preserve the privacy of the user at the
   1015  // risk of potentially allowing them to navigate to a site that is serving a
   1016  // revoked certificate.
   1017  if (mOCSPFetching == RevocationCheckMayFetch &&
   1018      mCRLiteMode == CRLiteMode::Enforce && mIsBuiltChainRootBuiltInRoot) {
   1019    return Success;
   1020  }
   1021 
   1022  if (aiaLocation.IsVoid()) {
   1023    if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
   1024      return Result::ERROR_OCSP_UNKNOWN_CERT;
   1025    }
   1026    if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
   1027      return Result::ERROR_OCSP_OLD_RESPONSE;
   1028    }
   1029    if (stapledOCSPResponseResult != Success) {
   1030      return stapledOCSPResponseResult;
   1031    }
   1032 
   1033    // Nothing to do if we don't have an OCSP responder URI for the cert; just
   1034    // assume it is good. Note that this is the confusing, but intended,
   1035    // interpretation of "strict" revocation checking in the face of a
   1036    // certificate that lacks an OCSP responder URI.
   1037    return Success;
   1038  }
   1039 
   1040  if (cachedResponseResult == Success ||
   1041      cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
   1042      cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
   1043    // Only send a request to, and process a response from, the server if we
   1044    // didn't have a cached indication of failure.  Also, don't keep requesting
   1045    // responses from a failing server.
   1046    return SynchronousCheckRevocationWithServer(
   1047        certID, aiaLocation, time, maxOCSPLifetimeInDays, cachedResponseResult,
   1048        stapledOCSPResponseResult);
   1049  }
   1050 
   1051  return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
   1052                           cachedResponseResult);
   1053 }
   1054 
   1055 Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
   1056    const CertID& certID, const nsCString& aiaLocation, Time time,
   1057    uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult,
   1058    const Result stapledOCSPResponseResult) {
   1059  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   1060    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   1061  }
   1062 
   1063  uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
   1064  size_t ocspRequestLength;
   1065  Result rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
   1066                                       ocspRequestLength);
   1067  if (rv != Success) {
   1068    return rv;
   1069  }
   1070 
   1071  Vector<uint8_t> ocspResponse;
   1072  Input response;
   1073  mOCSPFetchStatus = OCSPFetchStatus::Fetched;
   1074  rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes,
   1075                     ocspRequestLength, GetOCSPTimeout(), ocspResponse);
   1076  mozilla::glean::cert_verifier::cert_revocation_mechanisms.Get("OCSP"_ns).Add(
   1077      1);
   1078  if (rv == Success &&
   1079      response.Init(ocspResponse.begin(), ocspResponse.length()) != Success) {
   1080    rv = Result::ERROR_OCSP_MALFORMED_RESPONSE;  // too big
   1081  }
   1082 
   1083  if (rv != Success) {
   1084    Time timeout(time);
   1085    if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
   1086      return Result::FATAL_ERROR_LIBRARY_FAILURE;  // integer overflow
   1087    }
   1088 
   1089    Result cacheRV =
   1090        mOCSPCache.Put(certID, mOriginAttributes, rv, time, timeout);
   1091    if (cacheRV != Success) {
   1092      return cacheRV;
   1093    }
   1094 
   1095    return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
   1096                             rv);
   1097  }
   1098 
   1099  // If the response from the network has expired but indicates a revoked
   1100  // or unknown certificate, PR_GetError() will return the appropriate error.
   1101  // We actually ignore expired here.
   1102  bool expired;
   1103  rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
   1104                                              maxOCSPLifetimeInDays, response,
   1105                                              ResponseIsFromNetwork, expired);
   1106  if (rv == Success || mOCSPFetching == RevocationCheckRequired) {
   1107    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1108            ("NSSCertDBTrustDomain: returning after "
   1109             "VerifyEncodedOCSPResponse"));
   1110    return rv;
   1111  }
   1112 
   1113  if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
   1114      rv == Result::ERROR_REVOKED_CERTIFICATE) {
   1115    return rv;
   1116  }
   1117 
   1118  if (stapledOCSPResponseResult != Success) {
   1119    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1120            ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
   1121             "stapled response after OCSP request verification failure"));
   1122    return stapledOCSPResponseResult;
   1123  }
   1124 
   1125  return Success;  // Soft fail -> success :(
   1126 }
   1127 
   1128 Result NSSCertDBTrustDomain::HandleOCSPFailure(
   1129    const Result cachedResponseResult, const Result stapledOCSPResponseResult,
   1130    const Result error) {
   1131  if (mOCSPFetching == RevocationCheckRequired) {
   1132    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1133            ("NSSCertDBTrustDomain: returning SECFailure after OCSP request "
   1134             "failure"));
   1135    return error;
   1136  }
   1137 
   1138  if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
   1139    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1140            ("NSSCertDBTrustDomain: returning SECFailure from cached response "
   1141             "after OCSP request failure"));
   1142    return cachedResponseResult;
   1143  }
   1144 
   1145  if (stapledOCSPResponseResult != Success) {
   1146    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1147            ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
   1148             "stapled response after OCSP request failure"));
   1149    return stapledOCSPResponseResult;
   1150  }
   1151 
   1152  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1153          ("NSSCertDBTrustDomain: returning SECSuccess after OCSP request "
   1154           "failure"));
   1155 
   1156  return Success;  // Soft fail -> success :(
   1157 }
   1158 
   1159 Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
   1160    const CertID& certID, Time time, uint16_t maxLifetimeInDays,
   1161    Input encodedResponse, EncodedResponseSource responseSource,
   1162    /*out*/ bool& expired) {
   1163  Time thisUpdate(Time::uninitialized);
   1164  Time validThrough(Time::uninitialized);
   1165 
   1166  Result rv = VerifyEncodedOCSPResponse(*this, certID, time, maxLifetimeInDays,
   1167                                        encodedResponse, expired, &thisUpdate,
   1168                                        &validThrough);
   1169  // If a response was stapled and expired, we don't want to cache it. Return
   1170  // early to simplify the logic here.
   1171  if (responseSource == ResponseWasStapled && expired) {
   1172    MOZ_ASSERT(rv != Success);
   1173    return rv;
   1174  }
   1175  // validThrough is only trustworthy if the response successfully verifies
   1176  // or it indicates a revoked or unknown certificate.
   1177  // If this isn't the case, store an indication of failure (to prevent
   1178  // repeatedly requesting a response from a failing server).
   1179  if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
   1180      rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
   1181    validThrough = time;
   1182    if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
   1183      return Result::FATAL_ERROR_LIBRARY_FAILURE;  // integer overflow
   1184    }
   1185  }
   1186  if (responseSource == ResponseIsFromNetwork || rv == Success ||
   1187      rv == Result::ERROR_REVOKED_CERTIFICATE ||
   1188      rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
   1189    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1190            ("NSSCertDBTrustDomain: caching OCSP response"));
   1191    Result putRV =
   1192        mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate, validThrough);
   1193    if (putRV != Success) {
   1194      return putRV;
   1195    }
   1196  }
   1197 
   1198  return rv;
   1199 }
   1200 
   1201 nsresult IsDistrustedCertificateChain(
   1202    const nsTArray<nsTArray<uint8_t>>& certArray,
   1203    const SECTrustType certDBTrustType, bool& isDistrusted,
   1204    Maybe<mozilla::pkix::Time>& distrustAfterTimeOut) {
   1205  if (certArray.Length() == 0) {
   1206    return NS_ERROR_FAILURE;
   1207  }
   1208 
   1209  // Set the default result to be distrusted.
   1210  isDistrusted = true;
   1211 
   1212  CK_ATTRIBUTE_TYPE attrType;
   1213  switch (certDBTrustType) {
   1214    case trustSSL:
   1215      attrType = CKA_NSS_SERVER_DISTRUST_AFTER;
   1216      break;
   1217    case trustEmail:
   1218      attrType = CKA_NSS_EMAIL_DISTRUST_AFTER;
   1219      break;
   1220    default:
   1221      // There is no distrust to set if the certDBTrustType is not SSL or Email.
   1222      isDistrusted = false;
   1223      return NS_OK;
   1224  }
   1225 
   1226  Input endEntityDER;
   1227  mozilla::pkix::Result rv = endEntityDER.Init(
   1228      certArray.ElementAt(0).Elements(), certArray.ElementAt(0).Length());
   1229  if (rv != Success) {
   1230    return NS_ERROR_FAILURE;
   1231  }
   1232 
   1233  BackCert endEntityBackCert(endEntityDER, EndEntityOrCA::MustBeEndEntity,
   1234                             nullptr);
   1235  rv = endEntityBackCert.Init();
   1236  if (rv != Success) {
   1237    return NS_ERROR_FAILURE;
   1238  }
   1239 
   1240  Time endEntityNotBefore(Time::uninitialized);
   1241  rv = ParseValidity(endEntityBackCert.GetValidity(), &endEntityNotBefore,
   1242                     nullptr);
   1243  if (rv != Success) {
   1244    return NS_ERROR_FAILURE;
   1245  }
   1246 
   1247  Input rootDER;
   1248  rv = rootDER.Init(certArray.LastElement().Elements(),
   1249                    certArray.LastElement().Length());
   1250  if (rv != Success) {
   1251    return NS_ERROR_FAILURE;
   1252  }
   1253  SECItem rootDERItem(UnsafeMapInputToSECItem(rootDER));
   1254 
   1255  PRBool distrusted;
   1256  PRTime distrustAfter;  // time since epoch in microseconds
   1257  bool foundDistrust = false;
   1258 
   1259  // This strategy for searching for the builtins module is borrowed
   1260  // from CertVerifier::IsCertBuiltInRoot. See the comment on that
   1261  // function for more information.
   1262  AutoSECMODListReadLock lock;
   1263  for (SECMODModuleList* list = SECMOD_GetDefaultModuleList();
   1264       list && !foundDistrust; list = list->next) {
   1265    for (int i = 0; i < list->module->slotCount; i++) {
   1266      PK11SlotInfo* slot = list->module->slots[i];
   1267      if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) {
   1268        continue;
   1269      }
   1270      CK_OBJECT_HANDLE handle =
   1271          PK11_FindEncodedCertInSlot(slot, &rootDERItem, nullptr);
   1272      if (handle == CK_INVALID_HANDLE) {
   1273        continue;
   1274      }
   1275      // Distrust attributes are only set on builtin roots, so ensure this
   1276      // certificate has the CKA_NSS_MOZILLA_CA_POLICY attribute.
   1277      if (!PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
   1278                                false)) {
   1279        continue;
   1280      }
   1281      SECStatus srv = PK11_ReadDistrustAfterAttribute(
   1282          slot, handle, attrType, &distrusted, &distrustAfter);
   1283      if (srv == SECSuccess) {
   1284        foundDistrust = true;
   1285      }
   1286    }
   1287  }
   1288 
   1289  if (!foundDistrust || distrusted == PR_FALSE) {
   1290    isDistrusted = false;
   1291    return NS_OK;
   1292  }
   1293 
   1294  Time distrustAfterTime =
   1295      mozilla::pkix::TimeFromEpochInSeconds(distrustAfter / PR_USEC_PER_SEC);
   1296  distrustAfterTimeOut.emplace(distrustAfterTime);
   1297  if (endEntityNotBefore <= distrustAfterTime) {
   1298    isDistrusted = false;
   1299  }
   1300 
   1301  return NS_OK;
   1302 }
   1303 
   1304 Result NSSCertDBTrustDomain::IsChainValid(const DERArray& reversedDERArray,
   1305                                          Time time,
   1306                                          const CertPolicyId& requiredPolicy) {
   1307  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1308          ("NSSCertDBTrustDomain: IsChainValid"));
   1309 
   1310  size_t numCerts = reversedDERArray.GetLength();
   1311  if (numCerts < 1) {
   1312    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   1313  }
   1314  nsTArray<nsTArray<uint8_t>> certArray;
   1315  for (size_t i = numCerts; i > 0; --i) {
   1316    const Input* derInput = reversedDERArray.GetDER(i - 1);
   1317    certArray.EmplaceBack(derInput->UnsafeGetData(), derInput->GetLength());
   1318  }
   1319 
   1320  const nsTArray<uint8_t>& rootBytes = certArray.LastElement();
   1321  Input rootInput;
   1322  Result rv = rootInput.Init(rootBytes.Elements(), rootBytes.Length());
   1323  if (rv != Success) {
   1324    return rv;
   1325  }
   1326  rv = IsCertBuiltInRoot(rootInput, mIsBuiltChainRootBuiltInRoot);
   1327  if (rv != Result::Success) {
   1328    return rv;
   1329  }
   1330  nsresult nsrv;
   1331  // If mHostname isn't set, we're not verifying in the context of a TLS
   1332  // handshake, so don't verify key pinning in those cases.
   1333  if (mHostname) {
   1334    nsTArray<Span<const uint8_t>> derCertSpanList;
   1335    for (const auto& certDER : certArray) {
   1336      derCertSpanList.EmplaceBack(certDER.Elements(), certDER.Length());
   1337    }
   1338 
   1339    bool chainHasValidPins;
   1340    nsrv = PublicKeyPinningService::ChainHasValidPins(
   1341        derCertSpanList, mHostname, time, mIsBuiltChainRootBuiltInRoot,
   1342        chainHasValidPins, mPinningTelemetryInfo);
   1343    if (NS_FAILED(nsrv)) {
   1344      return Result::FATAL_ERROR_LIBRARY_FAILURE;
   1345    }
   1346    if (!chainHasValidPins) {
   1347      return Result::ERROR_KEY_PINNING_FAILURE;
   1348    }
   1349  }
   1350 
   1351  // Check that the childs' certificate NotBefore date is anterior to
   1352  // the NotAfter value of the parent when the root is a builtin.
   1353  if (mIsBuiltChainRootBuiltInRoot) {
   1354    bool isDistrusted;
   1355    nsrv = IsDistrustedCertificateChain(certArray, mCertDBTrustType,
   1356                                        isDistrusted, mDistrustAfterTime);
   1357    if (NS_FAILED(nsrv)) {
   1358      return Result::FATAL_ERROR_LIBRARY_FAILURE;
   1359    }
   1360    if (isDistrusted) {
   1361      // Check if this root is also a third-party root. If so, distrust after
   1362      // doesn't apply to it.
   1363      bool isThirdPartyRoot = false;
   1364      for (const auto& thirdPartyRoot : mThirdPartyRootInputs) {
   1365        if (InputsAreEqual(rootInput, thirdPartyRoot)) {
   1366          isThirdPartyRoot = true;
   1367          break;
   1368        }
   1369      }
   1370      if (!isThirdPartyRoot) {
   1371        MOZ_LOG(
   1372            gCertVerifierLog, LogLevel::Debug,
   1373            ("certificate has notBefore after distrust after value for root"));
   1374        return Result::ERROR_ISSUER_NO_LONGER_TRUSTED;
   1375      }
   1376      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1377              ("ignoring built-in distrust after for third-party root"));
   1378    }
   1379  }
   1380 
   1381  mBuiltChain = std::move(certArray);
   1382 
   1383  return Success;
   1384 }
   1385 
   1386 Result NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(
   1387    DigestAlgorithm aAlg, EndEntityOrCA /*endEntityOrCA*/, Time /*notBefore*/) {
   1388  switch (aAlg) {
   1389    case DigestAlgorithm::sha256:  // fall through
   1390    case DigestAlgorithm::sha384:  // fall through
   1391    case DigestAlgorithm::sha512:
   1392      return Success;
   1393    case DigestAlgorithm::sha1:
   1394      return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
   1395  }
   1396  return Result::FATAL_ERROR_LIBRARY_FAILURE;
   1397 }
   1398 
   1399 Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
   1400    EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
   1401  if (modulusSizeInBits < mMinRSABits) {
   1402    return Result::ERROR_INADEQUATE_KEY_SIZE;
   1403  }
   1404  return Success;
   1405 }
   1406 
   1407 Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedData(
   1408    Input data, DigestAlgorithm digestAlgorithm, Input signature,
   1409    Input subjectPublicKeyInfo) {
   1410  return VerifySignedDataWithCache(
   1411      der::PublicKeyAlgorithm::RSA_PKCS1,
   1412      mozilla::glean::cert_signature_cache::total,
   1413      mozilla::glean::cert_signature_cache::hits, data, digestAlgorithm,
   1414      signature, subjectPublicKeyInfo, mSignatureCache, mPinArg);
   1415 }
   1416 
   1417 Result NSSCertDBTrustDomain::VerifyRSAPSSSignedData(
   1418    Input data, DigestAlgorithm digestAlgorithm, Input signature,
   1419    Input subjectPublicKeyInfo) {
   1420  return VerifySignedDataWithCache(
   1421      der::PublicKeyAlgorithm::RSA_PSS,
   1422      mozilla::glean::cert_signature_cache::total,
   1423      mozilla::glean::cert_signature_cache::hits, data, digestAlgorithm,
   1424      signature, subjectPublicKeyInfo, mSignatureCache, mPinArg);
   1425 }
   1426 
   1427 Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
   1428    EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) {
   1429  switch (curve) {
   1430    case NamedCurve::secp256r1:  // fall through
   1431    case NamedCurve::secp384r1:  // fall through
   1432    case NamedCurve::secp521r1:
   1433      return Success;
   1434  }
   1435 
   1436  return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
   1437 }
   1438 
   1439 Result NSSCertDBTrustDomain::VerifyECDSASignedData(
   1440    Input data, DigestAlgorithm digestAlgorithm, Input signature,
   1441    Input subjectPublicKeyInfo) {
   1442  return VerifySignedDataWithCache(
   1443      der::PublicKeyAlgorithm::ECDSA,
   1444      mozilla::glean::cert_signature_cache::total,
   1445      mozilla::glean::cert_signature_cache::hits, data, digestAlgorithm,
   1446      signature, subjectPublicKeyInfo, mSignatureCache, mPinArg);
   1447 }
   1448 
   1449 Result NSSCertDBTrustDomain::CheckValidityIsAcceptable(
   1450    Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
   1451    KeyPurposeId keyPurpose) {
   1452  return Success;
   1453 }
   1454 
   1455 void NSSCertDBTrustDomain::ResetAccumulatedState() {
   1456  mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
   1457  mSCTListFromOCSPStapling = nullptr;
   1458  mSCTListFromCertificate = nullptr;
   1459  mIssuerSources.clear();
   1460  ResetCandidateBuiltChainState();
   1461 }
   1462 
   1463 void NSSCertDBTrustDomain::ResetCandidateBuiltChainState() {
   1464  mIsBuiltChainRootBuiltInRoot = false;
   1465  mDistrustAfterTime.reset();
   1466 }
   1467 
   1468 static Input SECItemToInput(const UniqueSECItem& item) {
   1469  Input result;
   1470  if (item) {
   1471    MOZ_ASSERT(item->type == siBuffer);
   1472    Result rv = result.Init(item->data, item->len);
   1473    // As used here, |item| originally comes from an Input,
   1474    // so there should be no issues converting it back.
   1475    MOZ_ASSERT(rv == Success);
   1476    (void)rv;  // suppresses warnings in release builds
   1477  }
   1478  return result;
   1479 }
   1480 
   1481 Input NSSCertDBTrustDomain::GetSCTListFromCertificate() const {
   1482  return SECItemToInput(mSCTListFromCertificate);
   1483 }
   1484 
   1485 Input NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const {
   1486  return SECItemToInput(mSCTListFromOCSPStapling);
   1487 }
   1488 
   1489 Maybe<CTVerifyResult>& NSSCertDBTrustDomain::GetCachedCTVerifyResult() {
   1490  return mCTVerifyResult;
   1491 }
   1492 
   1493 bool NSSCertDBTrustDomain::GetIsBuiltChainRootBuiltInRoot() const {
   1494  return mIsBuiltChainRootBuiltInRoot;
   1495 }
   1496 
   1497 void NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
   1498                                                  Input extensionData) {
   1499  UniqueSECItem* out = nullptr;
   1500  switch (extension) {
   1501    case AuxiliaryExtension::EmbeddedSCTList:
   1502      out = &mSCTListFromCertificate;
   1503      break;
   1504    case AuxiliaryExtension::SCTListFromOCSPResponse:
   1505      out = &mSCTListFromOCSPStapling;
   1506      break;
   1507    default:
   1508      MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
   1509  }
   1510  if (out) {
   1511    SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
   1512    out->reset(SECITEM_DupItem(&extensionDataItem));
   1513  }
   1514 }
   1515 
   1516 SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig,
   1517                        PKCS11DBConfig pkcs11DbConfig) {
   1518  MOZ_ASSERT(NS_IsMainThread());
   1519 
   1520  // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
   1521  // module by NSS_Initialize because we will load it in LoadLoadableRoots
   1522  // later.  It also allows us to work around a bug in the system NSS in
   1523  // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
   1524  // "/usr/lib/nss/libnssckbi.so".
   1525  uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
   1526  if (nssDbConfig == NSSDBConfig::ReadOnly) {
   1527    flags |= NSS_INIT_READONLY;
   1528  }
   1529  if (pkcs11DbConfig == PKCS11DBConfig::DoNotLoadModules) {
   1530    flags |= NSS_INIT_NOMODDB;
   1531  }
   1532  nsAutoCString dbTypeAndDirectory("sql:");
   1533  dbTypeAndDirectory.Append(dir);
   1534  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
   1535          ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(),
   1536           (int)nssDbConfig, (int)pkcs11DbConfig));
   1537  SECStatus srv =
   1538      NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags);
   1539  if (srv != SECSuccess) {
   1540    return srv;
   1541  }
   1542 
   1543  if (nssDbConfig == NSSDBConfig::ReadWrite) {
   1544    UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
   1545    if (!slot) {
   1546      return SECFailure;
   1547    }
   1548    // If the key DB doesn't have a password set, PK11_NeedUserInit will return
   1549    // true. For the SQL DB, we need to set a password or we won't be able to
   1550    // import any certificates or change trust settings.
   1551    if (PK11_NeedUserInit(slot.get())) {
   1552      srv = PK11_InitPin(slot.get(), nullptr, nullptr);
   1553      MOZ_ASSERT(srv == SECSuccess);
   1554      (void)srv;
   1555    }
   1556  }
   1557 
   1558  CollectThirdPartyPKCS11ModuleTelemetry(/*aIsInitialization=*/true);
   1559 
   1560  return SECSuccess;
   1561 }
   1562 
   1563 void DisableMD5() {
   1564  NSS_SetAlgorithmPolicy(
   1565      SEC_OID_MD5, 0,
   1566      NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
   1567  NSS_SetAlgorithmPolicy(
   1568      SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 0,
   1569      NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
   1570  NSS_SetAlgorithmPolicy(
   1571      SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC, 0,
   1572      NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
   1573 }
   1574 
   1575 // Load a given PKCS#11 module located in the given directory. It will be named
   1576 // the given module name. Optionally pass some string parameters to it via
   1577 // 'params'. This argument will be provided to C_Initialize when called on the
   1578 // module.
   1579 // |libraryName| and |dir| are encoded in UTF-8.
   1580 bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
   1581                      const nsCString& dir, /* optional */ const char* params) {
   1582  // If a module exists with the same name, make a best effort attempt to delete
   1583  // it. Note that it isn't possible to delete the internal module, so checking
   1584  // the return value would be detrimental in that case.
   1585  int unusedModType;
   1586  (void)SECMOD_DeleteModule(moduleName, &unusedModType);
   1587 
   1588  nsAutoCString fullLibraryPath;
   1589  if (!dir.IsEmpty()) {
   1590    fullLibraryPath.Assign(dir);
   1591    fullLibraryPath.AppendLiteral(FILE_PATH_SEPARATOR);
   1592  }
   1593  fullLibraryPath.Append(MOZ_DLL_PREFIX);
   1594  fullLibraryPath.Append(libraryName);
   1595  fullLibraryPath.Append(MOZ_DLL_SUFFIX);
   1596  // Escape the \ and " characters.
   1597  fullLibraryPath.ReplaceSubstring("\\", "\\\\");
   1598  fullLibraryPath.ReplaceSubstring("\"", "\\\"");
   1599 
   1600  nsAutoCString pkcs11ModuleSpec("name=\"");
   1601  pkcs11ModuleSpec.Append(moduleName);
   1602  pkcs11ModuleSpec.AppendLiteral("\" library=\"");
   1603  pkcs11ModuleSpec.Append(fullLibraryPath);
   1604  pkcs11ModuleSpec.AppendLiteral("\"");
   1605  if (params) {
   1606    pkcs11ModuleSpec.AppendLiteral("\" parameters=\"");
   1607    pkcs11ModuleSpec.Append(params);
   1608    pkcs11ModuleSpec.AppendLiteral("\"");
   1609  }
   1610 
   1611  UniqueSECMODModule userModule(SECMOD_LoadUserModule(
   1612      const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false));
   1613  if (!userModule) {
   1614    return false;
   1615  }
   1616 
   1617  if (!userModule->loaded) {
   1618    return false;
   1619  }
   1620 
   1621  return true;
   1622 }
   1623 
   1624 bool LoadUserModuleFromXul(const char* moduleName,
   1625                           CK_C_GetFunctionList fentry) {
   1626  // If a module exists with the same name, make a best effort attempt to delete
   1627  // it. Note that it isn't possible to delete the internal module, so checking
   1628  // the return value would be detrimental in that case.
   1629  int unusedModType;
   1630  (void)SECMOD_DeleteModule(moduleName, &unusedModType);
   1631 
   1632  UniqueSECMODModule userModule(
   1633      SECMOD_LoadUserModuleWithFunction(moduleName, fentry));
   1634  if (!userModule) {
   1635    return false;
   1636  }
   1637 
   1638  if (!userModule->loaded) {
   1639    return false;
   1640  }
   1641 
   1642  return true;
   1643 }
   1644 
   1645 extern "C" {
   1646 // Extern function to call ipcclientcerts module C_GetFunctionList.
   1647 // NSS calls it to obtain the list of functions comprising this module.
   1648 // ppFunctionList must be a valid pointer.
   1649 CK_RV IPCCC_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList);
   1650 }  // extern "C"
   1651 
   1652 bool LoadIPCClientCertsModule() {
   1653  // The IPC client certs module needs to be able to call back into gecko to be
   1654  // able to communicate with the parent process over IPC. This is achieved by
   1655  // calling the external to Rust module functions DoSign and DoFindObjects.
   1656 
   1657  if (!LoadUserModuleFromXul(kIPCClientCertsModuleName.get(),
   1658                             IPCCC_GetFunctionList)) {
   1659    return false;
   1660  }
   1661  RunOnShutdown(
   1662      []() {
   1663        UniqueSECMODModule ipcClientCertsModule(
   1664            SECMOD_FindModule(kIPCClientCertsModuleName.get()));
   1665        if (ipcClientCertsModule) {
   1666          SECMOD_UnloadUserModule(ipcClientCertsModule.get());
   1667        }
   1668      },
   1669      ShutdownPhase::XPCOMWillShutdown);
   1670  return true;
   1671 }
   1672 
   1673 extern "C" {
   1674 // Extern function to call osclientcerts module C_GetFunctionList.
   1675 // NSS calls it to obtain the list of functions comprising this module.
   1676 // ppFunctionList must be a valid pointer.
   1677 CK_RV OSClientCerts_C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList);
   1678 }  // extern "C"
   1679 
   1680 bool LoadOSClientCertsModule() {
   1681 // Corresponds to Rust cfg(any(
   1682 //  target_os = "macos",
   1683 //  target_os = "ios",
   1684 //  all(target_os = "windows", not(target_arch = "aarch64")),
   1685 //  target_os = "android"))]
   1686 #if defined(__APPLE__) || (defined WIN32 && !defined(__aarch64__)) || \
   1687    defined(MOZ_WIDGET_ANDROID)
   1688  return LoadUserModuleFromXul(kOSClientCertsModuleName.get(),
   1689                               OSClientCerts_C_GetFunctionList);
   1690 #else
   1691  return false;
   1692 #endif
   1693 }
   1694 
   1695 bool LoadLoadableRoots(const nsCString& dir) {
   1696  int unusedModType;
   1697  (void)SECMOD_DeleteModule("Root Certs", &unusedModType);
   1698  return LoadUserModuleAt(kRootModuleName.get(), "nssckbi", dir, nullptr);
   1699 }
   1700 
   1701 extern "C" {
   1702 // Extern function to call trust-anchors module C_GetFunctionList.
   1703 // NSS calls it to obtain the list of functions comprising this module.
   1704 // ppFunctionList must be a valid pointer.
   1705 CK_RV TRUST_ANCHORS_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList);
   1706 }  // extern "C"
   1707 
   1708 bool LoadLoadableRootsFromXul() {
   1709  // Some NSS command-line utilities will load a roots module under the name
   1710  // "Root Certs" if there happens to be a `MOZ_DLL_PREFIX "nssckbi"
   1711  // MOZ_DLL_SUFFIX` file in the directory being operated on. In some cases this
   1712  // can cause us to fail to load our roots module. In these cases, deleting the
   1713  // "Root Certs" module allows us to load the correct one. See bug 1406396.
   1714  int unusedModType;
   1715  (void)SECMOD_DeleteModule("Root Certs", &unusedModType);
   1716 
   1717  if (!LoadUserModuleFromXul(kRootModuleName.get(),
   1718                             TRUST_ANCHORS_GetFunctionList)) {
   1719    return false;
   1720  }
   1721  return true;
   1722 }
   1723 
   1724 nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
   1725                                      /*out*/ nsCString& nickname) {
   1726  MOZ_ASSERT(cert);
   1727  NS_ENSURE_ARG_POINTER(cert);
   1728 
   1729  UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
   1730  if (!baseName) {
   1731    baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
   1732  }
   1733  if (!baseName) {
   1734    baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
   1735  }
   1736  if (!baseName) {
   1737    baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
   1738  }
   1739  if (!baseName) {
   1740    baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
   1741  }
   1742  if (!baseName) {
   1743    baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
   1744  }
   1745  if (!baseName) {
   1746    return NS_ERROR_FAILURE;
   1747  }
   1748 
   1749  // This function is only used in contexts where a failure to find a suitable
   1750  // nickname does not block the overall task from succeeding.
   1751  // As such, we use an arbitrary limit to prevent this nickname searching
   1752  // process from taking forever.
   1753  static const uint32_t ARBITRARY_LIMIT = 500;
   1754  for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
   1755    nickname = baseName.get();
   1756    if (count != 1) {
   1757      nickname.AppendPrintf(" #%u", count);
   1758    }
   1759    if (nickname.IsEmpty()) {
   1760      return NS_ERROR_FAILURE;
   1761    }
   1762 
   1763    bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
   1764                                             cert->dbhandle);
   1765    if (!conflict) {
   1766      return NS_OK;
   1767    }
   1768  }
   1769 
   1770  return NS_ERROR_FAILURE;
   1771 }
   1772 
   1773 Result BuildRevocationCheckArrays(Input certDER, EndEntityOrCA endEntityOrCA,
   1774                                  /*out*/ nsTArray<uint8_t>& issuerBytes,
   1775                                  /*out*/ nsTArray<uint8_t>& serialBytes,
   1776                                  /*out*/ nsTArray<uint8_t>& subjectBytes,
   1777                                  /*out*/ nsTArray<uint8_t>& pubKeyBytes) {
   1778  BackCert cert(certDER, endEntityOrCA, nullptr);
   1779  Result rv = cert.Init();
   1780  if (rv != Success) {
   1781    return rv;
   1782  }
   1783  issuerBytes.Clear();
   1784  Input issuer(cert.GetIssuer());
   1785  issuerBytes.AppendElements(issuer.UnsafeGetData(), issuer.GetLength());
   1786  serialBytes.Clear();
   1787  Input serial(cert.GetSerialNumber());
   1788  serialBytes.AppendElements(serial.UnsafeGetData(), serial.GetLength());
   1789  subjectBytes.Clear();
   1790  Input subject(cert.GetSubject());
   1791  subjectBytes.AppendElements(subject.UnsafeGetData(), subject.GetLength());
   1792  pubKeyBytes.Clear();
   1793  Input pubKey(cert.GetSubjectPublicKeyInfo());
   1794  pubKeyBytes.AppendElements(pubKey.UnsafeGetData(), pubKey.GetLength());
   1795 
   1796  return Success;
   1797 }
   1798 
   1799 }  // namespace psm
   1800 }  // namespace mozilla