Bug 1883912: Enable Intl.ListFormat test for "unit" style. r=spidermonkey-reviewers...
[gecko.git] / security / certverifier / NSSCertDBTrustDomain.cpp
blobcfc17f46a7eacde7fcdc2428a94164d4d44a161a
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 nsTArray<Input>& thirdPartyRootInputs,
76 const nsTArray<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 mBuiltInRootsModule(SECMOD_FindModule(kRootModuleName)),
104 mOCSPFetchStatus(OCSPFetchStatus::NotFetched) {}
106 static void FindRootsWithSubject(UniqueSECMODModule& rootsModule,
107 SECItem subject,
108 /*out*/ nsTArray<nsTArray<uint8_t>>& roots) {
109 MOZ_ASSERT(rootsModule);
110 AutoSECMODListReadLock lock;
111 for (int slotIndex = 0; slotIndex < rootsModule->slotCount; slotIndex++) {
112 CERTCertificateList* rawResults = nullptr;
113 if (PK11_FindRawCertsWithSubject(rootsModule->slots[slotIndex], &subject,
114 &rawResults) != SECSuccess) {
115 continue;
117 // rawResults == nullptr means we didn't find any matching certificates
118 if (!rawResults) {
119 continue;
121 UniqueCERTCertificateList results(rawResults);
122 for (int certIndex = 0; certIndex < results->len; certIndex++) {
123 nsTArray<uint8_t> root;
124 root.AppendElements(results->certs[certIndex].data,
125 results->certs[certIndex].len);
126 roots.AppendElement(std::move(root));
131 // A self-signed issuer certificate should never be necessary in order to build
132 // a trusted certificate chain unless it is a trust anchor. This is because if
133 // it were necessary, there would exist another certificate with the same
134 // subject and public key that is also a valid issing certificate. Given this
135 // certificate, it is possible to build another chain using just it instead of
136 // it and the self-signed certificate. This is only true as long as the
137 // certificate extensions we support are restrictive rather than additive in
138 // terms of the rest of the chain (for example, we don't support policy mapping
139 // and we ignore any SCT information in intermediates).
140 bool NSSCertDBTrustDomain::ShouldSkipSelfSignedNonTrustAnchor(Input certDER) {
141 BackCert cert(certDER, EndEntityOrCA::MustBeCA, nullptr);
142 if (cert.Init() != Success) {
143 return false; // turn any failures into "don't skip trying this cert"
145 // If subject != issuer, this isn't a self-signed cert.
146 if (!InputsAreEqual(cert.GetSubject(), cert.GetIssuer())) {
147 return false;
149 TrustLevel trust;
150 if (GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy, certDER,
151 trust) != Success) {
152 return false;
154 // If the trust for this certificate is anything other than "inherit", we want
155 // to process it like normal.
156 if (trust != TrustLevel::InheritsTrust) {
157 return false;
159 if (VerifySignedData(*this, cert.GetSignedData(),
160 cert.GetSubjectPublicKeyInfo()) != Success) {
161 return false;
163 // This is a self-signed, non-trust-anchor certificate, so we shouldn't use it
164 // for path building. See bug 1056341.
165 return true;
168 Result NSSCertDBTrustDomain::CheckCandidates(
169 IssuerChecker& checker, nsTArray<IssuerCandidateWithSource>& candidates,
170 Input* nameConstraintsInputPtr, bool& keepGoing) {
171 for (const auto& candidate : candidates) {
172 // Stop path building if the program is shutting down.
173 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
174 keepGoing = false;
175 return Success;
177 if (ShouldSkipSelfSignedNonTrustAnchor(candidate.mDER)) {
178 continue;
180 Result rv =
181 checker.Check(candidate.mDER, nameConstraintsInputPtr, keepGoing);
182 if (rv != Success) {
183 return rv;
185 if (!keepGoing) {
186 mIssuerSources += candidate.mIssuerSource;
187 return Success;
191 return Success;
194 Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
195 IssuerChecker& checker, Time) {
196 SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
197 // Handle imposed name constraints, if any.
198 ScopedAutoSECItem nameConstraints;
199 Input nameConstraintsInput;
200 Input* nameConstraintsInputPtr = nullptr;
201 SECStatus srv =
202 CERT_GetImposedNameConstraints(&encodedIssuerNameItem, &nameConstraints);
203 if (srv == SECSuccess) {
204 if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len) !=
205 Success) {
206 return Result::FATAL_ERROR_LIBRARY_FAILURE;
208 nameConstraintsInputPtr = &nameConstraintsInput;
209 } else if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
210 return Result::FATAL_ERROR_LIBRARY_FAILURE;
213 // First try all relevant certificates known to Gecko, which avoids calling
214 // CERT_CreateSubjectCertList, because that can be expensive.
215 nsTArray<IssuerCandidateWithSource> geckoRootCandidates;
216 nsTArray<IssuerCandidateWithSource> geckoIntermediateCandidates;
218 // We might not have this module if e.g. we're on a Linux distribution that
219 // does something unexpected.
220 nsTArray<nsTArray<uint8_t>> builtInRoots;
221 if (mBuiltInRootsModule) {
222 FindRootsWithSubject(mBuiltInRootsModule, encodedIssuerNameItem,
223 builtInRoots);
224 for (const auto& root : builtInRoots) {
225 Input rootInput;
226 Result rv = rootInput.Init(root.Elements(), root.Length());
227 if (rv != Success) {
228 continue; // probably too big
230 geckoRootCandidates.AppendElement(IssuerCandidateWithSource{
231 rootInput, IssuerSource::BuiltInRootsModule});
233 } else {
234 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
235 ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module"));
238 if (mExtraCertificates.isSome()) {
239 for (const auto& extraCert : *mExtraCertificates) {
240 Input certInput;
241 Result rv = certInput.Init(extraCert.Elements(), extraCert.Length());
242 if (rv != Success) {
243 continue;
245 BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
246 rv = cert.Init();
247 if (rv != Success) {
248 continue;
250 // Filter out certificates that can't be issuers we're looking for because
251 // the subject distinguished name doesn't match. This prevents
252 // mozilla::pkix from accumulating spurious errors during path building.
253 if (!InputsAreEqual(encodedIssuerName, cert.GetSubject())) {
254 continue;
256 // We assume that extra certificates (presumably from the TLS handshake)
257 // are intermediates, since sending trust anchors would be superfluous.
258 geckoIntermediateCandidates.AppendElement(
259 IssuerCandidateWithSource{certInput, IssuerSource::TLSHandshake});
263 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
264 BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr);
265 Result rv = root.Init();
266 if (rv != Success) {
267 continue;
269 // Filter out 3rd party roots that can't be issuers we're looking for
270 // because the subject distinguished name doesn't match. This prevents
271 // mozilla::pkix from accumulating spurious errors during path building.
272 if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) {
273 continue;
275 geckoRootCandidates.AppendElement(IssuerCandidateWithSource{
276 thirdPartyRootInput, IssuerSource::ThirdPartyCertificates});
279 for (const auto& thirdPartyIntermediateInput :
280 mThirdPartyIntermediateInputs) {
281 BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA,
282 nullptr);
283 Result rv = intermediate.Init();
284 if (rv != Success) {
285 continue;
287 // Filter out 3rd party intermediates that can't be issuers we're looking
288 // for because the subject distinguished name doesn't match. This prevents
289 // mozilla::pkix from accumulating spurious errors during path building.
290 if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) {
291 continue;
293 geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{
294 thirdPartyIntermediateInput, IssuerSource::ThirdPartyCertificates});
297 if (!mCertStorage) {
298 return Result::FATAL_ERROR_LIBRARY_FAILURE;
300 nsTArray<uint8_t> subject;
301 subject.AppendElements(encodedIssuerName.UnsafeGetData(),
302 encodedIssuerName.GetLength());
303 nsTArray<nsTArray<uint8_t>> certs;
304 nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
305 if (NS_FAILED(rv)) {
306 return Result::FATAL_ERROR_LIBRARY_FAILURE;
308 for (auto& cert : certs) {
309 Input certDER;
310 Result rv = certDER.Init(cert.Elements(), cert.Length());
311 if (rv != Success) {
312 continue; // probably too big
314 // Currently we're only expecting intermediate certificates in cert storage.
315 geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{
316 std::move(certDER), IssuerSource::PreloadedIntermediates});
319 // Try all root certs first and then all (presumably) intermediates.
320 geckoRootCandidates.AppendElements(std::move(geckoIntermediateCandidates));
322 bool keepGoing = true;
323 Result result = CheckCandidates(checker, geckoRootCandidates,
324 nameConstraintsInputPtr, keepGoing);
325 if (result != Success) {
326 return result;
328 if (!keepGoing) {
329 return Success;
332 // Synchronously dispatch a task to the socket thread to find
333 // CERTCertificates with the given subject. This involves querying NSS
334 // structures and databases, so it should be done on the socket thread.
335 nsTArray<nsTArray<uint8_t>> nssRootCandidates;
336 nsTArray<nsTArray<uint8_t>> nssIntermediateCandidates;
337 RefPtr<Runnable> getCandidatesTask =
338 NS_NewRunnableFunction("NSSCertDBTrustDomain::FindIssuer", [&]() {
339 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
340 return;
342 // NSS seems not to differentiate between "no potential issuers found"
343 // and "there was an error trying to retrieve the potential issuers." We
344 // assume there was no error if CERT_CreateSubjectCertList returns
345 // nullptr.
346 UniqueCERTCertList candidates(
347 CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
348 &encodedIssuerNameItem, 0, false));
349 if (candidates) {
350 for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
351 !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
352 nsTArray<uint8_t> candidate;
353 candidate.AppendElements(n->cert->derCert.data,
354 n->cert->derCert.len);
355 if (n->cert->isRoot) {
356 nssRootCandidates.AppendElement(std::move(candidate));
357 } else {
358 nssIntermediateCandidates.AppendElement(std::move(candidate));
363 nsCOMPtr<nsIEventTarget> socketThread(
364 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
365 if (!socketThread) {
366 return Result::FATAL_ERROR_LIBRARY_FAILURE;
368 rv = SyncRunnable::DispatchToThread(socketThread, getCandidatesTask);
369 if (NS_FAILED(rv)) {
370 return Result::FATAL_ERROR_LIBRARY_FAILURE;
373 nsTArray<IssuerCandidateWithSource> nssCandidates;
374 for (const auto& rootCandidate : nssRootCandidates) {
375 Input certDER;
376 Result rv = certDER.Init(rootCandidate.Elements(), rootCandidate.Length());
377 if (rv != Success) {
378 continue; // probably too big
380 nssCandidates.AppendElement(
381 IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB});
383 for (const auto& intermediateCandidate : nssIntermediateCandidates) {
384 Input certDER;
385 Result rv = certDER.Init(intermediateCandidate.Elements(),
386 intermediateCandidate.Length());
387 if (rv != Success) {
388 continue; // probably too big
390 nssCandidates.AppendElement(
391 IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB});
394 return CheckCandidates(checker, nssCandidates, nameConstraintsInputPtr,
395 keepGoing);
398 Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
399 const CertPolicyId& policy,
400 Input candidateCertDER,
401 /*out*/ TrustLevel& trustLevel) {
402 // Check the certificate against the OneCRL cert blocklist
403 if (!mCertStorage) {
404 return Result::FATAL_ERROR_LIBRARY_FAILURE;
407 // The certificate blocklist currently only applies to TLS server
408 // certificates.
409 if (mCertDBTrustType == trustSSL) {
410 int16_t revocationState;
412 nsTArray<uint8_t> issuerBytes;
413 nsTArray<uint8_t> serialBytes;
414 nsTArray<uint8_t> subjectBytes;
415 nsTArray<uint8_t> pubKeyBytes;
417 Result result =
418 BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes,
419 serialBytes, subjectBytes, pubKeyBytes);
420 if (result != Success) {
421 return result;
424 nsresult nsrv = mCertStorage->GetRevocationState(
425 issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
426 if (NS_FAILED(nsrv)) {
427 return Result::FATAL_ERROR_LIBRARY_FAILURE;
430 if (revocationState == nsICertStorage::STATE_ENFORCE) {
431 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
432 ("NSSCertDBTrustDomain: certificate is in blocklist"));
433 Telemetry::AccumulateCategorical(
434 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::OneCRL);
435 return Result::ERROR_REVOKED_CERTIFICATE;
439 // This may be a third-party root.
440 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
441 if (InputsAreEqual(candidateCertDER, thirdPartyRootInput)) {
442 trustLevel = TrustLevel::TrustAnchor;
443 return Success;
447 // This may be a third-party intermediate.
448 for (const auto& thirdPartyIntermediateInput :
449 mThirdPartyIntermediateInputs) {
450 if (InputsAreEqual(candidateCertDER, thirdPartyIntermediateInput)) {
451 trustLevel = TrustLevel::InheritsTrust;
452 return Success;
456 // Synchronously dispatch a task to the socket thread to construct a
457 // CERTCertificate and get its trust from NSS. This involves querying NSS
458 // structures and databases, so it should be done on the socket thread.
459 Result result = Result::FATAL_ERROR_LIBRARY_FAILURE;
460 RefPtr<Runnable> getTrustTask =
461 NS_NewRunnableFunction("NSSCertDBTrustDomain::GetCertTrust", [&]() {
462 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
463 result = Result::FATAL_ERROR_LIBRARY_FAILURE;
464 return;
466 // This would be cleaner and more efficient if we could get the trust
467 // information without constructing a CERTCertificate here, but NSS
468 // doesn't expose it in any other easy-to-use fashion. The use of
469 // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
470 // performance problem for certificates already known to NSS because NSS
471 // will just find the existing CERTCertificate in its in-memory cache
472 // and return it. For certificates not already in NSS (namely
473 // third-party roots and intermediates), we want to avoid calling
474 // CERT_NewTempCertificate repeatedly, so we've already checked if the
475 // candidate certificate is a third-party certificate, above.
476 SECItem candidateCertDERSECItem =
477 UnsafeMapInputToSECItem(candidateCertDER);
478 UniqueCERTCertificate candidateCert(CERT_NewTempCertificate(
479 CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false,
480 true));
481 if (!candidateCert) {
482 result = MapPRErrorCodeToResult(PR_GetError());
483 return;
485 // NB: CERT_GetCertTrust seems to be abusing SECStatus as a boolean,
486 // where SECSuccess means that there is a trust record and SECFailure
487 // means there is not a trust record. I looked at NSS's internal uses of
488 // CERT_GetCertTrust, and all that code uses the result as a boolean
489 // meaning "We have a trust record."
490 CERTCertTrust trust;
491 if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
492 uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
494 // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
495 // because we can have active distrust for either type of cert. Note
496 // that CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so
497 // if the relevant trust bit isn't set then that means the cert must
498 // be considered distrusted.
499 uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
500 ? CERTDB_TRUSTED_CA
501 : CERTDB_TRUSTED;
502 if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) ==
503 CERTDB_TERMINAL_RECORD) {
504 trustLevel = TrustLevel::ActivelyDistrusted;
505 result = Success;
506 return;
509 // For TRUST, we use the CERTDB_TRUSTED_CA bit.
510 if (flags & CERTDB_TRUSTED_CA) {
511 if (policy.IsAnyPolicy()) {
512 trustLevel = TrustLevel::TrustAnchor;
513 result = Success;
514 return;
517 nsTArray<uint8_t> certBytes(candidateCert->derCert.data,
518 candidateCert->derCert.len);
519 if (CertIsAuthoritativeForEVPolicy(certBytes, policy)) {
520 trustLevel = TrustLevel::TrustAnchor;
521 result = Success;
522 return;
526 trustLevel = TrustLevel::InheritsTrust;
527 result = Success;
529 nsCOMPtr<nsIEventTarget> socketThread(
530 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
531 if (!socketThread) {
532 return Result::FATAL_ERROR_LIBRARY_FAILURE;
534 nsresult rv = SyncRunnable::DispatchToThread(socketThread, getTrustTask);
535 if (NS_FAILED(rv)) {
536 return Result::FATAL_ERROR_LIBRARY_FAILURE;
538 return result;
541 Result NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
542 /*out*/ uint8_t* digestBuf,
543 size_t digestBufLen) {
544 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
547 TimeDuration NSSCertDBTrustDomain::GetOCSPTimeout() const {
548 switch (mOCSPFetching) {
549 case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
550 return mOCSPTimeoutSoft;
551 case NSSCertDBTrustDomain::FetchOCSPForEV:
552 case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
553 return mOCSPTimeoutHard;
554 // The rest of these are error cases. Assert in debug builds, but return
555 // the soft timeout value in release builds.
556 case NSSCertDBTrustDomain::NeverFetchOCSP:
557 case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
558 MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
559 break;
562 MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
563 return mOCSPTimeoutSoft;
566 // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
567 // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
568 // Success with result.IsVoid() == true when an OCSP URI was not found, and
569 // Success with result.IsVoid() == false when an OCSP URI was found.
570 static Result GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
571 Input aiaExtension,
572 /*out*/ nsCString& result) {
573 MOZ_ASSERT(arena.get());
574 if (!arena.get()) {
575 return Result::FATAL_ERROR_INVALID_ARGS;
578 result.Assign(VoidCString());
579 SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
580 CERTAuthInfoAccess** aia =
581 CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
582 if (!aia) {
583 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
585 for (size_t i = 0; aia[i]; ++i) {
586 if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
587 // NSS chooses the **last** OCSP URL; we choose the **first**
588 CERTGeneralName* current = aia[i]->location;
589 if (!current) {
590 continue;
592 do {
593 if (current->type == certURI) {
594 const SECItem& location = current->name.other;
595 // (location.len + 1) must be small enough to fit into a uint32_t,
596 // but we limit it to a smaller bound to reduce OOM risk.
597 if (location.len > 1024 || memchr(location.data, 0, location.len)) {
598 // Reject embedded nulls. (NSS doesn't do this)
599 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
601 result.Assign(nsDependentCSubstring(
602 reinterpret_cast<const char*>(location.data), location.len));
603 return Success;
605 current = CERT_GetNextGeneralName(current);
606 } while (current != aia[i]->location);
610 return Success;
613 NS_IMPL_ISUPPORTS(CRLiteTimestamp, nsICRLiteTimestamp)
615 NS_IMETHODIMP
616 CRLiteTimestamp::GetLogID(nsTArray<uint8_t>& aLogID) {
617 aLogID.Clear();
618 aLogID.AppendElements(mLogID);
619 return NS_OK;
622 NS_IMETHODIMP
623 CRLiteTimestamp::GetTimestamp(uint64_t* aTimestamp) {
624 *aTimestamp = mTimestamp;
625 return NS_OK;
628 Result BuildCRLiteTimestampArray(
629 Input sctExtension,
630 /*out*/ nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps) {
631 Input sctList;
632 Result rv =
633 ExtractSignedCertificateTimestampListFromExtension(sctExtension, sctList);
634 if (rv != Success) {
635 return rv;
637 std::vector<SignedCertificateTimestamp> decodedSCTs;
638 size_t decodingErrors;
639 DecodeSCTs(sctList, decodedSCTs, decodingErrors);
640 Unused << decodingErrors;
642 for (const auto& sct : decodedSCTs) {
643 timestamps.AppendElement(new CRLiteTimestamp(sct));
645 return Success;
648 Result NSSCertDBTrustDomain::CheckCRLiteStash(
649 const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
650 const nsTArray<uint8_t>& serialNumberBytes) {
651 // This information is deterministic and has already been validated by our
652 // infrastructure (it comes from signed CRLs), so if the stash says a
653 // certificate is revoked, it is.
654 bool isRevokedByStash = false;
655 nsresult rv = mCertStorage->IsCertRevokedByStash(
656 issuerSubjectPublicKeyInfoBytes, serialNumberBytes, &isRevokedByStash);
657 if (NS_FAILED(rv)) {
658 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
659 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
660 "failed"));
661 return Result::FATAL_ERROR_LIBRARY_FAILURE;
663 if (isRevokedByStash) {
664 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
665 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
666 "returned true"));
667 return Result::ERROR_REVOKED_CERTIFICATE;
669 return Success;
672 Result NSSCertDBTrustDomain::CheckCRLite(
673 const nsTArray<uint8_t>& issuerBytes,
674 const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
675 const nsTArray<uint8_t>& serialNumberBytes,
676 const nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps,
677 /*out*/ bool& filterCoversCertificate) {
678 filterCoversCertificate = false;
679 int16_t crliteRevocationState;
680 nsresult rv = mCertStorage->GetCRLiteRevocationState(
681 issuerBytes, issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
682 timestamps, &crliteRevocationState);
683 if (NS_FAILED(rv)) {
684 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
685 ("NSSCertDBTrustDomain::CheckCRLite: CRLite call failed"));
686 return Result::FATAL_ERROR_LIBRARY_FAILURE;
688 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
689 ("NSSCertDBTrustDomain::CheckCRLite: CRLite check returned "
690 "state=%hd",
691 crliteRevocationState));
693 switch (crliteRevocationState) {
694 case nsICertStorage::STATE_ENFORCE:
695 filterCoversCertificate = true;
696 return Result::ERROR_REVOKED_CERTIFICATE;
697 case nsICertStorage::STATE_UNSET:
698 filterCoversCertificate = true;
699 return Success;
700 case nsICertStorage::STATE_NOT_ENROLLED:
701 filterCoversCertificate = false;
702 return Success;
703 case nsICertStorage::STATE_NOT_COVERED:
704 filterCoversCertificate = false;
705 return Success;
706 case nsICertStorage::STATE_NO_FILTER:
707 filterCoversCertificate = false;
708 return Success;
709 default:
710 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
711 ("NSSCertDBTrustDomain::CheckCRLite: Unknown CRLite revocation "
712 "state"));
713 return Result::FATAL_ERROR_LIBRARY_FAILURE;
717 Result NSSCertDBTrustDomain::CheckRevocation(
718 EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
719 Duration validityDuration,
720 /*optional*/ const Input* stapledOCSPResponse,
721 /*optional*/ const Input* aiaExtension,
722 /*optional*/ const Input* sctExtension) {
723 // Actively distrusted certificates will have already been blocked by
724 // GetCertTrust.
726 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
727 ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
729 // None of the revocation methods in this function are consulted for CA
730 // certificates. Revocation for CAs is handled by GetCertTrust.
731 if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
732 return Success;
735 // Look for an OCSP Authority Information Access URL. Our behavior in
736 // ConfirmRevocations mode depends on whether a synchronous OCSP
737 // request is possible.
738 nsCString aiaLocation(VoidCString());
739 if (aiaExtension) {
740 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
741 if (!arena) {
742 return Result::FATAL_ERROR_NO_MEMORY;
744 Result rv =
745 GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation);
746 if (rv != Success) {
747 return rv;
751 bool crliteCoversCertificate = false;
752 Result crliteResult = Success;
753 if (mCRLiteMode != CRLiteMode::Disabled && sctExtension) {
754 crliteResult =
755 CheckRevocationByCRLite(certID, *sctExtension, crliteCoversCertificate);
757 // If CheckCRLite returned an error other than "revoked certificate",
758 // propagate that error.
759 if (crliteResult != Success &&
760 crliteResult != Result::ERROR_REVOKED_CERTIFICATE) {
761 return crliteResult;
764 if (crliteCoversCertificate) {
765 Telemetry::AccumulateCategorical(
766 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::CRLite);
767 // If we don't return here we will consult OCSP.
768 // In Enforce CRLite mode we can return "Revoked" or "Not Revoked"
769 // without consulting OCSP.
770 if (mCRLiteMode == CRLiteMode::Enforce) {
771 return crliteResult;
773 // If we don't have a URL for an OCSP responder, then we can return any
774 // result ConfirmRevocations mode. Note that we might have a
775 // stapled or cached OCSP response which we ignore in this case.
776 if (mCRLiteMode == CRLiteMode::ConfirmRevocations &&
777 aiaLocation.IsVoid()) {
778 return crliteResult;
780 // In ConfirmRevocations mode we can return "Not Revoked"
781 // without consulting OCSP.
782 if (mCRLiteMode == CRLiteMode::ConfirmRevocations &&
783 crliteResult == Success) {
784 return Success;
789 bool ocspSoftFailure = false;
790 Result ocspResult = CheckRevocationByOCSP(
791 certID, time, validityDuration, aiaLocation, crliteCoversCertificate,
792 crliteResult, stapledOCSPResponse, ocspSoftFailure);
794 // In ConfirmRevocations mode we treat any OCSP failure as confirmation
795 // of a CRLite revoked result.
796 if (crliteCoversCertificate &&
797 crliteResult == Result::ERROR_REVOKED_CERTIFICATE &&
798 mCRLiteMode == CRLiteMode::ConfirmRevocations &&
799 (ocspResult != Success || ocspSoftFailure)) {
800 return Result::ERROR_REVOKED_CERTIFICATE;
803 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
804 ("NSSCertDBTrustDomain: end of CheckRevocation"));
806 return ocspResult;
809 Result NSSCertDBTrustDomain::CheckRevocationByCRLite(
810 const CertID& certID, const Input& sctExtension,
811 /*out*/ bool& crliteCoversCertificate) {
812 crliteCoversCertificate = false;
813 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
814 ("NSSCertDBTrustDomain::CheckRevocation: checking CRLite"));
815 nsTArray<uint8_t> issuerSubjectPublicKeyInfoBytes;
816 issuerSubjectPublicKeyInfoBytes.AppendElements(
817 certID.issuerSubjectPublicKeyInfo.UnsafeGetData(),
818 certID.issuerSubjectPublicKeyInfo.GetLength());
819 nsTArray<uint8_t> serialNumberBytes;
820 serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(),
821 certID.serialNumber.GetLength());
822 // The CRLite stash is essentially a subset of a collection of CRLs, so if
823 // it says a certificate is revoked, it is.
824 Result rv =
825 CheckCRLiteStash(issuerSubjectPublicKeyInfoBytes, serialNumberBytes);
826 if (rv != Success) {
827 crliteCoversCertificate = (rv == Result::ERROR_REVOKED_CERTIFICATE);
828 return rv;
831 nsTArray<uint8_t> issuerBytes;
832 issuerBytes.AppendElements(certID.issuer.UnsafeGetData(),
833 certID.issuer.GetLength());
835 nsTArray<RefPtr<nsICRLiteTimestamp>> timestamps;
836 rv = BuildCRLiteTimestampArray(sctExtension, timestamps);
837 if (rv != Success) {
838 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
839 ("decoding SCT extension failed - CRLite will be not be "
840 "consulted"));
841 return Success;
843 return CheckCRLite(issuerBytes, issuerSubjectPublicKeyInfoBytes,
844 serialNumberBytes, timestamps, crliteCoversCertificate);
847 Result NSSCertDBTrustDomain::CheckRevocationByOCSP(
848 const CertID& certID, Time time, Duration validityDuration,
849 const nsCString& aiaLocation, const bool crliteCoversCertificate,
850 const Result crliteResult,
851 /*optional*/ const Input* stapledOCSPResponse,
852 /*out*/ bool& softFailure) {
853 softFailure = false;
854 const uint16_t maxOCSPLifetimeInDays = 10;
855 // If we have a stapled OCSP response then the verification of that response
856 // determines the result unless the OCSP response is expired. We make an
857 // exception for expired responses because some servers, nginx in particular,
858 // are known to serve expired responses due to bugs.
859 // We keep track of the result of verifying the stapled response but don't
860 // immediately return failure if the response has expired.
861 Result stapledOCSPResponseResult = Success;
862 if (stapledOCSPResponse) {
863 bool expired;
864 stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse(
865 certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse,
866 ResponseWasStapled, expired);
867 Telemetry::AccumulateCategorical(
868 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::StapledOCSP);
869 if (stapledOCSPResponseResult == Success) {
870 // stapled OCSP response present and good
871 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
872 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
873 ("NSSCertDBTrustDomain: stapled OCSP response: good"));
874 return Success;
876 if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
877 expired) {
878 // stapled OCSP response present but expired
879 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
880 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
881 ("NSSCertDBTrustDomain: expired stapled OCSP response"));
882 } else if (stapledOCSPResponseResult ==
883 Result::ERROR_OCSP_TRY_SERVER_LATER ||
884 stapledOCSPResponseResult ==
885 Result::ERROR_OCSP_INVALID_SIGNING_CERT) {
886 // Stapled OCSP response present but invalid for a small number of reasons
887 // CAs/servers commonly get wrong. This will be treated similarly to an
888 // expired stapled response.
889 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
890 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
891 ("NSSCertDBTrustDomain: stapled OCSP response: "
892 "failure (allowed for compatibility)"));
893 } else {
894 // stapled OCSP response present but invalid for some reason
895 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
896 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
897 ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
898 return stapledOCSPResponseResult;
900 } else {
901 // no stapled OCSP response
902 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
903 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
904 ("NSSCertDBTrustDomain: no stapled OCSP response"));
907 Result cachedResponseResult = Success;
908 Time cachedResponseValidThrough(Time::uninitialized);
909 bool cachedResponsePresent =
910 mOCSPCache.Get(certID, mOriginAttributes, cachedResponseResult,
911 cachedResponseValidThrough);
912 if (cachedResponsePresent) {
913 Telemetry::AccumulateCategorical(
914 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::CachedOCSP);
915 if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
916 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
917 ("NSSCertDBTrustDomain: cached OCSP response: good"));
918 return Success;
920 // If we have a cached revoked response, use it.
921 if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
922 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
923 ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
924 return Result::ERROR_REVOKED_CERTIFICATE;
926 // The cached response may indicate an unknown certificate or it may be
927 // expired. Don't return with either of these statuses yet - we may be
928 // able to fetch a more recent one.
929 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
930 ("NSSCertDBTrustDomain: cached OCSP response: error %d",
931 static_cast<int>(cachedResponseResult)));
932 // When a good cached response has expired, it is more convenient
933 // to convert that to an error code and just deal with
934 // cachedResponseResult from here on out.
935 if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
936 cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
938 // We may have a cached indication of server failure. Ignore it if
939 // it has expired.
940 if (cachedResponseResult != Success &&
941 cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
942 cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
943 cachedResponseValidThrough < time) {
944 cachedResponseResult = Success;
945 cachedResponsePresent = false;
947 } else {
948 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
949 ("NSSCertDBTrustDomain: no cached OCSP response"));
951 // At this point, if and only if cachedErrorResult is Success, there was no
952 // cached response.
953 MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
954 (cachedResponsePresent && cachedResponseResult != Success));
956 // TODO: We still need to handle the fallback for invalid stapled responses.
957 // But, if/when we disable OCSP fetching by default, it would be ambiguous
958 // whether security.OCSP.enable==0 means "I want the default" or "I really
959 // never want you to ever fetch OCSP."
960 // Additionally, this doesn't properly handle OCSP-must-staple when OCSP
961 // fetching is disabled.
962 Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
963 if (validityDuration < shortLifetime) {
964 Telemetry::AccumulateCategorical(
965 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::ShortValidity);
967 if ((mOCSPFetching == NeverFetchOCSP) || (validityDuration < shortLifetime)) {
968 // We're not going to be doing any fetching, so if there was a cached
969 // "unknown" response, say so.
970 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
971 return Result::ERROR_OCSP_UNKNOWN_CERT;
973 // If we're doing hard-fail, we want to know if we have a cached response
974 // that has expired.
975 if (mOCSPFetching == FetchOCSPForDVHardFail &&
976 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
977 return Result::ERROR_OCSP_OLD_RESPONSE;
980 softFailure = true;
981 return Success;
984 if (mOCSPFetching == LocalOnlyOCSPForEV) {
985 if (cachedResponseResult != Success) {
986 return cachedResponseResult;
988 return Result::ERROR_OCSP_UNKNOWN_CERT;
991 if (aiaLocation.IsVoid()) {
992 if (mOCSPFetching == FetchOCSPForEV ||
993 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
994 return Result::ERROR_OCSP_UNKNOWN_CERT;
996 if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
997 return Result::ERROR_OCSP_OLD_RESPONSE;
999 if (stapledOCSPResponseResult != Success) {
1000 return stapledOCSPResponseResult;
1003 // Nothing to do if we don't have an OCSP responder URI for the cert; just
1004 // assume it is good. Note that this is the confusing, but intended,
1005 // interpretation of "strict" revocation checking in the face of a
1006 // certificate that lacks an OCSP responder URI. There's no need to set
1007 // softFailure here---we check for the presence of an AIA before attempting
1008 // OCSP when CRLite is configured in confirm revocations mode.
1009 return Success;
1012 if (cachedResponseResult == Success ||
1013 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
1014 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
1015 // Only send a request to, and process a response from, the server if we
1016 // didn't have a cached indication of failure. Also, don't keep requesting
1017 // responses from a failing server.
1018 return SynchronousCheckRevocationWithServer(
1019 certID, aiaLocation, time, maxOCSPLifetimeInDays, cachedResponseResult,
1020 stapledOCSPResponseResult, crliteCoversCertificate, crliteResult,
1021 softFailure);
1024 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
1025 cachedResponseResult, softFailure);
1028 Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
1029 const CertID& certID, const nsCString& aiaLocation, Time time,
1030 uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult,
1031 const Result stapledOCSPResponseResult, const bool crliteCoversCertificate,
1032 const Result crliteResult, /*out*/ bool& softFailure) {
1033 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1034 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1037 uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
1038 size_t ocspRequestLength;
1039 Result rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
1040 ocspRequestLength);
1041 if (rv != Success) {
1042 return rv;
1045 Vector<uint8_t> ocspResponse;
1046 Input response;
1047 mOCSPFetchStatus = OCSPFetchStatus::Fetched;
1048 rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes,
1049 ocspRequestLength, GetOCSPTimeout(), ocspResponse);
1050 Telemetry::AccumulateCategorical(
1051 Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::OCSP);
1052 if (rv == Success &&
1053 response.Init(ocspResponse.begin(), ocspResponse.length()) != Success) {
1054 rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
1057 if (rv != Success) {
1058 Time timeout(time);
1059 if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
1060 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
1063 Result cacheRV =
1064 mOCSPCache.Put(certID, mOriginAttributes, rv, time, timeout);
1065 if (cacheRV != Success) {
1066 return cacheRV;
1069 if (crliteCoversCertificate) {
1070 if (crliteResult == Success) {
1071 // CRLite says the certificate is OK, but OCSP fetching failed.
1072 Telemetry::AccumulateCategorical(
1073 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPFail);
1074 } else {
1075 // CRLite says the certificate is revoked, but OCSP fetching failed.
1076 Telemetry::AccumulateCategorical(
1077 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPFail);
1081 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
1082 rv, softFailure);
1085 // If the response from the network has expired but indicates a revoked
1086 // or unknown certificate, PR_GetError() will return the appropriate error.
1087 // We actually ignore expired here.
1088 bool expired;
1089 rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
1090 maxOCSPLifetimeInDays, response,
1091 ResponseIsFromNetwork, expired);
1093 // If the CRLite filter covers the certificate, compare the CRLite result
1094 // with the OCSP fetching result. OCSP may have succeeded, said the
1095 // certificate is revoked, said the certificate doesn't exist, or it may have
1096 // failed for a reason that results in a "soft fail" (i.e. there is no
1097 // indication that the certificate is either definitely revoked or definitely
1098 // not revoked, so for usability, revocation checking says the certificate is
1099 // valid by default).
1100 if (crliteCoversCertificate) {
1101 if (rv == Success) {
1102 if (crliteResult == Success) {
1103 // CRLite and OCSP fetching agree the certificate is OK.
1104 Telemetry::AccumulateCategorical(
1105 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPOk);
1106 } else {
1107 // CRLite says the certificate is revoked, but OCSP says it is OK.
1108 Telemetry::AccumulateCategorical(
1109 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPOk);
1111 } else if (rv == Result::ERROR_REVOKED_CERTIFICATE) {
1112 if (crliteResult == Success) {
1113 // CRLite says the certificate is OK, but OCSP says it is revoked.
1114 Telemetry::AccumulateCategorical(
1115 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPRev);
1116 } else {
1117 // CRLite and OCSP fetching agree the certificate is revoked.
1118 Telemetry::AccumulateCategorical(
1119 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPRev);
1121 } else if (rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
1122 if (crliteResult == Success) {
1123 // CRLite says the certificate is OK, but OCSP says it doesn't exist.
1124 Telemetry::AccumulateCategorical(
1125 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPUnk);
1126 } else {
1127 // CRLite says the certificate is revoked, but OCSP says it doesn't
1128 // exist.
1129 Telemetry::AccumulateCategorical(
1130 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPUnk);
1132 } else {
1133 if (crliteResult == Success) {
1134 // CRLite says the certificate is OK, but OCSP encountered a soft-fail
1135 // error.
1136 Telemetry::AccumulateCategorical(
1137 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPSoft);
1138 } else {
1139 // CRLite says the certificate is revoked, but OCSP encountered a
1140 // soft-fail error.
1141 Telemetry::AccumulateCategorical(
1142 Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPSoft);
1147 if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
1148 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1149 ("NSSCertDBTrustDomain: returning after "
1150 "VerifyEncodedOCSPResponse"));
1151 return rv;
1154 if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
1155 rv == Result::ERROR_REVOKED_CERTIFICATE) {
1156 return rv;
1159 if (stapledOCSPResponseResult != Success) {
1160 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1161 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
1162 "stapled response after OCSP request verification failure"));
1163 return stapledOCSPResponseResult;
1166 softFailure = true;
1167 return Success; // Soft fail -> success :(
1170 Result NSSCertDBTrustDomain::HandleOCSPFailure(
1171 const Result cachedResponseResult, const Result stapledOCSPResponseResult,
1172 const Result error, /*out*/ bool& softFailure) {
1173 if (mOCSPFetching != FetchOCSPForDVSoftFail) {
1174 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1175 ("NSSCertDBTrustDomain: returning SECFailure after OCSP request "
1176 "failure"));
1177 return error;
1180 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
1181 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1182 ("NSSCertDBTrustDomain: returning SECFailure from cached response "
1183 "after OCSP request failure"));
1184 return cachedResponseResult;
1187 if (stapledOCSPResponseResult != Success) {
1188 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1189 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
1190 "stapled response after OCSP request failure"));
1191 return stapledOCSPResponseResult;
1194 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1195 ("NSSCertDBTrustDomain: returning SECSuccess after OCSP request "
1196 "failure"));
1198 softFailure = true;
1199 return Success; // Soft fail -> success :(
1202 Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
1203 const CertID& certID, Time time, uint16_t maxLifetimeInDays,
1204 Input encodedResponse, EncodedResponseSource responseSource,
1205 /*out*/ bool& expired) {
1206 Time thisUpdate(Time::uninitialized);
1207 Time validThrough(Time::uninitialized);
1209 Result rv = VerifyEncodedOCSPResponse(*this, certID, time, maxLifetimeInDays,
1210 encodedResponse, expired, &thisUpdate,
1211 &validThrough);
1212 // If a response was stapled and expired, we don't want to cache it. Return
1213 // early to simplify the logic here.
1214 if (responseSource == ResponseWasStapled && expired) {
1215 MOZ_ASSERT(rv != Success);
1216 return rv;
1218 // validThrough is only trustworthy if the response successfully verifies
1219 // or it indicates a revoked or unknown certificate.
1220 // If this isn't the case, store an indication of failure (to prevent
1221 // repeatedly requesting a response from a failing server).
1222 if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
1223 rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
1224 validThrough = time;
1225 if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
1226 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
1229 if (responseSource == ResponseIsFromNetwork || rv == Success ||
1230 rv == Result::ERROR_REVOKED_CERTIFICATE ||
1231 rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
1232 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1233 ("NSSCertDBTrustDomain: caching OCSP response"));
1234 Result putRV =
1235 mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate, validThrough);
1236 if (putRV != Success) {
1237 return putRV;
1241 return rv;
1244 SECStatus GetCertDistrustAfterValue(const SECItem* distrustItem,
1245 PRTime& distrustTime) {
1246 if (!distrustItem || !distrustItem->data || distrustItem->len != 13) {
1247 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
1248 return SECFailure;
1250 return DER_DecodeTimeChoice(&distrustTime, distrustItem);
1253 SECStatus GetCertNotBeforeValue(const CERTCertificate* cert,
1254 PRTime& distrustTime) {
1255 return DER_DecodeTimeChoice(&distrustTime, &cert->validity.notBefore);
1258 nsresult isDistrustedCertificateChain(
1259 const nsTArray<nsTArray<uint8_t>>& certArray,
1260 const SECTrustType certDBTrustType, bool& isDistrusted) {
1261 if (certArray.Length() == 0) {
1262 return NS_ERROR_FAILURE;
1265 // Set the default result to be distrusted.
1266 isDistrusted = true;
1268 // There is no distrust to set if the certDBTrustType is not SSL or Email.
1269 if (certDBTrustType != trustSSL && certDBTrustType != trustEmail) {
1270 isDistrusted = false;
1271 return NS_OK;
1274 SECStatus runnableRV = SECFailure;
1276 RefPtr<Runnable> isDistrustedChainTask =
1277 NS_NewRunnableFunction("isDistrustedCertificateChain", [&]() {
1278 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1279 runnableRV = SECFailure;
1280 return;
1282 // Allocate objects and retreive the root and end-entity certificates.
1283 CERTCertDBHandle* certDB(CERT_GetDefaultCertDB());
1284 const nsTArray<uint8_t>& certRootDER = certArray.LastElement();
1285 SECItem certRootDERItem = {
1286 siBuffer, const_cast<unsigned char*>(certRootDER.Elements()),
1287 AssertedCast<unsigned int>(certRootDER.Length())};
1288 UniqueCERTCertificate certRoot(CERT_NewTempCertificate(
1289 certDB, &certRootDERItem, nullptr, false, true));
1290 if (!certRoot) {
1291 runnableRV = SECFailure;
1292 return;
1294 const nsTArray<uint8_t>& certLeafDER = certArray.ElementAt(0);
1295 SECItem certLeafDERItem = {
1296 siBuffer, const_cast<unsigned char*>(certLeafDER.Elements()),
1297 AssertedCast<unsigned int>(certLeafDER.Length())};
1298 UniqueCERTCertificate certLeaf(CERT_NewTempCertificate(
1299 certDB, &certLeafDERItem, nullptr, false, true));
1300 if (!certLeaf) {
1301 runnableRV = SECFailure;
1302 return;
1305 // Set isDistrusted to false if there is no distrust for the root.
1306 if (!certRoot->distrust) {
1307 isDistrusted = false;
1308 runnableRV = SECSuccess;
1309 return;
1312 // Create a pointer to refer to the selected distrust struct.
1313 SECItem* distrustPtr = nullptr;
1314 if (certDBTrustType == trustSSL) {
1315 distrustPtr = &certRoot->distrust->serverDistrustAfter;
1317 if (certDBTrustType == trustEmail) {
1318 distrustPtr = &certRoot->distrust->emailDistrustAfter;
1321 // Get validity for the current end-entity certificate
1322 // and get the distrust field for the root certificate.
1323 PRTime certRootDistrustAfter;
1324 PRTime certLeafNotBefore;
1326 runnableRV =
1327 GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter);
1328 if (runnableRV != SECSuccess) {
1329 return;
1332 runnableRV = GetCertNotBeforeValue(certLeaf.get(), certLeafNotBefore);
1333 if (runnableRV != SECSuccess) {
1334 return;
1337 // Compare the validity of the end-entity certificate with
1338 // the distrust value of the root.
1339 if (certLeafNotBefore <= certRootDistrustAfter) {
1340 isDistrusted = false;
1343 runnableRV = SECSuccess;
1345 nsCOMPtr<nsIEventTarget> socketThread(
1346 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
1347 if (!socketThread) {
1348 return NS_ERROR_FAILURE;
1350 nsresult rv =
1351 SyncRunnable::DispatchToThread(socketThread, isDistrustedChainTask);
1352 if (NS_FAILED(rv) || runnableRV != SECSuccess) {
1353 return NS_ERROR_FAILURE;
1355 return NS_OK;
1358 Result NSSCertDBTrustDomain::IsChainValid(const DERArray& reversedDERArray,
1359 Time time,
1360 const CertPolicyId& requiredPolicy) {
1361 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1362 ("NSSCertDBTrustDomain: IsChainValid"));
1364 size_t numCerts = reversedDERArray.GetLength();
1365 if (numCerts < 1) {
1366 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1368 nsTArray<nsTArray<uint8_t>> certArray;
1369 for (size_t i = numCerts; i > 0; --i) {
1370 const Input* derInput = reversedDERArray.GetDER(i - 1);
1371 certArray.EmplaceBack(derInput->UnsafeGetData(), derInput->GetLength());
1374 const nsTArray<uint8_t>& rootBytes = certArray.LastElement();
1375 Input rootInput;
1376 Result rv = rootInput.Init(rootBytes.Elements(), rootBytes.Length());
1377 if (rv != Success) {
1378 return rv;
1380 rv = IsCertBuiltInRoot(rootInput, mIsBuiltChainRootBuiltInRoot);
1381 if (rv != Result::Success) {
1382 return rv;
1384 nsresult nsrv;
1385 // If mHostname isn't set, we're not verifying in the context of a TLS
1386 // handshake, so don't verify key pinning in those cases.
1387 if (mHostname) {
1388 nsTArray<Span<const uint8_t>> derCertSpanList;
1389 for (const auto& certDER : certArray) {
1390 derCertSpanList.EmplaceBack(certDER.Elements(), certDER.Length());
1393 bool chainHasValidPins;
1394 nsrv = PublicKeyPinningService::ChainHasValidPins(
1395 derCertSpanList, mHostname, time, mIsBuiltChainRootBuiltInRoot,
1396 chainHasValidPins, mPinningTelemetryInfo);
1397 if (NS_FAILED(nsrv)) {
1398 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1400 if (!chainHasValidPins) {
1401 return Result::ERROR_KEY_PINNING_FAILURE;
1405 // Check that the childs' certificate NotBefore date is anterior to
1406 // the NotAfter value of the parent when the root is a builtin.
1407 if (mIsBuiltChainRootBuiltInRoot) {
1408 bool isDistrusted;
1409 nsrv =
1410 isDistrustedCertificateChain(certArray, mCertDBTrustType, isDistrusted);
1411 if (NS_FAILED(nsrv)) {
1412 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1414 if (isDistrusted) {
1415 return Result::ERROR_UNTRUSTED_ISSUER;
1419 // See bug 1434300. If the root is a Symantec root, see if we distrust this
1420 // path. Since we already have the root available, we can check that cheaply
1421 // here before proceeding with the rest of the algorithm.
1423 // This algorithm only applies if we are verifying in the context of a TLS
1424 // handshake. To determine this, we check mHostname: If it isn't set, this is
1425 // not TLS, so don't run the algorithm.
1426 const nsTArray<uint8_t>& rootCertDER = certArray.LastElement();
1427 if (mHostname && CertDNIsInList(rootCertDER, RootSymantecDNs)) {
1428 if (numCerts <= 1) {
1429 // This chain is supposed to be complete, so this is an error.
1430 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1432 nsTArray<Input> intCerts;
1434 for (size_t i = 1; i < certArray.Length() - 1; ++i) {
1435 const nsTArray<uint8_t>& certBytes = certArray.ElementAt(i);
1436 Input certInput;
1437 rv = certInput.Init(certBytes.Elements(), certBytes.Length());
1438 if (rv != Success) {
1439 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1442 intCerts.EmplaceBack(certInput);
1445 bool isDistrusted = false;
1446 nsrv = CheckForSymantecDistrust(intCerts, RootAppleAndGoogleSPKIs,
1447 isDistrusted);
1448 if (NS_FAILED(nsrv)) {
1449 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1451 if (isDistrusted) {
1452 mSawDistrustedCAByPolicyError = true;
1453 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1457 mBuiltChain = std::move(certArray);
1459 return Success;
1462 Result NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(
1463 DigestAlgorithm aAlg, EndEntityOrCA /*endEntityOrCA*/, Time /*notBefore*/) {
1464 switch (aAlg) {
1465 case DigestAlgorithm::sha256: // fall through
1466 case DigestAlgorithm::sha384: // fall through
1467 case DigestAlgorithm::sha512:
1468 return Success;
1469 case DigestAlgorithm::sha1:
1470 return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
1472 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1475 Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
1476 EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
1477 if (modulusSizeInBits < mMinRSABits) {
1478 return Result::ERROR_INADEQUATE_KEY_SIZE;
1480 return Success;
1483 Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedData(
1484 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1485 Input subjectPublicKeyInfo) {
1486 return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
1487 subjectPublicKeyInfo, mPinArg);
1490 Result NSSCertDBTrustDomain::VerifyRSAPSSSignedData(
1491 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1492 Input subjectPublicKeyInfo) {
1493 return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
1494 subjectPublicKeyInfo, mPinArg);
1497 Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
1498 EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) {
1499 switch (curve) {
1500 case NamedCurve::secp256r1: // fall through
1501 case NamedCurve::secp384r1: // fall through
1502 case NamedCurve::secp521r1:
1503 return Success;
1506 return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
1509 Result NSSCertDBTrustDomain::VerifyECDSASignedData(
1510 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1511 Input subjectPublicKeyInfo) {
1512 return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
1513 subjectPublicKeyInfo, mPinArg);
1516 Result NSSCertDBTrustDomain::CheckValidityIsAcceptable(
1517 Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
1518 KeyPurposeId keyPurpose) {
1519 if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
1520 return Success;
1522 if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
1523 return Success;
1526 Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
1527 Time::ONE_DAY_IN_SECONDS);
1528 Duration maxValidityDuration(UINT64_MAX);
1529 Duration validityDuration(notBefore, notAfter);
1531 switch (mValidityCheckingMode) {
1532 case ValidityCheckingMode::CheckingOff:
1533 return Success;
1534 case ValidityCheckingMode::CheckForEV:
1535 // The EV Guidelines say the maximum is 27 months, but we use a slightly
1536 // higher limit here to (hopefully) minimize compatibility breakage.
1537 maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
1538 break;
1539 default:
1540 MOZ_ASSERT_UNREACHABLE(
1541 "We're not handling every ValidityCheckingMode type");
1544 if (validityDuration > maxValidityDuration) {
1545 return Result::ERROR_VALIDITY_TOO_LONG;
1548 return Success;
1551 Result NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(
1552 Time notBefore,
1553 /*out*/ bool& matches) {
1554 // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
1555 static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
1556 // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
1557 static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
1559 switch (mNetscapeStepUpPolicy) {
1560 case NetscapeStepUpPolicy::AlwaysMatch:
1561 matches = true;
1562 return Success;
1563 case NetscapeStepUpPolicy::MatchBefore23August2016:
1564 matches = notBefore < AUGUST_23_2016;
1565 return Success;
1566 case NetscapeStepUpPolicy::MatchBefore23August2015:
1567 matches = notBefore < AUGUST_23_2015;
1568 return Success;
1569 case NetscapeStepUpPolicy::NeverMatch:
1570 matches = false;
1571 return Success;
1572 default:
1573 MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
1575 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1578 void NSSCertDBTrustDomain::ResetAccumulatedState() {
1579 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
1580 mSCTListFromOCSPStapling = nullptr;
1581 mSCTListFromCertificate = nullptr;
1582 mSawDistrustedCAByPolicyError = false;
1583 mIsBuiltChainRootBuiltInRoot = false;
1584 mIssuerSources.clear();
1587 static Input SECItemToInput(const UniqueSECItem& item) {
1588 Input result;
1589 if (item) {
1590 MOZ_ASSERT(item->type == siBuffer);
1591 Result rv = result.Init(item->data, item->len);
1592 // As used here, |item| originally comes from an Input,
1593 // so there should be no issues converting it back.
1594 MOZ_ASSERT(rv == Success);
1595 Unused << rv; // suppresses warnings in release builds
1597 return result;
1600 Input NSSCertDBTrustDomain::GetSCTListFromCertificate() const {
1601 return SECItemToInput(mSCTListFromCertificate);
1604 Input NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const {
1605 return SECItemToInput(mSCTListFromOCSPStapling);
1608 bool NSSCertDBTrustDomain::GetIsBuiltChainRootBuiltInRoot() const {
1609 return mIsBuiltChainRootBuiltInRoot;
1612 bool NSSCertDBTrustDomain::GetIsErrorDueToDistrustedCAPolicy() const {
1613 return mSawDistrustedCAByPolicyError;
1616 void NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
1617 Input extensionData) {
1618 UniqueSECItem* out = nullptr;
1619 switch (extension) {
1620 case AuxiliaryExtension::EmbeddedSCTList:
1621 out = &mSCTListFromCertificate;
1622 break;
1623 case AuxiliaryExtension::SCTListFromOCSPResponse:
1624 out = &mSCTListFromOCSPStapling;
1625 break;
1626 default:
1627 MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
1629 if (out) {
1630 SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
1631 out->reset(SECITEM_DupItem(&extensionDataItem));
1635 SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig,
1636 PKCS11DBConfig pkcs11DbConfig) {
1637 MOZ_ASSERT(NS_IsMainThread());
1639 // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
1640 // module by NSS_Initialize because we will load it in LoadLoadableRoots
1641 // later. It also allows us to work around a bug in the system NSS in
1642 // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
1643 // "/usr/lib/nss/libnssckbi.so".
1644 uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
1645 if (nssDbConfig == NSSDBConfig::ReadOnly) {
1646 flags |= NSS_INIT_READONLY;
1648 if (pkcs11DbConfig == PKCS11DBConfig::DoNotLoadModules) {
1649 flags |= NSS_INIT_NOMODDB;
1651 nsAutoCString dbTypeAndDirectory("sql:");
1652 dbTypeAndDirectory.Append(dir);
1653 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1654 ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(),
1655 (int)nssDbConfig, (int)pkcs11DbConfig));
1656 SECStatus srv =
1657 NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags);
1658 if (srv != SECSuccess) {
1659 return srv;
1662 if (nssDbConfig == NSSDBConfig::ReadWrite) {
1663 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1664 if (!slot) {
1665 return SECFailure;
1667 // If the key DB doesn't have a password set, PK11_NeedUserInit will return
1668 // true. For the SQL DB, we need to set a password or we won't be able to
1669 // import any certificates or change trust settings.
1670 if (PK11_NeedUserInit(slot.get())) {
1671 srv = PK11_InitPin(slot.get(), nullptr, nullptr);
1672 MOZ_ASSERT(srv == SECSuccess);
1673 Unused << srv;
1677 return SECSuccess;
1680 void DisableMD5() {
1681 NSS_SetAlgorithmPolicy(
1682 SEC_OID_MD5, 0,
1683 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1684 NSS_SetAlgorithmPolicy(
1685 SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 0,
1686 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1687 NSS_SetAlgorithmPolicy(
1688 SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC, 0,
1689 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1692 // Load a given PKCS#11 module located in the given directory. It will be named
1693 // the given module name. Optionally pass some string parameters to it via
1694 // 'params'. This argument will be provided to C_Initialize when called on the
1695 // module.
1696 // |libraryName| and |dir| are encoded in UTF-8.
1697 bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
1698 const nsCString& dir, /* optional */ const char* params) {
1699 // If a module exists with the same name, make a best effort attempt to delete
1700 // it. Note that it isn't possible to delete the internal module, so checking
1701 // the return value would be detrimental in that case.
1702 int unusedModType;
1703 Unused << SECMOD_DeleteModule(moduleName, &unusedModType);
1705 nsAutoCString fullLibraryPath;
1706 if (!dir.IsEmpty()) {
1707 fullLibraryPath.Assign(dir);
1708 fullLibraryPath.AppendLiteral(FILE_PATH_SEPARATOR);
1710 fullLibraryPath.Append(MOZ_DLL_PREFIX);
1711 fullLibraryPath.Append(libraryName);
1712 fullLibraryPath.Append(MOZ_DLL_SUFFIX);
1713 // Escape the \ and " characters.
1714 fullLibraryPath.ReplaceSubstring("\\", "\\\\");
1715 fullLibraryPath.ReplaceSubstring("\"", "\\\"");
1717 nsAutoCString pkcs11ModuleSpec("name=\"");
1718 pkcs11ModuleSpec.Append(moduleName);
1719 pkcs11ModuleSpec.AppendLiteral("\" library=\"");
1720 pkcs11ModuleSpec.Append(fullLibraryPath);
1721 pkcs11ModuleSpec.AppendLiteral("\"");
1722 if (params) {
1723 pkcs11ModuleSpec.AppendLiteral("\" parameters=\"");
1724 pkcs11ModuleSpec.Append(params);
1725 pkcs11ModuleSpec.AppendLiteral("\"");
1728 UniqueSECMODModule userModule(SECMOD_LoadUserModule(
1729 const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false));
1730 if (!userModule) {
1731 return false;
1734 if (!userModule->loaded) {
1735 return false;
1738 return true;
1741 const char* kIPCClientCertsModuleName = "IPC Client Cert Module";
1743 bool LoadIPCClientCertsModule(const nsCString& dir) {
1744 // The IPC client certs module needs to be able to call back into gecko to be
1745 // able to communicate with the parent process over IPC. This is achieved by
1746 // serializing the addresses of the relevant functions and passing them as an
1747 // extra string parameter that will be available when C_Initialize is called
1748 // on IPC client certs.
1749 nsPrintfCString addrs("%p,%p", DoFindObjects, DoSign);
1750 if (!LoadUserModuleAt(kIPCClientCertsModuleName, "ipcclientcerts", dir,
1751 addrs.get())) {
1752 return false;
1754 RunOnShutdown(
1755 []() {
1756 UniqueSECMODModule ipcClientCertsModule(
1757 SECMOD_FindModule(kIPCClientCertsModuleName));
1758 if (ipcClientCertsModule) {
1759 SECMOD_UnloadUserModule(ipcClientCertsModule.get());
1762 ShutdownPhase::XPCOMWillShutdown);
1763 return true;
1766 const char* kOSClientCertsModuleName = "OS Client Cert Module";
1768 bool LoadOSClientCertsModule(const nsCString& dir) {
1769 nsLiteralCString params =
1770 StaticPrefs::security_osclientcerts_assume_rsa_pss_support()
1771 ? "RSA-PSS"_ns
1772 : ""_ns;
1773 return LoadUserModuleAt(kOSClientCertsModuleName, "osclientcerts", dir,
1774 params.get());
1777 bool LoadLoadableRoots(const nsCString& dir) {
1778 // Some NSS command-line utilities will load a roots module under the name
1779 // "Root Certs" if there happens to be a `MOZ_DLL_PREFIX "nssckbi"
1780 // MOZ_DLL_SUFFIX` file in the directory being operated on. In some cases this
1781 // can cause us to fail to load our roots module. In these cases, deleting the
1782 // "Root Certs" module allows us to load the correct one. See bug 1406396.
1783 int unusedModType;
1784 Unused << SECMOD_DeleteModule("Root Certs", &unusedModType);
1785 return LoadUserModuleAt(kRootModuleName, "nssckbi", dir, nullptr);
1788 nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
1789 /*out*/ nsCString& nickname) {
1790 MOZ_ASSERT(cert);
1791 NS_ENSURE_ARG_POINTER(cert);
1793 UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
1794 if (!baseName) {
1795 baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
1797 if (!baseName) {
1798 baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
1800 if (!baseName) {
1801 baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
1803 if (!baseName) {
1804 baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
1806 if (!baseName) {
1807 baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
1809 if (!baseName) {
1810 return NS_ERROR_FAILURE;
1813 // This function is only used in contexts where a failure to find a suitable
1814 // nickname does not block the overall task from succeeding.
1815 // As such, we use an arbitrary limit to prevent this nickname searching
1816 // process from taking forever.
1817 static const uint32_t ARBITRARY_LIMIT = 500;
1818 for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
1819 nickname = baseName.get();
1820 if (count != 1) {
1821 nickname.AppendPrintf(" #%u", count);
1823 if (nickname.IsEmpty()) {
1824 return NS_ERROR_FAILURE;
1827 bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
1828 cert->dbhandle);
1829 if (!conflict) {
1830 return NS_OK;
1834 return NS_ERROR_FAILURE;
1837 Result BuildRevocationCheckArrays(Input certDER, EndEntityOrCA endEntityOrCA,
1838 /*out*/ nsTArray<uint8_t>& issuerBytes,
1839 /*out*/ nsTArray<uint8_t>& serialBytes,
1840 /*out*/ nsTArray<uint8_t>& subjectBytes,
1841 /*out*/ nsTArray<uint8_t>& pubKeyBytes) {
1842 BackCert cert(certDER, endEntityOrCA, nullptr);
1843 Result rv = cert.Init();
1844 if (rv != Success) {
1845 return rv;
1847 issuerBytes.Clear();
1848 Input issuer(cert.GetIssuer());
1849 issuerBytes.AppendElements(issuer.UnsafeGetData(), issuer.GetLength());
1850 serialBytes.Clear();
1851 Input serial(cert.GetSerialNumber());
1852 serialBytes.AppendElements(serial.UnsafeGetData(), serial.GetLength());
1853 subjectBytes.Clear();
1854 Input subject(cert.GetSubject());
1855 subjectBytes.AppendElements(subject.UnsafeGetData(), subject.GetLength());
1856 pubKeyBytes.Clear();
1857 Input pubKey(cert.GetSubjectPublicKeyInfo());
1858 pubKeyBytes.AppendElements(pubKey.UnsafeGetData(), pubKey.GetLength());
1860 return Success;
1863 bool CertIsInCertStorage(const nsTArray<uint8_t>& certDER,
1864 nsICertStorage* certStorage) {
1865 MOZ_ASSERT(certStorage);
1866 if (!certStorage) {
1867 return false;
1869 Input certInput;
1870 Result rv = certInput.Init(certDER.Elements(), certDER.Length());
1871 if (rv != Success) {
1872 return false;
1874 BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
1875 rv = cert.Init();
1876 if (rv != Success) {
1877 return false;
1879 nsTArray<uint8_t> subject;
1880 subject.AppendElements(cert.GetSubject().UnsafeGetData(),
1881 cert.GetSubject().GetLength());
1882 nsTArray<nsTArray<uint8_t>> certStorageCerts;
1883 if (NS_FAILED(certStorage->FindCertsBySubject(subject, certStorageCerts))) {
1884 return false;
1886 for (const auto& certStorageCert : certStorageCerts) {
1887 if (certStorageCert.Length() != certDER.Length()) {
1888 continue;
1890 if (memcmp(certStorageCert.Elements(), certDER.Elements(),
1891 certStorageCert.Length()) == 0) {
1892 return true;
1895 return false;
1899 * Given a list of certificates representing a verified certificate path from an
1900 * end-entity certificate to a trust anchor, imports the intermediate
1901 * certificates into the permanent certificate database. This is an attempt to
1902 * cope with misconfigured servers that don't include the appropriate
1903 * intermediate certificates in the TLS handshake.
1905 * @param certList the verified certificate list
1907 void SaveIntermediateCerts(const nsTArray<nsTArray<uint8_t>>& certList) {
1908 if (certList.IsEmpty()) {
1909 return;
1911 nsTArray<nsTArray<uint8_t>> intermediates;
1912 // Skip the end-entity; we only want to store intermediates. Similarly,
1913 // there's no need to save the trust anchor - it's either already a permanent
1914 // certificate or it's the Microsoft Family Safety root or an enterprise root
1915 // temporarily imported via the child mode or enterprise root features. We
1916 // don't want to import these because they're intended to be temporary (and
1917 // because importing them happens to reset their trust settings, which breaks
1918 // these features).
1919 for (size_t index = 1; index < certList.Length() - 1; index++) {
1920 intermediates.AppendElement(certList.ElementAt(index).Clone());
1922 nsCOMPtr<nsIRunnable> importCertsRunnable(NS_NewRunnableFunction(
1923 "IdleSaveIntermediateCerts",
1924 [intermediates = std::move(intermediates)]() -> void {
1925 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1926 return;
1928 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1929 if (!slot) {
1930 return;
1932 size_t numCertsImported = 0;
1933 nsCOMPtr<nsICertStorage> certStorage(
1934 do_GetService(NS_CERT_STORAGE_CID));
1935 for (const auto& certDER : intermediates) {
1936 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1937 return;
1939 if (CertIsInCertStorage(certDER, certStorage)) {
1940 continue;
1942 SECItem certDERItem = {siBuffer,
1943 const_cast<unsigned char*>(certDER.Elements()),
1944 AssertedCast<unsigned int>(certDER.Length())};
1945 UniqueCERTCertificate cert(CERT_NewTempCertificate(
1946 CERT_GetDefaultCertDB(), &certDERItem, nullptr, false, true));
1947 if (!cert) {
1948 continue;
1950 if (cert->slot) {
1951 // This cert was found on a token; no need to remember it in the
1952 // permanent database.
1953 continue;
1955 PRBool isperm;
1956 if (CERT_GetCertIsPerm(cert.get(), &isperm) != SECSuccess) {
1957 continue;
1959 if (isperm) {
1960 // We don't need to remember certs already stored in perm db.
1961 continue;
1963 // This is a best-effort attempt at avoiding unknown issuer errors
1964 // in the future, so ignore failures here.
1965 nsAutoCString nickname;
1966 if (NS_FAILED(DefaultServerNicknameForCert(cert.get(), nickname))) {
1967 continue;
1969 Unused << PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
1970 nickname.get(), false);
1971 numCertsImported++;
1974 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
1975 "IdleSaveIntermediateCertsDone", [numCertsImported]() -> void {
1976 nsCOMPtr<nsIObserverService> observerService =
1977 mozilla::services::GetObserverService();
1978 if (observerService) {
1979 NS_ConvertUTF8toUTF16 numCertsImportedString(
1980 nsPrintfCString("%zu", numCertsImported));
1981 observerService->NotifyObservers(
1982 nullptr, "psm:intermediate-certs-cached",
1983 numCertsImportedString.get());
1985 }));
1986 Unused << NS_DispatchToMainThread(runnable.forget());
1987 }));
1988 Unused << NS_DispatchToCurrentThreadQueue(importCertsRunnable.forget(),
1989 EventQueuePriority::Idle);
1992 } // namespace psm
1993 } // namespace mozilla