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/. */
6
7 #include "NSSCertDBTrustDomain.h"
8
9 #include <stdint.h>
10 #include <utility>
11
12 #include "ExtendedValidation.h"
13 #include "MultiLogCTVerifier.h"
14 #include "NSSErrorsService.h"
15 #include "OCSPVerificationTrustDomain.h"
16 #include "PublicKeyPinningService.h"
17 #include "cert.h"
18 #include "cert_storage/src/cert_storage.h"
19 #include "certdb.h"
20 #include "mozilla/AppShutdown.h"
21 #include "mozilla/Assertions.h"
22 #include "mozilla/Casting.h"
23 #include "mozilla/PodOperations.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/TimeStamp.h"
26 #include "mozilla/Unused.h"
27 #include "mozpkix/Result.h"
28 #include "mozpkix/pkix.h"
29 #include "mozpkix/pkixnss.h"
30 #include "mozpkix/pkixutil.h"
31 #include "nsCRTGlue.h"
32 #include "nsIObserverService.h"
33 #include "nsNSSCertHelper.h"
34 #include "nsNSSCertificate.h"
35 #include "nsNSSCertificateDB.h"
36 #include "nsPrintfCString.h"
37 #include "nsServiceManagerUtils.h"
38 #include "nsThreadUtils.h"
39 #include "nss.h"
40 #include "pk11pub.h"
41 #include "prerror.h"
42 #include "secder.h"
43 #include "secerr.h"
44
45 #ifdef MOZ_WIDGET_COCOA
46 # include "nsCocoaFeatures.h"
47 #endif
48
49 #include "TrustOverrideUtils.h"
50 #include "TrustOverride-AppleGoogleDigiCertData.inc"
51 #include "TrustOverride-SymantecData.inc"
52
53 using namespace mozilla;
54 using namespace mozilla::ct;
55 using namespace mozilla::pkix;
56
57 extern LazyLogModule gCertVerifierLog;
58
59 static const uint64_t ServerFailureDelaySeconds = 5 * 60;
60
61 namespace mozilla {
62 namespace psm {
63
NSSCertDBTrustDomain(SECTrustType certDBTrustType,OCSPFetching ocspFetching,OCSPCache & ocspCache,void * pinArg,TimeDuration ocspTimeoutSoft,TimeDuration ocspTimeoutHard,uint32_t certShortLifetimeInDays,unsigned int minRSABits,ValidityCheckingMode validityCheckingMode,CertVerifier::SHA1Mode sha1Mode,NetscapeStepUpPolicy netscapeStepUpPolicy,CRLiteMode crliteMode,uint64_t crliteCTMergeDelaySeconds,const OriginAttributes & originAttributes,const Vector<Input> & thirdPartyRootInputs,const Vector<Input> & thirdPartyIntermediateInputs,const Maybe<nsTArray<nsTArray<uint8_t>>> & extraCertificates,UniqueCERTCertList & builtChain,PinningTelemetryInfo * pinningTelemetryInfo,const char * hostname)64 NSSCertDBTrustDomain::NSSCertDBTrustDomain(
65 SECTrustType certDBTrustType, OCSPFetching ocspFetching,
66 OCSPCache& ocspCache,
67 /*optional but shouldn't be*/ void* pinArg, TimeDuration ocspTimeoutSoft,
68 TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays,
69 unsigned int minRSABits, ValidityCheckingMode validityCheckingMode,
70 CertVerifier::SHA1Mode sha1Mode, NetscapeStepUpPolicy netscapeStepUpPolicy,
71 CRLiteMode crliteMode, uint64_t crliteCTMergeDelaySeconds,
72 const OriginAttributes& originAttributes,
73 const Vector<Input>& thirdPartyRootInputs,
74 const Vector<Input>& thirdPartyIntermediateInputs,
75 const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
76 /*out*/ UniqueCERTCertList& builtChain,
77 /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
78 /*optional*/ const char* hostname)
79 : mCertDBTrustType(certDBTrustType),
80 mOCSPFetching(ocspFetching),
81 mOCSPCache(ocspCache),
82 mPinArg(pinArg),
83 mOCSPTimeoutSoft(ocspTimeoutSoft),
84 mOCSPTimeoutHard(ocspTimeoutHard),
85 mCertShortLifetimeInDays(certShortLifetimeInDays),
86 mMinRSABits(minRSABits),
87 mValidityCheckingMode(validityCheckingMode),
88 mSHA1Mode(sha1Mode),
89 mNetscapeStepUpPolicy(netscapeStepUpPolicy),
90 mCRLiteMode(crliteMode),
91 mCRLiteCTMergeDelaySeconds(crliteCTMergeDelaySeconds),
92 mSawDistrustedCAByPolicyError(false),
93 mOriginAttributes(originAttributes),
94 mThirdPartyRootInputs(thirdPartyRootInputs),
95 mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs),
96 mExtraCertificates(extraCertificates),
97 mBuiltChain(builtChain),
98 mPinningTelemetryInfo(pinningTelemetryInfo),
99 mHostname(hostname),
100 mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
101 mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED),
102 mSCTListFromCertificate(),
103 mSCTListFromOCSPStapling(),
104 mBuiltInRootsModule(SECMOD_FindModule(kRootModuleName)) {}
105
FindRootsWithSubject(UniqueSECMODModule & rootsModule,SECItem subject,nsTArray<nsTArray<uint8_t>> & roots)106 static void FindRootsWithSubject(UniqueSECMODModule& rootsModule,
107 SECItem subject,
108 /*out*/ nsTArray<nsTArray<uint8_t>>& roots) {
109 MOZ_ASSERT(rootsModule);
110 for (int slotIndex = 0; slotIndex < rootsModule->slotCount; slotIndex++) {
111 CERTCertificateList* rawResults = nullptr;
112 if (PK11_FindRawCertsWithSubject(rootsModule->slots[slotIndex], &subject,
113 &rawResults) != SECSuccess) {
114 continue;
115 }
116 // rawResults == nullptr means we didn't find any matching certificates
117 if (!rawResults) {
118 continue;
119 }
120 UniqueCERTCertificateList results(rawResults);
121 for (int certIndex = 0; certIndex < results->len; certIndex++) {
122 nsTArray<uint8_t> root;
123 root.AppendElements(results->certs[certIndex].data,
124 results->certs[certIndex].len);
125 roots.AppendElement(std::move(root));
126 }
127 }
128 }
129
130 // A self-signed issuer certificate should never be necessary in order to build
131 // a trusted certificate chain unless it is a trust anchor. This is because if
132 // it were necessary, there would exist another certificate with the same
133 // subject and public key that is also a valid issing certificate. Given this
134 // certificate, it is possible to build another chain using just it instead of
135 // it and the self-signed certificate. This is only true as long as the
136 // certificate extensions we support are restrictive rather than additive in
137 // terms of the rest of the chain (for example, we don't support policy mapping
138 // and we ignore any SCT information in intermediates).
ShouldSkipSelfSignedNonTrustAnchor(TrustDomain & trustDomain,Input certDER)139 static bool ShouldSkipSelfSignedNonTrustAnchor(TrustDomain& trustDomain,
140 Input certDER) {
141 BackCert cert(certDER, EndEntityOrCA::MustBeCA, nullptr);
142 if (cert.Init() != Success) {
143 return false; // turn any failures into "don't skip trying this cert"
144 }
145 // If subject != issuer, this isn't a self-signed cert.
146 if (!InputsAreEqual(cert.GetSubject(), cert.GetIssuer())) {
147 return false;
148 }
149 TrustLevel trust;
150 if (trustDomain.GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy,
151 certDER, trust) != Success) {
152 return false;
153 }
154 // If the trust for this certificate is anything other than "inherit", we want
155 // to process it like normal.
156 if (trust != TrustLevel::InheritsTrust) {
157 return false;
158 }
159 uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES];
160 pkix::der::PublicKeyAlgorithm publicKeyAlg;
161 SignedDigest signature;
162 if (DigestSignedData(trustDomain, cert.GetSignedData(), digestBuf,
163 publicKeyAlg, signature) != Success) {
164 return false;
165 }
166 if (VerifySignedDigest(trustDomain, publicKeyAlg, signature,
167 cert.GetSubjectPublicKeyInfo()) != Success) {
168 return false;
169 }
170 // This is a self-signed, non-trust-anchor certificate, so we shouldn't use it
171 // for path building. See bug 1056341.
172 return true;
173 }
174
CheckCandidates(TrustDomain & trustDomain,TrustDomain::IssuerChecker & checker,nsTArray<Input> & candidates,Input * nameConstraintsInputPtr,bool & keepGoing)175 static Result CheckCandidates(TrustDomain& trustDomain,
176 TrustDomain::IssuerChecker& checker,
177 nsTArray<Input>& candidates,
178 Input* nameConstraintsInputPtr, bool& keepGoing) {
179 for (Input candidate : candidates) {
180 if (ShouldSkipSelfSignedNonTrustAnchor(trustDomain, candidate)) {
181 continue;
182 }
183 Result rv = checker.Check(candidate, nameConstraintsInputPtr, keepGoing);
184 if (rv != Success) {
185 return rv;
186 }
187 if (!keepGoing) {
188 return Success;
189 }
190 }
191
192 return Success;
193 }
194
FindIssuer(Input encodedIssuerName,IssuerChecker & checker,Time)195 Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
196 IssuerChecker& checker, Time) {
197 SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
198 // Handle imposed name constraints, if any.
199 ScopedAutoSECItem nameConstraints;
200 Input nameConstraintsInput;
201 Input* nameConstraintsInputPtr = nullptr;
202 SECStatus srv =
203 CERT_GetImposedNameConstraints(&encodedIssuerNameItem, &nameConstraints);
204 if (srv == SECSuccess) {
205 if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len) !=
206 Success) {
207 return Result::FATAL_ERROR_LIBRARY_FAILURE;
208 }
209 nameConstraintsInputPtr = &nameConstraintsInput;
210 } else if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
211 return Result::FATAL_ERROR_LIBRARY_FAILURE;
212 }
213
214 // First try all relevant certificates known to Gecko, which avoids calling
215 // CERT_CreateSubjectCertList, because that can be expensive.
216 nsTArray<Input> geckoRootCandidates;
217 nsTArray<Input> geckoIntermediateCandidates;
218
219 if (!mCertStorage) {
220 return Result::FATAL_ERROR_LIBRARY_FAILURE;
221 }
222 nsTArray<uint8_t> subject;
223 subject.AppendElements(encodedIssuerName.UnsafeGetData(),
224 encodedIssuerName.GetLength());
225 nsTArray<nsTArray<uint8_t>> certs;
226 nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
227 if (NS_FAILED(rv)) {
228 return Result::FATAL_ERROR_LIBRARY_FAILURE;
229 }
230 for (auto& cert : certs) {
231 Input certDER;
232 Result rv = certDER.Init(cert.Elements(), cert.Length());
233 if (rv != Success) {
234 continue; // probably too big
235 }
236 // Currently we're only expecting intermediate certificates in cert storage.
237 geckoIntermediateCandidates.AppendElement(std::move(certDER));
238 }
239
240 // We might not have this module if e.g. we're on a Linux distribution that
241 // does something unexpected.
242 nsTArray<nsTArray<uint8_t>> builtInRoots;
243 if (mBuiltInRootsModule) {
244 FindRootsWithSubject(mBuiltInRootsModule, encodedIssuerNameItem,
245 builtInRoots);
246 for (const auto& root : builtInRoots) {
247 Input rootInput;
248 Result rv = rootInput.Init(root.Elements(), root.Length());
249 if (rv != Success) {
250 continue; // probably too big
251 }
252 geckoRootCandidates.AppendElement(rootInput);
253 }
254 } else {
255 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
256 ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module"));
257 }
258
259 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
260 BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr);
261 Result rv = root.Init();
262 if (rv != Success) {
263 continue;
264 }
265 // Filter out 3rd party roots that can't be issuers we're looking for
266 // because the subject distinguished name doesn't match. This prevents
267 // mozilla::pkix from accumulating spurious errors during path building.
268 if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) {
269 continue;
270 }
271 geckoRootCandidates.AppendElement(thirdPartyRootInput);
272 }
273
274 for (const auto& thirdPartyIntermediateInput :
275 mThirdPartyIntermediateInputs) {
276 BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA,
277 nullptr);
278 Result rv = intermediate.Init();
279 if (rv != Success) {
280 continue;
281 }
282 // Filter out 3rd party intermediates that can't be issuers we're looking
283 // for because the subject distinguished name doesn't match. This prevents
284 // mozilla::pkix from accumulating spurious errors during path building.
285 if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) {
286 continue;
287 }
288 geckoIntermediateCandidates.AppendElement(thirdPartyIntermediateInput);
289 }
290
291 if (mExtraCertificates.isSome()) {
292 for (const auto& extraCert : *mExtraCertificates) {
293 Input certInput;
294 Result rv = certInput.Init(extraCert.Elements(), extraCert.Length());
295 if (rv != Success) {
296 continue;
297 }
298 BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr);
299 rv = cert.Init();
300 if (rv != Success) {
301 continue;
302 }
303 // Filter out certificates that can't be issuers we're looking for because
304 // the subject distinguished name doesn't match. This prevents
305 // mozilla::pkix from accumulating spurious errors during path building.
306 if (!InputsAreEqual(encodedIssuerName, cert.GetSubject())) {
307 continue;
308 }
309 // We assume that extra certificates (presumably from the TLS handshake)
310 // are intermediates, since sending trust anchors would be superfluous.
311 geckoIntermediateCandidates.AppendElement(certInput);
312 }
313 }
314
315 // Try all root certs first and then all (presumably) intermediates.
316 geckoRootCandidates.AppendElements(std::move(geckoIntermediateCandidates));
317
318 bool keepGoing = true;
319 Result result = CheckCandidates(*this, checker, geckoRootCandidates,
320 nameConstraintsInputPtr, keepGoing);
321 if (result != Success) {
322 return result;
323 }
324 if (!keepGoing) {
325 return Success;
326 }
327
328 nsTArray<nsTArray<uint8_t>> nssRootCandidates;
329 nsTArray<nsTArray<uint8_t>> nssIntermediateCandidates;
330 // NSS seems not to differentiate between "no potential issuers found"
331 // and "there was an error trying to retrieve the potential issuers." We
332 // assume there was no error if CERT_CreateSubjectCertList returns
333 // nullptr.
334 UniqueCERTCertList candidates(CERT_CreateSubjectCertList(
335 nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameItem, 0, false));
336 if (candidates) {
337 for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
338 !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
339 nsTArray<uint8_t> candidate;
340 candidate.AppendElements(n->cert->derCert.data, n->cert->derCert.len);
341 if (n->cert->isRoot) {
342 nssRootCandidates.AppendElement(std::move(candidate));
343 } else {
344 nssIntermediateCandidates.AppendElement(std::move(candidate));
345 }
346 }
347 }
348
349 nsTArray<Input> nssCandidates;
350 for (const auto& rootCandidate : nssRootCandidates) {
351 Input certDER;
352 Result rv = certDER.Init(rootCandidate.Elements(), rootCandidate.Length());
353 if (rv != Success) {
354 continue; // probably too big
355 }
356 nssCandidates.AppendElement(std::move(certDER));
357 }
358 for (const auto& intermediateCandidate : nssIntermediateCandidates) {
359 Input certDER;
360 Result rv = certDER.Init(intermediateCandidate.Elements(),
361 intermediateCandidate.Length());
362 if (rv != Success) {
363 continue; // probably too big
364 }
365 nssCandidates.AppendElement(std::move(certDER));
366 }
367
368 return CheckCandidates(*this, checker, nssCandidates, nameConstraintsInputPtr,
369 keepGoing);
370 }
371
GetCertTrust(EndEntityOrCA endEntityOrCA,const CertPolicyId & policy,Input candidateCertDER,TrustLevel & trustLevel)372 Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
373 const CertPolicyId& policy,
374 Input candidateCertDER,
375 /*out*/ TrustLevel& trustLevel) {
376 // Check the certificate against the OneCRL cert blocklist
377 if (!mCertStorage) {
378 return Result::FATAL_ERROR_LIBRARY_FAILURE;
379 }
380
381 // The certificate blocklist currently only applies to TLS server
382 // certificates.
383 if (mCertDBTrustType == trustSSL) {
384 int16_t revocationState;
385
386 nsTArray<uint8_t> issuerBytes;
387 nsTArray<uint8_t> serialBytes;
388 nsTArray<uint8_t> subjectBytes;
389 nsTArray<uint8_t> pubKeyBytes;
390
391 Result result =
392 BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes,
393 serialBytes, subjectBytes, pubKeyBytes);
394 if (result != Success) {
395 return result;
396 }
397
398 nsresult nsrv = mCertStorage->GetRevocationState(
399 issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState);
400 if (NS_FAILED(nsrv)) {
401 return Result::FATAL_ERROR_LIBRARY_FAILURE;
402 }
403
404 if (revocationState == nsICertStorage::STATE_ENFORCE) {
405 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
406 ("NSSCertDBTrustDomain: certificate is in blocklist"));
407 return Result::ERROR_REVOKED_CERTIFICATE;
408 }
409 }
410
411 // This may be a third-party root.
412 for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) {
413 if (InputsAreEqual(candidateCertDER, thirdPartyRootInput)) {
414 trustLevel = TrustLevel::TrustAnchor;
415 return Success;
416 }
417 }
418
419 // This may be a third-party intermediate.
420 for (const auto& thirdPartyIntermediateInput :
421 mThirdPartyIntermediateInputs) {
422 if (InputsAreEqual(candidateCertDER, thirdPartyIntermediateInput)) {
423 trustLevel = TrustLevel::InheritsTrust;
424 return Success;
425 }
426 }
427
428 // This would be cleaner and more efficient if we could get the trust
429 // information without constructing a CERTCertificate here, but NSS
430 // doesn't expose it in any other easy-to-use fashion. The use of
431 // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
432 // performance problem for certificates already known to NSS because NSS
433 // will just find the existing CERTCertificate in its in-memory cache
434 // and return it. For certificates not already in NSS (namely
435 // third-party roots and intermediates), we want to avoid calling
436 // CERT_NewTempCertificate repeatedly, so we've already checked if the
437 // candidate certificate is a third-party certificate, above.
438 SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
439 UniqueCERTCertificate candidateCert(CERT_NewTempCertificate(
440 CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, true));
441 if (!candidateCert) {
442 return MapPRErrorCodeToResult(PR_GetError());
443 }
444 // NB: CERT_GetCertTrust seems to be abusing SECStatus as a boolean,
445 // where SECSuccess means that there is a trust record and SECFailure
446 // means there is not a trust record. I looked at NSS's internal uses of
447 // CERT_GetCertTrust, and all that code uses the result as a boolean
448 // meaning "We have a trust record."
449 CERTCertTrust trust;
450 if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
451 uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
452
453 // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
454 // because we can have active distrust for either type of cert. Note
455 // that CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so
456 // if the relevant trust bit isn't set then that means the cert must
457 // be considered distrusted.
458 uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
459 ? CERTDB_TRUSTED_CA
460 : CERTDB_TRUSTED;
461 if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) ==
462 CERTDB_TERMINAL_RECORD) {
463 trustLevel = TrustLevel::ActivelyDistrusted;
464 return Success;
465 }
466
467 // For TRUST, we use the CERTDB_TRUSTED_CA bit.
468 if (flags & CERTDB_TRUSTED_CA) {
469 if (policy.IsAnyPolicy()) {
470 trustLevel = TrustLevel::TrustAnchor;
471 return Success;
472 }
473
474 nsTArray<uint8_t> certBytes(candidateCert->derCert.data,
475 candidateCert->derCert.len);
476 if (CertIsAuthoritativeForEVPolicy(certBytes, policy)) {
477 trustLevel = TrustLevel::TrustAnchor;
478 return Success;
479 }
480 }
481 }
482 trustLevel = TrustLevel::InheritsTrust;
483 return Success;
484 }
485
DigestBuf(Input item,DigestAlgorithm digestAlg,uint8_t * digestBuf,size_t digestBufLen)486 Result NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
487 /*out*/ uint8_t* digestBuf,
488 size_t digestBufLen) {
489 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
490 }
491
GetOCSPTimeout() const492 TimeDuration NSSCertDBTrustDomain::GetOCSPTimeout() const {
493 switch (mOCSPFetching) {
494 case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
495 return mOCSPTimeoutSoft;
496 case NSSCertDBTrustDomain::FetchOCSPForEV:
497 case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
498 return mOCSPTimeoutHard;
499 // The rest of these are error cases. Assert in debug builds, but return
500 // the soft timeout value in release builds.
501 case NSSCertDBTrustDomain::NeverFetchOCSP:
502 case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
503 MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
504 break;
505 }
506
507 MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
508 return mOCSPTimeoutSoft;
509 }
510
511 // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
512 // CERT_GetGeneralNameByType. Returns a non-Result::Success result on error,
513 // Success with result.IsVoid() == true when an OCSP URI was not found, and
514 // Success with result.IsVoid() == false when an OCSP URI was found.
GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool & arena,Input aiaExtension,nsCString & result)515 static Result GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena,
516 Input aiaExtension,
517 /*out*/ nsCString& result) {
518 MOZ_ASSERT(arena.get());
519 if (!arena.get()) {
520 return Result::FATAL_ERROR_INVALID_ARGS;
521 }
522
523 result.Assign(VoidCString());
524 SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension);
525 CERTAuthInfoAccess** aia =
526 CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem);
527 if (!aia) {
528 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
529 }
530 for (size_t i = 0; aia[i]; ++i) {
531 if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
532 // NSS chooses the **last** OCSP URL; we choose the **first**
533 CERTGeneralName* current = aia[i]->location;
534 if (!current) {
535 continue;
536 }
537 do {
538 if (current->type == certURI) {
539 const SECItem& location = current->name.other;
540 // (location.len + 1) must be small enough to fit into a uint32_t,
541 // but we limit it to a smaller bound to reduce OOM risk.
542 if (location.len > 1024 || memchr(location.data, 0, location.len)) {
543 // Reject embedded nulls. (NSS doesn't do this)
544 return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
545 }
546 result.Assign(nsDependentCSubstring(
547 reinterpret_cast<const char*>(location.data), location.len));
548 return Success;
549 }
550 current = CERT_GetNextGeneralName(current);
551 } while (current != aia[i]->location);
552 }
553 }
554
555 return Success;
556 }
557
GetEarliestSCTTimestamp(Input sctExtension,Maybe<uint64_t> & earliestTimestamp)558 Result GetEarliestSCTTimestamp(Input sctExtension,
559 Maybe<uint64_t>& earliestTimestamp) {
560 earliestTimestamp.reset();
561
562 Input sctList;
563 Result rv =
564 ExtractSignedCertificateTimestampListFromExtension(sctExtension, sctList);
565 if (rv != Success) {
566 return rv;
567 }
568 std::vector<SignedCertificateTimestamp> decodedSCTs;
569 size_t decodingErrors;
570 DecodeSCTs(sctList, decodedSCTs, decodingErrors);
571 Unused << decodingErrors;
572 for (const auto& scts : decodedSCTs) {
573 if (!earliestTimestamp.isSome() || scts.timestamp < *earliestTimestamp) {
574 earliestTimestamp = Some(scts.timestamp);
575 }
576 }
577 return Success;
578 }
579
CheckRevocation(EndEntityOrCA endEntityOrCA,const CertID & certID,Time time,Duration validityDuration,const Input * stapledOCSPResponse,const Input * aiaExtension,const Input * sctExtension)580 Result NSSCertDBTrustDomain::CheckRevocation(
581 EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
582 Duration validityDuration,
583 /*optional*/ const Input* stapledOCSPResponse,
584 /*optional*/ const Input* aiaExtension,
585 /*optional*/ const Input* sctExtension) {
586 // Actively distrusted certificates will have already been blocked by
587 // GetCertTrust.
588
589 // TODO: need to verify that IsRevoked isn't called for trust anchors AND
590 // that that fact is documented in mozillapkix.
591
592 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
593 ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
594
595 Maybe<uint64_t> earliestSCTTimestamp = Nothing();
596 if (sctExtension) {
597 Result rv = GetEarliestSCTTimestamp(*sctExtension, earliestSCTTimestamp);
598 if (rv != Success) {
599 MOZ_LOG(
600 gCertVerifierLog, LogLevel::Debug,
601 ("decoding SCT extension failed - CRLite will be not be consulted"));
602 }
603 }
604
605 if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
606 mCRLiteMode != CRLiteMode::Disabled && earliestSCTTimestamp.isSome()) {
607 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
608 ("NSSCertDBTrustDomain::CheckRevocation: checking CRLite"));
609 nsTArray<uint8_t> issuerBytes;
610 issuerBytes.AppendElements(certID.issuer.UnsafeGetData(),
611 certID.issuer.GetLength());
612 nsTArray<uint8_t> issuerSubjectPublicKeyInfoBytes;
613 issuerSubjectPublicKeyInfoBytes.AppendElements(
614 certID.issuerSubjectPublicKeyInfo.UnsafeGetData(),
615 certID.issuerSubjectPublicKeyInfo.GetLength());
616 nsTArray<uint8_t> serialNumberBytes;
617 serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(),
618 certID.serialNumber.GetLength());
619 uint64_t filterTimestamp;
620 int16_t crliteRevocationState;
621 nsresult rv = mCertStorage->GetCRLiteRevocationState(
622 issuerBytes, issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
623 &filterTimestamp, &crliteRevocationState);
624 bool certificateFoundValidInCRLiteFilter = false;
625 if (NS_FAILED(rv)) {
626 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
627 ("NSSCertDBTrustDomain::CheckRevocation: CRLite call failed"));
628 if (mCRLiteMode == CRLiteMode::Enforce) {
629 return Result::FATAL_ERROR_LIBRARY_FAILURE;
630 }
631 } else {
632 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
633 ("NSSCertDBTrustDomain::CheckRevocation: CRLite check returned "
634 "state=%hd filter timestamp=%llu",
635 crliteRevocationState,
636 // The cast is to silence warnings on compilers where uint64_t is
637 // an unsigned long as opposed to an unsigned long long.
638 static_cast<unsigned long long>(filterTimestamp)));
639 Time filterTimestampTime(TimeFromEpochInSeconds(filterTimestamp));
640 // We can only use this result if the earliest embedded signed
641 // certificate timestamp from the certificate is older than what cert
642 // storage returned for its CRLite timestamp. Otherwise, the CRLite
643 // filter cascade may have been created before this certificate existed,
644 // and if it would create a false positive, it hasn't been accounted for.
645 // SCT timestamps are milliseconds since the epoch.
646 Time earliestCertificateTimestamp(
647 TimeFromEpochInSeconds(*earliestSCTTimestamp / 1000));
648 Result result =
649 earliestCertificateTimestamp.AddSeconds(mCRLiteCTMergeDelaySeconds);
650 if (result != Success) {
651 // This shouldn't happen - the merge delay is at most a year in seconds,
652 // and the SCT timestamp is supposed to be in the past.
653 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
654 ("NSSCertDBTrustDomain::CheckRevocation: integer overflow "
655 "calculating sct timestamp + merge delay (%llu + %llu)",
656 static_cast<unsigned long long>(*earliestSCTTimestamp / 1000),
657 static_cast<unsigned long long>(mCRLiteCTMergeDelaySeconds)));
658 if (mCRLiteMode == CRLiteMode::Enforce) {
659 // While we do have control over the possible values of the CT merge
660 // delay parameter, we don't have control over the SCT timestamp.
661 // Thus, if we've reached this point, the CA has probably made a
662 // mistake and we should treat this certificate as revoked.
663 return Result::ERROR_REVOKED_CERTIFICATE;
664 }
665 // If Time::AddSeconds fails, the original value is unchanged. Since in
666 // this case `earliestCertificateTimestamp` must represent a value far
667 // in the future, any CRLite result will be discarded.
668 }
669 if (earliestCertificateTimestamp <= filterTimestampTime &&
670 crliteRevocationState == nsICertStorage::STATE_ENFORCE) {
671 if (mCRLiteMode == CRLiteMode::Enforce) {
672 MOZ_LOG(
673 gCertVerifierLog, LogLevel::Debug,
674 ("NSSCertDBTrustDomain::CheckRevocation: certificate revoked via "
675 "CRLite"));
676 return Result::ERROR_REVOKED_CERTIFICATE;
677 }
678 MOZ_LOG(
679 gCertVerifierLog, LogLevel::Debug,
680 ("NSSCertDBTrustDomain::CheckRevocation: certificate revoked via "
681 "CRLite (not enforced - telemetry only)"));
682 }
683
684 if (crliteRevocationState == nsICertStorage::STATE_NOT_ENROLLED) {
685 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
686 ("NSSCertDBTrustDomain::CheckRevocation: issuer not enrolled"));
687 }
688 if (filterTimestamp == 0) {
689 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
690 ("NSSCertDBTrustDomain::CheckRevocation: no timestamp"));
691 } else if (earliestCertificateTimestamp > filterTimestampTime) {
692 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
693 ("NSSCertDBTrustDomain::CheckRevocation: cert too new"));
694 } else if (crliteRevocationState == nsICertStorage::STATE_UNSET) {
695 certificateFoundValidInCRLiteFilter = true;
696 }
697 }
698
699 // Also check stashed CRLite revocations. This information is
700 // deterministic and has already been validated by our infrastructure (it
701 // comes from signed CRLs), so if the stash says a certificate is revoked,
702 // it is.
703 bool isRevokedByStash = false;
704 rv = mCertStorage->IsCertRevokedByStash(
705 issuerSubjectPublicKeyInfoBytes, serialNumberBytes, &isRevokedByStash);
706 if (NS_FAILED(rv)) {
707 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
708 ("NSSCertDBTrustDomain::CheckRevocation: IsCertRevokedByStash "
709 "failed"));
710 if (mCRLiteMode == CRLiteMode::Enforce) {
711 return Result::FATAL_ERROR_LIBRARY_FAILURE;
712 }
713 } else if (isRevokedByStash) {
714 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
715 ("NSSCertDBTrustDomain::CheckRevocation: IsCertRevokedByStash "
716 "returned true"));
717 if (mCRLiteMode == CRLiteMode::Enforce) {
718 return Result::ERROR_REVOKED_CERTIFICATE;
719 }
720 } else if (certificateFoundValidInCRLiteFilter &&
721 mCRLiteMode == CRLiteMode::Enforce) {
722 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
723 ("NSSCertDBTrustDomain::CheckRevocation: certificate covered by "
724 "CRLite, found to be valid -> skipping OCSP processing"));
725 return Success;
726 }
727 }
728
729 // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
730 // Since this affects EV there is no reason why DV should be more strict
731 // so all intermediates are allowed to have OCSP responses up to one year
732 // old.
733 uint16_t maxOCSPLifetimeInDays = 10;
734 if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
735 maxOCSPLifetimeInDays = 365;
736 }
737
738 // If we have a stapled OCSP response then the verification of that response
739 // determines the result unless the OCSP response is expired. We make an
740 // exception for expired responses because some servers, nginx in particular,
741 // are known to serve expired responses due to bugs.
742 // We keep track of the result of verifying the stapled response but don't
743 // immediately return failure if the response has expired.
744 //
745 // We only set the OCSP stapling status if we're validating the end-entity
746 // certificate. Non-end-entity certificates would always be
747 // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
748 Result stapledOCSPResponseResult = Success;
749 if (stapledOCSPResponse) {
750 MOZ_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
751 bool expired;
752 stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse(
753 certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse,
754 ResponseWasStapled, expired);
755 if (stapledOCSPResponseResult == Success) {
756 // stapled OCSP response present and good
757 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
758 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
759 ("NSSCertDBTrustDomain: stapled OCSP response: good"));
760 return Success;
761 }
762 if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
763 expired) {
764 // stapled OCSP response present but expired
765 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
766 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
767 ("NSSCertDBTrustDomain: expired stapled OCSP response"));
768 } else if (stapledOCSPResponseResult ==
769 Result::ERROR_OCSP_TRY_SERVER_LATER ||
770 stapledOCSPResponseResult ==
771 Result::ERROR_OCSP_INVALID_SIGNING_CERT) {
772 // Stapled OCSP response present but invalid for a small number of reasons
773 // CAs/servers commonly get wrong. This will be treated similarly to an
774 // expired stapled response.
775 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
776 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
777 ("NSSCertDBTrustDomain: stapled OCSP response: "
778 "failure (allowed for compatibility)"));
779 } else {
780 // stapled OCSP response present but invalid for some reason
781 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
782 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
783 ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
784 return stapledOCSPResponseResult;
785 }
786 } else if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
787 // no stapled OCSP response
788 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
789 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
790 ("NSSCertDBTrustDomain: no stapled OCSP response"));
791 }
792
793 Result cachedResponseResult = Success;
794 Time cachedResponseValidThrough(Time::uninitialized);
795 bool cachedResponsePresent =
796 mOCSPCache.Get(certID, mOriginAttributes, cachedResponseResult,
797 cachedResponseValidThrough);
798 if (cachedResponsePresent) {
799 if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
800 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
801 ("NSSCertDBTrustDomain: cached OCSP response: good"));
802 return Success;
803 }
804 // If we have a cached revoked response, use it.
805 if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
806 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
807 ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
808 return Result::ERROR_REVOKED_CERTIFICATE;
809 }
810 // The cached response may indicate an unknown certificate or it may be
811 // expired. Don't return with either of these statuses yet - we may be
812 // able to fetch a more recent one.
813 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
814 ("NSSCertDBTrustDomain: cached OCSP response: error %d",
815 static_cast<int>(cachedResponseResult)));
816 // When a good cached response has expired, it is more convenient
817 // to convert that to an error code and just deal with
818 // cachedResponseResult from here on out.
819 if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
820 cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
821 }
822 // We may have a cached indication of server failure. Ignore it if
823 // it has expired.
824 if (cachedResponseResult != Success &&
825 cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
826 cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
827 cachedResponseValidThrough < time) {
828 cachedResponseResult = Success;
829 cachedResponsePresent = false;
830 }
831 } else {
832 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
833 ("NSSCertDBTrustDomain: no cached OCSP response"));
834 }
835 // At this point, if and only if cachedErrorResult is Success, there was no
836 // cached response.
837 MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
838 (cachedResponsePresent && cachedResponseResult != Success));
839
840 // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
841 bool blocklistIsFresh;
842 nsresult nsrv = mCertStorage->IsBlocklistFresh(&blocklistIsFresh);
843 if (NS_FAILED(nsrv)) {
844 return Result::FATAL_ERROR_LIBRARY_FAILURE;
845 }
846
847 // TODO: We still need to handle the fallback for invalid stapled responses.
848 // But, if/when we disable OCSP fetching by default, it would be ambiguous
849 // whether security.OCSP.enable==0 means "I want the default" or "I really
850 // never want you to ever fetch OCSP."
851 // Additionally, this doesn't properly handle OCSP-must-staple when OCSP
852 // fetching is disabled.
853 Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS);
854 if ((mOCSPFetching == NeverFetchOCSP) || (validityDuration < shortLifetime) ||
855 (endEntityOrCA == EndEntityOrCA::MustBeCA &&
856 (mOCSPFetching == FetchOCSPForDVHardFail ||
857 mOCSPFetching == FetchOCSPForDVSoftFail || blocklistIsFresh))) {
858 // We're not going to be doing any fetching, so if there was a cached
859 // "unknown" response, say so.
860 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
861 return Result::ERROR_OCSP_UNKNOWN_CERT;
862 }
863 // If we're doing hard-fail, we want to know if we have a cached response
864 // that has expired.
865 if (mOCSPFetching == FetchOCSPForDVHardFail &&
866 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
867 return Result::ERROR_OCSP_OLD_RESPONSE;
868 }
869
870 return Success;
871 }
872
873 if (mOCSPFetching == LocalOnlyOCSPForEV) {
874 if (cachedResponseResult != Success) {
875 return cachedResponseResult;
876 }
877 return Result::ERROR_OCSP_UNKNOWN_CERT;
878 }
879
880 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
881 if (!arena) {
882 return Result::FATAL_ERROR_NO_MEMORY;
883 }
884
885 Result rv;
886 nsCString aiaLocation(VoidCString());
887
888 if (aiaExtension) {
889 rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation);
890 if (rv != Success) {
891 return rv;
892 }
893 }
894
895 if (aiaLocation.IsVoid()) {
896 if (mOCSPFetching == FetchOCSPForEV ||
897 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
898 return Result::ERROR_OCSP_UNKNOWN_CERT;
899 }
900 if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
901 return Result::ERROR_OCSP_OLD_RESPONSE;
902 }
903 if (stapledOCSPResponseResult != Success) {
904 return stapledOCSPResponseResult;
905 }
906
907 // Nothing to do if we don't have an OCSP responder URI for the cert; just
908 // assume it is good. Note that this is the confusing, but intended,
909 // interpretation of "strict" revocation checking in the face of a
910 // certificate that lacks an OCSP responder URI.
911 return Success;
912 }
913
914 if (cachedResponseResult == Success ||
915 cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
916 cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
917 // Only send a request to, and process a response from, the server if we
918 // didn't have a cached indication of failure. Also, don't keep requesting
919 // responses from a failing server.
920 return SynchronousCheckRevocationWithServer(
921 certID, aiaLocation, time, maxOCSPLifetimeInDays, cachedResponseResult,
922 stapledOCSPResponseResult);
923 }
924
925 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
926 cachedResponseResult);
927 }
928
SynchronousCheckRevocationWithServer(const CertID & certID,const nsCString & aiaLocation,Time time,uint16_t maxOCSPLifetimeInDays,const Result cachedResponseResult,const Result stapledOCSPResponseResult)929 Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
930 const CertID& certID, const nsCString& aiaLocation, Time time,
931 uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult,
932 const Result stapledOCSPResponseResult) {
933 uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
934 size_t ocspRequestLength;
935
936 Result rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
937 ocspRequestLength);
938 if (rv != Success) {
939 return rv;
940 }
941
942 Vector<uint8_t> ocspResponse;
943 Input response;
944 rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes,
945 ocspRequestLength, GetOCSPTimeout(), ocspResponse);
946 if (rv == Success &&
947 response.Init(ocspResponse.begin(), ocspResponse.length()) != Success) {
948 rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big
949 }
950
951 if (rv != Success) {
952 Time timeout(time);
953 if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
954 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
955 }
956
957 Result cacheRV =
958 mOCSPCache.Put(certID, mOriginAttributes, rv, time, timeout);
959 if (cacheRV != Success) {
960 return cacheRV;
961 }
962
963 return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
964 rv);
965 }
966
967 // If the response from the network has expired but indicates a revoked
968 // or unknown certificate, PR_GetError() will return the appropriate error.
969 // We actually ignore expired here.
970 bool expired;
971 rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
972 maxOCSPLifetimeInDays, response,
973 ResponseIsFromNetwork, expired);
974 if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
975 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
976 ("NSSCertDBTrustDomain: returning after "
977 "VerifyEncodedOCSPResponse"));
978 return rv;
979 }
980
981 if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
982 rv == Result::ERROR_REVOKED_CERTIFICATE) {
983 return rv;
984 }
985
986 if (stapledOCSPResponseResult != Success) {
987 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
988 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
989 "stapled response after OCSP request verification failure"));
990 return stapledOCSPResponseResult;
991 }
992
993 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
994 ("NSSCertDBTrustDomain: end of CheckRevocation"));
995
996 return Success; // Soft fail -> success :(
997 }
998
HandleOCSPFailure(const Result cachedResponseResult,const Result stapledOCSPResponseResult,const Result error)999 Result NSSCertDBTrustDomain::HandleOCSPFailure(
1000 const Result cachedResponseResult, const Result stapledOCSPResponseResult,
1001 const Result error) {
1002 if (mOCSPFetching != FetchOCSPForDVSoftFail) {
1003 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1004 ("NSSCertDBTrustDomain: returning SECFailure after OCSP request "
1005 "failure"));
1006 return error;
1007 }
1008
1009 if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
1010 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1011 ("NSSCertDBTrustDomain: returning SECFailure from cached response "
1012 "after OCSP request failure"));
1013 return cachedResponseResult;
1014 }
1015
1016 if (stapledOCSPResponseResult != Success) {
1017 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1018 ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
1019 "stapled response after OCSP request failure"));
1020 return stapledOCSPResponseResult;
1021 }
1022
1023 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1024 ("NSSCertDBTrustDomain: returning SECSuccess after OCSP request "
1025 "failure"));
1026 return Success; // Soft fail -> success :(
1027 }
1028
VerifyAndMaybeCacheEncodedOCSPResponse(const CertID & certID,Time time,uint16_t maxLifetimeInDays,Input encodedResponse,EncodedResponseSource responseSource,bool & expired)1029 Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
1030 const CertID& certID, Time time, uint16_t maxLifetimeInDays,
1031 Input encodedResponse, EncodedResponseSource responseSource,
1032 /*out*/ bool& expired) {
1033 Time thisUpdate(Time::uninitialized);
1034 Time validThrough(Time::uninitialized);
1035
1036 // We use a try and fallback approach which first mandates good signature
1037 // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
1038 // OCSP response signing certificate was issued with a SHA-1 signature,
1039 // verification initially fails. We cache the failure and then re-use that
1040 // result even when doing fallback (i.e. when weak signature digest algorithms
1041 // should succeed). To address this we use an OCSPVerificationTrustDomain
1042 // here, rather than using *this, to ensure verification succeeds for all
1043 // allowed signature digest algorithms.
1044 OCSPVerificationTrustDomain trustDomain(*this);
1045 Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
1046 maxLifetimeInDays, encodedResponse,
1047 expired, &thisUpdate, &validThrough);
1048 // If a response was stapled and expired, we don't want to cache it. Return
1049 // early to simplify the logic here.
1050 if (responseSource == ResponseWasStapled && expired) {
1051 MOZ_ASSERT(rv != Success);
1052 return rv;
1053 }
1054 // validThrough is only trustworthy if the response successfully verifies
1055 // or it indicates a revoked or unknown certificate.
1056 // If this isn't the case, store an indication of failure (to prevent
1057 // repeatedly requesting a response from a failing server).
1058 if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
1059 rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
1060 validThrough = time;
1061 if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
1062 return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
1063 }
1064 }
1065 if (responseSource == ResponseIsFromNetwork || rv == Success ||
1066 rv == Result::ERROR_REVOKED_CERTIFICATE ||
1067 rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
1068 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1069 ("NSSCertDBTrustDomain: caching OCSP response"));
1070 Result putRV =
1071 mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate, validThrough);
1072 if (putRV != Success) {
1073 return putRV;
1074 }
1075 }
1076
1077 return rv;
1078 }
1079
GetCertDistrustAfterValue(const SECItem * distrustItem,PRTime & distrustTime)1080 SECStatus GetCertDistrustAfterValue(const SECItem* distrustItem,
1081 PRTime& distrustTime) {
1082 if (!distrustItem || !distrustItem->data || distrustItem->len != 13) {
1083 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
1084 return SECFailure;
1085 }
1086 return DER_DecodeTimeChoice(&distrustTime, distrustItem);
1087 }
1088
GetCertNotBeforeValue(const CERTCertificate * cert,PRTime & distrustTime)1089 SECStatus GetCertNotBeforeValue(const CERTCertificate* cert,
1090 PRTime& distrustTime) {
1091 return DER_DecodeTimeChoice(&distrustTime, &cert->validity.notBefore);
1092 }
1093
isDistrustedCertificateChain(const UniqueCERTCertList & certList,const SECTrustType certDBTrustType,bool & isDistrusted)1094 nsresult isDistrustedCertificateChain(const UniqueCERTCertList& certList,
1095 const SECTrustType certDBTrustType,
1096 bool& isDistrusted) {
1097 // Set the default result to be distrusted.
1098 isDistrusted = true;
1099
1100 // There is no distrust to set if the certDBTrustType is not SSL or Email.
1101 if (certDBTrustType != trustSSL && certDBTrustType != trustEmail) {
1102 isDistrusted = false;
1103 return NS_OK;
1104 }
1105
1106 // Allocate objects and retreive the root and end-entity certificates.
1107 const CERTCertificate* certRoot = CERT_LIST_TAIL(certList)->cert;
1108 const CERTCertificate* certLeaf = CERT_LIST_HEAD(certList)->cert;
1109
1110 // Set isDistrusted to false if there is no distrust for the root.
1111 if (!certRoot->distrust) {
1112 isDistrusted = false;
1113 return NS_OK;
1114 }
1115
1116 // Create a pointer to refer to the selected distrust struct.
1117 SECItem* distrustPtr = nullptr;
1118 if (certDBTrustType == trustSSL) {
1119 distrustPtr = &certRoot->distrust->serverDistrustAfter;
1120 }
1121 if (certDBTrustType == trustEmail) {
1122 distrustPtr = &certRoot->distrust->emailDistrustAfter;
1123 }
1124
1125 // Get validity for the current end-entity certificate
1126 // and get the distrust field for the root certificate.
1127 PRTime certRootDistrustAfter;
1128 PRTime certLeafNotBefore;
1129
1130 SECStatus rv = GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter);
1131 if (rv != SECSuccess) {
1132 return NS_ERROR_FAILURE;
1133 }
1134
1135 rv = GetCertNotBeforeValue(certLeaf, certLeafNotBefore);
1136 if (rv != SECSuccess) {
1137 return NS_ERROR_FAILURE;
1138 }
1139
1140 // Compare the validity of the end-entity certificate with
1141 // the distrust value of the root.
1142 if (certLeafNotBefore <= certRootDistrustAfter) {
1143 isDistrusted = false;
1144 }
1145
1146 return NS_OK;
1147 }
1148
IsChainValid(const DERArray & certArray,Time time,const CertPolicyId & requiredPolicy)1149 Result NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
1150 const CertPolicyId& requiredPolicy) {
1151 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1152 ("NSSCertDBTrustDomain: IsChainValid"));
1153
1154 UniqueCERTCertList certList;
1155 SECStatus srv =
1156 ConstructCERTCertListFromReversedDERArray(certArray, certList);
1157 if (srv != SECSuccess) {
1158 return MapPRErrorCodeToResult(PR_GetError());
1159 }
1160 if (CERT_LIST_EMPTY(certList)) {
1161 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1162 }
1163
1164 // Modernization in-progress: Keep certList as a CERTCertList for storage into
1165 // the mBuiltChain variable at the end.
1166 nsTArray<RefPtr<nsIX509Cert>> nssCertList;
1167 nsresult nsrv = nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
1168 certList, nssCertList);
1169
1170 if (NS_FAILED(nsrv)) {
1171 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1172 }
1173 nsCOMPtr<nsIX509Cert> rootCert;
1174 nsrv = nsNSSCertificate::GetRootCertificate(nssCertList, rootCert);
1175 if (NS_FAILED(nsrv)) {
1176 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1177 }
1178 UniqueCERTCertificate root(rootCert->GetCert());
1179 if (!root) {
1180 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1181 }
1182 bool isBuiltInRoot = false;
1183 nsrv = rootCert->GetIsBuiltInRoot(&isBuiltInRoot);
1184 if (NS_FAILED(nsrv)) {
1185 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1186 }
1187 // If mHostname isn't set, we're not verifying in the context of a TLS
1188 // handshake, so don't verify key pinning in those cases.
1189 if (mHostname) {
1190 nsTArray<Span<const uint8_t>> derCertSpanList;
1191 size_t numCerts = certArray.GetLength();
1192 for (size_t i = numCerts; i > 0; --i) {
1193 const Input* der = certArray.GetDER(i - 1);
1194 if (!der) {
1195 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1196 }
1197 derCertSpanList.EmplaceBack(der->UnsafeGetData(), der->GetLength());
1198 }
1199
1200 bool chainHasValidPins;
1201 nsrv = PublicKeyPinningService::ChainHasValidPins(
1202 derCertSpanList, mHostname, time, isBuiltInRoot, chainHasValidPins,
1203 mPinningTelemetryInfo);
1204 if (NS_FAILED(nsrv)) {
1205 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1206 }
1207 if (!chainHasValidPins) {
1208 return Result::ERROR_KEY_PINNING_FAILURE;
1209 }
1210 }
1211
1212 // Check that the childs' certificate NotBefore date is anterior to
1213 // the NotAfter value of the parent when the root is a builtin.
1214 if (isBuiltInRoot) {
1215 bool isDistrusted;
1216 nsrv =
1217 isDistrustedCertificateChain(certList, mCertDBTrustType, isDistrusted);
1218 if (NS_FAILED(nsrv)) {
1219 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1220 }
1221 if (isDistrusted) {
1222 return Result::ERROR_UNTRUSTED_ISSUER;
1223 }
1224 }
1225
1226 // See bug 1434300. If the root is a Symantec root, see if we distrust this
1227 // path. Since we already have the root available, we can check that cheaply
1228 // here before proceeding with the rest of the algorithm.
1229
1230 // This algorithm only applies if we are verifying in the context of a TLS
1231 // handshake. To determine this, we check mHostname: If it isn't set, this is
1232 // not TLS, so don't run the algorithm.
1233 nsTArray<uint8_t> rootCertDER(root.get()->derCert.data,
1234 root.get()->derCert.len);
1235 if (mHostname && CertDNIsInList(rootCertDER, RootSymantecDNs)) {
1236 nsTArray<nsTArray<uint8_t>> intCerts;
1237
1238 nsrv = nsNSSCertificate::GetIntermediatesAsDER(nssCertList, intCerts);
1239 if (NS_FAILED(nsrv)) {
1240 // This chain is supposed to be complete, so this is an error.
1241 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1242 }
1243
1244 bool isDistrusted = false;
1245 nsrv = CheckForSymantecDistrust(intCerts, RootAppleAndGoogleSPKIs,
1246 isDistrusted);
1247 if (NS_FAILED(nsrv)) {
1248 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1249 }
1250 if (isDistrusted) {
1251 mSawDistrustedCAByPolicyError = true;
1252 return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
1253 }
1254 }
1255
1256 mBuiltChain = std::move(certList);
1257
1258 return Success;
1259 }
1260
CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,EndEntityOrCA endEntityOrCA,Time notBefore)1261 Result NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(
1262 DigestAlgorithm aAlg, EndEntityOrCA endEntityOrCA, Time notBefore) {
1263 // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
1264 static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
1265
1266 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1267 ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
1268 if (aAlg == DigestAlgorithm::sha1) {
1269 switch (mSHA1Mode) {
1270 case CertVerifier::SHA1Mode::Forbidden:
1271 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1272 ("SHA-1 certificate rejected"));
1273 return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
1274 case CertVerifier::SHA1Mode::ImportedRootOrBefore2016:
1275 if (JANUARY_FIRST_2016 <= notBefore) {
1276 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1277 ("Post-2015 SHA-1 certificate rejected"));
1278 return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
1279 }
1280 break;
1281 case CertVerifier::SHA1Mode::Allowed:
1282 // Enforcing that the resulting chain uses an imported root is only
1283 // possible at a higher level. This is done in CertVerifier::VerifyCert.
1284 case CertVerifier::SHA1Mode::ImportedRoot:
1285 default:
1286 break;
1287 // MSVC warns unless we explicitly handle this now-unused option.
1288 case CertVerifier::SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
1289 MOZ_ASSERT_UNREACHABLE("unexpected SHA1Mode type");
1290 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1291 }
1292 }
1293
1294 return Success;
1295 }
1296
CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,unsigned int modulusSizeInBits)1297 Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
1298 EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
1299 if (modulusSizeInBits < mMinRSABits) {
1300 return Result::ERROR_INADEQUATE_KEY_SIZE;
1301 }
1302 return Success;
1303 }
1304
VerifyRSAPKCS1SignedDigest(const SignedDigest & signedDigest,Input subjectPublicKeyInfo)1305 Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
1306 const SignedDigest& signedDigest, Input subjectPublicKeyInfo) {
1307 return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
1308 mPinArg);
1309 }
1310
CheckECDSACurveIsAcceptable(EndEntityOrCA,NamedCurve curve)1311 Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
1312 EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) {
1313 switch (curve) {
1314 case NamedCurve::secp256r1: // fall through
1315 case NamedCurve::secp384r1: // fall through
1316 case NamedCurve::secp521r1:
1317 return Success;
1318 }
1319
1320 return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
1321 }
1322
VerifyECDSASignedDigest(const SignedDigest & signedDigest,Input subjectPublicKeyInfo)1323 Result NSSCertDBTrustDomain::VerifyECDSASignedDigest(
1324 const SignedDigest& signedDigest, Input subjectPublicKeyInfo) {
1325 return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
1326 mPinArg);
1327 }
1328
CheckValidityIsAcceptable(Time notBefore,Time notAfter,EndEntityOrCA endEntityOrCA,KeyPurposeId keyPurpose)1329 Result NSSCertDBTrustDomain::CheckValidityIsAcceptable(
1330 Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
1331 KeyPurposeId keyPurpose) {
1332 if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) {
1333 return Success;
1334 }
1335 if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) {
1336 return Success;
1337 }
1338
1339 Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) *
1340 Time::ONE_DAY_IN_SECONDS);
1341 Duration maxValidityDuration(UINT64_MAX);
1342 Duration validityDuration(notBefore, notAfter);
1343
1344 switch (mValidityCheckingMode) {
1345 case ValidityCheckingMode::CheckingOff:
1346 return Success;
1347 case ValidityCheckingMode::CheckForEV:
1348 // The EV Guidelines say the maximum is 27 months, but we use a slightly
1349 // higher limit here to (hopefully) minimize compatibility breakage.
1350 maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP;
1351 break;
1352 default:
1353 MOZ_ASSERT_UNREACHABLE(
1354 "We're not handling every ValidityCheckingMode type");
1355 }
1356
1357 if (validityDuration > maxValidityDuration) {
1358 return Result::ERROR_VALIDITY_TOO_LONG;
1359 }
1360
1361 return Success;
1362 }
1363
NetscapeStepUpMatchesServerAuth(Time notBefore,bool & matches)1364 Result NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(
1365 Time notBefore,
1366 /*out*/ bool& matches) {
1367 // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
1368 static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
1369 // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
1370 static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
1371
1372 switch (mNetscapeStepUpPolicy) {
1373 case NetscapeStepUpPolicy::AlwaysMatch:
1374 matches = true;
1375 return Success;
1376 case NetscapeStepUpPolicy::MatchBefore23August2016:
1377 matches = notBefore < AUGUST_23_2016;
1378 return Success;
1379 case NetscapeStepUpPolicy::MatchBefore23August2015:
1380 matches = notBefore < AUGUST_23_2015;
1381 return Success;
1382 case NetscapeStepUpPolicy::NeverMatch:
1383 matches = false;
1384 return Success;
1385 default:
1386 MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
1387 }
1388 return Result::FATAL_ERROR_LIBRARY_FAILURE;
1389 }
1390
ResetAccumulatedState()1391 void NSSCertDBTrustDomain::ResetAccumulatedState() {
1392 mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
1393 mSCTListFromOCSPStapling = nullptr;
1394 mSCTListFromCertificate = nullptr;
1395 mSawDistrustedCAByPolicyError = false;
1396 }
1397
SECItemToInput(const UniqueSECItem & item)1398 static Input SECItemToInput(const UniqueSECItem& item) {
1399 Input result;
1400 if (item) {
1401 MOZ_ASSERT(item->type == siBuffer);
1402 Result rv = result.Init(item->data, item->len);
1403 // As used here, |item| originally comes from an Input,
1404 // so there should be no issues converting it back.
1405 MOZ_ASSERT(rv == Success);
1406 Unused << rv; // suppresses warnings in release builds
1407 }
1408 return result;
1409 }
1410
GetSCTListFromCertificate() const1411 Input NSSCertDBTrustDomain::GetSCTListFromCertificate() const {
1412 return SECItemToInput(mSCTListFromCertificate);
1413 }
1414
GetSCTListFromOCSPStapling() const1415 Input NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const {
1416 return SECItemToInput(mSCTListFromOCSPStapling);
1417 }
1418
GetIsErrorDueToDistrustedCAPolicy() const1419 bool NSSCertDBTrustDomain::GetIsErrorDueToDistrustedCAPolicy() const {
1420 return mSawDistrustedCAByPolicyError;
1421 }
1422
NoteAuxiliaryExtension(AuxiliaryExtension extension,Input extensionData)1423 void NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
1424 Input extensionData) {
1425 UniqueSECItem* out = nullptr;
1426 switch (extension) {
1427 case AuxiliaryExtension::EmbeddedSCTList:
1428 out = &mSCTListFromCertificate;
1429 break;
1430 case AuxiliaryExtension::SCTListFromOCSPResponse:
1431 out = &mSCTListFromOCSPStapling;
1432 break;
1433 default:
1434 MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
1435 }
1436 if (out) {
1437 SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
1438 out->reset(SECITEM_DupItem(&extensionDataItem));
1439 }
1440 }
1441
InitializeNSS(const nsACString & dir,NSSDBConfig nssDbConfig,PKCS11DBConfig pkcs11DbConfig)1442 SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig,
1443 PKCS11DBConfig pkcs11DbConfig) {
1444 MOZ_ASSERT(NS_IsMainThread());
1445
1446 // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
1447 // module by NSS_Initialize because we will load it in LoadLoadableRoots
1448 // later. It also allows us to work around a bug in the system NSS in
1449 // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
1450 // "/usr/lib/nss/libnssckbi.so".
1451 uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
1452 if (nssDbConfig == NSSDBConfig::ReadOnly) {
1453 flags |= NSS_INIT_READONLY;
1454 }
1455 if (pkcs11DbConfig == PKCS11DBConfig::DoNotLoadModules) {
1456 flags |= NSS_INIT_NOMODDB;
1457 }
1458 nsAutoCString dbTypeAndDirectory("sql:");
1459 dbTypeAndDirectory.Append(dir);
1460 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
1461 ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(),
1462 (int)nssDbConfig, (int)pkcs11DbConfig));
1463 SECStatus srv =
1464 NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags);
1465 if (srv != SECSuccess) {
1466 return srv;
1467 }
1468
1469 if (nssDbConfig == NSSDBConfig::ReadWrite) {
1470 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1471 if (!slot) {
1472 return SECFailure;
1473 }
1474 // If the key DB doesn't have a password set, PK11_NeedUserInit will return
1475 // true. For the SQL DB, we need to set a password or we won't be able to
1476 // import any certificates or change trust settings.
1477 if (PK11_NeedUserInit(slot.get())) {
1478 srv = PK11_InitPin(slot.get(), nullptr, nullptr);
1479 MOZ_ASSERT(srv == SECSuccess);
1480 Unused << srv;
1481 }
1482 }
1483
1484 return SECSuccess;
1485 }
1486
DisableMD5()1487 void DisableMD5() {
1488 NSS_SetAlgorithmPolicy(
1489 SEC_OID_MD5, 0,
1490 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1491 NSS_SetAlgorithmPolicy(
1492 SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 0,
1493 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1494 NSS_SetAlgorithmPolicy(
1495 SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC, 0,
1496 NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
1497 }
1498
LoadUserModuleAt(const char * moduleName,const char * libraryName,const nsCString & dir)1499 bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
1500 const nsCString& dir) {
1501 // If a module exists with the same name, make a best effort attempt to delete
1502 // it. Note that it isn't possible to delete the internal module, so checking
1503 // the return value would be detrimental in that case.
1504 int unusedModType;
1505 Unused << SECMOD_DeleteModule(moduleName, &unusedModType);
1506
1507 nsAutoCString fullLibraryPath;
1508 if (!dir.IsEmpty()) {
1509 fullLibraryPath.Assign(dir);
1510 fullLibraryPath.AppendLiteral(FILE_PATH_SEPARATOR);
1511 }
1512 fullLibraryPath.Append(MOZ_DLL_PREFIX);
1513 fullLibraryPath.Append(libraryName);
1514 fullLibraryPath.Append(MOZ_DLL_SUFFIX);
1515 // Escape the \ and " characters.
1516 fullLibraryPath.ReplaceSubstring("\\", "\\\\");
1517 fullLibraryPath.ReplaceSubstring("\"", "\\\"");
1518
1519 nsAutoCString pkcs11ModuleSpec("name=\"");
1520 pkcs11ModuleSpec.Append(moduleName);
1521 pkcs11ModuleSpec.AppendLiteral("\" library=\"");
1522 pkcs11ModuleSpec.Append(fullLibraryPath);
1523 pkcs11ModuleSpec.AppendLiteral("\"");
1524
1525 UniqueSECMODModule userModule(SECMOD_LoadUserModule(
1526 const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false));
1527 if (!userModule) {
1528 return false;
1529 }
1530
1531 if (!userModule->loaded) {
1532 return false;
1533 }
1534
1535 return true;
1536 }
1537
1538 const char* kOSClientCertsModuleName = "OS Client Cert Module";
1539
LoadOSClientCertsModule(const nsCString & dir)1540 bool LoadOSClientCertsModule(const nsCString& dir) {
1541 #ifdef MOZ_WIDGET_COCOA
1542 // osclientcerts requires macOS 10.14 or later
1543 if (!nsCocoaFeatures::OnMojaveOrLater()) {
1544 return false;
1545 }
1546 #endif
1547 return LoadUserModuleAt(kOSClientCertsModuleName, "osclientcerts", dir);
1548 }
1549
LoadLoadableRoots(const nsCString & dir)1550 bool LoadLoadableRoots(const nsCString& dir) {
1551 // Some NSS command-line utilities will load a roots module under the name
1552 // "Root Certs" if there happens to be a `MOZ_DLL_PREFIX "nssckbi"
1553 // MOZ_DLL_SUFFIX` file in the directory being operated on. In some cases this
1554 // can cause us to fail to load our roots module. In these cases, deleting the
1555 // "Root Certs" module allows us to load the correct one. See bug 1406396.
1556 int unusedModType;
1557 Unused << SECMOD_DeleteModule("Root Certs", &unusedModType);
1558 return LoadUserModuleAt(kRootModuleName, "nssckbi", dir);
1559 }
1560
UnloadUserModules()1561 void UnloadUserModules() {
1562 UniqueSECMODModule rootsModule(SECMOD_FindModule(kRootModuleName));
1563 if (rootsModule) {
1564 SECMOD_UnloadUserModule(rootsModule.get());
1565 }
1566 UniqueSECMODModule osClientCertsModule(
1567 SECMOD_FindModule(kOSClientCertsModuleName));
1568 if (osClientCertsModule) {
1569 SECMOD_UnloadUserModule(osClientCertsModule.get());
1570 }
1571 }
1572
DefaultServerNicknameForCert(const CERTCertificate * cert,nsCString & nickname)1573 nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
1574 /*out*/ nsCString& nickname) {
1575 MOZ_ASSERT(cert);
1576 NS_ENSURE_ARG_POINTER(cert);
1577
1578 UniquePORTString baseName(CERT_GetCommonName(&cert->subject));
1579 if (!baseName) {
1580 baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject));
1581 }
1582 if (!baseName) {
1583 baseName = UniquePORTString(CERT_GetOrgName(&cert->subject));
1584 }
1585 if (!baseName) {
1586 baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject));
1587 }
1588 if (!baseName) {
1589 baseName = UniquePORTString(CERT_GetStateName(&cert->subject));
1590 }
1591 if (!baseName) {
1592 baseName = UniquePORTString(CERT_GetCountryName(&cert->subject));
1593 }
1594 if (!baseName) {
1595 return NS_ERROR_FAILURE;
1596 }
1597
1598 // This function is only used in contexts where a failure to find a suitable
1599 // nickname does not block the overall task from succeeding.
1600 // As such, we use an arbitrary limit to prevent this nickname searching
1601 // process from taking forever.
1602 static const uint32_t ARBITRARY_LIMIT = 500;
1603 for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) {
1604 nickname = baseName.get();
1605 if (count != 1) {
1606 nickname.AppendPrintf(" #%u", count);
1607 }
1608 if (nickname.IsEmpty()) {
1609 return NS_ERROR_FAILURE;
1610 }
1611
1612 bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject,
1613 cert->dbhandle);
1614 if (!conflict) {
1615 return NS_OK;
1616 }
1617 }
1618
1619 return NS_ERROR_FAILURE;
1620 }
1621
BuildRevocationCheckArrays(Input certDER,EndEntityOrCA endEntityOrCA,nsTArray<uint8_t> & issuerBytes,nsTArray<uint8_t> & serialBytes,nsTArray<uint8_t> & subjectBytes,nsTArray<uint8_t> & pubKeyBytes)1622 Result BuildRevocationCheckArrays(Input certDER, EndEntityOrCA endEntityOrCA,
1623 /*out*/ nsTArray<uint8_t>& issuerBytes,
1624 /*out*/ nsTArray<uint8_t>& serialBytes,
1625 /*out*/ nsTArray<uint8_t>& subjectBytes,
1626 /*out*/ nsTArray<uint8_t>& pubKeyBytes) {
1627 BackCert cert(certDER, endEntityOrCA, nullptr);
1628 Result rv = cert.Init();
1629 if (rv != Success) {
1630 return rv;
1631 }
1632 issuerBytes.Clear();
1633 Input issuer(cert.GetIssuer());
1634 issuerBytes.AppendElements(issuer.UnsafeGetData(), issuer.GetLength());
1635 serialBytes.Clear();
1636 Input serial(cert.GetSerialNumber());
1637 serialBytes.AppendElements(serial.UnsafeGetData(), serial.GetLength());
1638 subjectBytes.Clear();
1639 Input subject(cert.GetSubject());
1640 subjectBytes.AppendElements(subject.UnsafeGetData(), subject.GetLength());
1641 pubKeyBytes.Clear();
1642 Input pubKey(cert.GetSubjectPublicKeyInfo());
1643 pubKeyBytes.AppendElements(pubKey.UnsafeGetData(), pubKey.GetLength());
1644
1645 return Success;
1646 }
1647
CertIsInCertStorage(CERTCertificate * cert,nsICertStorage * certStorage)1648 bool CertIsInCertStorage(CERTCertificate* cert, nsICertStorage* certStorage) {
1649 MOZ_ASSERT(cert);
1650 MOZ_ASSERT(certStorage);
1651 if (!cert || !certStorage) {
1652 return false;
1653 }
1654 nsTArray<uint8_t> subject;
1655 subject.AppendElements(cert->derSubject.data, cert->derSubject.len);
1656 nsTArray<nsTArray<uint8_t>> certStorageCerts;
1657 nsresult rv = certStorage->FindCertsBySubject(subject, certStorageCerts);
1658 if (NS_FAILED(rv)) {
1659 return false;
1660 }
1661 for (const auto& certStorageCert : certStorageCerts) {
1662 if (certStorageCert.Length() != cert->derCert.len) {
1663 continue;
1664 }
1665 if (memcmp(certStorageCert.Elements(), cert->derCert.data,
1666 certStorageCert.Length()) == 0) {
1667 return true;
1668 }
1669 }
1670 return false;
1671 }
1672
1673 /**
1674 * Given a list of certificates representing a verified certificate path from an
1675 * end-entity certificate to a trust anchor, imports the intermediate
1676 * certificates into the permanent certificate database. This is an attempt to
1677 * cope with misconfigured servers that don't include the appropriate
1678 * intermediate certificates in the TLS handshake.
1679 *
1680 * @param certList the verified certificate list
1681 */
SaveIntermediateCerts(const nsTArray<nsTArray<uint8_t>> & certList)1682 void SaveIntermediateCerts(const nsTArray<nsTArray<uint8_t>>& certList) {
1683 UniqueCERTCertList intermediates(CERT_NewCertList());
1684 if (!intermediates) {
1685 return;
1686 }
1687
1688 size_t index = 0;
1689 size_t numIntermediates = 0;
1690 for (const auto& certDER : certList) {
1691 // Skip the end-entity; we only want to store intermediates. Similarly,
1692 // there's no need to save the trust anchor - it's either already a
1693 // permanent certificate or it's the Microsoft Family Safety root or an
1694 // enterprise root temporarily imported via the child mode or enterprise
1695 // root features. We don't want to import these because they're intended to
1696 // be temporary (and because importing them happens to reset their trust
1697 // settings, which breaks these features).
1698 index++;
1699 if (index == 1 || index == certList.Length()) {
1700 continue;
1701 }
1702
1703 SECItem certDERItem = {siBuffer,
1704 const_cast<unsigned char*>(certDER.Elements()),
1705 AssertedCast<unsigned int>(certDER.Length())};
1706 UniqueCERTCertificate certHandle(CERT_NewTempCertificate(
1707 CERT_GetDefaultCertDB(), &certDERItem, nullptr, false, true));
1708 if (!certHandle) {
1709 continue;
1710 }
1711 if (certHandle->slot) {
1712 // This cert was found on a token; no need to remember it in the permanent
1713 // database.
1714 continue;
1715 }
1716
1717 PRBool isperm;
1718 if (CERT_GetCertIsPerm(certHandle.get(), &isperm) != SECSuccess) {
1719 continue;
1720 }
1721 if (isperm) {
1722 // We don't need to remember certs already stored in perm db.
1723 continue;
1724 }
1725
1726 if (CERT_AddCertToListTail(intermediates.get(), certHandle.get()) !=
1727 SECSuccess) {
1728 // If this fails, we're probably out of memory. Just return.
1729 return;
1730 }
1731 certHandle.release(); // intermediates now owns the reference
1732 numIntermediates++;
1733 }
1734
1735 if (numIntermediates > 0) {
1736 nsCOMPtr<nsIRunnable> importCertsRunnable(NS_NewRunnableFunction(
1737 "IdleSaveIntermediateCerts",
1738 [intermediates = std::move(intermediates)]() -> void {
1739 if (AppShutdown::IsShuttingDown()) {
1740 return;
1741 }
1742
1743 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1744 if (!slot) {
1745 return;
1746 }
1747 nsCOMPtr<nsICertStorage> certStorage(
1748 do_GetService(NS_CERT_STORAGE_CID));
1749 size_t numCertsImported = 0;
1750 for (CERTCertListNode* node = CERT_LIST_HEAD(intermediates);
1751 !CERT_LIST_END(node, intermediates);
1752 node = CERT_LIST_NEXT(node)) {
1753 if (AppShutdown::IsShuttingDown()) {
1754 return;
1755 }
1756
1757 if (CertIsInCertStorage(node->cert, certStorage)) {
1758 continue;
1759 }
1760 PRBool isperm;
1761 if (CERT_GetCertIsPerm(node->cert, &isperm) != SECSuccess) {
1762 continue;
1763 }
1764 if (isperm) {
1765 // This may be a certificate that has already been imported by
1766 // another background import task that happened to run before
1767 // this one.
1768 continue;
1769 }
1770 // This is a best-effort attempt at avoiding unknown issuer errors
1771 // in the future, so ignore failures here.
1772 nsAutoCString nickname;
1773 if (NS_FAILED(DefaultServerNicknameForCert(node->cert, nickname))) {
1774 continue;
1775 }
1776 Unused << PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
1777 nickname.get(), false);
1778 numCertsImported++;
1779 }
1780
1781 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
1782 "IdleSaveIntermediateCertsDone", [numCertsImported]() -> void {
1783 nsCOMPtr<nsIObserverService> observerService =
1784 mozilla::services::GetObserverService();
1785 if (observerService) {
1786 NS_ConvertUTF8toUTF16 numCertsImportedString(
1787 nsPrintfCString("%zu", numCertsImported));
1788 observerService->NotifyObservers(
1789 nullptr, "psm:intermediate-certs-cached",
1790 numCertsImportedString.get());
1791 }
1792 }));
1793 Unused << NS_DispatchToMainThread(runnable.forget());
1794 }));
1795 Unused << NS_DispatchToCurrentThreadQueue(importCertsRunnable.forget(),
1796 EventQueuePriority::Idle);
1797 }
1798 }
1799
1800 } // namespace psm
1801 } // namespace mozilla
1802