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