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"
12 #include "CRLiteTimestamp.h"
13 #include "ExtendedValidation.h"
14 #include "MultiLogCTVerifier.h"
15 #include "NSSErrorsService.h"
16 #include "PKCS11ModuleDB.h"
17 #include "PublicKeyPinningService.h"
19 #include "cert_storage/src/cert_storage.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"
45 #include "nsPrintfCString.h"
46 #include "nsServiceManagerUtils.h"
47 #include "nsThreadUtils.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;
69 NSSCertDBTrustDomain::NSSCertDBTrustDomain(
70 SECTrustType certDBTrustType
, OCSPFetching ocspFetching
,
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
),
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
),
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
,
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
) {
119 // rawResults == nullptr means we didn't find any matching certificates
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())) {
152 if (GetCertTrust(EndEntityOrCA::MustBeCA
, CertPolicyId::anyPolicy
, certDER
,
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
) {
161 if (VerifySignedData(*this, cert
.GetSignedData(),
162 cert
.GetSubjectPublicKeyInfo()) != Success
) {
165 // This is a self-signed, non-trust-anchor certificate, so we shouldn't use it
166 // for path building. See bug 1056341.
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
)) {
179 if (ShouldSkipSelfSignedNonTrustAnchor(candidate
.mDER
)) {
183 checker
.Check(candidate
.mDER
, nameConstraintsInputPtr
, keepGoing
);
188 mIssuerSources
+= candidate
.mIssuerSource
;
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;
204 CERT_GetImposedNameConstraints(&encodedIssuerNameItem
, &nameConstraints
);
205 if (srv
== SECSuccess
) {
206 if (nameConstraintsInput
.Init(nameConstraints
.data
, nameConstraints
.len
) !=
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
,
226 for (const auto& root
: builtInRoots
) {
228 Result rv
= rootInput
.Init(root
.Elements(), root
.Length());
230 continue; // probably too big
232 geckoRootCandidates
.AppendElement(IssuerCandidateWithSource
{
233 rootInput
, IssuerSource::BuiltInRootsModule
});
236 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
237 ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module"));
240 if (mExtraCertificates
.isSome()) {
241 for (const auto& extraCert
: *mExtraCertificates
) {
243 Result rv
= certInput
.Init(extraCert
.Elements(), extraCert
.Length());
247 BackCert
cert(certInput
, EndEntityOrCA::MustBeCA
, nullptr);
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())) {
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();
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())) {
277 geckoRootCandidates
.AppendElement(IssuerCandidateWithSource
{
278 thirdPartyRootInput
, IssuerSource::ThirdPartyCertificates
});
281 for (const auto& thirdPartyIntermediateInput
:
282 mThirdPartyIntermediateInputs
) {
283 BackCert
intermediate(thirdPartyIntermediateInput
, EndEntityOrCA::MustBeCA
,
285 Result rv
= intermediate
.Init();
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())) {
295 geckoIntermediateCandidates
.AppendElement(IssuerCandidateWithSource
{
296 thirdPartyIntermediateInput
, IssuerSource::ThirdPartyCertificates
});
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
);
308 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
310 for (auto& cert
: certs
) {
312 Result rv
= certDER
.Init(cert
.Elements(), cert
.Length());
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
) {
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
)) {
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
348 UniqueCERTCertList
candidates(
349 CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
350 &encodedIssuerNameItem
, 0, false));
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
));
360 nssIntermediateCandidates
.AppendElement(std::move(candidate
));
365 nsCOMPtr
<nsIEventTarget
> socketThread(
366 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID
));
368 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
370 rv
= SyncRunnable::DispatchToThread(socketThread
, getCandidatesTask
);
372 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
375 nsTArray
<IssuerCandidateWithSource
> nssCandidates
;
376 for (const auto& rootCandidate
: nssRootCandidates
) {
378 Result rv
= certDER
.Init(rootCandidate
.Elements(), rootCandidate
.Length());
380 continue; // probably too big
382 nssCandidates
.AppendElement(
383 IssuerCandidateWithSource
{std::move(certDER
), IssuerSource::NSSCertDB
});
385 for (const auto& intermediateCandidate
: nssIntermediateCandidates
) {
387 Result rv
= certDER
.Init(intermediateCandidate
.Elements(),
388 intermediateCandidate
.Length());
390 continue; // probably too big
392 nssCandidates
.AppendElement(
393 IssuerCandidateWithSource
{std::move(certDER
), IssuerSource::NSSCertDB
});
396 return CheckCandidates(checker
, nssCandidates
, nameConstraintsInputPtr
,
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
406 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
409 // The certificate blocklist currently only applies to TLS server
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
;
420 BuildRevocationCheckArrays(candidateCertDER
, endEntityOrCA
, issuerBytes
,
421 serialBytes
, subjectBytes
, pubKeyBytes
);
422 if (result
!= Success
) {
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
)
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
;
449 // This may be a third-party intermediate.
450 for (const auto& thirdPartyIntermediateInput
:
451 mThirdPartyIntermediateInputs
) {
452 if (InputsAreEqual(candidateCertDER
, thirdPartyIntermediateInput
)) {
453 trustLevel
= TrustLevel::InheritsTrust
;
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
;
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
486 mozilla::glean::cert_verifier::cert_trust_evaluation_time
.Start();
488 UniqueCERTCertificate
candidateCert(CERT_NewTempCertificate(
489 CERT_GetDefaultCertDB(), &candidateCertDERSECItem
, nullptr, false,
492 #ifndef MOZ_WIDGET_ANDROID
493 mozilla::glean::cert_verifier::cert_trust_evaluation_time
494 .StopAndAccumulate(std::move(timerId
));
496 if (!candidateCert
) {
497 result
= MapPRErrorCodeToResult(PR_GetError());
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."
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
518 if (((flags
& (relevantTrustBit
| CERTDB_TERMINAL_RECORD
))) ==
519 CERTDB_TERMINAL_RECORD
) {
520 trustLevel
= TrustLevel::ActivelyDistrusted
;
525 // For TRUST, we use the CERTDB_TRUSTED_CA bit.
526 if (flags
& CERTDB_TRUSTED_CA
) {
527 if (policy
.IsAnyPolicy()) {
528 trustLevel
= TrustLevel::TrustAnchor
;
533 nsTArray
<uint8_t> certBytes(candidateCert
->derCert
.data
,
534 candidateCert
->derCert
.len
);
535 if (CertIsAuthoritativeForEVPolicy(certBytes
, policy
)) {
536 trustLevel
= TrustLevel::TrustAnchor
;
542 trustLevel
= TrustLevel::InheritsTrust
;
545 nsCOMPtr
<nsIEventTarget
> socketThread(
546 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID
));
548 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
550 nsresult rv
= SyncRunnable::DispatchToThread(socketThread
, getTrustTask
);
552 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
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");
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
,
588 /*out*/ nsCString
& result
) {
589 MOZ_ASSERT(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
);
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
;
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
));
621 current
= CERT_GetNextGeneralName(current
);
622 } while (current
!= aia
[i
]->location
);
629 NS_IMPL_ISUPPORTS(CRLiteTimestamp
, nsICRLiteTimestamp
)
632 CRLiteTimestamp::GetLogID(nsTArray
<uint8_t>& aLogID
) {
634 aLogID
.AppendElements(mLogID
);
639 CRLiteTimestamp::GetTimestamp(uint64_t* aTimestamp
) {
640 *aTimestamp
= mTimestamp
;
644 Result
BuildCRLiteTimestampArray(
646 /*out*/ nsTArray
<RefPtr
<nsICRLiteTimestamp
>>& timestamps
) {
649 ExtractSignedCertificateTimestampListFromExtension(sctExtension
, sctList
);
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
));
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
);
674 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
675 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
677 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
679 if (isRevokedByStash
) {
680 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
681 ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
683 mozilla::glean::cert_verifier::crlite_status
.Get("revoked_in_stash"_ns
)
685 return Result::ERROR_REVOKED_CERTIFICATE
;
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
);
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 "
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
)
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);
721 case nsICertStorage::STATE_NOT_ENROLLED
:
722 filterCoversCertificate
= false;
723 mozilla::glean::cert_verifier::crlite_status
.Get("not_enrolled"_ns
)
726 case nsICertStorage::STATE_NOT_COVERED
:
727 filterCoversCertificate
= false;
728 mozilla::glean::cert_verifier::crlite_status
.Get("not_covered"_ns
).Add(1);
730 case nsICertStorage::STATE_NO_FILTER
:
731 filterCoversCertificate
= false;
732 mozilla::glean::cert_verifier::crlite_status
.Get("no_filter"_ns
).Add(1);
735 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
736 ("NSSCertDBTrustDomain::CheckCRLite: Unknown CRLite revocation "
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
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
) {
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());
765 UniquePLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
767 return Result::FATAL_ERROR_NO_MEMORY
;
770 GetOCSPAuthorityInfoAccessLocation(arena
, *aiaExtension
, aiaLocation
);
776 bool crliteCoversCertificate
= false;
777 Result crliteResult
= Success
;
778 if (mCRLiteMode
!= CRLiteMode::Disabled
&& sctExtension
) {
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
) {
789 if (crliteCoversCertificate
) {
790 mozilla::glean::cert_verifier::cert_revocation_mechanisms
.Get("CRLite"_ns
)
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
) {
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()) {
805 // In ConfirmRevocations mode we can return "Not Revoked"
806 // without consulting OCSP.
807 if (mCRLiteMode
== CRLiteMode::ConfirmRevocations
&&
808 crliteResult
== 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"));
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.
850 CheckCRLiteStash(issuerSubjectPublicKeyInfoBytes
, serialNumberBytes
);
852 crliteCoversCertificate
= (rv
== Result::ERROR_REVOKED_CERTIFICATE
);
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
);
863 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
864 ("decoding SCT extension failed - CRLite will be not be "
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
) {
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
) {
889 stapledOCSPResponseResult
= VerifyAndMaybeCacheEncodedOCSPResponse(
890 certID
, time
, maxOCSPLifetimeInDays
, *stapledOCSPResponse
,
891 ResponseWasStapled
, expired
);
892 mozilla::glean::cert_verifier::cert_revocation_mechanisms
893 .Get("StapledOCSP"_ns
)
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"));
902 if (stapledOCSPResponseResult
== Result::ERROR_OCSP_OLD_RESPONSE
||
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)"));
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
;
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
)
942 if (cachedResponseResult
== Success
&& cachedResponseValidThrough
>= time
) {
943 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
944 ("NSSCertDBTrustDomain: cached OCSP response: good"));
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
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;
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
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
)
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
;
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.
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
,
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
,
1069 if (rv
!= Success
) {
1073 Vector
<uint8_t> ocspResponse
;
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
) {
1087 if (timeout
.AddSeconds(ServerFailureDelaySeconds
) != Success
) {
1088 return Result::FATAL_ERROR_LIBRARY_FAILURE
; // integer overflow
1092 mOCSPCache
.Put(certID
, mOriginAttributes
, rv
, time
, timeout
);
1093 if (cacheRV
!= Success
) {
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
)
1105 return HandleOCSPFailure(cachedResponseResult
, stapledOCSPResponseResult
,
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.
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
)
1129 } else if (rv
== Result::ERROR_REVOKED_CERTIFICATE
) {
1130 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1131 .Get("CRLiteRevOCSPRev"_ns
)
1133 } else if (rv
== Result::ERROR_OCSP_UNKNOWN_CERT
) {
1134 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1135 .Get("CRLiteRevOCSPUnk"_ns
)
1138 mozilla::glean::cert_verifier::crlite_vs_ocsp_result
1139 .Get("CRLiteRevOCSPSoft"_ns
)
1144 if (rv
== Success
|| mOCSPFetching
!= FetchOCSPForDVSoftFail
) {
1145 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
1146 ("NSSCertDBTrustDomain: returning after "
1147 "VerifyEncodedOCSPResponse"));
1151 if (rv
== Result::ERROR_OCSP_UNKNOWN_CERT
||
1152 rv
== Result::ERROR_REVOKED_CERTIFICATE
) {
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
;
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 "
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 "
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
,
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
);
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"));
1232 mOCSPCache
.Put(certID
, mOriginAttributes
, rv
, thisUpdate
, validThrough
);
1233 if (putRV
!= Success
) {
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
) {
1254 attrType
= CKA_NSS_SERVER_DISTRUST_AFTER
;
1257 attrType
= CKA_NSS_EMAIL_DISTRUST_AFTER
;
1260 // There is no distrust to set if the certDBTrustType is not SSL or Email.
1261 isDistrusted
= false;
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
,
1274 rv
= endEntityBackCert
.Init();
1275 if (rv
!= Success
) {
1276 return NS_ERROR_FAILURE
;
1279 Time
endEntityNotBefore(Time::uninitialized
);
1280 rv
= ParseValidity(endEntityBackCert
.GetValidity(), &endEntityNotBefore
,
1282 if (rv
!= Success
) {
1283 return NS_ERROR_FAILURE
;
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
));
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
)) {
1309 CK_OBJECT_HANDLE handle
=
1310 PK11_FindEncodedCertInSlot(slot
, &rootDERItem
, nullptr);
1311 if (handle
== CK_INVALID_HANDLE
) {
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
,
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;
1333 Time distrustAfterTime
=
1334 mozilla::pkix::TimeFromEpochInSeconds(distrustAfter
/ PR_USEC_PER_SEC
);
1335 if (endEntityNotBefore
<= distrustAfterTime
) {
1336 isDistrusted
= false;
1342 Result
NSSCertDBTrustDomain::IsChainValid(const DERArray
& reversedDERArray
,
1344 const CertPolicyId
& requiredPolicy
) {
1345 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
1346 ("NSSCertDBTrustDomain: IsChainValid"));
1348 size_t numCerts
= reversedDERArray
.GetLength();
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();
1360 Result rv
= rootInput
.Init(rootBytes
.Elements(), rootBytes
.Length());
1361 if (rv
!= Success
) {
1364 rv
= IsCertBuiltInRoot(rootInput
, mIsBuiltChainRootBuiltInRoot
);
1365 if (rv
!= Result::Success
) {
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.
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
) {
1394 isDistrustedCertificateChain(certArray
, mCertDBTrustType
, isDistrusted
);
1395 if (NS_FAILED(nsrv
)) {
1396 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
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
);
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
,
1432 if (NS_FAILED(nsrv
)) {
1433 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
1436 mSawDistrustedCAByPolicyError
= true;
1437 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED
;
1441 mBuiltChain
= std::move(certArray
);
1446 Result
NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(
1447 DigestAlgorithm aAlg
, EndEntityOrCA
/*endEntityOrCA*/, Time
/*notBefore*/) {
1449 case DigestAlgorithm::sha256
: // fall through
1450 case DigestAlgorithm::sha384
: // fall through
1451 case DigestAlgorithm::sha512
:
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
;
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
) {
1484 case NamedCurve::secp256r1
: // fall through
1485 case NamedCurve::secp384r1
: // fall through
1486 case NamedCurve::secp521r1
:
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
) {
1506 if (keyPurpose
== KeyPurposeId::id_kp_OCSPSigning
) {
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
:
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
;
1524 MOZ_ASSERT_UNREACHABLE(
1525 "We're not handling every ValidityCheckingMode type");
1528 if (validityDuration
> maxValidityDuration
) {
1529 return Result::ERROR_VALIDITY_TOO_LONG
;
1535 Result
NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(
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
:
1547 case NetscapeStepUpPolicy::MatchBefore23August2016
:
1548 matches
= notBefore
< AUGUST_23_2016
;
1550 case NetscapeStepUpPolicy::MatchBefore23August2015
:
1551 matches
= notBefore
< AUGUST_23_2015
;
1553 case NetscapeStepUpPolicy::NeverMatch
:
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
) {
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
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
;
1607 case AuxiliaryExtension::SCTListFromOCSPResponse
:
1608 out
= &mSCTListFromOCSPStapling
;
1611 MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
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
));
1641 NSS_Initialize(dbTypeAndDirectory
.get(), "", "", SECMOD_DB
, flags
);
1642 if (srv
!= SECSuccess
) {
1646 if (nssDbConfig
== NSSDBConfig::ReadWrite
) {
1647 UniquePK11SlotInfo
slot(PK11_GetInternalKeySlot());
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
);
1661 CollectThirdPartyPKCS11ModuleTelemetry();
1667 NSS_SetAlgorithmPolicy(
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
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.
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("\"");
1709 pkcs11ModuleSpec
.AppendLiteral("\" parameters=\"");
1710 pkcs11ModuleSpec
.Append(params
);
1711 pkcs11ModuleSpec
.AppendLiteral("\"");
1714 UniqueSECMODModule
userModule(SECMOD_LoadUserModule(
1715 const_cast<char*>(pkcs11ModuleSpec
.get()), nullptr, false));
1720 if (!userModule
->loaded
) {
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
,
1740 UniqueSECMODModule
ipcClientCertsModule(
1741 SECMOD_FindModule(kIPCClientCertsModuleName
.get()));
1742 if (ipcClientCertsModule
) {
1743 SECMOD_UnloadUserModule(ipcClientCertsModule
.get());
1746 ShutdownPhase::XPCOMWillShutdown
);
1750 bool LoadOSClientCertsModule(const nsCString
& dir
) {
1751 nsLiteralCString params
=
1752 StaticPrefs::security_osclientcerts_assume_rsa_pss_support()
1755 return LoadUserModuleAt(kOSClientCertsModuleName
.get(), "osclientcerts", dir
,
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.
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
) {
1773 NS_ENSURE_ARG_POINTER(cert
);
1775 UniquePORTString
baseName(CERT_GetCommonName(&cert
->subject
));
1777 baseName
= UniquePORTString(CERT_GetOrgUnitName(&cert
->subject
));
1780 baseName
= UniquePORTString(CERT_GetOrgName(&cert
->subject
));
1783 baseName
= UniquePORTString(CERT_GetLocalityName(&cert
->subject
));
1786 baseName
= UniquePORTString(CERT_GetStateName(&cert
->subject
));
1789 baseName
= UniquePORTString(CERT_GetCountryName(&cert
->subject
));
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();
1803 nickname
.AppendPrintf(" #%u", count
);
1805 if (nickname
.IsEmpty()) {
1806 return NS_ERROR_FAILURE
;
1809 bool conflict
= SEC_CertNicknameConflict(nickname
.get(), &cert
->derSubject
,
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
) {
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());
1846 } // namespace mozilla