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