Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / security / certverifier / NSSCertDBTrustDomain.cpp
blobd8e327888011a1fd6cf7cdcfcd33b49cd122a4c5
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/. */
7 #include "NSSCertDBTrustDomain.h"
9 #include <stdint.h>
10 #include <utility>
12 #include "CRLiteTimestamp.h"
13 #include "ExtendedValidation.h"
14 #include "MultiLogCTVerifier.h"
15 #include "NSSErrorsService.h"
16 #include "PublicKeyPinningService.h"
17 #include "cert.h"
18 #include "cert_storage/src/cert_storage.h"
19 #include "certdb.h"
20 #include "mozilla/AppShutdown.h"
21 #include "mozilla/Assertions.h"
22 #include "mozilla/Casting.h"
23 #include "mozilla/ClearOnShutdown.h"
24 #include "mozilla/Logging.h"
25 #include "mozilla/PodOperations.h"
26 #include "mozilla/Services.h"
27 #include "mozilla/StaticPrefs_security.h"
28 #include "mozilla/SyncRunnable.h"
29 #include "mozilla/TimeStamp.h"
30 #include "mozilla/Unused.h"
31 #include "mozpkix/Result.h"
32 #include "mozpkix/pkix.h"
33 #include "mozpkix/pkixnss.h"
34 #include "mozpkix/pkixutil.h"
35 #include "nsCRTGlue.h"
36 #include "nsIObserverService.h"
37 #include "nsNetCID.h"
38 #include "nsNSSCallbacks.h"
39 #include "nsNSSCertHelper.h"
40 #include "nsNSSCertificate.h"
41 #include "nsNSSCertificateDB.h"
42 #include "nsNSSIOLayer.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"
52 #include "TrustOverrideUtils.h"
53 #include "TrustOverride-AppleGoogleDigiCertData.inc"
54 #include "TrustOverride-SymantecData.inc"
56 using namespace mozilla;
57 using namespace mozilla::ct;
58 using namespace mozilla::pkix;
60 extern LazyLogModule gCertVerifierLog;
62 static const uint64_t ServerFailureDelaySeconds = 5 * 60;
64 namespace mozilla {
65 namespace psm {
67 NSSCertDBTrustDomain::NSSCertDBTrustDomain(
68 SECTrustType certDBTrustType, OCSPFetching ocspFetching,
69 OCSPCache& ocspCache,
70 /*optional but shouldn't be*/ void* pinArg, TimeDuration ocspTimeoutSoft,
71 TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays,
72 unsigned int minRSABits, ValidityCheckingMode validityCheckingMode,
73 NetscapeStepUpPolicy netscapeStepUpPolicy, CRLiteMode crliteMode,
74 const OriginAttributes& originAttributes,
75 const Vector<Input>& thirdPartyRootInputs,
76 const Vector<Input>& thirdPartyIntermediateInputs,
77 const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
78 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
79 /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
80 /*optional*/ const char* hostname)
81 : mCertDBTrustType(certDBTrustType),
82 mOCSPFetching(ocspFetching),
83 mOCSPCache(ocspCache),
84 mPinArg(pinArg),
85 mOCSPTimeoutSoft(ocspTimeoutSoft),
86 mOCSPTimeoutHard(ocspTimeoutHard),
87 mCertShortLifetimeInDays(certShortLifetimeInDays),
88 mMinRSABits(minRSABits),
89 mValidityCheckingMode(validityCheckingMode),
90 mNetscapeStepUpPolicy(netscapeStepUpPolicy),
91 mCRLiteMode(crliteMode),
92 mSawDistrustedCAByPolicyError(false),
93 mOriginAttributes(originAttributes),
94 mThirdPartyRootInputs(thirdPartyRootInputs),
95 mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs),
96 mExtraCertificates(extraCertificates),
97 mBuiltChain(builtChain),
98 mIsBuiltChainRootBuiltInRoot(false),
99 mPinningTelemetryInfo(pinningTelemetryInfo),
100 mHostname(hostname),
101 mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
102 mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED),
103 mSCTListFromCertificate(),
104 mSCTListFromOCSPStapling(),
105 mBuiltInRootsModule(SECMOD_FindModule(kRootModuleName)),
106 mOCSPFetchStatus(OCSPFetchStatus::NotFetched) {}
108 static void FindRootsWithSubject(UniqueSECMODModule& rootsModule,
109 SECItem subject,
110 /*out*/ nsTArray<nsTArray<uint8_t>>& roots) {
111 MOZ_ASSERT(rootsModule);
112 AutoSECMODListReadLock lock;
113 for (int slotIndex = 0; slotIndex < rootsModule->slotCount; slotIndex++) {
114 CERTCertificateList* rawResults = nullptr;
115 if (PK11_FindRawCertsWithSubject(rootsModule->slots[slotIndex], &subject,
116 &rawResults) != SECSuccess) {
117 continue;
119 // rawResults == nullptr means we didn't find any matching certificates
120 if (!rawResults) {
121 continue;
123 UniqueCERTCertificateList results(rawResults);
124 for (int certIndex = 0; certIndex < results->len; certIndex++) {
125 nsTArray<uint8_t> root;
126 root.AppendElements(results->certs[certIndex].data,
127 results->certs[certIndex].len);
128 roots.AppendElement(std::move(root));
133 // A self-signed issuer certificate should never be necessary in order to build
134 // a trusted certificate chain unless it is a trust anchor. This is because if
135 // it were necessary, there would exist another certificate with the same
136 // subject and public key that is also a valid issing certificate. Given this
137 // certificate, it is possible to build another chain using just it instead of
138 // it and the self-signed certificate. This is only true as long as the
139 // certificate extensions we support are restrictive rather than additive in
140 // terms of the rest of the chain (for example, we don't support policy mapping
141 // and we ignore any SCT information in intermediates).
142 static bool ShouldSkipSelfSignedNonTrustAnchor(TrustDomain& trustDomain,
143 Input certDER) {
144 BackCert cert(certDER, EndEntityOrCA::MustBeCA, nullptr);
145 if (cert.Init() != Success) {
146 return false; // turn any failures into "don't skip trying this cert"
148 // If subject != issuer, this isn't a self-signed cert.
149 if (!InputsAreEqual(cert.GetSubject(), cert.GetIssuer())) {
150 return false;
152 TrustLevel trust;
153 if (trustDomain.GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy,
154 certDER, trust) != Success) {
155 return false;
157 // If the trust for this certificate is anything other than "inherit", we want
158 // to process it like normal.
159 if (trust != TrustLevel::InheritsTrust) {
160 return false;
162 if (VerifySignedData(trustDomain, cert.GetSignedData(),
163 cert.GetSubjectPublicKeyInfo()) != Success) {
164 return false;
166 // This is a self-signed, non-trust-anchor certificate, so we shouldn't use it
167 // for path building. See bug 1056341.
168 return true;
171 static Result CheckCandidates(TrustDomain& trustDomain,
172 TrustDomain::IssuerChecker& checker,
173 nsTArray<Input>& candidates,
174 Input* nameConstraintsInputPtr, bool& keepGoing) {
175 for (Input candidate : candidates) {
176 // Stop path building if the program is shutting down.
177 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
178 keepGoing = false;
179 return Success;
181 if (ShouldSkipSelfSignedNonTrustAnchor(trustDomain, candidate)) {
182 continue;
184 Result rv = checker.Check(candidate, nameConstraintsInputPtr, keepGoing);
185 if (rv != Success) {
186 return rv;
188 if (!keepGoing) {
189 return Success;
193 return Success;
196 Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
197 IssuerChecker& checker, Time) {
198 SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
199 // Handle imposed name constraints, if any.
200 ScopedAutoSECItem nameConstraints;
201 Input nameConstraintsInput;
202 Input* nameConstraintsInputPtr = nullptr;
203 SECStatus srv =
204 CERT_GetImposedNameConstraints(&encodedIssuerNameItem, &nameConstraints);
205 if (srv == SECSuccess) {
206 if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len) !=
207 Success) {
208 return Result::FATAL_ERROR_LIBRARY_FAILURE;
210 nameConstraintsInputPtr = &nameConstraintsInput;
211 } else if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
212 return Result::FATAL_ERROR_LIBRARY_FAILURE;
215 // First try all relevant certificates known to Gecko, which avoids calling
216 // CERT_CreateSubjectCertList, because that can be expensive.
217 nsTArray<Input> geckoRootCandidates;
218 nsTArray<Input> geckoIntermediateCandidates;
220 if (!mCertStorage) {
221 return Result::FATAL_ERROR_LIBRARY_FAILURE;
223 nsTArray<uint8_t> subject;
224 subject.AppendElements(encodedIssuerName.UnsafeGetData(),
225 encodedIssuerName.GetLength());
226 nsTArray<nsTArray<uint8_t>> certs;
227 nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
228 if (NS_FAILED(rv)) {
229 return Result::FATAL_ERROR_LIBRARY_FAILURE;
231 for (auto& cert : certs) {
232 Input certDER;
233 Result rv = certDER.Init(cert.Elements(), cert.Length());
234 if (rv != Success) {
235 continue; // probably too big
237 // Currently we're only expecting intermediate certificates in cert storage.
238 geckoIntermediateCandidates.AppendElement(std::move(certDER));
241 // We might not have this module if e.g. we're on a Linux distribution that
242 // does something unexpected.
243 nsTArray<nsTArray<uint8_t>> builtInRoots;
244 if (mBuiltInRootsModule) {
245 FindRootsWithSubject(mBuiltInRootsModule, encodedIssuerNameItem,
246 builtInRoots);
247 for (const auto& root : builtInRoots) {
248 Input rootInput;
249 Result rv = rootInput.Init(root.Elements(), root.Length());
250 if (rv != Success) {
251 continue; // probably too big
253 geckoRootCandidates.AppendElement(rootInput);
255 } else {
256 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
257 ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module"));
260 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
261 BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr);
262 Result rv = root.Init();
263 if (rv != Success) {
264 continue;
266 // Filter out 3rd party roots that can't be issuers we're looking for
267 // because the subject distinguished name doesn't match. This prevents
268 // mozilla::pkix from accumulating spurious errors during path building.
269 if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) {
270 continue;
272 geckoRootCandidates.AppendElement(thirdPartyRootInput);
275 for (const auto& thirdPartyIntermediateInput :
276 mThirdPartyIntermediateInputs) {
277 BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA,
278 nullptr);
279 Result rv = intermediate.Init();
280 if (rv != Success) {
281 continue;
283 // Filter out 3rd party intermediates that can't be issuers we're looking
284 // for because the subject distinguished name doesn't match. This prevents
285 // mozilla::pkix from accumulating spurious errors during path building.
286 if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) {
287 continue;
289 geckoIntermediateCandidates.AppendElement(thirdPartyIntermediateInput);
292 if (mExtraCertificates.isSome()) {
293 for (const auto& extraCert : *mExtraCertificates) {
294 Input certInput;
295 Result rv = certInput.Init(extraCert.Elements(), extraCert.Length());
296 if (rv != Success) {
297 continue;
299 BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
300 rv = cert.Init();
301 if (rv != Success) {
302 continue;
304 // Filter out certificates that can't be issuers we're looking for because
305 // the subject distinguished name doesn't match. This prevents
306 // mozilla::pkix from accumulating spurious errors during path building.
307 if (!InputsAreEqual(encodedIssuerName, cert.GetSubject())) {
308 continue;
310 // We assume that extra certificates (presumably from the TLS handshake)
311 // are intermediates, since sending trust anchors would be superfluous.
312 geckoIntermediateCandidates.AppendElement(certInput);
316 // Try all root certs first and then all (presumably) intermediates.
317 geckoRootCandidates.AppendElements(std::move(geckoIntermediateCandidates));
319 bool keepGoing = true;
320 Result result = CheckCandidates(*this, checker, geckoRootCandidates,
321 nameConstraintsInputPtr, keepGoing);
322 if (result != Success) {
323 return result;
325 if (!keepGoing) {
326 return Success;
329 // Synchronously dispatch a task to the socket thread to find
330 // CERTCertificates with the given subject. This involves querying NSS
331 // structures and databases, so it should be done on the socket thread.
332 nsTArray<nsTArray<uint8_t>> nssRootCandidates;
333 nsTArray<nsTArray<uint8_t>> nssIntermediateCandidates;
334 RefPtr<Runnable> getCandidatesTask =
335 NS_NewRunnableFunction("NSSCertDBTrustDomain::FindIssuer", [&]() {
336 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
337 return;
339 // NSS seems not to differentiate between "no potential issuers found"
340 // and "there was an error trying to retrieve the potential issuers." We
341 // assume there was no error if CERT_CreateSubjectCertList returns
342 // nullptr.
343 UniqueCERTCertList candidates(
344 CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
345 &encodedIssuerNameItem, 0, false));
346 if (candidates) {
347 for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
348 !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
349 nsTArray<uint8_t> candidate;
350 candidate.AppendElements(n->cert->derCert.data,
351 n->cert->derCert.len);
352 if (n->cert->isRoot) {
353 nssRootCandidates.AppendElement(std::move(candidate));
354 } else {
355 nssIntermediateCandidates.AppendElement(std::move(candidate));
360 nsCOMPtr<nsIEventTarget> socketThread(
361 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
362 if (!socketThread) {
363 return Result::FATAL_ERROR_LIBRARY_FAILURE;
365 rv = SyncRunnable::DispatchToThread(socketThread, getCandidatesTask);
366 if (NS_FAILED(rv)) {
367 return Result::FATAL_ERROR_LIBRARY_FAILURE;
370 nsTArray<Input> nssCandidates;
371 for (const auto& rootCandidate : nssRootCandidates) {
372 Input certDER;
373 Result rv = certDER.Init(rootCandidate.Elements(), rootCandidate.Length());
374 if (rv != Success) {
375 continue; // probably too big
377 nssCandidates.AppendElement(std::move(certDER));
379 for (const auto& intermediateCandidate : nssIntermediateCandidates) {
380 Input certDER;
381 Result rv = certDER.Init(intermediateCandidate.Elements(),
382 intermediateCandidate.Length());
383 if (rv != Success) {
384 continue; // probably too big
386 nssCandidates.AppendElement(std::move(certDER));
389 return CheckCandidates(*this, checker, nssCandidates, nameConstraintsInputPtr,
390 keepGoing);
393 Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
394 const CertPolicyId& policy,
395 Input candidateCertDER,
396 /*out*/ TrustLevel& trustLevel) {
397 // Check the certificate against the OneCRL cert blocklist
398 if (!mCertStorage) {
399 return Result::FATAL_ERROR_LIBRARY_FAILURE;
402 // The certificate blocklist currently only applies to TLS server
403 // certificates.
404 if (mCertDBTrustType == trustSSL) {
405 int16_t revocationState;
407 nsTArray<uint8_t> issuerBytes;
408 nsTArray<uint8_t> serialBytes;
409 nsTArray<uint8_t> subjectBytes;
410 nsTArray<uint8_t> pubKeyBytes;
412 Result result =
413 BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes,
414 serialBytes, subjectBytes, pubKeyBytes);
415 if (result != Success) {
416 return result;
419 nsresult nsrv = mCertStorage->GetRevocationState(
420 issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
421 if (NS_FAILED(nsrv)) {
422 return Result::FATAL_ERROR_LIBRARY_FAILURE;
425 if (revocationState == nsICertStorage::STATE_ENFORCE) {
426 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
427 ("NSSCertDBTrustDomain: certificate is in blocklist"));
428 Telemetry::AccumulateCategorical(
429 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::OneCRL);
430 return Result::ERROR_REVOKED_CERTIFICATE;
434 // This may be a third-party root.
435 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
436 if (InputsAreEqual(candidateCertDER, thirdPartyRootInput)) {
437 trustLevel = TrustLevel::TrustAnchor;
438 return Success;
442 // This may be a third-party intermediate.
443 for (const auto& thirdPartyIntermediateInput :
444 mThirdPartyIntermediateInputs) {
445 if (InputsAreEqual(candidateCertDER, thirdPartyIntermediateInput)) {
446 trustLevel = TrustLevel::InheritsTrust;
447 return Success;
451 // Synchronously dispatch a task to the socket thread to construct a
452 // CERTCertificate and get its trust from NSS. This involves querying NSS
453 // structures and databases, so it should be done on the socket thread.
454 Result result = Result::FATAL_ERROR_LIBRARY_FAILURE;
455 RefPtr<Runnable> getTrustTask =
456 NS_NewRunnableFunction("NSSCertDBTrustDomain::GetCertTrust", [&]() {
457 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
458 result = Result::FATAL_ERROR_LIBRARY_FAILURE;
459 return;
461 // This would be cleaner and more efficient if we could get the trust
462 // information without constructing a CERTCertificate here, but NSS
463 // doesn't expose it in any other easy-to-use fashion. The use of
464 // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
465 // performance problem for certificates already known to NSS because NSS
466 // will just find the existing CERTCertificate in its in-memory cache
467 // and return it. For certificates not already in NSS (namely
468 // third-party roots and intermediates), we want to avoid calling
469 // CERT_NewTempCertificate repeatedly, so we've already checked if the
470 // candidate certificate is a third-party certificate, above.
471 SECItem candidateCertDERSECItem =
472 UnsafeMapInputToSECItem(candidateCertDER);
473 UniqueCERTCertificate candidateCert(CERT_NewTempCertificate(
474 CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false,
475 true));
476 if (!candidateCert) {
477 result = MapPRErrorCodeToResult(PR_GetError());
478 return;
480 // NB: CERT_GetCertTrust seems to be abusing SECStatus as a boolean,
481 // where SECSuccess means that there is a trust record and SECFailure
482 // means there is not a trust record. I looked at NSS's internal uses of
483 // CERT_GetCertTrust, and all that code uses the result as a boolean
484 // meaning "We have a trust record."
485 CERTCertTrust trust;
486 if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
487 uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
489 // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
490 // because we can have active distrust for either type of cert. Note
491 // that CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so
492 // if the relevant trust bit isn't set then that means the cert must
493 // be considered distrusted.
494 uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
495 ? CERTDB_TRUSTED_CA
496 : CERTDB_TRUSTED;
497 if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) ==
498 CERTDB_TERMINAL_RECORD) {
499 trustLevel = TrustLevel::ActivelyDistrusted;
500 result = Success;
501 return;
504 // For TRUST, we use the CERTDB_TRUSTED_CA bit.
505 if (flags & CERTDB_TRUSTED_CA) {
506 if (policy.IsAnyPolicy()) {
507 trustLevel = TrustLevel::TrustAnchor;
508 result = Success;
509 return;
512 nsTArray<uint8_t> certBytes(candidateCert->derCert.data,
513 candidateCert->derCert.len);
514 if (CertIsAuthoritativeForEVPolicy(certBytes, policy)) {
515 trustLevel = TrustLevel::TrustAnchor;
516 result = Success;
517 return;
521 trustLevel = TrustLevel::InheritsTrust;
522 result = Success;
524 nsCOMPtr<nsIEventTarget> socketThread(
525 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
526 if (!socketThread) {
527 return Result::FATAL_ERROR_LIBRARY_FAILURE;
529 nsresult rv = SyncRunnable::DispatchToThread(socketThread, getTrustTask);
530 if (NS_FAILED(rv)) {
531 return Result::FATAL_ERROR_LIBRARY_FAILURE;
533 return result;
536 Result NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
537 /*out*/ uint8_t* digestBuf,
538 size_t digestBufLen) {
539 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
542 TimeDuration NSSCertDBTrustDomain::GetOCSPTimeout() const {
543 switch (mOCSPFetching) {
544 case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
545 return mOCSPTimeoutSoft;
546 case NSSCertDBTrustDomain::FetchOCSPForEV:
547 case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
548 return mOCSPTimeoutHard;
549 // The rest of these are error cases. Assert in debug builds, but return
550 // the soft timeout value in release builds.
551 case NSSCertDBTrustDomain::NeverFetchOCSP:
552 case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
553 MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
554 break;
557 MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
558 return mOCSPTimeoutSoft;
561 // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
562 // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
563 // Success with result.IsVoid() == true when an OCSP URI was not found, and
564 // Success with result.IsVoid() == false when an OCSP URI was found.
565 static Result GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
566 Input aiaExtension,
567 /*out*/ nsCString& result) {
568 MOZ_ASSERT(arena.get());
569 if (!arena.get()) {
570 return Result::FATAL_ERROR_INVALID_ARGS;
573 result.Assign(VoidCString());
574 SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
575 CERTAuthInfoAccess** aia =
576 CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
577 if (!aia) {
578 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
580 for (size_t i = 0; aia[i]; ++i) {
581 if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
582 // NSS chooses the **last** OCSP URL; we choose the **first**
583 CERTGeneralName* current = aia[i]->location;
584 if (!current) {
585 continue;
587 do {
588 if (current->type == certURI) {
589 const SECItem& location = current->name.other;
590 // (location.len + 1) must be small enough to fit into a uint32_t,
591 // but we limit it to a smaller bound to reduce OOM risk.
592 if (location.len > 1024 || memchr(location.data, 0, location.len)) {
593 // Reject embedded nulls. (NSS doesn't do this)
594 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
596 result.Assign(nsDependentCSubstring(
597 reinterpret_cast<const char*>(location.data), location.len));
598 return Success;
600 current = CERT_GetNextGeneralName(current);
601 } while (current != aia[i]->location);
605 return Success;
608 NS_IMPL_ISUPPORTS(CRLiteTimestamp, nsICRLiteTimestamp)
610 NS_IMETHODIMP
611 CRLiteTimestamp::GetLogID(nsTArray<uint8_t>& aLogID) {
612 aLogID.Clear();
613 aLogID.AppendElements(mLogID);
614 return NS_OK;
617 NS_IMETHODIMP
618 CRLiteTimestamp::GetTimestamp(uint64_t* aTimestamp) {
619 *aTimestamp = mTimestamp;
620 return NS_OK;
623 Result BuildCRLiteTimestampArray(
624 Input sctExtension,
625 /*out*/ nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps) {
626 Input sctList;
627 Result rv =
628 ExtractSignedCertificateTimestampListFromExtension(sctExtension, sctList);
629 if (rv != Success) {
630 return rv;
632 std::vector<SignedCertificateTimestamp> decodedSCTs;
633 size_t decodingErrors;
634 DecodeSCTs(sctList, decodedSCTs, decodingErrors);
635 Unused << decodingErrors;
637 for (const auto& sct : decodedSCTs) {
638 timestamps.AppendElement(new CRLiteTimestamp(sct));
640 return Success;
643 Result NSSCertDBTrustDomain::CheckCRLiteStash(
644 const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
645 const nsTArray<uint8_t>& serialNumberBytes) {
646 // This information is deterministic and has already been validated by our
647 // infrastructure (it comes from signed CRLs), so if the stash says a
648 // certificate is revoked, it is.
649 bool isRevokedByStash = false;
650 nsresult rv = mCertStorage->IsCertRevokedByStash(
651 issuerSubjectPublicKeyInfoBytes, serialNumberBytes, &isRevokedByStash);
652 if (NS_FAILED(rv)) {
653 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
654 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
655 "failed"));
656 return Result::FATAL_ERROR_LIBRARY_FAILURE;
658 if (isRevokedByStash) {
659 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
660 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
661 "returned true"));
662 return Result::ERROR_REVOKED_CERTIFICATE;
664 return Success;
667 Result NSSCertDBTrustDomain::CheckCRLite(
668 const nsTArray<uint8_t>& issuerBytes,
669 const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
670 const nsTArray<uint8_t>& serialNumberBytes,
671 const nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps,
672 /*out*/ bool& filterCoversCertificate) {
673 filterCoversCertificate = false;
674 int16_t crliteRevocationState;
675 nsresult rv = mCertStorage->GetCRLiteRevocationState(
676 issuerBytes, issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
677 timestamps, &crliteRevocationState);
678 if (NS_FAILED(rv)) {
679 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
680 ("NSSCertDBTrustDomain::CheckCRLite: CRLite call failed"));
681 return Result::FATAL_ERROR_LIBRARY_FAILURE;
683 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
684 ("NSSCertDBTrustDomain::CheckCRLite: CRLite check returned "
685 "state=%hd",
686 crliteRevocationState));
688 switch (crliteRevocationState) {
689 case nsICertStorage::STATE_ENFORCE:
690 filterCoversCertificate = true;
691 return Result::ERROR_REVOKED_CERTIFICATE;
692 case nsICertStorage::STATE_UNSET:
693 filterCoversCertificate = true;
694 return Success;
695 case nsICertStorage::STATE_NOT_ENROLLED:
696 filterCoversCertificate = false;
697 return Success;
698 case nsICertStorage::STATE_NOT_COVERED:
699 filterCoversCertificate = false;
700 return Success;
701 case nsICertStorage::STATE_NO_FILTER:
702 filterCoversCertificate = false;
703 return Success;
704 default:
705 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
706 ("NSSCertDBTrustDomain::CheckCRLite: Unknown CRLite revocation "
707 "state"));
708 return Result::FATAL_ERROR_LIBRARY_FAILURE;
712 Result NSSCertDBTrustDomain::CheckRevocation(
713 EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
714 Duration validityDuration,
715 /*optional*/ const Input* stapledOCSPResponse,
716 /*optional*/ const Input* aiaExtension,
717 /*optional*/ const Input* sctExtension) {
718 // Actively distrusted certificates will have already been blocked by
719 // GetCertTrust.
721 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
722 ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
724 // None of the revocation methods in this function are consulted for CA
725 // certificates. Revocation for CAs is handled by GetCertTrust.
726 if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
727 return Success;
730 // Look for an OCSP Authority Information Access URL. Our behavior in
731 // ConfirmRevocations mode depends on whether a synchronous OCSP
732 // request is possible.
733 nsCString aiaLocation(VoidCString());
734 if (aiaExtension) {
735 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
736 if (!arena) {
737 return Result::FATAL_ERROR_NO_MEMORY;
739 Result rv =
740 GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation);
741 if (rv != Success) {
742 return rv;
746 bool crliteCoversCertificate = false;
747 Result crliteResult = Success;
748 if (mCRLiteMode != CRLiteMode::Disabled && sctExtension) {
749 crliteResult =
750 CheckRevocationByCRLite(certID, *sctExtension, crliteCoversCertificate);
752 // If CheckCRLite returned an error other than "revoked certificate",
753 // propagate that error.
754 if (crliteResult != Success &&
755 crliteResult != Result::ERROR_REVOKED_CERTIFICATE) {
756 return crliteResult;
759 if (crliteCoversCertificate) {
760 Telemetry::AccumulateCategorical(
761 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::CRLite);
762 // If we don't return here we will consult OCSP.
763 // In Enforce CRLite mode we can return "Revoked" or "Not Revoked"
764 // without consulting OCSP.
765 if (mCRLiteMode == CRLiteMode::Enforce) {
766 return crliteResult;
768 // If we don't have a URL for an OCSP responder, then we can return any
769 // result ConfirmRevocations mode. Note that we might have a
770 // stapled or cached OCSP response which we ignore in this case.
771 if (mCRLiteMode == CRLiteMode::ConfirmRevocations &&
772 aiaLocation.IsVoid()) {
773 return crliteResult;
775 // In ConfirmRevocations mode we can return "Not Revoked"
776 // without consulting OCSP.
777 if (mCRLiteMode == CRLiteMode::ConfirmRevocations &&
778 crliteResult == Success) {
779 return Success;
784 bool ocspSoftFailure = false;
785 Result ocspResult = CheckRevocationByOCSP(
786 certID, time, validityDuration, aiaLocation, crliteCoversCertificate,
787 crliteResult, stapledOCSPResponse, ocspSoftFailure);
789 // In ConfirmRevocations mode we treat any OCSP failure as confirmation
790 // of a CRLite revoked result.
791 if (crliteCoversCertificate &&
792 crliteResult == Result::ERROR_REVOKED_CERTIFICATE &&
793 mCRLiteMode == CRLiteMode::ConfirmRevocations &&
794 (ocspResult != Success || ocspSoftFailure)) {
795 return Result::ERROR_REVOKED_CERTIFICATE;
798 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
799 ("NSSCertDBTrustDomain: end of CheckRevocation"));
801 return ocspResult;
804 Result NSSCertDBTrustDomain::CheckRevocationByCRLite(
805 const CertID& certID, const Input& sctExtension,
806 /*out*/ bool& crliteCoversCertificate) {
807 crliteCoversCertificate = false;
808 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
809 ("NSSCertDBTrustDomain::CheckRevocation: checking CRLite"));
810 nsTArray<uint8_t> issuerSubjectPublicKeyInfoBytes;
811 issuerSubjectPublicKeyInfoBytes.AppendElements(
812 certID.issuerSubjectPublicKeyInfo.UnsafeGetData(),
813 certID.issuerSubjectPublicKeyInfo.GetLength());
814 nsTArray<uint8_t> serialNumberBytes;
815 serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(),
816 certID.serialNumber.GetLength());
817 // The CRLite stash is essentially a subset of a collection of CRLs, so if
818 // it says a certificate is revoked, it is.
819 Result rv =
820 CheckCRLiteStash(issuerSubjectPublicKeyInfoBytes, serialNumberBytes);
821 if (rv != Success) {
822 crliteCoversCertificate = (rv == Result::ERROR_REVOKED_CERTIFICATE);
823 return rv;
826 nsTArray<uint8_t> issuerBytes;
827 issuerBytes.AppendElements(certID.issuer.UnsafeGetData(),
828 certID.issuer.GetLength());
830 nsTArray<RefPtr<nsICRLiteTimestamp>> timestamps;
831 rv = BuildCRLiteTimestampArray(sctExtension, timestamps);
832 if (rv != Success) {
833 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
834 ("decoding SCT extension failed - CRLite will be not be "
835 "consulted"));
836 return Success;
838 return CheckCRLite(issuerBytes, issuerSubjectPublicKeyInfoBytes,
839 serialNumberBytes, timestamps, crliteCoversCertificate);
842 Result NSSCertDBTrustDomain::CheckRevocationByOCSP(
843 const CertID& certID, Time time, Duration validityDuration,
844 const nsCString& aiaLocation, const bool crliteCoversCertificate,
845 const Result crliteResult,
846 /*optional*/ const Input* stapledOCSPResponse,
847 /*out*/ bool& softFailure) {
848 softFailure = false;
849 const uint16_t maxOCSPLifetimeInDays = 10;
850 // If we have a stapled OCSP response then the verification of that response
851 // determines the result unless the OCSP response is expired. We make an
852 // exception for expired responses because some servers, nginx in particular,
853 // are known to serve expired responses due to bugs.
854 // We keep track of the result of verifying the stapled response but don't
855 // immediately return failure if the response has expired.
856 Result stapledOCSPResponseResult = Success;
857 if (stapledOCSPResponse) {
858 bool expired;
859 uint32_t ageInHours;
860 stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse(
861 certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse,
862 ResponseWasStapled, expired, ageInHours);
863 Telemetry::AccumulateCategorical(
864 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::StapledOCSP);
865 if (stapledOCSPResponseResult == Success) {
866 // stapled OCSP response present and good
867 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
868 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
869 ("NSSCertDBTrustDomain: stapled OCSP response: good"));
870 return Success;
872 if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
873 expired) {
874 // stapled OCSP response present but expired
875 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
876 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
877 ("NSSCertDBTrustDomain: expired stapled OCSP response"));
878 } else if (stapledOCSPResponseResult ==
879 Result::ERROR_OCSP_TRY_SERVER_LATER ||
880 stapledOCSPResponseResult ==
881 Result::ERROR_OCSP_INVALID_SIGNING_CERT) {
882 // Stapled OCSP response present but invalid for a small number of reasons
883 // CAs/servers commonly get wrong. This will be treated similarly to an
884 // expired stapled response.
885 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
886 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
887 ("NSSCertDBTrustDomain: stapled OCSP response: "
888 "failure (allowed for compatibility)"));
889 } else {
890 // stapled OCSP response present but invalid for some reason
891 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
892 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
893 ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
894 return stapledOCSPResponseResult;
896 } else {
897 // no stapled OCSP response
898 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
899 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
900 ("NSSCertDBTrustDomain: no stapled OCSP response"));
903 Result cachedResponseResult = Success;
904 Time cachedResponseValidThrough(Time::uninitialized);
905 bool cachedResponsePresent =
906 mOCSPCache.Get(certID, mOriginAttributes, cachedResponseResult,
907 cachedResponseValidThrough);
908 if (cachedResponsePresent) {
909 Telemetry::AccumulateCategorical(
910 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::CachedOCSP);
911 if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
912 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
913 ("NSSCertDBTrustDomain: cached OCSP response: good"));
914 return Success;
916 // If we have a cached revoked response, use it.
917 if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
918 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
919 ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
920 return Result::ERROR_REVOKED_CERTIFICATE;
922 // The cached response may indicate an unknown certificate or it may be
923 // expired. Don't return with either of these statuses yet - we may be
924 // able to fetch a more recent one.
925 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
926 ("NSSCertDBTrustDomain: cached OCSP response: error %d",
927 static_cast<int>(cachedResponseResult)));
928 // When a good cached response has expired, it is more convenient
929 // to convert that to an error code and just deal with
930 // cachedResponseResult from here on out.
931 if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
932 cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
934 // We may have a cached indication of server failure. Ignore it if
935 // it has expired.
936 if (cachedResponseResult != Success &&
937 cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
938 cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
939 cachedResponseValidThrough < time) {
940 cachedResponseResult = Success;
941 cachedResponsePresent = false;
943 } else {
944 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
945 ("NSSCertDBTrustDomain: no cached OCSP response"));
947 // At this point, if and only if cachedErrorResult is Success, there was no
948 // cached response.
949 MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
950 (cachedResponsePresent && cachedResponseResult != Success));
952 // TODO: We still need to handle the fallback for invalid stapled responses.
953 // But, if/when we disable OCSP fetching by default, it would be ambiguous
954 // whether security.OCSP.enable==0 means "I want the default" or "I really
955 // never want you to ever fetch OCSP."
956 // Additionally, this doesn't properly handle OCSP-must-staple when OCSP
957 // fetching is disabled.
958 Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
959 if (validityDuration < shortLifetime) {
960 Telemetry::AccumulateCategorical(
961 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::ShortValidity);
963 if ((mOCSPFetching == NeverFetchOCSP) || (validityDuration < shortLifetime)) {
964 // We're not going to be doing any fetching, so if there was a cached
965 // "unknown" response, say so.
966 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
967 return Result::ERROR_OCSP_UNKNOWN_CERT;
969 // If we're doing hard-fail, we want to know if we have a cached response
970 // that has expired.
971 if (mOCSPFetching == FetchOCSPForDVHardFail &&
972 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
973 return Result::ERROR_OCSP_OLD_RESPONSE;
976 softFailure = true;
977 return Success;
980 if (mOCSPFetching == LocalOnlyOCSPForEV) {
981 if (cachedResponseResult != Success) {
982 return cachedResponseResult;
984 return Result::ERROR_OCSP_UNKNOWN_CERT;
987 if (aiaLocation.IsVoid()) {
988 if (mOCSPFetching == FetchOCSPForEV ||
989 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
990 return Result::ERROR_OCSP_UNKNOWN_CERT;
992 if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
993 return Result::ERROR_OCSP_OLD_RESPONSE;
995 if (stapledOCSPResponseResult != Success) {
996 return stapledOCSPResponseResult;
999 // Nothing to do if we don't have an OCSP responder URI for the cert; just
1000 // assume it is good. Note that this is the confusing, but intended,
1001 // interpretation of "strict" revocation checking in the face of a
1002 // certificate that lacks an OCSP responder URI. There's no need to set
1003 // softFailure here---we check for the presence of an AIA before attempting
1004 // OCSP when CRLite is configured in confirm revocations mode.
1005 return Success;
1008 if (cachedResponseResult == Success ||
1009 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
1010 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
1011 // Only send a request to, and process a response from, the server if we
1012 // didn't have a cached indication of failure. Also, don't keep requesting
1013 // responses from a failing server.
1014 return SynchronousCheckRevocationWithServer(
1015 certID, aiaLocation, time, maxOCSPLifetimeInDays, cachedResponseResult,
1016 stapledOCSPResponseResult, crliteCoversCertificate, crliteResult,
1017 softFailure);
1020 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
1021 cachedResponseResult, softFailure);
1024 Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
1025 const CertID& certID, const nsCString& aiaLocation, Time time,
1026 uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult,
1027 const Result stapledOCSPResponseResult, const bool crliteCoversCertificate,
1028 const Result crliteResult, /*out*/ bool& softFailure) {
1029 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1030 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1033 uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
1034 size_t ocspRequestLength;
1035 Result rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
1036 ocspRequestLength);
1037 if (rv != Success) {
1038 return rv;
1041 Vector<uint8_t> ocspResponse;
1042 Input response;
1043 mOCSPFetchStatus = OCSPFetchStatus::Fetched;
1044 rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes,
1045 ocspRequestLength, GetOCSPTimeout(), ocspResponse);
1046 Telemetry::AccumulateCategorical(
1047 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::OCSP);
1048 if (rv == Success &&
1049 response.Init(ocspResponse.begin(), ocspResponse.length()) != Success) {
1050 rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
1053 if (rv != Success) {
1054 Time timeout(time);
1055 if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
1056 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
1059 Result cacheRV =
1060 mOCSPCache.Put(certID, mOriginAttributes, rv, time, timeout);
1061 if (cacheRV != Success) {
1062 return cacheRV;
1065 if (crliteCoversCertificate) {
1066 if (crliteResult == Success) {
1067 // CRLite says the certificate is OK, but OCSP fetching failed.
1068 Telemetry::AccumulateCategorical(
1069 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPFail);
1070 } else {
1071 // CRLite says the certificate is revoked, but OCSP fetching failed.
1072 Telemetry::AccumulateCategorical(
1073 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPFail);
1077 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
1078 rv, softFailure);
1081 // If the response from the network has expired but indicates a revoked
1082 // or unknown certificate, PR_GetError() will return the appropriate error.
1083 // We actually ignore expired here.
1084 bool expired;
1085 uint32_t ageInHours;
1086 rv = VerifyAndMaybeCacheEncodedOCSPResponse(
1087 certID, time, maxOCSPLifetimeInDays, response, ResponseIsFromNetwork,
1088 expired, ageInHours);
1090 // If the CRLite filter covers the certificate, compare the CRLite result
1091 // with the OCSP fetching result. OCSP may have succeeded, said the
1092 // certificate is revoked, said the certificate doesn't exist, or it may have
1093 // failed for a reason that results in a "soft fail" (i.e. there is no
1094 // indication that the certificate is either definitely revoked or definitely
1095 // not revoked, so for usability, revocation checking says the certificate is
1096 // valid by default).
1097 if (crliteCoversCertificate) {
1098 if (rv == Success) {
1099 if (crliteResult == Success) {
1100 // CRLite and OCSP fetching agree the certificate is OK.
1101 Telemetry::AccumulateCategorical(
1102 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPOk);
1103 } else {
1104 // CRLite says the certificate is revoked, but OCSP says it is OK.
1105 Telemetry::AccumulateCategorical(
1106 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPOk);
1108 if (mCRLiteMode == CRLiteMode::ConfirmRevocations) {
1109 Telemetry::Accumulate(Telemetry::OCSP_AGE_AT_CRLITE_OVERRIDE,
1110 ageInHours);
1113 } else if (rv == Result::ERROR_REVOKED_CERTIFICATE) {
1114 if (crliteResult == Success) {
1115 // CRLite says the certificate is OK, but OCSP says it is revoked.
1116 Telemetry::AccumulateCategorical(
1117 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPRev);
1118 } else {
1119 // CRLite and OCSP fetching agree the certificate is revoked.
1120 Telemetry::AccumulateCategorical(
1121 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPRev);
1123 } else if (rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
1124 if (crliteResult == Success) {
1125 // CRLite says the certificate is OK, but OCSP says it doesn't exist.
1126 Telemetry::AccumulateCategorical(
1127 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPUnk);
1128 } else {
1129 // CRLite says the certificate is revoked, but OCSP says it doesn't
1130 // exist.
1131 Telemetry::AccumulateCategorical(
1132 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPUnk);
1134 } else {
1135 if (crliteResult == Success) {
1136 // CRLite says the certificate is OK, but OCSP encountered a soft-fail
1137 // error.
1138 Telemetry::AccumulateCategorical(
1139 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPSoft);
1140 } else {
1141 // CRLite says the certificate is revoked, but OCSP encountered a
1142 // soft-fail error.
1143 Telemetry::AccumulateCategorical(
1144 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPSoft);
1149 if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
1150 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1151 ("NSSCertDBTrustDomain: returning after "
1152 "VerifyEncodedOCSPResponse"));
1153 return rv;
1156 if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
1157 rv == Result::ERROR_REVOKED_CERTIFICATE) {
1158 return rv;
1161 if (stapledOCSPResponseResult != Success) {
1162 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1163 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
1164 "stapled response after OCSP request verification failure"));
1165 return stapledOCSPResponseResult;
1168 softFailure = true;
1169 return Success; // Soft fail -> success :(
1172 Result NSSCertDBTrustDomain::HandleOCSPFailure(
1173 const Result cachedResponseResult, const Result stapledOCSPResponseResult,
1174 const Result error, /*out*/ bool& softFailure) {
1175 if (mOCSPFetching != FetchOCSPForDVSoftFail) {
1176 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1177 ("NSSCertDBTrustDomain: returning SECFailure after OCSP request "
1178 "failure"));
1179 return error;
1182 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
1183 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1184 ("NSSCertDBTrustDomain: returning SECFailure from cached response "
1185 "after OCSP request failure"));
1186 return cachedResponseResult;
1189 if (stapledOCSPResponseResult != Success) {
1190 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1191 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
1192 "stapled response after OCSP request failure"));
1193 return stapledOCSPResponseResult;
1196 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1197 ("NSSCertDBTrustDomain: returning SECSuccess after OCSP request "
1198 "failure"));
1200 softFailure = true;
1201 return Success; // Soft fail -> success :(
1204 Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
1205 const CertID& certID, Time time, uint16_t maxLifetimeInDays,
1206 Input encodedResponse, EncodedResponseSource responseSource,
1207 /*out*/ bool& expired,
1208 /*out*/ uint32_t& ageInHours) {
1209 Time thisUpdate(Time::uninitialized);
1210 Time validThrough(Time::uninitialized);
1212 Result rv = VerifyEncodedOCSPResponse(*this, certID, time, maxLifetimeInDays,
1213 encodedResponse, expired, &thisUpdate,
1214 &validThrough);
1215 // If a response was stapled and expired, we don't want to cache it. Return
1216 // early to simplify the logic here.
1217 if (responseSource == ResponseWasStapled && expired) {
1218 MOZ_ASSERT(rv != Success);
1219 return rv;
1221 // validThrough is only trustworthy if the response successfully verifies
1222 // or it indicates a revoked or unknown certificate.
1223 // If this isn't the case, store an indication of failure (to prevent
1224 // repeatedly requesting a response from a failing server).
1225 if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
1226 rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
1227 validThrough = time;
1228 if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
1229 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
1232 // The `thisUpdate` field holds the latest time at which the server knew the
1233 // response was correct. The age of the response is the time that has elapsed
1234 // since. We only use this for the telemetry defined in Bug 1794479.
1235 uint64_t timeInSeconds;
1236 uint64_t thisUpdateInSeconds;
1237 uint64_t ageInSeconds;
1238 SecondsSinceEpochFromTime(time, &timeInSeconds);
1239 SecondsSinceEpochFromTime(thisUpdate, &thisUpdateInSeconds);
1240 if (timeInSeconds >= thisUpdateInSeconds) {
1241 ageInSeconds = timeInSeconds - thisUpdateInSeconds;
1242 // ageInHours is 32 bits because of the telemetry api.
1243 if (ageInSeconds > UINT32_MAX) {
1244 // We could divide by 3600 before checking the UINT32_MAX bound, but if
1245 // ageInSeconds is more than UINT32_MAX then there's been some sort of
1246 // error.
1247 ageInHours = UINT32_MAX;
1248 } else {
1249 // We start at 1 and divide with truncation to reserve ageInHours=0 for
1250 // the case where `thisUpdate` is in the future.
1251 ageInHours = 1 + ageInSeconds / (60 * 60);
1253 } else {
1254 ageInHours = 0;
1256 if (responseSource == ResponseIsFromNetwork || rv == Success ||
1257 rv == Result::ERROR_REVOKED_CERTIFICATE ||
1258 rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
1259 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1260 ("NSSCertDBTrustDomain: caching OCSP response"));
1261 Result putRV =
1262 mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate, validThrough);
1263 if (putRV != Success) {
1264 return putRV;
1268 return rv;
1271 SECStatus GetCertDistrustAfterValue(const SECItem* distrustItem,
1272 PRTime& distrustTime) {
1273 if (!distrustItem || !distrustItem->data || distrustItem->len != 13) {
1274 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
1275 return SECFailure;
1277 return DER_DecodeTimeChoice(&distrustTime, distrustItem);
1280 SECStatus GetCertNotBeforeValue(const CERTCertificate* cert,
1281 PRTime& distrustTime) {
1282 return DER_DecodeTimeChoice(&distrustTime, &cert->validity.notBefore);
1285 nsresult isDistrustedCertificateChain(
1286 const nsTArray<nsTArray<uint8_t>>& certArray,
1287 const SECTrustType certDBTrustType, bool& isDistrusted) {
1288 if (certArray.Length() == 0) {
1289 return NS_ERROR_FAILURE;
1292 // Set the default result to be distrusted.
1293 isDistrusted = true;
1295 // There is no distrust to set if the certDBTrustType is not SSL or Email.
1296 if (certDBTrustType != trustSSL && certDBTrustType != trustEmail) {
1297 isDistrusted = false;
1298 return NS_OK;
1301 SECStatus runnableRV = SECFailure;
1303 RefPtr<Runnable> isDistrustedChainTask =
1304 NS_NewRunnableFunction("isDistrustedCertificateChain", [&]() {
1305 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1306 runnableRV = SECFailure;
1307 return;
1309 // Allocate objects and retreive the root and end-entity certificates.
1310 CERTCertDBHandle* certDB(CERT_GetDefaultCertDB());
1311 const nsTArray<uint8_t>& certRootDER = certArray.LastElement();
1312 SECItem certRootDERItem = {
1313 siBuffer, const_cast<unsigned char*>(certRootDER.Elements()),
1314 AssertedCast<unsigned int>(certRootDER.Length())};
1315 UniqueCERTCertificate certRoot(CERT_NewTempCertificate(
1316 certDB, &certRootDERItem, nullptr, false, true));
1317 if (!certRoot) {
1318 runnableRV = SECFailure;
1319 return;
1321 const nsTArray<uint8_t>& certLeafDER = certArray.ElementAt(0);
1322 SECItem certLeafDERItem = {
1323 siBuffer, const_cast<unsigned char*>(certLeafDER.Elements()),
1324 AssertedCast<unsigned int>(certLeafDER.Length())};
1325 UniqueCERTCertificate certLeaf(CERT_NewTempCertificate(
1326 certDB, &certLeafDERItem, nullptr, false, true));
1327 if (!certLeaf) {
1328 runnableRV = SECFailure;
1329 return;
1332 // Set isDistrusted to false if there is no distrust for the root.
1333 if (!certRoot->distrust) {
1334 isDistrusted = false;
1335 runnableRV = SECSuccess;
1336 return;
1339 // Create a pointer to refer to the selected distrust struct.
1340 SECItem* distrustPtr = nullptr;
1341 if (certDBTrustType == trustSSL) {
1342 distrustPtr = &certRoot->distrust->serverDistrustAfter;
1344 if (certDBTrustType == trustEmail) {
1345 distrustPtr = &certRoot->distrust->emailDistrustAfter;
1348 // Get validity for the current end-entity certificate
1349 // and get the distrust field for the root certificate.
1350 PRTime certRootDistrustAfter;
1351 PRTime certLeafNotBefore;
1353 runnableRV =
1354 GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter);
1355 if (runnableRV != SECSuccess) {
1356 return;
1359 runnableRV = GetCertNotBeforeValue(certLeaf.get(), certLeafNotBefore);
1360 if (runnableRV != SECSuccess) {
1361 return;
1364 // Compare the validity of the end-entity certificate with
1365 // the distrust value of the root.
1366 if (certLeafNotBefore <= certRootDistrustAfter) {
1367 isDistrusted = false;
1370 runnableRV = SECSuccess;
1372 nsCOMPtr<nsIEventTarget> socketThread(
1373 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
1374 if (!socketThread) {
1375 return NS_ERROR_FAILURE;
1377 nsresult rv =
1378 SyncRunnable::DispatchToThread(socketThread, isDistrustedChainTask);
1379 if (NS_FAILED(rv) || runnableRV != SECSuccess) {
1380 return NS_ERROR_FAILURE;
1382 return NS_OK;
1385 Result NSSCertDBTrustDomain::IsChainValid(const DERArray& reversedDERArray,
1386 Time time,
1387 const CertPolicyId& requiredPolicy) {
1388 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1389 ("NSSCertDBTrustDomain: IsChainValid"));
1391 size_t numCerts = reversedDERArray.GetLength();
1392 if (numCerts < 1) {
1393 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1395 nsTArray<nsTArray<uint8_t>> certArray;
1396 for (size_t i = numCerts; i > 0; --i) {
1397 const Input* derInput = reversedDERArray.GetDER(i - 1);
1398 certArray.EmplaceBack(derInput->UnsafeGetData(), derInput->GetLength());
1401 const nsTArray<uint8_t>& rootBytes = certArray.LastElement();
1402 Input rootInput;
1403 Result rv = rootInput.Init(rootBytes.Elements(), rootBytes.Length());
1404 if (rv != Success) {
1405 return rv;
1407 rv = IsCertBuiltInRoot(rootInput, mIsBuiltChainRootBuiltInRoot);
1408 if (rv != Result::Success) {
1409 return rv;
1411 nsresult nsrv;
1412 // If mHostname isn't set, we're not verifying in the context of a TLS
1413 // handshake, so don't verify key pinning in those cases.
1414 if (mHostname) {
1415 nsTArray<Span<const uint8_t>> derCertSpanList;
1416 for (const auto& certDER : certArray) {
1417 derCertSpanList.EmplaceBack(certDER.Elements(), certDER.Length());
1420 bool chainHasValidPins;
1421 nsrv = PublicKeyPinningService::ChainHasValidPins(
1422 derCertSpanList, mHostname, time, mIsBuiltChainRootBuiltInRoot,
1423 chainHasValidPins, mPinningTelemetryInfo);
1424 if (NS_FAILED(nsrv)) {
1425 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1427 if (!chainHasValidPins) {
1428 return Result::ERROR_KEY_PINNING_FAILURE;
1432 // Check that the childs' certificate NotBefore date is anterior to
1433 // the NotAfter value of the parent when the root is a builtin.
1434 if (mIsBuiltChainRootBuiltInRoot) {
1435 bool isDistrusted;
1436 nsrv =
1437 isDistrustedCertificateChain(certArray, mCertDBTrustType, isDistrusted);
1438 if (NS_FAILED(nsrv)) {
1439 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1441 if (isDistrusted) {
1442 return Result::ERROR_UNTRUSTED_ISSUER;
1446 // See bug 1434300. If the root is a Symantec root, see if we distrust this
1447 // path. Since we already have the root available, we can check that cheaply
1448 // here before proceeding with the rest of the algorithm.
1450 // This algorithm only applies if we are verifying in the context of a TLS
1451 // handshake. To determine this, we check mHostname: If it isn't set, this is
1452 // not TLS, so don't run the algorithm.
1453 const nsTArray<uint8_t>& rootCertDER = certArray.LastElement();
1454 if (mHostname && CertDNIsInList(rootCertDER, RootSymantecDNs)) {
1455 if (numCerts <= 1) {
1456 // This chain is supposed to be complete, so this is an error.
1457 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1459 nsTArray<Input> intCerts;
1461 for (size_t i = 1; i < certArray.Length() - 1; ++i) {
1462 const nsTArray<uint8_t>& certBytes = certArray.ElementAt(i);
1463 Input certInput;
1464 rv = certInput.Init(certBytes.Elements(), certBytes.Length());
1465 if (rv != Success) {
1466 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1469 intCerts.EmplaceBack(certInput);
1472 bool isDistrusted = false;
1473 nsrv = CheckForSymantecDistrust(intCerts, RootAppleAndGoogleSPKIs,
1474 isDistrusted);
1475 if (NS_FAILED(nsrv)) {
1476 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1478 if (isDistrusted) {
1479 mSawDistrustedCAByPolicyError = true;
1480 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1484 mBuiltChain = std::move(certArray);
1486 return Success;
1489 Result NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(
1490 DigestAlgorithm aAlg, EndEntityOrCA /*endEntityOrCA*/, Time /*notBefore*/) {
1491 switch (aAlg) {
1492 case DigestAlgorithm::sha256: // fall through
1493 case DigestAlgorithm::sha384: // fall through
1494 case DigestAlgorithm::sha512:
1495 return Success;
1496 case DigestAlgorithm::sha1:
1497 return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
1499 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1502 Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
1503 EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
1504 if (modulusSizeInBits < mMinRSABits) {
1505 return Result::ERROR_INADEQUATE_KEY_SIZE;
1507 return Success;
1510 Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedData(
1511 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1512 Input subjectPublicKeyInfo) {
1513 return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
1514 subjectPublicKeyInfo, mPinArg);
1517 Result NSSCertDBTrustDomain::VerifyRSAPSSSignedData(
1518 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1519 Input subjectPublicKeyInfo) {
1520 return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
1521 subjectPublicKeyInfo, mPinArg);
1524 Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
1525 EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) {
1526 switch (curve) {
1527 case NamedCurve::secp256r1: // fall through
1528 case NamedCurve::secp384r1: // fall through
1529 case NamedCurve::secp521r1:
1530 return Success;
1533 return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
1536 Result NSSCertDBTrustDomain::VerifyECDSASignedData(
1537 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1538 Input subjectPublicKeyInfo) {
1539 return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
1540 subjectPublicKeyInfo, mPinArg);
1543 Result NSSCertDBTrustDomain::CheckValidityIsAcceptable(
1544 Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
1545 KeyPurposeId keyPurpose) {
1546 if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
1547 return Success;
1549 if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
1550 return Success;
1553 Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
1554 Time::ONE_DAY_IN_SECONDS);
1555 Duration maxValidityDuration(UINT64_MAX);
1556 Duration validityDuration(notBefore, notAfter);
1558 switch (mValidityCheckingMode) {
1559 case ValidityCheckingMode::CheckingOff:
1560 return Success;
1561 case ValidityCheckingMode::CheckForEV:
1562 // The EV Guidelines say the maximum is 27 months, but we use a slightly
1563 // higher limit here to (hopefully) minimize compatibility breakage.
1564 maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
1565 break;
1566 default:
1567 MOZ_ASSERT_UNREACHABLE(
1568 "We're not handling every ValidityCheckingMode type");
1571 if (validityDuration > maxValidityDuration) {
1572 return Result::ERROR_VALIDITY_TOO_LONG;
1575 return Success;
1578 Result NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(
1579 Time notBefore,
1580 /*out*/ bool& matches) {
1581 // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
1582 static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
1583 // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
1584 static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
1586 switch (mNetscapeStepUpPolicy) {
1587 case NetscapeStepUpPolicy::AlwaysMatch:
1588 matches = true;
1589 return Success;
1590 case NetscapeStepUpPolicy::MatchBefore23August2016:
1591 matches = notBefore < AUGUST_23_2016;
1592 return Success;
1593 case NetscapeStepUpPolicy::MatchBefore23August2015:
1594 matches = notBefore < AUGUST_23_2015;
1595 return Success;
1596 case NetscapeStepUpPolicy::NeverMatch:
1597 matches = false;
1598 return Success;
1599 default:
1600 MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
1602 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1605 void NSSCertDBTrustDomain::ResetAccumulatedState() {
1606 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
1607 mSCTListFromOCSPStapling = nullptr;
1608 mSCTListFromCertificate = nullptr;
1609 mSawDistrustedCAByPolicyError = false;
1610 mIsBuiltChainRootBuiltInRoot = false;
1613 static Input SECItemToInput(const UniqueSECItem& item) {
1614 Input result;
1615 if (item) {
1616 MOZ_ASSERT(item->type == siBuffer);
1617 Result rv = result.Init(item->data, item->len);
1618 // As used here, |item| originally comes from an Input,
1619 // so there should be no issues converting it back.
1620 MOZ_ASSERT(rv == Success);
1621 Unused << rv; // suppresses warnings in release builds
1623 return result;
1626 Input NSSCertDBTrustDomain::GetSCTListFromCertificate() const {
1627 return SECItemToInput(mSCTListFromCertificate);
1630 Input NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const {
1631 return SECItemToInput(mSCTListFromOCSPStapling);
1634 bool NSSCertDBTrustDomain::GetIsBuiltChainRootBuiltInRoot() const {
1635 return mIsBuiltChainRootBuiltInRoot;
1638 bool NSSCertDBTrustDomain::GetIsErrorDueToDistrustedCAPolicy() const {
1639 return mSawDistrustedCAByPolicyError;
1642 void NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
1643 Input extensionData) {
1644 UniqueSECItem* out = nullptr;
1645 switch (extension) {
1646 case AuxiliaryExtension::EmbeddedSCTList:
1647 out = &mSCTListFromCertificate;
1648 break;
1649 case AuxiliaryExtension::SCTListFromOCSPResponse:
1650 out = &mSCTListFromOCSPStapling;
1651 break;
1652 default:
1653 MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
1655 if (out) {
1656 SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
1657 out->reset(SECITEM_DupItem(&extensionDataItem));
1661 SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig,
1662 PKCS11DBConfig pkcs11DbConfig) {
1663 MOZ_ASSERT(NS_IsMainThread());
1665 // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
1666 // module by NSS_Initialize because we will load it in LoadLoadableRoots
1667 // later. It also allows us to work around a bug in the system NSS in
1668 // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
1669 // "/usr/lib/nss/libnssckbi.so".
1670 uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
1671 if (nssDbConfig == NSSDBConfig::ReadOnly) {
1672 flags |= NSS_INIT_READONLY;
1674 if (pkcs11DbConfig == PKCS11DBConfig::DoNotLoadModules) {
1675 flags |= NSS_INIT_NOMODDB;
1677 nsAutoCString dbTypeAndDirectory("sql:");
1678 dbTypeAndDirectory.Append(dir);
1679 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1680 ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(),
1681 (int)nssDbConfig, (int)pkcs11DbConfig));
1682 SECStatus srv =
1683 NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags);
1684 if (srv != SECSuccess) {
1685 return srv;
1688 if (nssDbConfig == NSSDBConfig::ReadWrite) {
1689 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1690 if (!slot) {
1691 return SECFailure;
1693 // If the key DB doesn't have a password set, PK11_NeedUserInit will return
1694 // true. For the SQL DB, we need to set a password or we won't be able to
1695 // import any certificates or change trust settings.
1696 if (PK11_NeedUserInit(slot.get())) {
1697 srv = PK11_InitPin(slot.get(), nullptr, nullptr);
1698 MOZ_ASSERT(srv == SECSuccess);
1699 Unused << srv;
1703 return SECSuccess;
1706 void DisableMD5() {
1707 NSS_SetAlgorithmPolicy(
1708 SEC_OID_MD5, 0,
1709 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1710 NSS_SetAlgorithmPolicy(
1711 SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 0,
1712 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1713 NSS_SetAlgorithmPolicy(
1714 SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC, 0,
1715 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1718 // Load a given PKCS#11 module located in the given directory. It will be named
1719 // the given module name. Optionally pass some string parameters to it via
1720 // 'params'. This argument will be provided to C_Initialize when called on the
1721 // module.
1722 // |libraryName| and |dir| are encoded in UTF-8.
1723 bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
1724 const nsCString& dir, /* optional */ const char* params) {
1725 // If a module exists with the same name, make a best effort attempt to delete
1726 // it. Note that it isn't possible to delete the internal module, so checking
1727 // the return value would be detrimental in that case.
1728 int unusedModType;
1729 Unused << SECMOD_DeleteModule(moduleName, &unusedModType);
1731 nsAutoCString fullLibraryPath;
1732 if (!dir.IsEmpty()) {
1733 fullLibraryPath.Assign(dir);
1734 fullLibraryPath.AppendLiteral(FILE_PATH_SEPARATOR);
1736 fullLibraryPath.Append(MOZ_DLL_PREFIX);
1737 fullLibraryPath.Append(libraryName);
1738 fullLibraryPath.Append(MOZ_DLL_SUFFIX);
1739 // Escape the \ and " characters.
1740 fullLibraryPath.ReplaceSubstring("\\", "\\\\");
1741 fullLibraryPath.ReplaceSubstring("\"", "\\\"");
1743 nsAutoCString pkcs11ModuleSpec("name=\"");
1744 pkcs11ModuleSpec.Append(moduleName);
1745 pkcs11ModuleSpec.AppendLiteral("\" library=\"");
1746 pkcs11ModuleSpec.Append(fullLibraryPath);
1747 pkcs11ModuleSpec.AppendLiteral("\"");
1748 if (params) {
1749 pkcs11ModuleSpec.AppendLiteral("\" parameters=\"");
1750 pkcs11ModuleSpec.Append(params);
1751 pkcs11ModuleSpec.AppendLiteral("\"");
1754 UniqueSECMODModule userModule(SECMOD_LoadUserModule(
1755 const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false));
1756 if (!userModule) {
1757 return false;
1760 if (!userModule->loaded) {
1761 return false;
1764 return true;
1767 const char* kIPCClientCertsModuleName = "IPC Client Cert Module";
1769 bool LoadIPCClientCertsModule(const nsCString& dir) {
1770 // The IPC client certs module needs to be able to call back into gecko to be
1771 // able to communicate with the parent process over IPC. This is achieved by
1772 // serializing the addresses of the relevant functions and passing them as an
1773 // extra string parameter that will be available when C_Initialize is called
1774 // on IPC client certs.
1775 nsPrintfCString addrs("%p,%p", DoFindObjects, DoSign);
1776 if (!LoadUserModuleAt(kIPCClientCertsModuleName, "ipcclientcerts", dir,
1777 addrs.get())) {
1778 return false;
1780 RunOnShutdown(
1781 []() {
1782 UniqueSECMODModule ipcClientCertsModule(
1783 SECMOD_FindModule(kIPCClientCertsModuleName));
1784 if (ipcClientCertsModule) {
1785 SECMOD_UnloadUserModule(ipcClientCertsModule.get());
1788 ShutdownPhase::XPCOMWillShutdown);
1789 return true;
1792 const char* kOSClientCertsModuleName = "OS Client Cert Module";
1794 bool LoadOSClientCertsModule(const nsCString& dir) {
1795 nsLiteralCString params =
1796 StaticPrefs::security_osclientcerts_assume_rsa_pss_support()
1797 ? "RSA-PSS"_ns
1798 : ""_ns;
1799 return LoadUserModuleAt(kOSClientCertsModuleName, "osclientcerts", dir,
1800 params.get());
1803 bool LoadLoadableRoots(const nsCString& dir) {
1804 // Some NSS command-line utilities will load a roots module under the name
1805 // "Root Certs" if there happens to be a `MOZ_DLL_PREFIX "nssckbi"
1806 // MOZ_DLL_SUFFIX` file in the directory being operated on. In some cases this
1807 // can cause us to fail to load our roots module. In these cases, deleting the
1808 // "Root Certs" module allows us to load the correct one. See bug 1406396.
1809 int unusedModType;
1810 Unused << SECMOD_DeleteModule("Root Certs", &unusedModType);
1811 return LoadUserModuleAt(kRootModuleName, "nssckbi", dir, nullptr);
1814 nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
1815 /*out*/ nsCString& nickname) {
1816 MOZ_ASSERT(cert);
1817 NS_ENSURE_ARG_POINTER(cert);
1819 UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
1820 if (!baseName) {
1821 baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
1823 if (!baseName) {
1824 baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
1826 if (!baseName) {
1827 baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
1829 if (!baseName) {
1830 baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
1832 if (!baseName) {
1833 baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
1835 if (!baseName) {
1836 return NS_ERROR_FAILURE;
1839 // This function is only used in contexts where a failure to find a suitable
1840 // nickname does not block the overall task from succeeding.
1841 // As such, we use an arbitrary limit to prevent this nickname searching
1842 // process from taking forever.
1843 static const uint32_t ARBITRARY_LIMIT = 500;
1844 for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
1845 nickname = baseName.get();
1846 if (count != 1) {
1847 nickname.AppendPrintf(" #%u", count);
1849 if (nickname.IsEmpty()) {
1850 return NS_ERROR_FAILURE;
1853 bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
1854 cert->dbhandle);
1855 if (!conflict) {
1856 return NS_OK;
1860 return NS_ERROR_FAILURE;
1863 Result BuildRevocationCheckArrays(Input certDER, EndEntityOrCA endEntityOrCA,
1864 /*out*/ nsTArray<uint8_t>& issuerBytes,
1865 /*out*/ nsTArray<uint8_t>& serialBytes,
1866 /*out*/ nsTArray<uint8_t>& subjectBytes,
1867 /*out*/ nsTArray<uint8_t>& pubKeyBytes) {
1868 BackCert cert(certDER, endEntityOrCA, nullptr);
1869 Result rv = cert.Init();
1870 if (rv != Success) {
1871 return rv;
1873 issuerBytes.Clear();
1874 Input issuer(cert.GetIssuer());
1875 issuerBytes.AppendElements(issuer.UnsafeGetData(), issuer.GetLength());
1876 serialBytes.Clear();
1877 Input serial(cert.GetSerialNumber());
1878 serialBytes.AppendElements(serial.UnsafeGetData(), serial.GetLength());
1879 subjectBytes.Clear();
1880 Input subject(cert.GetSubject());
1881 subjectBytes.AppendElements(subject.UnsafeGetData(), subject.GetLength());
1882 pubKeyBytes.Clear();
1883 Input pubKey(cert.GetSubjectPublicKeyInfo());
1884 pubKeyBytes.AppendElements(pubKey.UnsafeGetData(), pubKey.GetLength());
1886 return Success;
1889 bool CertIsInCertStorage(const nsTArray<uint8_t>& certDER,
1890 nsICertStorage* certStorage) {
1891 MOZ_ASSERT(certStorage);
1892 if (!certStorage) {
1893 return false;
1895 Input certInput;
1896 Result rv = certInput.Init(certDER.Elements(), certDER.Length());
1897 if (rv != Success) {
1898 return false;
1900 BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
1901 rv = cert.Init();
1902 if (rv != Success) {
1903 return false;
1905 nsTArray<uint8_t> subject;
1906 subject.AppendElements(cert.GetSubject().UnsafeGetData(),
1907 cert.GetSubject().GetLength());
1908 nsTArray<nsTArray<uint8_t>> certStorageCerts;
1909 if (NS_FAILED(certStorage->FindCertsBySubject(subject, certStorageCerts))) {
1910 return false;
1912 for (const auto& certStorageCert : certStorageCerts) {
1913 if (certStorageCert.Length() != certDER.Length()) {
1914 continue;
1916 if (memcmp(certStorageCert.Elements(), certDER.Elements(),
1917 certStorageCert.Length()) == 0) {
1918 return true;
1921 return false;
1925 * Given a list of certificates representing a verified certificate path from an
1926 * end-entity certificate to a trust anchor, imports the intermediate
1927 * certificates into the permanent certificate database. This is an attempt to
1928 * cope with misconfigured servers that don't include the appropriate
1929 * intermediate certificates in the TLS handshake.
1931 * @param certList the verified certificate list
1933 void SaveIntermediateCerts(const nsTArray<nsTArray<uint8_t>>& certList) {
1934 if (certList.IsEmpty()) {
1935 return;
1937 nsTArray<nsTArray<uint8_t>> intermediates;
1938 // Skip the end-entity; we only want to store intermediates. Similarly,
1939 // there's no need to save the trust anchor - it's either already a permanent
1940 // certificate or it's the Microsoft Family Safety root or an enterprise root
1941 // temporarily imported via the child mode or enterprise root features. We
1942 // don't want to import these because they're intended to be temporary (and
1943 // because importing them happens to reset their trust settings, which breaks
1944 // these features).
1945 for (size_t index = 1; index < certList.Length() - 1; index++) {
1946 intermediates.AppendElement(certList.ElementAt(index).Clone());
1948 nsCOMPtr<nsIRunnable> importCertsRunnable(NS_NewRunnableFunction(
1949 "IdleSaveIntermediateCerts",
1950 [intermediates = std::move(intermediates)]() -> void {
1951 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1952 return;
1954 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1955 if (!slot) {
1956 return;
1958 size_t numCertsImported = 0;
1959 nsCOMPtr<nsICertStorage> certStorage(
1960 do_GetService(NS_CERT_STORAGE_CID));
1961 for (const auto& certDER : intermediates) {
1962 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1963 return;
1965 if (CertIsInCertStorage(certDER, certStorage)) {
1966 continue;
1968 SECItem certDERItem = {siBuffer,
1969 const_cast<unsigned char*>(certDER.Elements()),
1970 AssertedCast<unsigned int>(certDER.Length())};
1971 UniqueCERTCertificate cert(CERT_NewTempCertificate(
1972 CERT_GetDefaultCertDB(), &certDERItem, nullptr, false, true));
1973 if (!cert) {
1974 continue;
1976 if (cert->slot) {
1977 // This cert was found on a token; no need to remember it in the
1978 // permanent database.
1979 continue;
1981 PRBool isperm;
1982 if (CERT_GetCertIsPerm(cert.get(), &isperm) != SECSuccess) {
1983 continue;
1985 if (isperm) {
1986 // We don't need to remember certs already stored in perm db.
1987 continue;
1989 // This is a best-effort attempt at avoiding unknown issuer errors
1990 // in the future, so ignore failures here.
1991 nsAutoCString nickname;
1992 if (NS_FAILED(DefaultServerNicknameForCert(cert.get(), nickname))) {
1993 continue;
1995 Unused << PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
1996 nickname.get(), false);
1997 numCertsImported++;
2000 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
2001 "IdleSaveIntermediateCertsDone", [numCertsImported]() -> void {
2002 nsCOMPtr<nsIObserverService> observerService =
2003 mozilla::services::GetObserverService();
2004 if (observerService) {
2005 NS_ConvertUTF8toUTF16 numCertsImportedString(
2006 nsPrintfCString("%zu", numCertsImported));
2007 observerService->NotifyObservers(
2008 nullptr, "psm:intermediate-certs-cached",
2009 numCertsImportedString.get());
2011 }));
2012 Unused << NS_DispatchToMainThread(runnable.forget());
2013 }));
2014 Unused << NS_DispatchToCurrentThreadQueue(importCertsRunnable.forget(),
2015 EventQueuePriority::Idle);
2018 } // namespace psm
2019 } // namespace mozilla