Bug 1909074. Don't pass OFFSET_BY_ORIGIN to GetResultingTransformMatrix when it's...
[gecko.git] / security / certverifier / NSSCertDBTrustDomain.cpp
blob7f56e9c431321495382342ea98f01b9479912fe1
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 "PKCS11ModuleDB.h"
17 #include "PublicKeyPinningService.h"
18 #include "cert.h"
19 #include "cert_storage/src/cert_storage.h"
20 #include "certdb.h"
21 #include "mozilla/AppShutdown.h"
22 #include "mozilla/Assertions.h"
23 #include "mozilla/Casting.h"
24 #include "mozilla/ClearOnShutdown.h"
25 #include "mozilla/Logging.h"
26 #include "mozilla/PodOperations.h"
27 #include "mozilla/Services.h"
28 #include "mozilla/StaticPrefs_security.h"
29 #include "mozilla/SyncRunnable.h"
30 #include "mozilla/TimeStamp.h"
31 #include "mozilla/Unused.h"
32 #include "mozilla/glean/GleanMetrics.h"
33 #include "mozpkix/Result.h"
34 #include "mozpkix/pkix.h"
35 #include "mozpkix/pkixcheck.h"
36 #include "mozpkix/pkixnss.h"
37 #include "mozpkix/pkixutil.h"
38 #include "nsCRTGlue.h"
39 #include "nsIObserverService.h"
40 #include "nsNSSCallbacks.h"
41 #include "nsNSSCertificate.h"
42 #include "nsNSSCertificateDB.h"
43 #include "nsNSSIOLayer.h"
44 #include "nsNetCID.h"
45 #include "nsPrintfCString.h"
46 #include "nsServiceManagerUtils.h"
47 #include "nsThreadUtils.h"
48 #include "nss.h"
49 #include "pk11pub.h"
50 #include "prerror.h"
51 #include "secder.h"
52 #include "secerr.h"
54 #include "TrustOverrideUtils.h"
55 #include "TrustOverride-AppleGoogleDigiCertData.inc"
56 #include "TrustOverride-SymantecData.inc"
58 using namespace mozilla;
59 using namespace mozilla::ct;
60 using namespace mozilla::pkix;
62 extern LazyLogModule gCertVerifierLog;
64 static const uint64_t ServerFailureDelaySeconds = 5 * 60;
66 namespace mozilla {
67 namespace psm {
69 NSSCertDBTrustDomain::NSSCertDBTrustDomain(
70 SECTrustType certDBTrustType, OCSPFetching ocspFetching,
71 OCSPCache& ocspCache,
72 /*optional but shouldn't be*/ void* pinArg, TimeDuration ocspTimeoutSoft,
73 TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays,
74 unsigned int minRSABits, ValidityCheckingMode validityCheckingMode,
75 NetscapeStepUpPolicy netscapeStepUpPolicy, CRLiteMode crliteMode,
76 const OriginAttributes& originAttributes,
77 const nsTArray<Input>& thirdPartyRootInputs,
78 const nsTArray<Input>& thirdPartyIntermediateInputs,
79 const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
80 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
81 /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
82 /*optional*/ const char* hostname)
83 : mCertDBTrustType(certDBTrustType),
84 mOCSPFetching(ocspFetching),
85 mOCSPCache(ocspCache),
86 mPinArg(pinArg),
87 mOCSPTimeoutSoft(ocspTimeoutSoft),
88 mOCSPTimeoutHard(ocspTimeoutHard),
89 mCertShortLifetimeInDays(certShortLifetimeInDays),
90 mMinRSABits(minRSABits),
91 mValidityCheckingMode(validityCheckingMode),
92 mNetscapeStepUpPolicy(netscapeStepUpPolicy),
93 mCRLiteMode(crliteMode),
94 mSawDistrustedCAByPolicyError(false),
95 mOriginAttributes(originAttributes),
96 mThirdPartyRootInputs(thirdPartyRootInputs),
97 mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs),
98 mExtraCertificates(extraCertificates),
99 mBuiltChain(builtChain),
100 mIsBuiltChainRootBuiltInRoot(false),
101 mPinningTelemetryInfo(pinningTelemetryInfo),
102 mHostname(hostname),
103 mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
104 mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED),
105 mBuiltInRootsModule(SECMOD_FindModule(kRootModuleName.get())),
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 bool NSSCertDBTrustDomain::ShouldSkipSelfSignedNonTrustAnchor(Input certDER) {
143 BackCert cert(certDER, EndEntityOrCA::MustBeCA, nullptr);
144 if (cert.Init() != Success) {
145 return false; // turn any failures into "don't skip trying this cert"
147 // If subject != issuer, this isn't a self-signed cert.
148 if (!InputsAreEqual(cert.GetSubject(), cert.GetIssuer())) {
149 return false;
151 TrustLevel trust;
152 if (GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy, certDER,
153 trust) != Success) {
154 return false;
156 // If the trust for this certificate is anything other than "inherit", we want
157 // to process it like normal.
158 if (trust != TrustLevel::InheritsTrust) {
159 return false;
161 if (VerifySignedData(*this, cert.GetSignedData(),
162 cert.GetSubjectPublicKeyInfo()) != Success) {
163 return false;
165 // This is a self-signed, non-trust-anchor certificate, so we shouldn't use it
166 // for path building. See bug 1056341.
167 return true;
170 Result NSSCertDBTrustDomain::CheckCandidates(
171 IssuerChecker& checker, nsTArray<IssuerCandidateWithSource>& candidates,
172 Input* nameConstraintsInputPtr, bool& keepGoing) {
173 for (const auto& candidate : candidates) {
174 // Stop path building if the program is shutting down.
175 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
176 keepGoing = false;
177 return Success;
179 if (ShouldSkipSelfSignedNonTrustAnchor(candidate.mDER)) {
180 continue;
182 Result rv =
183 checker.Check(candidate.mDER, nameConstraintsInputPtr, keepGoing);
184 if (rv != Success) {
185 return rv;
187 if (!keepGoing) {
188 mIssuerSources += candidate.mIssuerSource;
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<IssuerCandidateWithSource> geckoRootCandidates;
218 nsTArray<IssuerCandidateWithSource> geckoIntermediateCandidates;
220 // We might not have this module if e.g. we're on a Linux distribution that
221 // does something unexpected.
222 nsTArray<nsTArray<uint8_t>> builtInRoots;
223 if (mBuiltInRootsModule) {
224 FindRootsWithSubject(mBuiltInRootsModule, encodedIssuerNameItem,
225 builtInRoots);
226 for (const auto& root : builtInRoots) {
227 Input rootInput;
228 Result rv = rootInput.Init(root.Elements(), root.Length());
229 if (rv != Success) {
230 continue; // probably too big
232 geckoRootCandidates.AppendElement(IssuerCandidateWithSource{
233 rootInput, IssuerSource::BuiltInRootsModule});
235 } else {
236 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
237 ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module"));
240 if (mExtraCertificates.isSome()) {
241 for (const auto& extraCert : *mExtraCertificates) {
242 Input certInput;
243 Result rv = certInput.Init(extraCert.Elements(), extraCert.Length());
244 if (rv != Success) {
245 continue;
247 BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
248 rv = cert.Init();
249 if (rv != Success) {
250 continue;
252 // Filter out certificates that can't be issuers we're looking for because
253 // the subject distinguished name doesn't match. This prevents
254 // mozilla::pkix from accumulating spurious errors during path building.
255 if (!InputsAreEqual(encodedIssuerName, cert.GetSubject())) {
256 continue;
258 // We assume that extra certificates (presumably from the TLS handshake)
259 // are intermediates, since sending trust anchors would be superfluous.
260 geckoIntermediateCandidates.AppendElement(
261 IssuerCandidateWithSource{certInput, IssuerSource::TLSHandshake});
265 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
266 BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr);
267 Result rv = root.Init();
268 if (rv != Success) {
269 continue;
271 // Filter out 3rd party roots that can't be issuers we're looking for
272 // because the subject distinguished name doesn't match. This prevents
273 // mozilla::pkix from accumulating spurious errors during path building.
274 if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) {
275 continue;
277 geckoRootCandidates.AppendElement(IssuerCandidateWithSource{
278 thirdPartyRootInput, IssuerSource::ThirdPartyCertificates});
281 for (const auto& thirdPartyIntermediateInput :
282 mThirdPartyIntermediateInputs) {
283 BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA,
284 nullptr);
285 Result rv = intermediate.Init();
286 if (rv != Success) {
287 continue;
289 // Filter out 3rd party intermediates that can't be issuers we're looking
290 // for because the subject distinguished name doesn't match. This prevents
291 // mozilla::pkix from accumulating spurious errors during path building.
292 if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) {
293 continue;
295 geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{
296 thirdPartyIntermediateInput, IssuerSource::ThirdPartyCertificates});
299 if (!mCertStorage) {
300 return Result::FATAL_ERROR_LIBRARY_FAILURE;
302 nsTArray<uint8_t> subject;
303 subject.AppendElements(encodedIssuerName.UnsafeGetData(),
304 encodedIssuerName.GetLength());
305 nsTArray<nsTArray<uint8_t>> certs;
306 nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
307 if (NS_FAILED(rv)) {
308 return Result::FATAL_ERROR_LIBRARY_FAILURE;
310 for (auto& cert : certs) {
311 Input certDER;
312 Result rv = certDER.Init(cert.Elements(), cert.Length());
313 if (rv != Success) {
314 continue; // probably too big
316 // Currently we're only expecting intermediate certificates in cert storage.
317 geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{
318 std::move(certDER), IssuerSource::PreloadedIntermediates});
321 // Try all root certs first and then all (presumably) intermediates.
322 geckoRootCandidates.AppendElements(std::move(geckoIntermediateCandidates));
324 bool keepGoing = true;
325 Result result = CheckCandidates(checker, geckoRootCandidates,
326 nameConstraintsInputPtr, keepGoing);
327 if (result != Success) {
328 return result;
330 if (!keepGoing) {
331 return Success;
334 // Synchronously dispatch a task to the socket thread to find
335 // CERTCertificates with the given subject. This involves querying NSS
336 // structures and databases, so it should be done on the socket thread.
337 nsTArray<nsTArray<uint8_t>> nssRootCandidates;
338 nsTArray<nsTArray<uint8_t>> nssIntermediateCandidates;
339 RefPtr<Runnable> getCandidatesTask =
340 NS_NewRunnableFunction("NSSCertDBTrustDomain::FindIssuer", [&]() {
341 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
342 return;
344 // NSS seems not to differentiate between "no potential issuers found"
345 // and "there was an error trying to retrieve the potential issuers." We
346 // assume there was no error if CERT_CreateSubjectCertList returns
347 // nullptr.
348 UniqueCERTCertList candidates(
349 CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
350 &encodedIssuerNameItem, 0, false));
351 if (candidates) {
352 for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
353 !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
354 nsTArray<uint8_t> candidate;
355 candidate.AppendElements(n->cert->derCert.data,
356 n->cert->derCert.len);
357 if (n->cert->isRoot) {
358 nssRootCandidates.AppendElement(std::move(candidate));
359 } else {
360 nssIntermediateCandidates.AppendElement(std::move(candidate));
365 nsCOMPtr<nsIEventTarget> socketThread(
366 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
367 if (!socketThread) {
368 return Result::FATAL_ERROR_LIBRARY_FAILURE;
370 rv = SyncRunnable::DispatchToThread(socketThread, getCandidatesTask);
371 if (NS_FAILED(rv)) {
372 return Result::FATAL_ERROR_LIBRARY_FAILURE;
375 nsTArray<IssuerCandidateWithSource> nssCandidates;
376 for (const auto& rootCandidate : nssRootCandidates) {
377 Input certDER;
378 Result rv = certDER.Init(rootCandidate.Elements(), rootCandidate.Length());
379 if (rv != Success) {
380 continue; // probably too big
382 nssCandidates.AppendElement(
383 IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB});
385 for (const auto& intermediateCandidate : nssIntermediateCandidates) {
386 Input certDER;
387 Result rv = certDER.Init(intermediateCandidate.Elements(),
388 intermediateCandidate.Length());
389 if (rv != Success) {
390 continue; // probably too big
392 nssCandidates.AppendElement(
393 IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB});
396 return CheckCandidates(checker, nssCandidates, nameConstraintsInputPtr,
397 keepGoing);
400 Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
401 const CertPolicyId& policy,
402 Input candidateCertDER,
403 /*out*/ TrustLevel& trustLevel) {
404 // Check the certificate against the OneCRL cert blocklist
405 if (!mCertStorage) {
406 return Result::FATAL_ERROR_LIBRARY_FAILURE;
409 // The certificate blocklist currently only applies to TLS server
410 // certificates.
411 if (mCertDBTrustType == trustSSL) {
412 int16_t revocationState;
414 nsTArray<uint8_t> issuerBytes;
415 nsTArray<uint8_t> serialBytes;
416 nsTArray<uint8_t> subjectBytes;
417 nsTArray<uint8_t> pubKeyBytes;
419 Result result =
420 BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes,
421 serialBytes, subjectBytes, pubKeyBytes);
422 if (result != Success) {
423 return result;
426 nsresult nsrv = mCertStorage->GetRevocationState(
427 issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
428 if (NS_FAILED(nsrv)) {
429 return Result::FATAL_ERROR_LIBRARY_FAILURE;
432 if (revocationState == nsICertStorage::STATE_ENFORCE) {
433 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
434 ("NSSCertDBTrustDomain: certificate is in blocklist"));
435 mozilla::glean::cert_verifier::cert_revocation_mechanisms.Get("OneCRL"_ns)
436 .Add(1);
437 return Result::ERROR_REVOKED_CERTIFICATE;
441 // This may be a third-party root.
442 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
443 if (InputsAreEqual(candidateCertDER, thirdPartyRootInput)) {
444 trustLevel = TrustLevel::TrustAnchor;
445 return Success;
449 // This may be a third-party intermediate.
450 for (const auto& thirdPartyIntermediateInput :
451 mThirdPartyIntermediateInputs) {
452 if (InputsAreEqual(candidateCertDER, thirdPartyIntermediateInput)) {
453 trustLevel = TrustLevel::InheritsTrust;
454 return Success;
458 // Synchronously dispatch a task to the socket thread to construct a
459 // CERTCertificate and get its trust from NSS. This involves querying NSS
460 // structures and databases, so it should be done on the socket thread.
461 Result result = Result::FATAL_ERROR_LIBRARY_FAILURE;
462 RefPtr<Runnable> getTrustTask =
463 NS_NewRunnableFunction("NSSCertDBTrustDomain::GetCertTrust", [&]() {
464 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
465 result = Result::FATAL_ERROR_LIBRARY_FAILURE;
466 return;
468 // This would be cleaner and more efficient if we could get the trust
469 // information without constructing a CERTCertificate here, but NSS
470 // doesn't expose it in any other easy-to-use fashion. The use of
471 // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
472 // performance problem for certificates already known to NSS because NSS
473 // will just find the existing CERTCertificate in its in-memory cache
474 // and return it. For certificates not already in NSS (namely
475 // third-party roots and intermediates), we want to avoid calling
476 // CERT_NewTempCertificate repeatedly, so we've already checked if the
477 // candidate certificate is a third-party certificate, above.
478 SECItem candidateCertDERSECItem =
479 UnsafeMapInputToSECItem(candidateCertDER);
481 // This metric can be evaluated as many as 600 times during a cnn.com
482 // load so we avoid measuring it on Android because of the high
483 // cost of serializing the db everytime we measure.
484 #ifndef MOZ_WIDGET_ANDROID
485 auto timerId =
486 mozilla::glean::cert_verifier::cert_trust_evaluation_time.Start();
487 #endif
488 UniqueCERTCertificate candidateCert(CERT_NewTempCertificate(
489 CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false,
490 true));
492 #ifndef MOZ_WIDGET_ANDROID
493 mozilla::glean::cert_verifier::cert_trust_evaluation_time
494 .StopAndAccumulate(std::move(timerId));
495 #endif
496 if (!candidateCert) {
497 result = MapPRErrorCodeToResult(PR_GetError());
498 return;
500 // NB: CERT_GetCertTrust seems to be abusing SECStatus as a boolean,
501 // where SECSuccess means that there is a trust record and SECFailure
502 // means there is not a trust record. I looked at NSS's internal uses of
503 // CERT_GetCertTrust, and all that code uses the result as a boolean
504 // meaning "We have a trust record."
506 CERTCertTrust trust;
507 if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
508 uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
510 // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
511 // because we can have active distrust for either type of cert. Note
512 // that CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so
513 // if the relevant trust bit isn't set then that means the cert must
514 // be considered distrusted.
515 uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
516 ? CERTDB_TRUSTED_CA
517 : CERTDB_TRUSTED;
518 if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) ==
519 CERTDB_TERMINAL_RECORD) {
520 trustLevel = TrustLevel::ActivelyDistrusted;
521 result = Success;
522 return;
525 // For TRUST, we use the CERTDB_TRUSTED_CA bit.
526 if (flags & CERTDB_TRUSTED_CA) {
527 if (policy.IsAnyPolicy()) {
528 trustLevel = TrustLevel::TrustAnchor;
529 result = Success;
530 return;
533 nsTArray<uint8_t> certBytes(candidateCert->derCert.data,
534 candidateCert->derCert.len);
535 if (CertIsAuthoritativeForEVPolicy(certBytes, policy)) {
536 trustLevel = TrustLevel::TrustAnchor;
537 result = Success;
538 return;
542 trustLevel = TrustLevel::InheritsTrust;
543 result = Success;
545 nsCOMPtr<nsIEventTarget> socketThread(
546 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
547 if (!socketThread) {
548 return Result::FATAL_ERROR_LIBRARY_FAILURE;
550 nsresult rv = SyncRunnable::DispatchToThread(socketThread, getTrustTask);
551 if (NS_FAILED(rv)) {
552 return Result::FATAL_ERROR_LIBRARY_FAILURE;
554 return result;
557 Result NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
558 /*out*/ uint8_t* digestBuf,
559 size_t digestBufLen) {
560 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
563 TimeDuration NSSCertDBTrustDomain::GetOCSPTimeout() const {
564 switch (mOCSPFetching) {
565 case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
566 return mOCSPTimeoutSoft;
567 case NSSCertDBTrustDomain::FetchOCSPForEV:
568 case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
569 return mOCSPTimeoutHard;
570 // The rest of these are error cases. Assert in debug builds, but return
571 // the soft timeout value in release builds.
572 case NSSCertDBTrustDomain::NeverFetchOCSP:
573 case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
574 MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
575 break;
578 MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
579 return mOCSPTimeoutSoft;
582 // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
583 // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
584 // Success with result.IsVoid() == true when an OCSP URI was not found, and
585 // Success with result.IsVoid() == false when an OCSP URI was found.
586 static Result GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
587 Input aiaExtension,
588 /*out*/ nsCString& result) {
589 MOZ_ASSERT(arena.get());
590 if (!arena.get()) {
591 return Result::FATAL_ERROR_INVALID_ARGS;
594 result.Assign(VoidCString());
595 SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
596 CERTAuthInfoAccess** aia =
597 CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
598 if (!aia) {
599 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
601 for (size_t i = 0; aia[i]; ++i) {
602 if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
603 // NSS chooses the **last** OCSP URL; we choose the **first**
604 CERTGeneralName* current = aia[i]->location;
605 if (!current) {
606 continue;
608 do {
609 if (current->type == certURI) {
610 const SECItem& location = current->name.other;
611 // (location.len + 1) must be small enough to fit into a uint32_t,
612 // but we limit it to a smaller bound to reduce OOM risk.
613 if (location.len > 1024 || memchr(location.data, 0, location.len)) {
614 // Reject embedded nulls. (NSS doesn't do this)
615 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
617 result.Assign(nsDependentCSubstring(
618 reinterpret_cast<const char*>(location.data), location.len));
619 return Success;
621 current = CERT_GetNextGeneralName(current);
622 } while (current != aia[i]->location);
626 return Success;
629 NS_IMPL_ISUPPORTS(CRLiteTimestamp, nsICRLiteTimestamp)
631 NS_IMETHODIMP
632 CRLiteTimestamp::GetLogID(nsTArray<uint8_t>& aLogID) {
633 aLogID.Clear();
634 aLogID.AppendElements(mLogID);
635 return NS_OK;
638 NS_IMETHODIMP
639 CRLiteTimestamp::GetTimestamp(uint64_t* aTimestamp) {
640 *aTimestamp = mTimestamp;
641 return NS_OK;
644 Result BuildCRLiteTimestampArray(
645 Input sctExtension,
646 /*out*/ nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps) {
647 Input sctList;
648 Result rv =
649 ExtractSignedCertificateTimestampListFromExtension(sctExtension, sctList);
650 if (rv != Success) {
651 return rv;
653 std::vector<SignedCertificateTimestamp> decodedSCTs;
654 size_t decodingErrors;
655 DecodeSCTs(sctList, decodedSCTs, decodingErrors);
656 Unused << decodingErrors;
658 for (const auto& sct : decodedSCTs) {
659 timestamps.AppendElement(new CRLiteTimestamp(sct));
661 return Success;
664 Result NSSCertDBTrustDomain::CheckCRLiteStash(
665 const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
666 const nsTArray<uint8_t>& serialNumberBytes) {
667 // This information is deterministic and has already been validated by our
668 // infrastructure (it comes from signed CRLs), so if the stash says a
669 // certificate is revoked, it is.
670 bool isRevokedByStash = false;
671 nsresult rv = mCertStorage->IsCertRevokedByStash(
672 issuerSubjectPublicKeyInfoBytes, serialNumberBytes, &isRevokedByStash);
673 if (NS_FAILED(rv)) {
674 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
675 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
676 "failed"));
677 return Result::FATAL_ERROR_LIBRARY_FAILURE;
679 if (isRevokedByStash) {
680 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
681 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
682 "returned true"));
683 mozilla::glean::cert_verifier::crlite_status.Get("revoked_in_stash"_ns)
684 .Add(1);
685 return Result::ERROR_REVOKED_CERTIFICATE;
687 return Success;
690 Result NSSCertDBTrustDomain::CheckCRLite(
691 const nsTArray<uint8_t>& issuerBytes,
692 const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
693 const nsTArray<uint8_t>& serialNumberBytes,
694 const nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps,
695 /*out*/ bool& filterCoversCertificate) {
696 filterCoversCertificate = false;
697 int16_t crliteRevocationState;
698 nsresult rv = mCertStorage->GetCRLiteRevocationState(
699 issuerBytes, issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
700 timestamps, &crliteRevocationState);
701 if (NS_FAILED(rv)) {
702 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
703 ("NSSCertDBTrustDomain::CheckCRLite: CRLite call failed"));
704 return Result::FATAL_ERROR_LIBRARY_FAILURE;
706 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
707 ("NSSCertDBTrustDomain::CheckCRLite: CRLite check returned "
708 "state=%hd",
709 crliteRevocationState));
711 switch (crliteRevocationState) {
712 case nsICertStorage::STATE_ENFORCE:
713 filterCoversCertificate = true;
714 mozilla::glean::cert_verifier::crlite_status.Get("revoked_in_filter"_ns)
715 .Add(1);
716 return Result::ERROR_REVOKED_CERTIFICATE;
717 case nsICertStorage::STATE_UNSET:
718 filterCoversCertificate = true;
719 mozilla::glean::cert_verifier::crlite_status.Get("not_revoked"_ns).Add(1);
720 return Success;
721 case nsICertStorage::STATE_NOT_ENROLLED:
722 filterCoversCertificate = false;
723 mozilla::glean::cert_verifier::crlite_status.Get("not_enrolled"_ns)
724 .Add(1);
725 return Success;
726 case nsICertStorage::STATE_NOT_COVERED:
727 filterCoversCertificate = false;
728 mozilla::glean::cert_verifier::crlite_status.Get("not_covered"_ns).Add(1);
729 return Success;
730 case nsICertStorage::STATE_NO_FILTER:
731 filterCoversCertificate = false;
732 mozilla::glean::cert_verifier::crlite_status.Get("no_filter"_ns).Add(1);
733 return Success;
734 default:
735 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
736 ("NSSCertDBTrustDomain::CheckCRLite: Unknown CRLite revocation "
737 "state"));
738 return Result::FATAL_ERROR_LIBRARY_FAILURE;
742 Result NSSCertDBTrustDomain::CheckRevocation(
743 EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
744 Duration validityDuration,
745 /*optional*/ const Input* stapledOCSPResponse,
746 /*optional*/ const Input* aiaExtension,
747 /*optional*/ const Input* sctExtension) {
748 // Actively distrusted certificates will have already been blocked by
749 // GetCertTrust.
751 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
752 ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
754 // None of the revocation methods in this function are consulted for CA
755 // certificates. Revocation for CAs is handled by GetCertTrust.
756 if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
757 return Success;
760 // Look for an OCSP Authority Information Access URL. Our behavior in
761 // ConfirmRevocations mode depends on whether a synchronous OCSP
762 // request is possible.
763 nsCString aiaLocation(VoidCString());
764 if (aiaExtension) {
765 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
766 if (!arena) {
767 return Result::FATAL_ERROR_NO_MEMORY;
769 Result rv =
770 GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation);
771 if (rv != Success) {
772 return rv;
776 bool crliteCoversCertificate = false;
777 Result crliteResult = Success;
778 if (mCRLiteMode != CRLiteMode::Disabled && sctExtension) {
779 crliteResult =
780 CheckRevocationByCRLite(certID, *sctExtension, crliteCoversCertificate);
782 // If CheckCRLite returned an error other than "revoked certificate",
783 // propagate that error.
784 if (crliteResult != Success &&
785 crliteResult != Result::ERROR_REVOKED_CERTIFICATE) {
786 return crliteResult;
789 if (crliteCoversCertificate) {
790 mozilla::glean::cert_verifier::cert_revocation_mechanisms.Get("CRLite"_ns)
791 .Add(1);
792 // If we don't return here we will consult OCSP.
793 // In Enforce CRLite mode we can return "Revoked" or "Not Revoked"
794 // without consulting OCSP.
795 if (mCRLiteMode == CRLiteMode::Enforce) {
796 return crliteResult;
798 // If we don't have a URL for an OCSP responder, then we can return any
799 // result ConfirmRevocations mode. Note that we might have a
800 // stapled or cached OCSP response which we ignore in this case.
801 if (mCRLiteMode == CRLiteMode::ConfirmRevocations &&
802 aiaLocation.IsVoid()) {
803 return crliteResult;
805 // In ConfirmRevocations mode we can return "Not Revoked"
806 // without consulting OCSP.
807 if (mCRLiteMode == CRLiteMode::ConfirmRevocations &&
808 crliteResult == Success) {
809 return Success;
814 bool ocspSoftFailure = false;
815 Result ocspResult = CheckRevocationByOCSP(
816 certID, time, validityDuration, aiaLocation, crliteCoversCertificate,
817 crliteResult, stapledOCSPResponse, ocspSoftFailure);
819 // In ConfirmRevocations mode we treat any OCSP failure as confirmation
820 // of a CRLite revoked result.
821 if (crliteCoversCertificate &&
822 crliteResult == Result::ERROR_REVOKED_CERTIFICATE &&
823 mCRLiteMode == CRLiteMode::ConfirmRevocations &&
824 (ocspResult != Success || ocspSoftFailure)) {
825 return Result::ERROR_REVOKED_CERTIFICATE;
828 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
829 ("NSSCertDBTrustDomain: end of CheckRevocation"));
831 return ocspResult;
834 Result NSSCertDBTrustDomain::CheckRevocationByCRLite(
835 const CertID& certID, const Input& sctExtension,
836 /*out*/ bool& crliteCoversCertificate) {
837 crliteCoversCertificate = false;
838 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
839 ("NSSCertDBTrustDomain::CheckRevocation: checking CRLite"));
840 nsTArray<uint8_t> issuerSubjectPublicKeyInfoBytes;
841 issuerSubjectPublicKeyInfoBytes.AppendElements(
842 certID.issuerSubjectPublicKeyInfo.UnsafeGetData(),
843 certID.issuerSubjectPublicKeyInfo.GetLength());
844 nsTArray<uint8_t> serialNumberBytes;
845 serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(),
846 certID.serialNumber.GetLength());
847 // The CRLite stash is essentially a subset of a collection of CRLs, so if
848 // it says a certificate is revoked, it is.
849 Result rv =
850 CheckCRLiteStash(issuerSubjectPublicKeyInfoBytes, serialNumberBytes);
851 if (rv != Success) {
852 crliteCoversCertificate = (rv == Result::ERROR_REVOKED_CERTIFICATE);
853 return rv;
856 nsTArray<uint8_t> issuerBytes;
857 issuerBytes.AppendElements(certID.issuer.UnsafeGetData(),
858 certID.issuer.GetLength());
860 nsTArray<RefPtr<nsICRLiteTimestamp>> timestamps;
861 rv = BuildCRLiteTimestampArray(sctExtension, timestamps);
862 if (rv != Success) {
863 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
864 ("decoding SCT extension failed - CRLite will be not be "
865 "consulted"));
866 return Success;
868 return CheckCRLite(issuerBytes, issuerSubjectPublicKeyInfoBytes,
869 serialNumberBytes, timestamps, crliteCoversCertificate);
872 Result NSSCertDBTrustDomain::CheckRevocationByOCSP(
873 const CertID& certID, Time time, Duration validityDuration,
874 const nsCString& aiaLocation, const bool crliteCoversCertificate,
875 const Result crliteResult,
876 /*optional*/ const Input* stapledOCSPResponse,
877 /*out*/ bool& softFailure) {
878 softFailure = false;
879 const uint16_t maxOCSPLifetimeInDays = 10;
880 // If we have a stapled OCSP response then the verification of that response
881 // determines the result unless the OCSP response is expired. We make an
882 // exception for expired responses because some servers, nginx in particular,
883 // are known to serve expired responses due to bugs.
884 // We keep track of the result of verifying the stapled response but don't
885 // immediately return failure if the response has expired.
886 Result stapledOCSPResponseResult = Success;
887 if (stapledOCSPResponse) {
888 bool expired;
889 stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse(
890 certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse,
891 ResponseWasStapled, expired);
892 mozilla::glean::cert_verifier::cert_revocation_mechanisms
893 .Get("StapledOCSP"_ns)
894 .Add(1);
895 if (stapledOCSPResponseResult == Success) {
896 // stapled OCSP response present and good
897 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
898 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
899 ("NSSCertDBTrustDomain: stapled OCSP response: good"));
900 return Success;
902 if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
903 expired) {
904 // stapled OCSP response present but expired
905 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
906 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
907 ("NSSCertDBTrustDomain: expired stapled OCSP response"));
908 } else if (stapledOCSPResponseResult ==
909 Result::ERROR_OCSP_TRY_SERVER_LATER ||
910 stapledOCSPResponseResult ==
911 Result::ERROR_OCSP_INVALID_SIGNING_CERT) {
912 // Stapled OCSP response present but invalid for a small number of reasons
913 // CAs/servers commonly get wrong. This will be treated similarly to an
914 // expired stapled response.
915 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
916 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
917 ("NSSCertDBTrustDomain: stapled OCSP response: "
918 "failure (allowed for compatibility)"));
919 } else {
920 // stapled OCSP response present but invalid for some reason
921 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
922 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
923 ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
924 return stapledOCSPResponseResult;
926 } else {
927 // no stapled OCSP response
928 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
929 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
930 ("NSSCertDBTrustDomain: no stapled OCSP response"));
933 Result cachedResponseResult = Success;
934 Time cachedResponseValidThrough(Time::uninitialized);
935 bool cachedResponsePresent =
936 mOCSPCache.Get(certID, mOriginAttributes, cachedResponseResult,
937 cachedResponseValidThrough);
938 if (cachedResponsePresent) {
939 mozilla::glean::cert_verifier::cert_revocation_mechanisms
940 .Get("CachedOCSP"_ns)
941 .Add(1);
942 if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
943 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
944 ("NSSCertDBTrustDomain: cached OCSP response: good"));
945 return Success;
947 // If we have a cached revoked response, use it.
948 if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
949 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
950 ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
951 return Result::ERROR_REVOKED_CERTIFICATE;
953 // The cached response may indicate an unknown certificate or it may be
954 // expired. Don't return with either of these statuses yet - we may be
955 // able to fetch a more recent one.
956 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
957 ("NSSCertDBTrustDomain: cached OCSP response: error %d",
958 static_cast<int>(cachedResponseResult)));
959 // When a good cached response has expired, it is more convenient
960 // to convert that to an error code and just deal with
961 // cachedResponseResult from here on out.
962 if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
963 cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
965 // We may have a cached indication of server failure. Ignore it if
966 // it has expired.
967 if (cachedResponseResult != Success &&
968 cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
969 cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
970 cachedResponseValidThrough < time) {
971 cachedResponseResult = Success;
972 cachedResponsePresent = false;
974 } else {
975 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
976 ("NSSCertDBTrustDomain: no cached OCSP response"));
978 // At this point, if and only if cachedErrorResult is Success, there was no
979 // cached response.
980 MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
981 (cachedResponsePresent && cachedResponseResult != Success));
983 // TODO: We still need to handle the fallback for invalid stapled responses.
984 // But, if/when we disable OCSP fetching by default, it would be ambiguous
985 // whether security.OCSP.enable==0 means "I want the default" or "I really
986 // never want you to ever fetch OCSP."
987 // Additionally, this doesn't properly handle OCSP-must-staple when OCSP
988 // fetching is disabled.
989 Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
990 if (validityDuration < shortLifetime) {
991 mozilla::glean::cert_verifier::cert_revocation_mechanisms
992 .Get("ShortValidity"_ns)
993 .Add(1);
995 if ((mOCSPFetching == NeverFetchOCSP) || (validityDuration < shortLifetime)) {
996 // We're not going to be doing any fetching, so if there was a cached
997 // "unknown" response, say so.
998 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
999 return Result::ERROR_OCSP_UNKNOWN_CERT;
1001 // If we're doing hard-fail, we want to know if we have a cached response
1002 // that has expired.
1003 if (mOCSPFetching == FetchOCSPForDVHardFail &&
1004 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
1005 return Result::ERROR_OCSP_OLD_RESPONSE;
1008 softFailure = true;
1009 return Success;
1012 if (mOCSPFetching == LocalOnlyOCSPForEV) {
1013 if (cachedResponseResult != Success) {
1014 return cachedResponseResult;
1016 return Result::ERROR_OCSP_UNKNOWN_CERT;
1019 if (aiaLocation.IsVoid()) {
1020 if (mOCSPFetching == FetchOCSPForEV ||
1021 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
1022 return Result::ERROR_OCSP_UNKNOWN_CERT;
1024 if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
1025 return Result::ERROR_OCSP_OLD_RESPONSE;
1027 if (stapledOCSPResponseResult != Success) {
1028 return stapledOCSPResponseResult;
1031 // Nothing to do if we don't have an OCSP responder URI for the cert; just
1032 // assume it is good. Note that this is the confusing, but intended,
1033 // interpretation of "strict" revocation checking in the face of a
1034 // certificate that lacks an OCSP responder URI. There's no need to set
1035 // softFailure here---we check for the presence of an AIA before attempting
1036 // OCSP when CRLite is configured in confirm revocations mode.
1037 return Success;
1040 if (cachedResponseResult == Success ||
1041 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
1042 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
1043 // Only send a request to, and process a response from, the server if we
1044 // didn't have a cached indication of failure. Also, don't keep requesting
1045 // responses from a failing server.
1046 return SynchronousCheckRevocationWithServer(
1047 certID, aiaLocation, time, maxOCSPLifetimeInDays, cachedResponseResult,
1048 stapledOCSPResponseResult, crliteCoversCertificate, crliteResult,
1049 softFailure);
1052 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
1053 cachedResponseResult, softFailure);
1056 Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
1057 const CertID& certID, const nsCString& aiaLocation, Time time,
1058 uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult,
1059 const Result stapledOCSPResponseResult, const bool crliteCoversCertificate,
1060 const Result crliteResult, /*out*/ bool& softFailure) {
1061 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
1062 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1065 uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
1066 size_t ocspRequestLength;
1067 Result rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
1068 ocspRequestLength);
1069 if (rv != Success) {
1070 return rv;
1073 Vector<uint8_t> ocspResponse;
1074 Input response;
1075 mOCSPFetchStatus = OCSPFetchStatus::Fetched;
1076 rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes,
1077 ocspRequestLength, GetOCSPTimeout(), ocspResponse);
1078 mozilla::glean::cert_verifier::cert_revocation_mechanisms.Get("OCSP"_ns).Add(
1080 if (rv == Success &&
1081 response.Init(ocspResponse.begin(), ocspResponse.length()) != Success) {
1082 rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
1085 if (rv != Success) {
1086 Time timeout(time);
1087 if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
1088 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
1091 Result cacheRV =
1092 mOCSPCache.Put(certID, mOriginAttributes, rv, time, timeout);
1093 if (cacheRV != Success) {
1094 return cacheRV;
1097 if (crliteCoversCertificate &&
1098 crliteResult == Result::ERROR_REVOKED_CERTIFICATE) {
1099 // CRLite says the certificate is revoked, but OCSP fetching failed.
1100 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1101 .Get("CRLiteRevOCSPFail"_ns)
1102 .Add(1);
1105 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
1106 rv, softFailure);
1109 // If the response from the network has expired but indicates a revoked
1110 // or unknown certificate, PR_GetError() will return the appropriate error.
1111 // We actually ignore expired here.
1112 bool expired;
1113 rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
1114 maxOCSPLifetimeInDays, response,
1115 ResponseIsFromNetwork, expired);
1117 // If CRLite said that this certificate is revoked, report the OCSP
1118 // status. OCSP may have succeeded, said the certificate is revoked, said the
1119 // certificate doesn't exist, or it may have failed for a reason that results
1120 // in a "soft fail" (i.e. there is no indication that the certificate is
1121 // either definitely revoked or definitely not revoked, so for usability,
1122 // revocation checking says the certificate is valid by default).
1123 if (crliteCoversCertificate &&
1124 crliteResult == Result::ERROR_REVOKED_CERTIFICATE) {
1125 if (rv == Success) {
1126 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1127 .Get("CRLiteRevOCSPOk"_ns)
1128 .Add(1);
1129 } else if (rv == Result::ERROR_REVOKED_CERTIFICATE) {
1130 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1131 .Get("CRLiteRevOCSPRev"_ns)
1132 .Add(1);
1133 } else if (rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
1134 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1135 .Get("CRLiteRevOCSPUnk"_ns)
1136 .Add(1);
1137 } else {
1138 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1139 .Get("CRLiteRevOCSPSoft"_ns)
1140 .Add(1);
1144 if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
1145 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1146 ("NSSCertDBTrustDomain: returning after "
1147 "VerifyEncodedOCSPResponse"));
1148 return rv;
1151 if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
1152 rv == Result::ERROR_REVOKED_CERTIFICATE) {
1153 return rv;
1156 if (stapledOCSPResponseResult != Success) {
1157 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1158 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
1159 "stapled response after OCSP request verification failure"));
1160 return stapledOCSPResponseResult;
1163 softFailure = true;
1164 return Success; // Soft fail -> success :(
1167 Result NSSCertDBTrustDomain::HandleOCSPFailure(
1168 const Result cachedResponseResult, const Result stapledOCSPResponseResult,
1169 const Result error, /*out*/ bool& softFailure) {
1170 if (mOCSPFetching != FetchOCSPForDVSoftFail) {
1171 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1172 ("NSSCertDBTrustDomain: returning SECFailure after OCSP request "
1173 "failure"));
1174 return error;
1177 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
1178 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1179 ("NSSCertDBTrustDomain: returning SECFailure from cached response "
1180 "after OCSP request failure"));
1181 return cachedResponseResult;
1184 if (stapledOCSPResponseResult != Success) {
1185 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1186 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
1187 "stapled response after OCSP request failure"));
1188 return stapledOCSPResponseResult;
1191 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1192 ("NSSCertDBTrustDomain: returning SECSuccess after OCSP request "
1193 "failure"));
1195 softFailure = true;
1196 return Success; // Soft fail -> success :(
1199 Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
1200 const CertID& certID, Time time, uint16_t maxLifetimeInDays,
1201 Input encodedResponse, EncodedResponseSource responseSource,
1202 /*out*/ bool& expired) {
1203 Time thisUpdate(Time::uninitialized);
1204 Time validThrough(Time::uninitialized);
1206 Result rv = VerifyEncodedOCSPResponse(*this, certID, time, maxLifetimeInDays,
1207 encodedResponse, expired, &thisUpdate,
1208 &validThrough);
1209 // If a response was stapled and expired, we don't want to cache it. Return
1210 // early to simplify the logic here.
1211 if (responseSource == ResponseWasStapled && expired) {
1212 MOZ_ASSERT(rv != Success);
1213 return rv;
1215 // validThrough is only trustworthy if the response successfully verifies
1216 // or it indicates a revoked or unknown certificate.
1217 // If this isn't the case, store an indication of failure (to prevent
1218 // repeatedly requesting a response from a failing server).
1219 if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
1220 rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
1221 validThrough = time;
1222 if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
1223 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
1226 if (responseSource == ResponseIsFromNetwork || rv == Success ||
1227 rv == Result::ERROR_REVOKED_CERTIFICATE ||
1228 rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
1229 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1230 ("NSSCertDBTrustDomain: caching OCSP response"));
1231 Result putRV =
1232 mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate, validThrough);
1233 if (putRV != Success) {
1234 return putRV;
1238 return rv;
1241 nsresult isDistrustedCertificateChain(
1242 const nsTArray<nsTArray<uint8_t>>& certArray,
1243 const SECTrustType certDBTrustType, bool& isDistrusted) {
1244 if (certArray.Length() == 0) {
1245 return NS_ERROR_FAILURE;
1248 // Set the default result to be distrusted.
1249 isDistrusted = true;
1251 CK_ATTRIBUTE_TYPE attrType;
1252 switch (certDBTrustType) {
1253 case trustSSL:
1254 attrType = CKA_NSS_SERVER_DISTRUST_AFTER;
1255 break;
1256 case trustEmail:
1257 attrType = CKA_NSS_EMAIL_DISTRUST_AFTER;
1258 break;
1259 default:
1260 // There is no distrust to set if the certDBTrustType is not SSL or Email.
1261 isDistrusted = false;
1262 return NS_OK;
1265 Input endEntityDER;
1266 mozilla::pkix::Result rv = endEntityDER.Init(
1267 certArray.ElementAt(0).Elements(), certArray.ElementAt(0).Length());
1268 if (rv != Success) {
1269 return NS_ERROR_FAILURE;
1272 BackCert endEntityBackCert(endEntityDER, EndEntityOrCA::MustBeEndEntity,
1273 nullptr);
1274 rv = endEntityBackCert.Init();
1275 if (rv != Success) {
1276 return NS_ERROR_FAILURE;
1279 Time endEntityNotBefore(Time::uninitialized);
1280 rv = ParseValidity(endEntityBackCert.GetValidity(), &endEntityNotBefore,
1281 nullptr);
1282 if (rv != Success) {
1283 return NS_ERROR_FAILURE;
1286 Input rootDER;
1287 rv = rootDER.Init(certArray.LastElement().Elements(),
1288 certArray.LastElement().Length());
1289 if (rv != Success) {
1290 return NS_ERROR_FAILURE;
1292 SECItem rootDERItem(UnsafeMapInputToSECItem(rootDER));
1294 PRBool distrusted;
1295 PRTime distrustAfter; // time since epoch in microseconds
1296 bool foundDistrust = false;
1298 // This strategy for searching for the builtins module is borrowed
1299 // from CertVerifier::IsCertBuiltInRoot. See the comment on that
1300 // function for more information.
1301 AutoSECMODListReadLock lock;
1302 for (SECMODModuleList* list = SECMOD_GetDefaultModuleList();
1303 list && !foundDistrust; list = list->next) {
1304 for (int i = 0; i < list->module->slotCount; i++) {
1305 PK11SlotInfo* slot = list->module->slots[i];
1306 if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) {
1307 continue;
1309 CK_OBJECT_HANDLE handle =
1310 PK11_FindEncodedCertInSlot(slot, &rootDERItem, nullptr);
1311 if (handle == CK_INVALID_HANDLE) {
1312 continue;
1314 // Distrust attributes are only set on builtin roots, so ensure this
1315 // certificate has the CKA_NSS_MOZILLA_CA_POLICY attribute.
1316 if (!PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
1317 false)) {
1318 continue;
1320 SECStatus srv = PK11_ReadDistrustAfterAttribute(
1321 slot, handle, attrType, &distrusted, &distrustAfter);
1322 if (srv == SECSuccess) {
1323 foundDistrust = true;
1328 if (!foundDistrust || distrusted == PR_FALSE) {
1329 isDistrusted = false;
1330 return NS_OK;
1333 Time distrustAfterTime =
1334 mozilla::pkix::TimeFromEpochInSeconds(distrustAfter / PR_USEC_PER_SEC);
1335 if (endEntityNotBefore <= distrustAfterTime) {
1336 isDistrusted = false;
1339 return NS_OK;
1342 Result NSSCertDBTrustDomain::IsChainValid(const DERArray& reversedDERArray,
1343 Time time,
1344 const CertPolicyId& requiredPolicy) {
1345 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1346 ("NSSCertDBTrustDomain: IsChainValid"));
1348 size_t numCerts = reversedDERArray.GetLength();
1349 if (numCerts < 1) {
1350 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1352 nsTArray<nsTArray<uint8_t>> certArray;
1353 for (size_t i = numCerts; i > 0; --i) {
1354 const Input* derInput = reversedDERArray.GetDER(i - 1);
1355 certArray.EmplaceBack(derInput->UnsafeGetData(), derInput->GetLength());
1358 const nsTArray<uint8_t>& rootBytes = certArray.LastElement();
1359 Input rootInput;
1360 Result rv = rootInput.Init(rootBytes.Elements(), rootBytes.Length());
1361 if (rv != Success) {
1362 return rv;
1364 rv = IsCertBuiltInRoot(rootInput, mIsBuiltChainRootBuiltInRoot);
1365 if (rv != Result::Success) {
1366 return rv;
1368 nsresult nsrv;
1369 // If mHostname isn't set, we're not verifying in the context of a TLS
1370 // handshake, so don't verify key pinning in those cases.
1371 if (mHostname) {
1372 nsTArray<Span<const uint8_t>> derCertSpanList;
1373 for (const auto& certDER : certArray) {
1374 derCertSpanList.EmplaceBack(certDER.Elements(), certDER.Length());
1377 bool chainHasValidPins;
1378 nsrv = PublicKeyPinningService::ChainHasValidPins(
1379 derCertSpanList, mHostname, time, mIsBuiltChainRootBuiltInRoot,
1380 chainHasValidPins, mPinningTelemetryInfo);
1381 if (NS_FAILED(nsrv)) {
1382 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1384 if (!chainHasValidPins) {
1385 return Result::ERROR_KEY_PINNING_FAILURE;
1389 // Check that the childs' certificate NotBefore date is anterior to
1390 // the NotAfter value of the parent when the root is a builtin.
1391 if (mIsBuiltChainRootBuiltInRoot) {
1392 bool isDistrusted;
1393 nsrv =
1394 isDistrustedCertificateChain(certArray, mCertDBTrustType, isDistrusted);
1395 if (NS_FAILED(nsrv)) {
1396 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1398 if (isDistrusted) {
1399 return Result::ERROR_UNTRUSTED_ISSUER;
1403 // See bug 1434300. If the root is a Symantec root, see if we distrust this
1404 // path. Since we already have the root available, we can check that cheaply
1405 // here before proceeding with the rest of the algorithm.
1407 // This algorithm only applies if we are verifying in the context of a TLS
1408 // handshake. To determine this, we check mHostname: If it isn't set, this is
1409 // not TLS, so don't run the algorithm.
1410 const nsTArray<uint8_t>& rootCertDER = certArray.LastElement();
1411 if (mHostname && CertDNIsInList(rootCertDER, RootSymantecDNs)) {
1412 if (numCerts <= 1) {
1413 // This chain is supposed to be complete, so this is an error.
1414 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1416 nsTArray<Input> intCerts;
1418 for (size_t i = 1; i < certArray.Length() - 1; ++i) {
1419 const nsTArray<uint8_t>& certBytes = certArray.ElementAt(i);
1420 Input certInput;
1421 rv = certInput.Init(certBytes.Elements(), certBytes.Length());
1422 if (rv != Success) {
1423 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1426 intCerts.EmplaceBack(certInput);
1429 bool isDistrusted = false;
1430 nsrv = CheckForSymantecDistrust(intCerts, RootAppleAndGoogleSPKIs,
1431 isDistrusted);
1432 if (NS_FAILED(nsrv)) {
1433 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1435 if (isDistrusted) {
1436 mSawDistrustedCAByPolicyError = true;
1437 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1441 mBuiltChain = std::move(certArray);
1443 return Success;
1446 Result NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(
1447 DigestAlgorithm aAlg, EndEntityOrCA /*endEntityOrCA*/, Time /*notBefore*/) {
1448 switch (aAlg) {
1449 case DigestAlgorithm::sha256: // fall through
1450 case DigestAlgorithm::sha384: // fall through
1451 case DigestAlgorithm::sha512:
1452 return Success;
1453 case DigestAlgorithm::sha1:
1454 return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
1456 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1459 Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
1460 EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
1461 if (modulusSizeInBits < mMinRSABits) {
1462 return Result::ERROR_INADEQUATE_KEY_SIZE;
1464 return Success;
1467 Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedData(
1468 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1469 Input subjectPublicKeyInfo) {
1470 return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature,
1471 subjectPublicKeyInfo, mPinArg);
1474 Result NSSCertDBTrustDomain::VerifyRSAPSSSignedData(
1475 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1476 Input subjectPublicKeyInfo) {
1477 return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature,
1478 subjectPublicKeyInfo, mPinArg);
1481 Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
1482 EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) {
1483 switch (curve) {
1484 case NamedCurve::secp256r1: // fall through
1485 case NamedCurve::secp384r1: // fall through
1486 case NamedCurve::secp521r1:
1487 return Success;
1490 return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
1493 Result NSSCertDBTrustDomain::VerifyECDSASignedData(
1494 Input data, DigestAlgorithm digestAlgorithm, Input signature,
1495 Input subjectPublicKeyInfo) {
1496 return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature,
1497 subjectPublicKeyInfo, mPinArg);
1500 Result NSSCertDBTrustDomain::CheckValidityIsAcceptable(
1501 Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
1502 KeyPurposeId keyPurpose) {
1503 if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
1504 return Success;
1506 if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
1507 return Success;
1510 Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
1511 Time::ONE_DAY_IN_SECONDS);
1512 Duration maxValidityDuration(UINT64_MAX);
1513 Duration validityDuration(notBefore, notAfter);
1515 switch (mValidityCheckingMode) {
1516 case ValidityCheckingMode::CheckingOff:
1517 return Success;
1518 case ValidityCheckingMode::CheckForEV:
1519 // The EV Guidelines say the maximum is 27 months, but we use a slightly
1520 // higher limit here to (hopefully) minimize compatibility breakage.
1521 maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
1522 break;
1523 default:
1524 MOZ_ASSERT_UNREACHABLE(
1525 "We're not handling every ValidityCheckingMode type");
1528 if (validityDuration > maxValidityDuration) {
1529 return Result::ERROR_VALIDITY_TOO_LONG;
1532 return Success;
1535 Result NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(
1536 Time notBefore,
1537 /*out*/ bool& matches) {
1538 // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
1539 static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
1540 // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
1541 static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
1543 switch (mNetscapeStepUpPolicy) {
1544 case NetscapeStepUpPolicy::AlwaysMatch:
1545 matches = true;
1546 return Success;
1547 case NetscapeStepUpPolicy::MatchBefore23August2016:
1548 matches = notBefore < AUGUST_23_2016;
1549 return Success;
1550 case NetscapeStepUpPolicy::MatchBefore23August2015:
1551 matches = notBefore < AUGUST_23_2015;
1552 return Success;
1553 case NetscapeStepUpPolicy::NeverMatch:
1554 matches = false;
1555 return Success;
1556 default:
1557 MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
1559 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1562 void NSSCertDBTrustDomain::ResetAccumulatedState() {
1563 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
1564 mSCTListFromOCSPStapling = nullptr;
1565 mSCTListFromCertificate = nullptr;
1566 mSawDistrustedCAByPolicyError = false;
1567 mIsBuiltChainRootBuiltInRoot = false;
1568 mIssuerSources.clear();
1571 static Input SECItemToInput(const UniqueSECItem& item) {
1572 Input result;
1573 if (item) {
1574 MOZ_ASSERT(item->type == siBuffer);
1575 Result rv = result.Init(item->data, item->len);
1576 // As used here, |item| originally comes from an Input,
1577 // so there should be no issues converting it back.
1578 MOZ_ASSERT(rv == Success);
1579 Unused << rv; // suppresses warnings in release builds
1581 return result;
1584 Input NSSCertDBTrustDomain::GetSCTListFromCertificate() const {
1585 return SECItemToInput(mSCTListFromCertificate);
1588 Input NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const {
1589 return SECItemToInput(mSCTListFromOCSPStapling);
1592 bool NSSCertDBTrustDomain::GetIsBuiltChainRootBuiltInRoot() const {
1593 return mIsBuiltChainRootBuiltInRoot;
1596 bool NSSCertDBTrustDomain::GetIsErrorDueToDistrustedCAPolicy() const {
1597 return mSawDistrustedCAByPolicyError;
1600 void NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
1601 Input extensionData) {
1602 UniqueSECItem* out = nullptr;
1603 switch (extension) {
1604 case AuxiliaryExtension::EmbeddedSCTList:
1605 out = &mSCTListFromCertificate;
1606 break;
1607 case AuxiliaryExtension::SCTListFromOCSPResponse:
1608 out = &mSCTListFromOCSPStapling;
1609 break;
1610 default:
1611 MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
1613 if (out) {
1614 SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
1615 out->reset(SECITEM_DupItem(&extensionDataItem));
1619 SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig,
1620 PKCS11DBConfig pkcs11DbConfig) {
1621 MOZ_ASSERT(NS_IsMainThread());
1623 // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
1624 // module by NSS_Initialize because we will load it in LoadLoadableRoots
1625 // later. It also allows us to work around a bug in the system NSS in
1626 // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
1627 // "/usr/lib/nss/libnssckbi.so".
1628 uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
1629 if (nssDbConfig == NSSDBConfig::ReadOnly) {
1630 flags |= NSS_INIT_READONLY;
1632 if (pkcs11DbConfig == PKCS11DBConfig::DoNotLoadModules) {
1633 flags |= NSS_INIT_NOMODDB;
1635 nsAutoCString dbTypeAndDirectory("sql:");
1636 dbTypeAndDirectory.Append(dir);
1637 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1638 ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(),
1639 (int)nssDbConfig, (int)pkcs11DbConfig));
1640 SECStatus srv =
1641 NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags);
1642 if (srv != SECSuccess) {
1643 return srv;
1646 if (nssDbConfig == NSSDBConfig::ReadWrite) {
1647 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1648 if (!slot) {
1649 return SECFailure;
1651 // If the key DB doesn't have a password set, PK11_NeedUserInit will return
1652 // true. For the SQL DB, we need to set a password or we won't be able to
1653 // import any certificates or change trust settings.
1654 if (PK11_NeedUserInit(slot.get())) {
1655 srv = PK11_InitPin(slot.get(), nullptr, nullptr);
1656 MOZ_ASSERT(srv == SECSuccess);
1657 Unused << srv;
1661 CollectThirdPartyPKCS11ModuleTelemetry();
1663 return SECSuccess;
1666 void DisableMD5() {
1667 NSS_SetAlgorithmPolicy(
1668 SEC_OID_MD5, 0,
1669 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1670 NSS_SetAlgorithmPolicy(
1671 SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 0,
1672 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1673 NSS_SetAlgorithmPolicy(
1674 SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC, 0,
1675 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1678 // Load a given PKCS#11 module located in the given directory. It will be named
1679 // the given module name. Optionally pass some string parameters to it via
1680 // 'params'. This argument will be provided to C_Initialize when called on the
1681 // module.
1682 // |libraryName| and |dir| are encoded in UTF-8.
1683 bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
1684 const nsCString& dir, /* optional */ const char* params) {
1685 // If a module exists with the same name, make a best effort attempt to delete
1686 // it. Note that it isn't possible to delete the internal module, so checking
1687 // the return value would be detrimental in that case.
1688 int unusedModType;
1689 Unused << SECMOD_DeleteModule(moduleName, &unusedModType);
1691 nsAutoCString fullLibraryPath;
1692 if (!dir.IsEmpty()) {
1693 fullLibraryPath.Assign(dir);
1694 fullLibraryPath.AppendLiteral(FILE_PATH_SEPARATOR);
1696 fullLibraryPath.Append(MOZ_DLL_PREFIX);
1697 fullLibraryPath.Append(libraryName);
1698 fullLibraryPath.Append(MOZ_DLL_SUFFIX);
1699 // Escape the \ and " characters.
1700 fullLibraryPath.ReplaceSubstring("\\", "\\\\");
1701 fullLibraryPath.ReplaceSubstring("\"", "\\\"");
1703 nsAutoCString pkcs11ModuleSpec("name=\"");
1704 pkcs11ModuleSpec.Append(moduleName);
1705 pkcs11ModuleSpec.AppendLiteral("\" library=\"");
1706 pkcs11ModuleSpec.Append(fullLibraryPath);
1707 pkcs11ModuleSpec.AppendLiteral("\"");
1708 if (params) {
1709 pkcs11ModuleSpec.AppendLiteral("\" parameters=\"");
1710 pkcs11ModuleSpec.Append(params);
1711 pkcs11ModuleSpec.AppendLiteral("\"");
1714 UniqueSECMODModule userModule(SECMOD_LoadUserModule(
1715 const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false));
1716 if (!userModule) {
1717 return false;
1720 if (!userModule->loaded) {
1721 return false;
1724 return true;
1727 bool LoadIPCClientCertsModule(const nsCString& dir) {
1728 // The IPC client certs module needs to be able to call back into gecko to be
1729 // able to communicate with the parent process over IPC. This is achieved by
1730 // serializing the addresses of the relevant functions and passing them as an
1731 // extra string parameter that will be available when C_Initialize is called
1732 // on IPC client certs.
1733 nsPrintfCString addrs("%p,%p", DoFindObjects, DoSign);
1734 if (!LoadUserModuleAt(kIPCClientCertsModuleName.get(), "ipcclientcerts", dir,
1735 addrs.get())) {
1736 return false;
1738 RunOnShutdown(
1739 []() {
1740 UniqueSECMODModule ipcClientCertsModule(
1741 SECMOD_FindModule(kIPCClientCertsModuleName.get()));
1742 if (ipcClientCertsModule) {
1743 SECMOD_UnloadUserModule(ipcClientCertsModule.get());
1746 ShutdownPhase::XPCOMWillShutdown);
1747 return true;
1750 bool LoadOSClientCertsModule(const nsCString& dir) {
1751 nsLiteralCString params =
1752 StaticPrefs::security_osclientcerts_assume_rsa_pss_support()
1753 ? "RSA-PSS"_ns
1754 : ""_ns;
1755 return LoadUserModuleAt(kOSClientCertsModuleName.get(), "osclientcerts", dir,
1756 params.get());
1759 bool LoadLoadableRoots(const nsCString& dir) {
1760 // Some NSS command-line utilities will load a roots module under the name
1761 // "Root Certs" if there happens to be a `MOZ_DLL_PREFIX "nssckbi"
1762 // MOZ_DLL_SUFFIX` file in the directory being operated on. In some cases this
1763 // can cause us to fail to load our roots module. In these cases, deleting the
1764 // "Root Certs" module allows us to load the correct one. See bug 1406396.
1765 int unusedModType;
1766 Unused << SECMOD_DeleteModule("Root Certs", &unusedModType);
1767 return LoadUserModuleAt(kRootModuleName.get(), "nssckbi", dir, nullptr);
1770 nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
1771 /*out*/ nsCString& nickname) {
1772 MOZ_ASSERT(cert);
1773 NS_ENSURE_ARG_POINTER(cert);
1775 UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
1776 if (!baseName) {
1777 baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
1779 if (!baseName) {
1780 baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
1782 if (!baseName) {
1783 baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
1785 if (!baseName) {
1786 baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
1788 if (!baseName) {
1789 baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
1791 if (!baseName) {
1792 return NS_ERROR_FAILURE;
1795 // This function is only used in contexts where a failure to find a suitable
1796 // nickname does not block the overall task from succeeding.
1797 // As such, we use an arbitrary limit to prevent this nickname searching
1798 // process from taking forever.
1799 static const uint32_t ARBITRARY_LIMIT = 500;
1800 for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
1801 nickname = baseName.get();
1802 if (count != 1) {
1803 nickname.AppendPrintf(" #%u", count);
1805 if (nickname.IsEmpty()) {
1806 return NS_ERROR_FAILURE;
1809 bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
1810 cert->dbhandle);
1811 if (!conflict) {
1812 return NS_OK;
1816 return NS_ERROR_FAILURE;
1819 Result BuildRevocationCheckArrays(Input certDER, EndEntityOrCA endEntityOrCA,
1820 /*out*/ nsTArray<uint8_t>& issuerBytes,
1821 /*out*/ nsTArray<uint8_t>& serialBytes,
1822 /*out*/ nsTArray<uint8_t>& subjectBytes,
1823 /*out*/ nsTArray<uint8_t>& pubKeyBytes) {
1824 BackCert cert(certDER, endEntityOrCA, nullptr);
1825 Result rv = cert.Init();
1826 if (rv != Success) {
1827 return rv;
1829 issuerBytes.Clear();
1830 Input issuer(cert.GetIssuer());
1831 issuerBytes.AppendElements(issuer.UnsafeGetData(), issuer.GetLength());
1832 serialBytes.Clear();
1833 Input serial(cert.GetSerialNumber());
1834 serialBytes.AppendElements(serial.UnsafeGetData(), serial.GetLength());
1835 subjectBytes.Clear();
1836 Input subject(cert.GetSubject());
1837 subjectBytes.AppendElements(subject.UnsafeGetData(), subject.GetLength());
1838 pubKeyBytes.Clear();
1839 Input pubKey(cert.GetSubjectPublicKeyInfo());
1840 pubKeyBytes.AppendElements(pubKey.UnsafeGetData(), pubKey.GetLength());
1842 return Success;
1845 } // namespace psm
1846 } // namespace mozilla