123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "NSSCertDBTrustDomain.h"
- #include <stdint.h>
- #include "ExtendedValidation.h"
- #include "NSSErrorsService.h"
- #include "OCSPRequestor.h"
- #include "OCSPVerificationTrustDomain.h"
- #include "cert.h"
- #include "certdb.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/Casting.h"
- #include "mozilla/PodOperations.h"
- #include "mozilla/UniquePtr.h"
- #include "mozilla/Unused.h"
- #include "nsNSSCertificate.h"
- #include "nsServiceManagerUtils.h"
- #include "nsThreadUtils.h"
- #include "nss.h"
- #include "pk11pub.h"
- #include "pkix/Result.h"
- #include "pkix/pkix.h"
- #include "pkix/pkixnss.h"
- #include "prerror.h"
- #include "prmem.h"
- #include "prprf.h"
- #include "secerr.h"
- #include "CNNICHashWhitelist.inc"
- #include "StartComAndWoSignData.inc"
- using namespace mozilla;
- using namespace mozilla::pkix;
- extern LazyLogModule gCertVerifierLog;
- static const uint64_t ServerFailureDelaySeconds = 5 * 60;
- namespace mozilla { namespace psm {
- const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module";
- NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
- OCSPFetching ocspFetching,
- OCSPCache& ocspCache,
- /*optional but shouldn't be*/ void* pinArg,
- CertVerifier::OcspGetConfig ocspGETConfig,
- uint32_t certShortLifetimeInDays,
- unsigned int minRSABits,
- ValidityCheckingMode validityCheckingMode,
- CertVerifier::SHA1Mode sha1Mode,
- NetscapeStepUpPolicy netscapeStepUpPolicy,
- const NeckoOriginAttributes& originAttributes,
- UniqueCERTCertList& builtChain)
- : mCertDBTrustType(certDBTrustType)
- , mOCSPFetching(ocspFetching)
- , mOCSPCache(ocspCache)
- , mPinArg(pinArg)
- , mOCSPGetConfig(ocspGETConfig)
- , mCertShortLifetimeInDays(certShortLifetimeInDays)
- , mMinRSABits(minRSABits)
- , mValidityCheckingMode(validityCheckingMode)
- , mSHA1Mode(sha1Mode)
- , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
- , mOriginAttributes(originAttributes)
- , mBuiltChain(builtChain)
- , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
- , mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
- , mSCTListFromCertificate()
- , mSCTListFromOCSPStapling()
- {
- }
- // If useRoots is true, we only use root certificates in the candidate list.
- // If useRoots is false, we only use non-root certificates in the list.
- static Result
- FindIssuerInner(const UniqueCERTCertList& candidates, bool useRoots,
- Input encodedIssuerName, TrustDomain::IssuerChecker& checker,
- /*out*/ bool& keepGoing)
- {
- keepGoing = true;
- for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
- !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
- bool candidateIsRoot = !!n->cert->isRoot;
- if (candidateIsRoot != useRoots) {
- continue;
- }
- Input certDER;
- Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
- if (rv != Success) {
- continue; // probably too big
- }
- const SECItem encodedIssuerNameItem = {
- siBuffer,
- const_cast<unsigned char*>(encodedIssuerName.UnsafeGetData()),
- encodedIssuerName.GetLength()
- };
- ScopedAutoSECItem nameConstraints;
- SECStatus srv = CERT_GetImposedNameConstraints(&encodedIssuerNameItem,
- &nameConstraints);
- if (srv != SECSuccess) {
- if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- // If no imposed name constraints were found, continue without them
- rv = checker.Check(certDER, nullptr, keepGoing);
- } else {
- // Otherwise apply the constraints
- Input nameConstraintsInput;
- if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len)
- != Success) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- rv = checker.Check(certDER, &nameConstraintsInput, keepGoing);
- }
- if (rv != Success) {
- return rv;
- }
- if (!keepGoing) {
- break;
- }
- }
- return Success;
- }
- Result
- NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
- IssuerChecker& checker, Time)
- {
- // TODO: NSS seems to be ambiguous between "no potential issuers found" and
- // "there was an error trying to retrieve the potential issuers."
- SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
- UniqueCERTCertList
- candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
- &encodedIssuerNameItem, 0,
- false));
- if (candidates) {
- // First, try all the root certs; then try all the non-root certs.
- bool keepGoing;
- Result rv = FindIssuerInner(candidates, true, encodedIssuerName, checker,
- keepGoing);
- if (rv != Success) {
- return rv;
- }
- if (keepGoing) {
- rv = FindIssuerInner(candidates, false, encodedIssuerName, checker,
- keepGoing);
- if (rv != Success) {
- return rv;
- }
- }
- }
- return Success;
- }
- Result
- NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
- const CertPolicyId& policy,
- Input candidateCertDER,
- /*out*/ TrustLevel& trustLevel)
- {
- // XXX: This would be cleaner and more efficient if we could get the trust
- // information without constructing a CERTCertificate here, but NSS doesn't
- // expose it in any other easy-to-use fashion. The use of
- // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
- // performance problem because NSS will just find the existing
- // CERTCertificate in its in-memory cache and return it.
- SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
- UniqueCERTCertificate candidateCert(
- CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
- nullptr, false, true));
- if (!candidateCert) {
- return MapPRErrorCodeToResult(PR_GetError());
- }
- // Check the certificate against the OneCRL cert blocklist
- if (!mCertBlocklist) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- // The certificate blocklist currently only applies to TLS server
- // certificates.
- if (mCertDBTrustType == trustSSL) {
- bool isCertRevoked;
- nsresult nsrv = mCertBlocklist->IsCertRevoked(
- candidateCert->derIssuer.data,
- candidateCert->derIssuer.len,
- candidateCert->serialNumber.data,
- candidateCert->serialNumber.len,
- candidateCert->derSubject.data,
- candidateCert->derSubject.len,
- candidateCert->derPublicKey.data,
- candidateCert->derPublicKey.len,
- &isCertRevoked);
- if (NS_FAILED(nsrv)) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- if (isCertRevoked) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: certificate is in blocklist"));
- return Result::ERROR_REVOKED_CERTIFICATE;
- }
- }
- // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
- // SECSuccess means that there is a trust record and SECFailure means there
- // is not a trust record. I looked at NSS's internal uses of
- // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
- // "We have a trust record."
- CERTCertTrust trust;
- if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
- uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
- // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
- // because we can have active distrust for either type of cert. Note that
- // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
- // relevant trust bit isn't set then that means the cert must be considered
- // distrusted.
- uint32_t relevantTrustBit =
- endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA
- : CERTDB_TRUSTED;
- if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
- == CERTDB_TERMINAL_RECORD) {
- trustLevel = TrustLevel::ActivelyDistrusted;
- return Success;
- }
- // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Goanna hasn't
- // needed to consider end-entity certs to be their own trust anchors since
- // Goanna implemented nsICertOverrideService.
- // Of course, for this to work as expected, we need to make sure we're
- // inquiring about the trust of a CA and not an end-entity. If an end-entity
- // has the CERTDB_TRUSTED_CA bit set, Gecko does not consider it to be a
- // trust anchor; it must inherit its trust.
- if (flags & CERTDB_TRUSTED_CA && endEntityOrCA == EndEntityOrCA::MustBeCA) {
- if (policy.IsAnyPolicy()) {
- trustLevel = TrustLevel::TrustAnchor;
- return Success;
- }
- if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
- trustLevel = TrustLevel::TrustAnchor;
- return Success;
- }
- }
- }
- trustLevel = TrustLevel::InheritsTrust;
- return Success;
- }
- Result
- NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
- /*out*/ uint8_t* digestBuf, size_t digestBufLen)
- {
- return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
- }
- static PRIntervalTime
- OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
- {
- switch (ocspFetching) {
- case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
- return PR_SecondsToInterval(2);
- case NSSCertDBTrustDomain::FetchOCSPForEV:
- case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
- return PR_SecondsToInterval(10);
- // The rest of these are error cases. Assert in debug builds, but return
- // the default value corresponding to 2 seconds in release builds.
- case NSSCertDBTrustDomain::NeverFetchOCSP:
- case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
- PR_NOT_REACHED("we should never see this OCSPFetching type here");
- break;
- }
- PR_NOT_REACHED("we're not handling every OCSPFetching type");
- return PR_SecondsToInterval(2);
- }
- // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
- // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
- // Success with url == nullptr when an OCSP URI was not found, and Success with
- // url != nullptr when an OCSP URI was found. The output url will be owned
- // by the arena.
- static Result
- GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
- Input aiaExtension,
- /*out*/ char const*& url)
- {
- MOZ_ASSERT(arena.get());
- if (!arena.get()) {
- return Result::FATAL_ERROR_INVALID_ARGS;
- }
- url = nullptr;
- SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
- CERTAuthInfoAccess** aia =
- CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
- if (!aia) {
- return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
- }
- for (size_t i = 0; aia[i]; ++i) {
- if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
- // NSS chooses the **last** OCSP URL; we choose the **first**
- CERTGeneralName* current = aia[i]->location;
- if (!current) {
- continue;
- }
- do {
- if (current->type == certURI) {
- const SECItem& location = current->name.other;
- // (location.len + 1) must be small enough to fit into a uint32_t,
- // but we limit it to a smaller bound to reduce OOM risk.
- if (location.len > 1024 || memchr(location.data, 0, location.len)) {
- // Reject embedded nulls. (NSS doesn't do this)
- return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
- }
- // Copy the non-null-terminated SECItem into a null-terminated string.
- char* nullTerminatedURL(
- static_cast<char*>(PORT_ArenaAlloc(arena.get(), location.len + 1)));
- if (!nullTerminatedURL) {
- return Result::FATAL_ERROR_NO_MEMORY;
- }
- memcpy(nullTerminatedURL, location.data, location.len);
- nullTerminatedURL[location.len] = 0;
- url = nullTerminatedURL;
- return Success;
- }
- current = CERT_GetNextGeneralName(current);
- } while (current != aia[i]->location);
- }
- }
- return Success;
- }
- Result
- NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
- const CertID& certID, Time time,
- Duration validityDuration,
- /*optional*/ const Input* stapledOCSPResponse,
- /*optional*/ const Input* aiaExtension,
- /*optional*/ const Input* sctExtension)
- {
- // Actively distrusted certificates will have already been blocked by
- // GetCertTrust.
- // TODO: need to verify that IsRevoked isn't called for trust anchors AND
- // that that fact is documented in mozillapkix.
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
- // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
- // Since this affects EV there is no reason why DV should be more strict
- // so all intermediatates are allowed to have OCSP responses up to one year
- // old.
- uint16_t maxOCSPLifetimeInDays = 10;
- if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
- maxOCSPLifetimeInDays = 365;
- }
- // If we have a stapled OCSP response then the verification of that response
- // determines the result unless the OCSP response is expired. We make an
- // exception for expired responses because some servers, nginx in particular,
- // are known to serve expired responses due to bugs.
- // We keep track of the result of verifying the stapled response but don't
- // immediately return failure if the response has expired.
- //
- // We only set the OCSP stapling status if we're validating the end-entity
- // certificate. Non-end-entity certificates would always be
- // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
- Result stapledOCSPResponseResult = Success;
- if (stapledOCSPResponse) {
- PR_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
- bool expired;
- stapledOCSPResponseResult =
- VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
- maxOCSPLifetimeInDays,
- *stapledOCSPResponse,
- ResponseWasStapled, expired);
- if (stapledOCSPResponseResult == Success) {
- // stapled OCSP response present and good
- mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: stapled OCSP response: good"));
- return Success;
- }
- if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
- expired) {
- // stapled OCSP response present but expired
- mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: expired stapled OCSP response"));
- } else {
- // stapled OCSP response present but invalid for some reason
- mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
- return stapledOCSPResponseResult;
- }
- } else if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
- // no stapled OCSP response
- mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: no stapled OCSP response"));
- }
- Result cachedResponseResult = Success;
- Time cachedResponseValidThrough(Time::uninitialized);
- bool cachedResponsePresent = mOCSPCache.Get(certID, mOriginAttributes,
- cachedResponseResult,
- cachedResponseValidThrough);
- if (cachedResponsePresent) {
- if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: cached OCSP response: good"));
- return Success;
- }
- // If we have a cached revoked response, use it.
- if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
- return Result::ERROR_REVOKED_CERTIFICATE;
- }
- // The cached response may indicate an unknown certificate or it may be
- // expired. Don't return with either of these statuses yet - we may be
- // able to fetch a more recent one.
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: cached OCSP response: error %d",
- cachedResponseResult));
- // When a good cached response has expired, it is more convenient
- // to convert that to an error code and just deal with
- // cachedResponseResult from here on out.
- if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
- cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
- }
- // We may have a cached indication of server failure. Ignore it if
- // it has expired.
- if (cachedResponseResult != Success &&
- cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
- cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
- cachedResponseValidThrough < time) {
- cachedResponseResult = Success;
- cachedResponsePresent = false;
- }
- } else {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: no cached OCSP response"));
- }
- // At this point, if and only if cachedErrorResult is Success, there was no
- // cached response.
- PR_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
- (cachedResponsePresent && cachedResponseResult != Success));
- // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
- bool blocklistIsFresh;
- nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
- if (NS_FAILED(nsrv)) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- // TODO: We still need to handle the fallback for expired responses. But,
- // if/when we disable OCSP fetching by default, it would be ambiguous whether
- // security.OCSP.enable==0 means "I want the default" or "I really never want
- // you to ever fetch OCSP."
- Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
- if ((mOCSPFetching == NeverFetchOCSP) ||
- (validityDuration < shortLifetime) ||
- (endEntityOrCA == EndEntityOrCA::MustBeCA &&
- (mOCSPFetching == FetchOCSPForDVHardFail ||
- mOCSPFetching == FetchOCSPForDVSoftFail ||
- blocklistIsFresh))) {
- // We're not going to be doing any fetching, so if there was a cached
- // "unknown" response, say so.
- if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
- return Result::ERROR_OCSP_UNKNOWN_CERT;
- }
- // If we're doing hard-fail, we want to know if we have a cached response
- // that has expired.
- if (mOCSPFetching == FetchOCSPForDVHardFail &&
- cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
- return Result::ERROR_OCSP_OLD_RESPONSE;
- }
- return Success;
- }
- if (mOCSPFetching == LocalOnlyOCSPForEV) {
- if (cachedResponseResult != Success) {
- return cachedResponseResult;
- }
- return Result::ERROR_OCSP_UNKNOWN_CERT;
- }
- UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
- if (!arena) {
- return Result::FATAL_ERROR_NO_MEMORY;
- }
- Result rv;
- const char* url = nullptr; // owned by the arena
- if (aiaExtension) {
- rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, url);
- if (rv != Success) {
- return rv;
- }
- }
- if (!url) {
- if (mOCSPFetching == FetchOCSPForEV ||
- cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
- return Result::ERROR_OCSP_UNKNOWN_CERT;
- }
- if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
- return Result::ERROR_OCSP_OLD_RESPONSE;
- }
- if (stapledOCSPResponseResult != Success) {
- return stapledOCSPResponseResult;
- }
- // Nothing to do if we don't have an OCSP responder URI for the cert; just
- // assume it is good. Note that this is the confusing, but intended,
- // interpretation of "strict" revocation checking in the face of a
- // certificate that lacks an OCSP responder URI.
- return Success;
- }
- // Only request a response if we didn't have a cached indication of failure
- // (don't keep requesting responses from a failing server).
- Input response;
- bool attemptedRequest;
- if (cachedResponseResult == Success ||
- cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
- cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
- uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
- size_t ocspRequestLength;
- rv = CreateEncodedOCSPRequest(*this, certID, ocspRequest,
- ocspRequestLength);
- if (rv != Success) {
- return rv;
- }
- SECItem ocspRequestItem = {
- siBuffer,
- ocspRequest,
- static_cast<unsigned int>(ocspRequestLength)
- };
- // Owned by arena
- SECItem* responseSECItem = nullptr;
- Result tempRV =
- DoOCSPRequest(arena, url, mOriginAttributes, &ocspRequestItem,
- OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
- mOCSPGetConfig == CertVerifier::ocspGetEnabled,
- responseSECItem);
- MOZ_ASSERT((tempRV != Success) || responseSECItem);
- if (tempRV != Success) {
- rv = tempRV;
- } else if (response.Init(responseSECItem->data, responseSECItem->len)
- != Success) {
- rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
- }
- attemptedRequest = true;
- } else {
- rv = cachedResponseResult;
- attemptedRequest = false;
- }
- if (response.GetLength() == 0) {
- Result error = rv;
- if (attemptedRequest) {
- Time timeout(time);
- if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
- }
- rv = mOCSPCache.Put(certID, mOriginAttributes, error, time, timeout);
- if (rv != Success) {
- return rv;
- }
- }
- if (mOCSPFetching != FetchOCSPForDVSoftFail) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: returning SECFailure after "
- "OCSP request failure"));
- return error;
- }
- if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: returning SECFailure from cached "
- "response after OCSP request failure"));
- return cachedResponseResult;
- }
- if (stapledOCSPResponseResult != Success) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: returning SECFailure from expired "
- "stapled response after OCSP request failure"));
- return stapledOCSPResponseResult;
- }
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: returning SECSuccess after "
- "OCSP request failure"));
- return Success; // Soft fail -> success :(
- }
- // If the response from the network has expired but indicates a revoked
- // or unknown certificate, PR_GetError() will return the appropriate error.
- // We actually ignore expired here.
- bool expired;
- rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
- maxOCSPLifetimeInDays,
- response, ResponseIsFromNetwork,
- expired);
- if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
- return rv;
- }
- if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
- rv == Result::ERROR_REVOKED_CERTIFICATE) {
- return rv;
- }
- if (stapledOCSPResponseResult != Success) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
- "response after OCSP request verification failure"));
- return stapledOCSPResponseResult;
- }
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: end of CheckRevocation"));
- return Success; // Soft fail -> success :(
- }
- Result
- NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
- const CertID& certID, Time time, uint16_t maxLifetimeInDays,
- Input encodedResponse, EncodedResponseSource responseSource,
- /*out*/ bool& expired)
- {
- Time thisUpdate(Time::uninitialized);
- Time validThrough(Time::uninitialized);
- // We use a try and fallback approach which first mandates good signature
- // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
- // OCSP response signing certificate was issued with a SHA-1 signature,
- // verification initially fails. We cache the failure and then re-use that
- // result even when doing fallback (i.e. when weak signature digest algorithms
- // should succeed). To address this we use an OCSPVerificationTrustDomain
- // here, rather than using *this, to ensure verification succeeds for all
- // allowed signature digest algorithms.
- OCSPVerificationTrustDomain trustDomain(*this);
- Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
- maxLifetimeInDays, encodedResponse,
- expired, &thisUpdate, &validThrough);
- // If a response was stapled and expired, we don't want to cache it. Return
- // early to simplify the logic here.
- if (responseSource == ResponseWasStapled && expired) {
- PR_ASSERT(rv != Success);
- return rv;
- }
- // validThrough is only trustworthy if the response successfully verifies
- // or it indicates a revoked or unknown certificate.
- // If this isn't the case, store an indication of failure (to prevent
- // repeatedly requesting a response from a failing server).
- if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
- rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
- validThrough = time;
- if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
- }
- }
- if (responseSource == ResponseIsFromNetwork ||
- rv == Success ||
- rv == Result::ERROR_REVOKED_CERTIFICATE ||
- rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: caching OCSP response"));
- Result putRV = mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate,
- validThrough);
- if (putRV != Success) {
- return putRV;
- }
- }
- return rv;
- }
- static const uint8_t CNNIC_ROOT_CA_SUBJECT_DATA[] =
- "\x30\x32\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x0E\x30"
- "\x0C\x06\x03\x55\x04\x0A\x13\x05\x43\x4E\x4E\x49\x43\x31\x13\x30\x11\x06"
- "\x03\x55\x04\x03\x13\x0A\x43\x4E\x4E\x49\x43\x20\x52\x4F\x4F\x54";
- static const uint8_t CNNIC_EV_ROOT_CA_SUBJECT_DATA[] =
- "\x30\x81\x8A\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x32"
- "\x30\x30\x06\x03\x55\x04\x0A\x0C\x29\x43\x68\x69\x6E\x61\x20\x49\x6E\x74"
- "\x65\x72\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F"
- "\x72\x6D\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x31\x47\x30\x45"
- "\x06\x03\x55\x04\x03\x0C\x3E\x43\x68\x69\x6E\x61\x20\x49\x6E\x74\x65\x72"
- "\x6E\x65\x74\x20\x4E\x65\x74\x77\x6F\x72\x6B\x20\x49\x6E\x66\x6F\x72\x6D"
- "\x61\x74\x69\x6F\x6E\x20\x43\x65\x6E\x74\x65\x72\x20\x45\x56\x20\x43\x65"
- "\x72\x74\x69\x66\x69\x63\x61\x74\x65\x73\x20\x52\x6F\x6F\x74";
- class WhitelistedCNNICHashBinarySearchComparator
- {
- public:
- explicit WhitelistedCNNICHashBinarySearchComparator(const uint8_t* aTarget,
- size_t aTargetLength)
- : mTarget(aTarget)
- {
- MOZ_ASSERT(aTargetLength == CNNIC_WHITELIST_HASH_LEN,
- "Hashes should be of the same length.");
- }
- int operator()(const WhitelistedCNNICHash val) const {
- return memcmp(mTarget, val.hash, CNNIC_WHITELIST_HASH_LEN);
- }
- private:
- const uint8_t* mTarget;
- };
- static bool
- CertIsStartComOrWoSign(const CERTCertificate* cert)
- {
- for (const DataAndLength& dn : StartComAndWoSignDNs) {
- if (cert->derSubject.len == dn.len &&
- PodEqual(cert->derSubject.data, dn.data, dn.len)) {
- return true;
- }
- }
- return false;
- }
- // If a certificate in the given chain appears to have been issued by one of
- // seven roots operated by StartCom and WoSign that are not trusted to issue new
- // certificates, verify that the end-entity has a notBefore date before 21
- // October 2016. If the value of notBefore is after this time, the chain is not
- // valid.
- // (NB: While there are seven distinct roots being checked for, two of them
- // share distinguished names, resulting in six distinct distinguished names to
- // actually look for.)
- static Result
- CheckForStartComOrWoSign(const UniqueCERTCertList& certChain)
- {
- if (CERT_LIST_EMPTY(certChain)) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- const CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certChain);
- if (!endEntityNode || !endEntityNode->cert) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- PRTime notBefore;
- PRTime notAfter;
- if (CERT_GetCertTimes(endEntityNode->cert, ¬Before, ¬After)
- != SECSuccess) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
- // (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
- static const PRTime OCTOBER_21_2016 = 1477008000000000;
- if (notBefore <= OCTOBER_21_2016) {
- return Success;
- }
- for (const CERTCertListNode* node = CERT_LIST_HEAD(certChain);
- !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
- if (!node || !node->cert) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- if (CertIsStartComOrWoSign(node->cert)) {
- return Result::ERROR_REVOKED_CERTIFICATE;
- }
- }
- return Success;
- }
- Result
- NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray,
- Time time,
- const CertPolicyId& requiredPolicy)
- {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: IsChainValid"));
- UniqueCERTCertList certList;
- SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray,
- certList);
- if (srv != SECSuccess) {
- return MapPRErrorCodeToResult(PR_GetError());
- }
- if (CERT_LIST_EMPTY(certList)) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- Result rv = CheckForStartComOrWoSign(certList);
- if (rv != Success) {
- return rv;
- }
- // If the certificate appears to have been issued by a CNNIC root, only allow
- // it if it is on the whitelist.
- CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
- if (!rootNode) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- CERTCertificate* root = rootNode->cert;
- if (!root) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- if ((root->derSubject.len == sizeof(CNNIC_ROOT_CA_SUBJECT_DATA) - 1 &&
- memcmp(root->derSubject.data, CNNIC_ROOT_CA_SUBJECT_DATA,
- root->derSubject.len) == 0) ||
- (root->derSubject.len == sizeof(CNNIC_EV_ROOT_CA_SUBJECT_DATA) - 1 &&
- memcmp(root->derSubject.data, CNNIC_EV_ROOT_CA_SUBJECT_DATA,
- root->derSubject.len) == 0)) {
- CERTCertListNode* certNode = CERT_LIST_HEAD(certList);
- if (!certNode) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- CERTCertificate* cert = certNode->cert;
- if (!cert) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- Digest digest;
- nsresult nsrv = digest.DigestBuf(SEC_OID_SHA256, cert->derCert.data,
- cert->derCert.len);
- if (NS_FAILED(nsrv)) {
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- const uint8_t* certHash(
- BitwiseCast<uint8_t*, unsigned char*>(digest.get().data));
- size_t certHashLen = digest.get().len;
- size_t unused;
- if (!mozilla::BinarySearchIf(WhitelistedCNNICHashes, 0,
- ArrayLength(WhitelistedCNNICHashes),
- WhitelistedCNNICHashBinarySearchComparator(
- certHash, certHashLen),
- &unused)) {
- return Result::ERROR_REVOKED_CERTIFICATE;
- }
- }
- bool isBuiltInRoot = false;
- rv = IsCertBuiltInRoot(root, isBuiltInRoot);
- if (rv != Success) {
- return rv;
- }
- mBuiltChain = Move(certList);
- return Success;
- }
- Result
- NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
- EndEntityOrCA endEntityOrCA,
- Time notBefore)
- {
- // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
- static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
- if (aAlg == DigestAlgorithm::sha1) {
- switch (mSHA1Mode) {
- case CertVerifier::SHA1Mode::Forbidden:
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
- return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
- case CertVerifier::SHA1Mode::ImportedRootOrBefore2016:
- if (JANUARY_FIRST_2016 <= notBefore) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Post-2015 SHA-1 certificate rejected"));
- return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
- }
- break;
- case CertVerifier::SHA1Mode::Allowed:
- // Enforcing that the resulting chain uses an imported root is only
- // possible at a higher level. This is done in CertVerifier::VerifyCert.
- case CertVerifier::SHA1Mode::ImportedRoot:
- default:
- break;
- // MSVC warns unless we explicitly handle this now-unused option.
- case CertVerifier::SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
- MOZ_ASSERT_UNREACHABLE("unexpected SHA1Mode type");
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- }
- return Success;
- }
- Result
- NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
- EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
- {
- if (modulusSizeInBits < mMinRSABits) {
- return Result::ERROR_INADEQUATE_KEY_SIZE;
- }
- return Success;
- }
- Result
- NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
- const SignedDigest& signedDigest,
- Input subjectPublicKeyInfo)
- {
- return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
- mPinArg);
- }
- Result
- NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
- EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve)
- {
- switch (curve) {
- case NamedCurve::secp256r1: // fall through
- case NamedCurve::secp384r1: // fall through
- case NamedCurve::secp521r1:
- return Success;
- }
- return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
- }
- Result
- NSSCertDBTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
- Input subjectPublicKeyInfo)
- {
- return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
- mPinArg);
- }
- Result
- NSSCertDBTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
- EndEntityOrCA endEntityOrCA,
- KeyPurposeId keyPurpose)
- {
- if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
- return Success;
- }
- if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
- return Success;
- }
- Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
- Time::ONE_DAY_IN_SECONDS);
- Duration maxValidityDuration(UINT64_MAX);
- Duration validityDuration(notBefore, notAfter);
- switch (mValidityCheckingMode) {
- case ValidityCheckingMode::CheckingOff:
- return Success;
- case ValidityCheckingMode::CheckForEV:
- // The EV Guidelines say the maximum is 27 months, but we use a slightly
- // higher limit here to (hopefully) minimize compatibility breakage.
- maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
- break;
- default:
- PR_NOT_REACHED("We're not handling every ValidityCheckingMode type");
- }
- if (validityDuration > maxValidityDuration) {
- return Result::ERROR_VALIDITY_TOO_LONG;
- }
- return Success;
- }
- Result
- NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
- /*out*/ bool& matches)
- {
- // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
- static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
- // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
- static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
- switch (mNetscapeStepUpPolicy) {
- case NetscapeStepUpPolicy::AlwaysMatch:
- matches = true;
- return Success;
- case NetscapeStepUpPolicy::MatchBefore23August2016:
- matches = notBefore < AUGUST_23_2016;
- return Success;
- case NetscapeStepUpPolicy::MatchBefore23August2015:
- matches = notBefore < AUGUST_23_2015;
- return Success;
- case NetscapeStepUpPolicy::NeverMatch:
- matches = false;
- return Success;
- default:
- MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
- }
- return Result::FATAL_ERROR_LIBRARY_FAILURE;
- }
- void
- NSSCertDBTrustDomain::ResetAccumulatedState()
- {
- mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
- mSCTListFromOCSPStapling = nullptr;
- mSCTListFromCertificate = nullptr;
- }
- static Input
- SECItemToInput(const UniqueSECItem& item)
- {
- Input result;
- if (item) {
- MOZ_ASSERT(item->type == siBuffer);
- Result rv = result.Init(item->data, item->len);
- // As used here, |item| originally comes from an Input,
- // so there should be no issues converting it back.
- MOZ_ASSERT(rv == Success);
- Unused << rv; // suppresses warnings in release builds
- }
- return result;
- }
- Input
- NSSCertDBTrustDomain::GetSCTListFromCertificate() const
- {
- return SECItemToInput(mSCTListFromCertificate);
- }
- Input
- NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const
- {
- return SECItemToInput(mSCTListFromOCSPStapling);
- }
- void
- NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
- Input extensionData)
- {
- UniqueSECItem* out = nullptr;
- switch (extension) {
- case AuxiliaryExtension::EmbeddedSCTList:
- out = &mSCTListFromCertificate;
- break;
- case AuxiliaryExtension::SCTListFromOCSPResponse:
- out = &mSCTListFromOCSPStapling;
- break;
- default:
- MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
- }
- if (out) {
- SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
- out->reset(SECITEM_DupItem(&extensionDataItem));
- }
- }
- SECStatus
- InitializeNSS(const nsACString& dir, bool readOnly, bool loadPKCS11Modules)
- {
- MOZ_ASSERT(NS_IsMainThread());
- // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
- // module by NSS_Initialize because we will load it in InstallLoadableRoots
- // later. It also allows us to work around a bug in the system NSS in
- // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
- // "/usr/lib/nss/libnssckbi.so".
- uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
- if (readOnly) {
- flags |= NSS_INIT_READONLY;
- }
- if (!loadPKCS11Modules) {
- flags |= NSS_INIT_NOMODDB;
- }
- nsAutoCString dbTypeAndDirectory;
- #ifdef MOZ_SECURITY_SQLSTORE
- // Not strictly necessary with current NSS versions, but can't hurt to be explicit.
- dbTypeAndDirectory.Append("sql:");
- #else
- dbTypeAndDirectory.Append("dbm:");
- #endif
- dbTypeAndDirectory.Append(dir);
- return ::NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags);
- }
- void
- DisableMD5()
- {
- NSS_SetAlgorithmPolicy(SEC_OID_MD5,
- 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
- NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
- 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
- NSS_SetAlgorithmPolicy(SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC,
- 0, NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
- }
- SECStatus
- LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8)
- {
- PR_ASSERT(modNameUTF8);
- if (!modNameUTF8) {
- PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
- return SECFailure;
- }
- UniquePtr<char, void(&)(char*)>
- fullLibraryPath(PR_GetLibraryName(dir, "nssckbi"), PR_FreeLibraryName);
- if (!fullLibraryPath) {
- return SECFailure;
- }
- // Escape the \ and " characters.
- nsAutoCString escapedFullLibraryPath(fullLibraryPath.get());
- escapedFullLibraryPath.ReplaceSubstring("\\", "\\\\");
- escapedFullLibraryPath.ReplaceSubstring("\"", "\\\"");
- if (escapedFullLibraryPath.IsEmpty()) {
- return SECFailure;
- }
- // If a module exists with the same name, delete it.
- int modType;
- SECMOD_DeleteModule(modNameUTF8, &modType);
- nsAutoCString pkcs11ModuleSpec;
- pkcs11ModuleSpec.AppendPrintf("name=\"%s\" library=\"%s\"", modNameUTF8,
- escapedFullLibraryPath.get());
- if (pkcs11ModuleSpec.IsEmpty()) {
- return SECFailure;
- }
- UniqueSECMODModule rootsModule(
- SECMOD_LoadUserModule(const_cast<char*>(pkcs11ModuleSpec.get()), nullptr,
- false));
- if (!rootsModule) {
- return SECFailure;
- }
- if (!rootsModule->loaded) {
- PR_SetError(PR_INVALID_STATE_ERROR, 0);
- return SECFailure;
- }
- return SECSuccess;
- }
- void
- UnloadLoadableRoots(const char* modNameUTF8)
- {
- PR_ASSERT(modNameUTF8);
- UniqueSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
- if (rootsModule) {
- SECMOD_UnloadUserModule(rootsModule.get());
- }
- }
- nsresult
- DefaultServerNicknameForCert(const CERTCertificate* cert,
- /*out*/ nsCString& nickname)
- {
- MOZ_ASSERT(cert);
- NS_ENSURE_ARG_POINTER(cert);
- UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
- if (!baseName) {
- baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
- }
- if (!baseName) {
- baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
- }
- if (!baseName) {
- baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
- }
- if (!baseName) {
- baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
- }
- if (!baseName) {
- baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
- }
- if (!baseName) {
- return NS_ERROR_FAILURE;
- }
- // This function is only used in contexts where a failure to find a suitable
- // nickname does not block the overall task from succeeding.
- // As such, we use an arbitrary limit to prevent this nickname searching
- // process from taking forever.
- static const uint32_t ARBITRARY_LIMIT = 500;
- for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
- nickname = baseName.get();
- if (count != 1) {
- nickname.AppendPrintf(" #%u", count);
- }
- if (nickname.IsEmpty()) {
- return NS_ERROR_FAILURE;
- }
- bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
- cert->dbhandle);
- if (!conflict) {
- return NS_OK;
- }
- }
- return NS_ERROR_FAILURE;
- }
- /**
- * Given a list of certificates representing a verified certificate path from an
- * end-entity certificate to a trust anchor, imports the intermediate
- * certificates into the permanent certificate database. This is an attempt to
- * cope with misconfigured servers that don't include the appropriate
- * intermediate certificates in the TLS handshake.
- *
- * @param certList the verified certificate list
- */
- void
- SaveIntermediateCerts(const UniqueCERTCertList& certList)
- {
- if (!certList) {
- return;
- }
- UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
- if (!slot) {
- return;
- }
- bool isEndEntity = true;
- for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
- !CERT_LIST_END(node, certList);
- node = CERT_LIST_NEXT(node)) {
- if (isEndEntity) {
- // Skip the end-entity; we only want to store intermediates
- isEndEntity = false;
- continue;
- }
- if (node->cert->slot) {
- // This cert was found on a token; no need to remember it in the permanent
- // database.
- continue;
- }
- if (node->cert->isperm) {
- // We don't need to remember certs already stored in perm db.
- continue;
- }
- // No need to save the trust anchor - it's either already a permanent
- // certificate or it's the Microsoft Family Safety root or an enterprise
- // root temporarily imported via the child mode or enterprise root features.
- // We don't want to import these because they're intended to be temporary
- // (and because importing them happens to reset their trust settings, which
- // breaks these features).
- if (node == CERT_LIST_TAIL(certList)) {
- continue;
- }
- nsAutoCString nickname;
- nsresult rv = DefaultServerNicknameForCert(node->cert, nickname);
- if (NS_FAILED(rv)) {
- continue;
- }
- // As mentioned in the documentation of this function, we're importing only
- // to cope with misconfigured servers. As such, we ignore the return value
- // below, since it doesn't really matter if the import fails.
- Unused << PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
- nickname.get(), false);
- }
- }
- } } // namespace mozilla::psm
|