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