Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / security / manager / ssl / nsNSSCertificateDB.cpp
blob244ab78ee0909486d0ccc07bb50fe39e48811ba9
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsNSSCertificateDB.h"
7 #include "CertVerifier.h"
8 #include "CryptoTask.h"
9 #include "ExtendedValidation.h"
10 #include "NSSCertDBTrustDomain.h"
11 #include "certdb.h"
12 #include "mozilla/glean/GleanMetrics.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Base64.h"
15 #include "mozilla/Casting.h"
16 #include "mozilla/Logging.h"
17 #include "mozilla/Services.h"
18 #include "mozilla/Unused.h"
19 #include "mozpkix/Time.h"
20 #include "mozpkix/pkixnss.h"
21 #include "mozpkix/pkixtypes.h"
22 #include "nsArray.h"
23 #include "nsArrayUtils.h"
24 #include "nsCOMPtr.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsICertificateDialogs.h"
27 #include "nsIFile.h"
28 #include "nsIMutableArray.h"
29 #include "nsIObserverService.h"
30 #include "nsIPrompt.h"
31 #include "nsNSSCertHelper.h"
32 #include "nsNSSCertTrust.h"
33 #include "nsNSSCertificate.h"
34 #include "nsNSSComponent.h"
35 #include "nsNSSHelper.h"
36 #include "nsPKCS12Blob.h"
37 #include "nsPromiseFlatString.h"
38 #include "nsProxyRelease.h"
39 #include "nsReadableUtils.h"
40 #include "nsThreadUtils.h"
41 #include "nspr.h"
42 #include "secasn1.h"
43 #include "secder.h"
44 #include "secerr.h"
45 #include "ssl.h"
47 #ifdef XP_WIN
48 # include <winsock.h> // for ntohl
49 #endif
51 using namespace mozilla;
52 using namespace mozilla::psm;
54 extern LazyLogModule gPIPNSSLog;
56 NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)
58 NS_IMETHODIMP
59 nsNSSCertificateDB::CountTrustObjects(uint32_t* aCount) {
60 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
61 PK11GenericObject* objects =
62 PK11_FindGenericObjects(slot.get(), CKO_NSS_TRUST);
63 int count = 0;
64 for (PK11GenericObject* cursor = objects; cursor;
65 cursor = PK11_GetNextGenericObject(cursor)) {
66 count++;
68 PK11_DestroyGenericObjects(objects);
70 mozilla::glean::cert_verifier::trust_obj_count.Set(count);
72 *aCount = count;
73 return NS_OK;
76 NS_IMETHODIMP
77 nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
78 /*out*/ nsIX509Cert** _cert) {
79 NS_ENSURE_ARG_POINTER(_cert);
80 *_cert = nullptr;
82 if (aDBKey.IsEmpty()) {
83 return NS_ERROR_INVALID_ARG;
86 nsresult rv = BlockUntilLoadableCertsLoaded();
87 if (NS_FAILED(rv)) {
88 return rv;
91 UniqueCERTCertificate cert;
92 rv = FindCertByDBKey(aDBKey, cert);
93 if (NS_FAILED(rv)) {
94 return rv;
96 // If we can't find the certificate, that's not an error. Just return null.
97 if (!cert) {
98 return NS_OK;
100 nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
101 nssCert.forget(_cert);
102 return NS_OK;
105 nsresult nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
106 UniqueCERTCertificate& cert) {
107 static_assert(sizeof(uint64_t) == 8, "type size sanity check");
108 static_assert(sizeof(uint32_t) == 4, "type size sanity check");
109 // (From nsNSSCertificate::GetDbKey)
110 // The format of the key is the base64 encoding of the following:
111 // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
112 // never implemented)
113 // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
114 // never implemented)
115 // 4 bytes: <serial number length in big-endian order>
116 // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
117 // n bytes: <bytes of serial number>
118 // m bytes: <DER-encoded issuer distinguished name>
119 nsAutoCString decoded;
120 nsAutoCString tmpDBKey(aDBKey);
121 // Filter out any whitespace for backwards compatibility.
122 tmpDBKey.StripWhitespace();
123 nsresult rv = Base64Decode(tmpDBKey, decoded);
124 if (NS_FAILED(rv)) {
125 return rv;
127 if (decoded.Length() < 16) {
128 return NS_ERROR_ILLEGAL_INPUT;
130 const char* reader = decoded.BeginReading();
131 uint64_t zeroes = *BitwiseCast<const uint64_t*, const char*>(reader);
132 if (zeroes != 0) {
133 return NS_ERROR_ILLEGAL_INPUT;
135 reader += sizeof(uint64_t);
136 // Note: We surround the ntohl() argument with parentheses to stop the macro
137 // from thinking two arguments were passed.
138 uint32_t serialNumberLen =
139 ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
140 reader += sizeof(uint32_t);
141 uint32_t issuerLen =
142 ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
143 reader += sizeof(uint32_t);
144 if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) {
145 return NS_ERROR_ILLEGAL_INPUT;
147 CERTIssuerAndSN issuerSN;
148 issuerSN.serialNumber.len = serialNumberLen;
149 issuerSN.serialNumber.data = BitwiseCast<unsigned char*, const char*>(reader);
150 reader += serialNumberLen;
151 issuerSN.derIssuer.len = issuerLen;
152 issuerSN.derIssuer.data = BitwiseCast<unsigned char*, const char*>(reader);
153 reader += issuerLen;
154 MOZ_ASSERT(reader == decoded.EndReading());
156 cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN));
157 return NS_OK;
160 SECStatus collect_certs(void* arg, SECItem** certs, int numcerts) {
161 nsTArray<nsTArray<uint8_t>>* certsArray =
162 reinterpret_cast<nsTArray<nsTArray<uint8_t>>*>(arg);
164 while (numcerts--) {
165 nsTArray<uint8_t> certArray;
166 SECItem* cert = *certs;
167 certArray.AppendElements(cert->data, cert->len);
168 certsArray->AppendElement(std::move(certArray));
169 certs++;
171 return (SECSuccess);
174 nsresult nsNSSCertificateDB::getCertsFromPackage(
175 nsTArray<nsTArray<uint8_t>>& collectArgs, uint8_t* data, uint32_t length) {
176 if (CERT_DecodeCertPackage(BitwiseCast<char*, uint8_t*>(data), length,
177 collect_certs, &collectArgs) != SECSuccess) {
178 return NS_ERROR_FAILURE;
180 return NS_OK;
183 // When using the sql-backed softoken, trust settings are authenticated using a
184 // key in the secret database. Thus, if the user has a password, we need to
185 // authenticate to the token in order to be able to change trust settings.
186 SECStatus ChangeCertTrustWithPossibleAuthentication(
187 const UniqueCERTCertificate& cert, CERTCertTrust& trust, void* ctx) {
188 MOZ_ASSERT(cert, "cert must be non-null");
189 if (!cert) {
190 PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
191 return SECFailure;
194 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
195 if (!certVerifier) {
196 PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
197 return SECFailure;
200 // NSS ignores the first argument to CERT_ChangeCertTrust
201 SECStatus srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
202 if (srv != SECSuccess && PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
203 return SECFailure;
205 if (srv == SECSuccess) {
206 certVerifier->ClearTrustCache();
207 return SECSuccess;
210 // CERT_ChangeCertTrust failed with SEC_ERROR_TOKEN_NOT_LOGGED_IN, so
211 // authenticate and try again.
212 if (cert->slot) {
213 // If this certificate is on an external PKCS#11 token, we have to
214 // authenticate to that token.
215 srv = PK11_Authenticate(cert->slot, PR_TRUE, ctx);
216 } else {
217 // Otherwise, the certificate is on the internal module.
218 UniquePK11SlotInfo internalSlot(PK11_GetInternalKeySlot());
219 srv = PK11_Authenticate(internalSlot.get(), PR_TRUE, ctx);
221 if (srv != SECSuccess) {
222 return srv;
224 srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
225 if (srv != SECSuccess) {
226 return srv;
229 certVerifier->ClearTrustCache();
230 return SECSuccess;
233 static nsresult ImportCertsIntoPermanentStorage(
234 const UniqueCERTCertList& certChain) {
235 bool encounteredFailure = false;
236 PRErrorCode savedErrorCode = 0;
237 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
238 for (CERTCertListNode* chainNode = CERT_LIST_HEAD(certChain);
239 !CERT_LIST_END(chainNode, certChain);
240 chainNode = CERT_LIST_NEXT(chainNode)) {
241 UniquePORTString nickname(CERT_MakeCANickname(chainNode->cert));
242 SECStatus srv = PK11_ImportCert(slot.get(), chainNode->cert,
243 CK_INVALID_HANDLE, nickname.get(),
244 false); // this parameter is ignored by NSS
245 if (srv != SECSuccess) {
246 encounteredFailure = true;
247 savedErrorCode = PR_GetError();
251 if (encounteredFailure) {
252 return GetXPCOMFromNSSError(savedErrorCode);
255 return NS_OK;
258 nsresult nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
259 nsIInterfaceRequestor* ctx) {
260 // First thing we have to do is figure out which certificate we're
261 // gonna present to the user. The CA may have sent down a list of
262 // certs which may or may not be a chained list of certs. Until
263 // the day we can design some solid UI for the general case, we'll
264 // code to the > 90% case. That case is where a CA sends down a
265 // list that is a hierarchy whose root is either the first or
266 // the last cert. What we're gonna do is compare the first
267 // 2 entries, if the second was signed by the first, we assume
268 // the root cert is the first cert and display it. Otherwise,
269 // we compare the last 2 entries, if the second to last cert was
270 // signed by the last cert, then we assume the last cert is the
271 // root and display it.
273 uint32_t numCerts;
275 x509Certs->GetLength(&numCerts);
277 if (numCerts == 0) return NS_OK; // Nothing to import, so nothing to do.
279 nsCOMPtr<nsIX509Cert> certToShow;
280 uint32_t selCertIndex;
281 if (numCerts == 1) {
282 // There's only one cert, so let's show it.
283 selCertIndex = 0;
284 certToShow = do_QueryElementAt(x509Certs, selCertIndex);
285 } else {
286 nsCOMPtr<nsIX509Cert> cert0; // first cert
287 nsCOMPtr<nsIX509Cert> cert1; // second cert
288 nsCOMPtr<nsIX509Cert> certn_2; // second to last cert
289 nsCOMPtr<nsIX509Cert> certn_1; // last cert
291 cert0 = do_QueryElementAt(x509Certs, 0);
292 cert1 = do_QueryElementAt(x509Certs, 1);
293 certn_2 = do_QueryElementAt(x509Certs, numCerts - 2);
294 certn_1 = do_QueryElementAt(x509Certs, numCerts - 1);
296 nsAutoString cert0SubjectName;
297 nsAutoString cert1IssuerName;
298 nsAutoString certn_2IssuerName;
299 nsAutoString certn_1SubjectName;
301 cert0->GetSubjectName(cert0SubjectName);
302 cert1->GetIssuerName(cert1IssuerName);
303 certn_2->GetIssuerName(certn_2IssuerName);
304 certn_1->GetSubjectName(certn_1SubjectName);
306 if (cert1IssuerName.Equals(cert0SubjectName)) {
307 // In this case, the first cert in the list signed the second,
308 // so the first cert is the root. Let's display it.
309 selCertIndex = 0;
310 certToShow = cert0;
311 } else if (certn_2IssuerName.Equals(certn_1SubjectName)) {
312 // In this case the last cert has signed the second to last cert.
313 // The last cert is the root, so let's display it.
314 selCertIndex = numCerts - 1;
315 certToShow = certn_1;
316 } else {
317 // It's not a chain, so let's just show the first one in the
318 // downloaded list.
319 selCertIndex = 0;
320 certToShow = cert0;
324 if (!certToShow) return NS_ERROR_FAILURE;
326 nsCOMPtr<nsICertificateDialogs> dialogs;
327 nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
328 NS_GET_IID(nsICertificateDialogs),
329 NS_CERTIFICATEDIALOGS_CONTRACTID);
330 if (NS_FAILED(rv)) {
331 return rv;
334 UniqueCERTCertificate tmpCert(certToShow->GetCert());
335 if (!tmpCert) {
336 return NS_ERROR_FAILURE;
339 if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
340 DisplayCertificateAlert(ctx, "NotACACert", certToShow);
341 return NS_ERROR_FAILURE;
344 if (tmpCert->isperm) {
345 DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
346 return NS_ERROR_FAILURE;
349 uint32_t trustBits;
350 bool allows;
351 rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
352 if (NS_FAILED(rv)) return rv;
354 if (!allows) return NS_ERROR_NOT_AVAILABLE;
356 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
357 UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
359 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
360 ("Created nick \"%s\"\n", nickname.get()));
362 nsNSSCertTrust trust;
363 trust.SetValidCA();
364 trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
365 !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL));
367 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
368 SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
369 nickname.get(),
370 false); // this parameter is ignored by NSS
371 if (srv != SECSuccess) {
372 return MapSECStatus(srv);
374 srv =
375 ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(), ctx);
376 if (srv != SECSuccess) {
377 return MapSECStatus(srv);
380 // Import additional delivered certificates that can be verified.
382 // build a CertList for filtering
383 UniqueCERTCertList certList(CERT_NewCertList());
384 if (!certList) {
385 return NS_ERROR_FAILURE;
388 // get all remaining certs into temp store
390 for (uint32_t i = 0; i < numCerts; i++) {
391 if (i == selCertIndex) {
392 // we already processed that one
393 continue;
396 nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
397 if (!remainingCert) {
398 continue;
401 UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
402 if (!tmpCert2) {
403 continue; // Let's try to import the rest of 'em
406 if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
407 continue;
410 Unused << tmpCert2.release();
413 return ImportCertsIntoPermanentStorage(certList);
416 nsresult nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
417 const UniqueCERTCertList& aCertListIn,
418 nsTArray<RefPtr<nsIX509Cert>>& aCertListOut) {
419 if (!aCertListIn.get()) {
420 return NS_ERROR_INVALID_ARG;
423 for (CERTCertListNode* node = CERT_LIST_HEAD(aCertListIn.get());
424 !CERT_LIST_END(node, aCertListIn.get()); node = CERT_LIST_NEXT(node)) {
425 RefPtr<nsIX509Cert> cert = new nsNSSCertificate(node->cert);
426 aCertListOut.AppendElement(cert);
428 return NS_OK;
431 NS_IMETHODIMP
432 nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
433 uint32_t type,
434 nsIInterfaceRequestor* ctx) {
435 // We currently only handle CA certificates.
436 if (type != nsIX509Cert::CA_CERT) {
437 return NS_ERROR_FAILURE;
440 nsTArray<nsTArray<uint8_t>> certsArray;
441 nsresult rv = getCertsFromPackage(certsArray, data, length);
442 if (NS_FAILED(rv)) {
443 return rv;
446 nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
447 if (!array) {
448 return NS_ERROR_FAILURE;
451 // Now let's create some certs to work with
452 for (nsTArray<uint8_t>& certDER : certsArray) {
453 nsCOMPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
454 nsresult rv = array->AppendElement(cert);
455 if (NS_FAILED(rv)) {
456 return rv;
460 return handleCACertDownload(WrapNotNull(array), ctx);
464 * Decodes a given array of DER-encoded certificates into temporary storage.
466 * @param certs
467 * Array in which the decoded certificates are stored as arrays of
468 * unsigned chars.
469 * @param temporaryCerts
470 * List of decoded certificates.
472 static nsresult ImportCertsIntoTempStorage(
473 nsTArray<nsTArray<uint8_t>>& certs,
474 /*out*/ const UniqueCERTCertList& temporaryCerts) {
475 NS_ENSURE_ARG_POINTER(temporaryCerts);
477 for (nsTArray<uint8_t>& certDER : certs) {
478 CERTCertificate* certificate;
479 SECItem certItem;
480 certItem.len = certDER.Length();
481 certItem.data = certDER.Elements();
482 certificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem,
483 nullptr, false, true);
485 UniqueCERTCertificate cert(certificate);
486 if (!cert) {
487 continue;
490 if (CERT_AddCertToListTail(temporaryCerts.get(), cert.get()) ==
491 SECSuccess) {
492 Unused << cert.release();
496 return NS_OK;
499 NS_IMETHODIMP
500 nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
501 nsIInterfaceRequestor* ctx) {
502 nsTArray<nsTArray<uint8_t>> certsArray;
504 nsresult rv = getCertsFromPackage(certsArray, data, length);
505 if (NS_FAILED(rv)) {
506 return rv;
509 UniqueCERTCertList temporaryCerts(CERT_NewCertList());
510 if (!temporaryCerts) {
511 return NS_ERROR_FAILURE;
514 rv = ImportCertsIntoTempStorage(certsArray, temporaryCerts);
515 if (NS_FAILED(rv)) {
516 return rv;
519 return ImportCertsIntoPermanentStorage(temporaryCerts);
522 nsresult nsNSSCertificateDB::ImportCACerts(nsTArray<nsTArray<uint8_t>>& caCerts,
523 nsIInterfaceRequestor* ctx) {
524 UniqueCERTCertList temporaryCerts(CERT_NewCertList());
525 if (!temporaryCerts) {
526 return NS_ERROR_FAILURE;
529 nsresult rv = ImportCertsIntoTempStorage(caCerts, temporaryCerts);
530 if (NS_FAILED(rv)) {
531 return rv;
534 return ImportCertsIntoPermanentStorage(temporaryCerts);
537 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor* ctx,
538 const char* stringID,
539 nsIX509Cert* certToShow) {
540 if (!NS_IsMainThread()) {
541 NS_ERROR(
542 "nsNSSCertificateDB::DisplayCertificateAlert called off the main "
543 "thread");
544 return;
547 nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
548 if (!my_ctx) {
549 my_ctx = new PipUIContext();
552 // This shall be replaced by embedding ovverridable prompts
553 // as discussed in bug 310446, and should make use of certToShow.
555 nsAutoString tmpMessage;
556 GetPIPNSSBundleString(stringID, tmpMessage);
557 nsCOMPtr<nsIPrompt> prompt(do_GetInterface(my_ctx));
558 if (!prompt) {
559 return;
562 prompt->Alert(nullptr, tmpMessage.get());
565 NS_IMETHODIMP
566 nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
567 nsIInterfaceRequestor* ctx) {
568 if (!NS_IsMainThread()) {
569 NS_ERROR(
570 "nsNSSCertificateDB::ImportUserCertificate called off the main thread");
571 return NS_ERROR_NOT_SAME_THREAD;
574 nsTArray<nsTArray<uint8_t>> certsArray;
576 nsresult rv = getCertsFromPackage(certsArray, data, length);
577 if (NS_FAILED(rv)) {
578 return rv;
581 SECItem certItem;
583 if (certsArray.IsEmpty()) {
584 return NS_OK;
587 certItem.len = certsArray.ElementAt(0).Length();
588 certItem.data = certsArray.ElementAt(0).Elements();
590 UniqueCERTCertificate cert(CERT_NewTempCertificate(
591 CERT_GetDefaultCertDB(), &certItem, nullptr, false, true));
592 if (!cert) {
593 return NS_ERROR_FAILURE;
596 UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
597 if (!slot) {
598 nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
599 DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow);
600 return NS_ERROR_FAILURE;
602 slot = nullptr;
604 /* pick a nickname for the cert */
605 nsAutoCString nickname;
606 if (cert->nickname) {
607 nickname = cert->nickname;
608 } else {
609 get_default_nickname(cert.get(), ctx, nickname);
612 /* user wants to import the cert */
613 slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
614 if (!slot) {
615 return NS_ERROR_FAILURE;
617 slot = nullptr;
620 nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
621 DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
624 rv = NS_OK;
625 if (!certsArray.IsEmpty()) {
626 certsArray.RemoveElementAt(0);
627 rv = ImportCACerts(certsArray, ctx);
630 nsCOMPtr<nsIObserverService> observerService =
631 mozilla::services::GetObserverService();
632 if (observerService) {
633 observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
634 nullptr);
637 return rv;
640 NS_IMETHODIMP
641 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert* aCert) {
642 NS_ENSURE_ARG_POINTER(aCert);
643 UniqueCERTCertificate cert(aCert->GetCert());
644 if (!cert) {
645 return NS_ERROR_FAILURE;
648 // Temporary certificates aren't on a slot and will go away when the
649 // nsIX509Cert is destructed.
650 if (cert->slot) {
651 uint32_t certType;
652 nsresult rv = aCert->GetCertType(&certType);
653 if (NS_WARN_IF(NS_FAILED(rv))) {
654 return rv;
656 if (certType == nsIX509Cert::USER_CERT) {
657 SECStatus srv = PK11_Authenticate(cert->slot, true, nullptr);
658 if (srv != SECSuccess) {
659 return NS_ERROR_FAILURE;
661 srv = PK11_DeleteTokenCertAndKey(cert.get(), nullptr);
662 if (srv != SECSuccess) {
663 return NS_ERROR_FAILURE;
665 } else {
666 // For certificates that can't be deleted (e.g. built-in roots), un-set
667 // all trust bits.
668 nsNSSCertTrust trust(0, 0);
669 SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
670 cert, trust.GetTrust(), nullptr);
671 if (srv != SECSuccess) {
672 return NS_ERROR_FAILURE;
674 if (!PK11_IsReadOnly(cert->slot)) {
675 srv = SEC_DeletePermCertificate(cert.get());
676 if (srv != SECSuccess) {
677 return NS_ERROR_FAILURE;
683 nsCOMPtr<nsIObserverService> observerService =
684 mozilla::services::GetObserverService();
685 if (observerService) {
686 observerService->NotifyObservers(nullptr, "psm:user-certificate-deleted",
687 nullptr);
690 return NS_OK;
693 NS_IMETHODIMP
694 nsNSSCertificateDB::SetCertTrust(nsIX509Cert* cert, uint32_t type,
695 uint32_t trusted) {
696 NS_ENSURE_ARG_POINTER(cert);
697 nsNSSCertTrust trust;
698 switch (type) {
699 case nsIX509Cert::CA_CERT:
700 trust.SetValidCA();
701 trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
702 !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
703 break;
704 case nsIX509Cert::SERVER_CERT:
705 trust.SetValidPeer();
706 trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false);
707 break;
708 case nsIX509Cert::EMAIL_CERT:
709 trust.SetValidPeer();
710 trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
711 break;
712 default:
713 // Ignore any other type of certificate (including invalid types).
714 return NS_OK;
717 UniqueCERTCertificate nsscert(cert->GetCert());
718 SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
719 nsscert, trust.GetTrust(), nullptr);
720 return MapSECStatus(srv);
723 NS_IMETHODIMP
724 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert* cert, uint32_t certType,
725 uint32_t trustType, bool* _isTrusted) {
726 NS_ENSURE_ARG_POINTER(_isTrusted);
727 *_isTrusted = false;
729 nsresult rv = BlockUntilLoadableCertsLoaded();
730 if (NS_FAILED(rv)) {
731 return rv;
734 SECStatus srv;
735 UniqueCERTCertificate nsscert(cert->GetCert());
736 CERTCertTrust nsstrust;
737 srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
738 if (srv != SECSuccess) {
739 // CERT_GetCertTrust returns SECFailure if given a temporary cert that
740 // doesn't have any trust information yet. This isn't an error.
741 return NS_OK;
744 nsNSSCertTrust trust(&nsstrust);
745 if (certType == nsIX509Cert::CA_CERT) {
746 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
747 *_isTrusted = trust.HasTrustedCA(true, false);
748 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
749 *_isTrusted = trust.HasTrustedCA(false, true);
750 } else {
751 return NS_ERROR_FAILURE;
753 } else if (certType == nsIX509Cert::SERVER_CERT) {
754 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
755 *_isTrusted = trust.HasTrustedPeer(true, false);
756 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
757 *_isTrusted = trust.HasTrustedPeer(false, true);
758 } else {
759 return NS_ERROR_FAILURE;
761 } else if (certType == nsIX509Cert::EMAIL_CERT) {
762 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
763 *_isTrusted = trust.HasTrustedPeer(true, false);
764 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
765 *_isTrusted = trust.HasTrustedPeer(false, true);
766 } else {
767 return NS_ERROR_FAILURE;
769 } /* user: ignore */
770 return NS_OK;
773 NS_IMETHODIMP
774 nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) {
775 NS_ENSURE_ARG(aFile);
776 switch (aType) {
777 case nsIX509Cert::CA_CERT:
778 case nsIX509Cert::EMAIL_CERT:
779 // good
780 break;
782 default:
783 // not supported (yet)
784 return NS_ERROR_FAILURE;
787 PRFileDesc* fd = nullptr;
788 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
789 if (NS_FAILED(rv)) {
790 return rv;
792 if (!fd) {
793 return NS_ERROR_FAILURE;
796 PRFileInfo fileInfo;
797 if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
798 return NS_ERROR_FAILURE;
801 auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
802 int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
803 PR_Close(fd);
805 if (bytesObtained != fileInfo.size) {
806 return NS_ERROR_FAILURE;
809 nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
811 switch (aType) {
812 case nsIX509Cert::CA_CERT:
813 return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
814 case nsIX509Cert::EMAIL_CERT:
815 return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
816 default:
817 MOZ_ASSERT(false, "Unsupported type should have been filtered out");
818 break;
821 return NS_ERROR_FAILURE;
824 NS_IMETHODIMP
825 nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile, const nsAString& aPassword,
826 uint32_t* aError) {
827 if (!NS_IsMainThread()) {
828 return NS_ERROR_NOT_SAME_THREAD;
830 nsresult rv = BlockUntilLoadableCertsLoaded();
831 if (NS_FAILED(rv)) {
832 return rv;
835 NS_ENSURE_ARG(aFile);
836 nsPKCS12Blob blob;
837 rv = blob.ImportFromFile(aFile, aPassword, *aError);
838 nsCOMPtr<nsIObserverService> observerService =
839 mozilla::services::GetObserverService();
840 if (NS_SUCCEEDED(rv) && observerService) {
841 observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
842 nullptr);
845 return rv;
848 NS_IMETHODIMP
849 nsNSSCertificateDB::ExportPKCS12File(
850 nsIFile* aFile, const nsTArray<RefPtr<nsIX509Cert>>& aCerts,
851 const nsAString& aPassword, uint32_t* aError) {
852 if (!NS_IsMainThread()) {
853 return NS_ERROR_NOT_SAME_THREAD;
855 nsresult rv = BlockUntilLoadableCertsLoaded();
856 if (NS_FAILED(rv)) {
857 return rv;
860 NS_ENSURE_ARG(aFile);
861 if (aCerts.IsEmpty()) {
862 return NS_OK;
864 nsPKCS12Blob blob;
865 return blob.ExportToFile(aFile, aCerts, aPassword, *aError);
868 NS_IMETHODIMP
869 nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
870 /*out*/ nsIX509Cert** _retval) {
871 if (!_retval) {
872 return NS_ERROR_INVALID_POINTER;
875 // Base64Decode() doesn't consider a zero length input as an error, and just
876 // returns the empty string. We don't want this behavior, so the below check
877 // catches this case.
878 if (base64.Length() < 1) {
879 return NS_ERROR_ILLEGAL_VALUE;
882 nsAutoCString certDER;
883 nsresult rv = Base64Decode(base64, certDER);
884 if (NS_FAILED(rv)) {
885 return rv;
888 return ConstructX509FromSpan(AsBytes(Span(certDER)), _retval);
891 NS_IMETHODIMP
892 nsNSSCertificateDB::ConstructX509(const nsTArray<uint8_t>& certDER,
893 nsIX509Cert** _retval) {
894 return ConstructX509FromSpan(Span(certDER.Elements(), certDER.Length()),
895 _retval);
898 nsresult nsNSSCertificateDB::ConstructX509FromSpan(
899 Span<const uint8_t> aInputSpan, nsIX509Cert** _retval) {
900 if (NS_WARN_IF(!_retval)) {
901 return NS_ERROR_INVALID_POINTER;
904 if (aInputSpan.Length() > std::numeric_limits<unsigned int>::max()) {
905 return NS_ERROR_ILLEGAL_VALUE;
908 SECItem certData;
909 certData.type = siDERCertBuffer;
910 certData.data = const_cast<unsigned char*>(
911 reinterpret_cast<const unsigned char*>(aInputSpan.Elements()));
912 certData.len = aInputSpan.Length();
914 UniqueCERTCertificate cert(CERT_NewTempCertificate(
915 CERT_GetDefaultCertDB(), &certData, nullptr, false, true));
916 if (!cert)
917 return (PORT_GetError() == SEC_ERROR_NO_MEMORY) ? NS_ERROR_OUT_OF_MEMORY
918 : NS_ERROR_FAILURE;
920 nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
921 nssCert.forget(_retval);
922 return NS_OK;
925 void nsNSSCertificateDB::get_default_nickname(CERTCertificate* cert,
926 nsIInterfaceRequestor* ctx,
927 nsCString& nickname) {
928 nickname.Truncate();
930 CK_OBJECT_HANDLE keyHandle;
932 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
933 return;
936 CERTCertDBHandle* defaultcertdb = CERT_GetDefaultCertDB();
937 nsAutoCString username;
938 UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
939 if (tempCN) {
940 username = tempCN.get();
943 nsAutoCString caname;
944 UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
945 if (tempIssuerOrg) {
946 caname = tempIssuerOrg.get();
949 nsAutoString tmpNickFmt;
950 GetPIPNSSBundleString("nick_template", tmpNickFmt);
951 NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
953 nsAutoCString baseName;
954 baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
955 if (baseName.IsEmpty()) {
956 return;
959 nickname = baseName;
962 * We need to see if the private key exists on a token, if it does
963 * then we need to check for nicknames that already exist on the smart
964 * card.
966 UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
967 if (!slot) return;
969 if (!PK11_IsInternal(slot.get())) {
970 nsAutoCString tmp;
971 tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
972 if (tmp.IsEmpty()) {
973 nickname.Truncate();
974 return;
976 baseName = tmp;
977 nickname = baseName;
980 int count = 1;
981 while (true) {
982 if (count > 1) {
983 nsAutoCString tmp;
984 tmp.AppendPrintf("%s #%d", baseName.get(), count);
985 if (tmp.IsEmpty()) {
986 nickname.Truncate();
987 return;
989 nickname = tmp;
992 UniqueCERTCertificate dummycert;
994 if (PK11_IsInternal(slot.get())) {
995 /* look up the nickname to make sure it isn't in use already */
996 dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
997 } else {
998 // Check the cert against others that already live on the smart card.
999 dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
1000 if (dummycert) {
1001 // Make sure the subject names are different.
1002 if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) {
1004 * There is another certificate with the same nickname and
1005 * the same subject name on the smart card, so let's use this
1006 * nickname.
1008 dummycert = nullptr;
1012 if (!dummycert) {
1013 break;
1015 count++;
1019 NS_IMETHODIMP
1020 nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
1021 const nsACString& aTrust,
1022 nsIX509Cert** addedCertificate) {
1023 // Base64Decode() doesn't consider a zero length input as an error, and just
1024 // returns the empty string. We don't want this behavior, so the below check
1025 // catches this case.
1026 if (aBase64.Length() < 1) {
1027 return NS_ERROR_ILLEGAL_VALUE;
1030 nsAutoCString aCertDER;
1031 nsresult rv = Base64Decode(aBase64, aCertDER);
1032 if (NS_FAILED(rv)) {
1033 return rv;
1035 return AddCert(aCertDER, aTrust, addedCertificate);
1038 NS_IMETHODIMP
1039 nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
1040 const nsACString& aTrust,
1041 nsIX509Cert** addedCertificate) {
1042 MOZ_ASSERT(addedCertificate);
1043 if (!addedCertificate) {
1044 return NS_ERROR_INVALID_ARG;
1046 *addedCertificate = nullptr;
1048 nsNSSCertTrust trust;
1049 if (CERT_DecodeTrustString(&trust.GetTrust(),
1050 PromiseFlatCString(aTrust).get()) != SECSuccess) {
1051 return NS_ERROR_FAILURE;
1054 nsCOMPtr<nsIX509Cert> newCert;
1055 nsresult rv =
1056 ConstructX509FromSpan(AsBytes(Span(aCertDER)), getter_AddRefs(newCert));
1057 if (NS_FAILED(rv)) {
1058 return rv;
1061 UniqueCERTCertificate tmpCert(newCert->GetCert());
1062 if (!tmpCert) {
1063 return NS_ERROR_FAILURE;
1066 // If there's already a certificate that matches this one in the database, we
1067 // still want to set its trust to the given value.
1068 if (tmpCert->isperm) {
1069 rv = SetCertTrustFromString(newCert, aTrust);
1070 if (NS_FAILED(rv)) {
1071 return rv;
1073 newCert.forget(addedCertificate);
1074 return NS_OK;
1077 UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
1079 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1080 ("Created nick \"%s\"\n", nickname.get()));
1082 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1083 SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
1084 nickname.get(),
1085 false); // this parameter is ignored by NSS
1086 if (srv != SECSuccess) {
1087 return MapSECStatus(srv);
1089 srv = ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(),
1090 nullptr);
1091 if (srv != SECSuccess) {
1092 return MapSECStatus(srv);
1094 newCert.forget(addedCertificate);
1095 return NS_OK;
1098 NS_IMETHODIMP
1099 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
1100 const nsACString& trustString) {
1101 NS_ENSURE_ARG(cert);
1103 CERTCertTrust trust;
1104 SECStatus srv =
1105 CERT_DecodeTrustString(&trust, PromiseFlatCString(trustString).get());
1106 if (srv != SECSuccess) {
1107 return MapSECStatus(srv);
1109 UniqueCERTCertificate nssCert(cert->GetCert());
1111 srv = ChangeCertTrustWithPossibleAuthentication(nssCert, trust, nullptr);
1112 return MapSECStatus(srv);
1115 NS_IMETHODIMP nsNSSCertificateDB::AsPKCS7Blob(
1116 const nsTArray<RefPtr<nsIX509Cert>>& certList, nsACString& _retval) {
1117 if (certList.IsEmpty()) {
1118 return NS_ERROR_INVALID_ARG;
1121 UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
1122 if (!cmsg) {
1123 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1124 ("nsNSSCertificateDB::AsPKCS7Blob - can't create CMS message"));
1125 return NS_ERROR_OUT_OF_MEMORY;
1128 UniqueNSSCMSSignedData sigd(nullptr);
1129 for (const auto& cert : certList) {
1130 // We need an owning handle when calling nsIX509Cert::GetCert().
1131 UniqueCERTCertificate nssCert(cert->GetCert());
1132 if (!sigd) {
1133 sigd.reset(
1134 NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), false));
1135 if (!sigd) {
1136 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1137 ("nsNSSCertificateDB::AsPKCS7Blob - can't create SignedData"));
1138 return NS_ERROR_FAILURE;
1140 } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) !=
1141 SECSuccess) {
1142 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1143 ("nsNSSCertificateDB::AsPKCS7Blob - can't add cert"));
1144 return NS_ERROR_FAILURE;
1148 NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get());
1149 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) !=
1150 SECSuccess) {
1151 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1152 ("nsNSSCertificateDB::AsPKCS7Blob - can't attach SignedData"));
1153 return NS_ERROR_FAILURE;
1155 // cmsg owns sigd now.
1156 Unused << sigd.release();
1158 UniquePLArenaPool arena(PORT_NewArena(1024));
1159 if (!arena) {
1160 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1161 ("nsNSSCertificateDB::AsPKCS7Blob - out of memory"));
1162 return NS_ERROR_OUT_OF_MEMORY;
1165 SECItem certP7 = {siBuffer, nullptr, 0};
1166 NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(
1167 cmsg.get(), nullptr, nullptr, &certP7, arena.get(), nullptr, nullptr,
1168 nullptr, nullptr, nullptr, nullptr);
1169 if (!ecx) {
1170 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1171 ("nsNSSCertificateDB::AsPKCS7Blob - can't create encoder"));
1172 return NS_ERROR_FAILURE;
1175 if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
1176 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1177 ("nsNSSCertificateDB::AsPKCS7Blob - failed to add encoded data"));
1178 return NS_ERROR_FAILURE;
1181 _retval.Assign(nsDependentCSubstring(
1182 reinterpret_cast<const char*>(certP7.data), certP7.len));
1183 return NS_OK;
1186 NS_IMETHODIMP
1187 nsNSSCertificateDB::GetCerts(nsTArray<RefPtr<nsIX509Cert>>& _retval) {
1188 nsresult rv = BlockUntilLoadableCertsLoaded();
1189 if (NS_FAILED(rv)) {
1190 return rv;
1193 rv = CheckForSmartCardChanges();
1194 if (NS_FAILED(rv)) {
1195 return rv;
1198 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1199 UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
1200 if (!certList) {
1201 return NS_ERROR_FAILURE;
1203 return nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(certList,
1204 _retval);
1207 nsresult IsCertBuiltInRoot(const RefPtr<nsIX509Cert>& cert,
1208 bool& isBuiltInRoot) {
1209 nsTArray<uint8_t> der;
1210 nsresult rv = cert->GetRawDER(der);
1211 if (NS_FAILED(rv)) {
1212 return rv;
1214 pkix::Input certInput;
1215 pkix::Result result = certInput.Init(der.Elements(), der.Length());
1216 if (result != pkix::Result::Success) {
1217 return NS_ERROR_FAILURE;
1219 result = IsCertBuiltInRoot(certInput, isBuiltInRoot);
1220 if (result != pkix::Result::Success) {
1221 return NS_ERROR_FAILURE;
1223 return NS_OK;
1226 NS_IMETHODIMP
1227 nsNSSCertificateDB::AsyncHasThirdPartyRoots(nsIAsyncBoolCallback* aCallback) {
1228 NS_ENSURE_ARG_POINTER(aCallback);
1229 nsMainThreadPtrHandle<nsIAsyncBoolCallback> callback(
1230 new nsMainThreadPtrHolder<nsIAsyncBoolCallback>("AsyncHasThirdPartyRoots",
1231 aCallback));
1233 return NS_DispatchBackgroundTask(
1234 NS_NewRunnableFunction(
1235 "nsNSSCertificateDB::AsyncHasThirdPartyRoots",
1236 [cb = std::move(callback), self = RefPtr{this}] {
1237 bool hasThirdPartyRoots = [self]() -> bool {
1238 nsTArray<RefPtr<nsIX509Cert>> certs;
1239 nsresult rv = self->GetCerts(certs);
1240 if (NS_FAILED(rv)) {
1241 return false;
1244 for (const auto& cert : certs) {
1245 bool isTrusted = false;
1246 nsresult rv =
1247 self->IsCertTrusted(cert, nsIX509Cert::CA_CERT,
1248 nsIX509CertDB::TRUSTED_SSL, &isTrusted);
1249 if (NS_FAILED(rv)) {
1250 return false;
1253 if (!isTrusted) {
1254 continue;
1257 bool isBuiltInRoot = false;
1258 rv = IsCertBuiltInRoot(cert, isBuiltInRoot);
1259 if (NS_FAILED(rv)) {
1260 return false;
1263 if (!isBuiltInRoot) {
1264 return true;
1268 return false;
1269 }();
1271 NS_DispatchToMainThread(NS_NewRunnableFunction(
1272 "nsNSSCertificateDB::AsyncHasThirdPartyRoots callback",
1273 [cb, hasThirdPartyRoots]() {
1274 cb->OnResult(hasThirdPartyRoots);
1275 }));
1277 NS_DISPATCH_EVENT_MAY_BLOCK);
1280 nsresult VerifyCertAtTime(nsIX509Cert* aCert,
1281 int64_t /*SECCertificateUsage*/ aUsage,
1282 uint32_t aFlags, const nsACString& aHostname,
1283 mozilla::pkix::Time aTime,
1284 nsTArray<RefPtr<nsIX509Cert>>& aVerifiedChain,
1285 bool* aHasEVPolicy,
1286 int32_t* /*PRErrorCode*/ _retval) {
1287 NS_ENSURE_ARG_POINTER(aCert);
1288 NS_ENSURE_ARG_POINTER(aHasEVPolicy);
1289 NS_ENSURE_ARG_POINTER(_retval);
1291 if (!aVerifiedChain.IsEmpty()) {
1292 return NS_ERROR_INVALID_ARG;
1295 *aHasEVPolicy = false;
1296 *_retval = PR_UNKNOWN_ERROR;
1298 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1299 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1301 nsTArray<nsTArray<uint8_t>> resultChain;
1302 EVStatus evStatus;
1303 mozilla::pkix::Result result;
1305 nsTArray<uint8_t> certBytes;
1306 nsresult nsrv = aCert->GetRawDER(certBytes);
1307 if (NS_FAILED(nsrv)) {
1308 return nsrv;
1311 if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
1312 result =
1313 certVerifier->VerifySSLServerCert(certBytes, aTime,
1314 nullptr, // Assume no context
1315 aHostname, resultChain, aFlags,
1316 Nothing(), // extraCertificates
1317 Nothing(), // stapledOCSPResponse
1318 Nothing(), // sctsFromTLSExtension
1319 Nothing(), // dcInfo
1320 OriginAttributes(), &evStatus);
1321 } else {
1322 const nsCString& flatHostname = PromiseFlatCString(aHostname);
1323 result = certVerifier->VerifyCert(
1324 certBytes, aUsage, aTime,
1325 nullptr, // Assume no context
1326 aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
1327 Nothing(), // extraCertificates
1328 Nothing(), // stapledOCSPResponse
1329 Nothing(), // sctsFromTLSExtension
1330 OriginAttributes(), &evStatus);
1333 if (result == mozilla::pkix::Success) {
1334 for (auto& certDER : resultChain) {
1335 RefPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
1336 aVerifiedChain.AppendElement(cert);
1339 if (evStatus == EVStatus::EV) {
1340 *aHasEVPolicy = true;
1344 *_retval = mozilla::pkix::MapResultToPRErrorCode(result);
1346 return NS_OK;
1349 class VerifyCertAtTimeTask final : public CryptoTask {
1350 public:
1351 VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
1352 const nsACString& aHostname, uint64_t aTime,
1353 nsICertVerificationCallback* aCallback)
1354 : mCert(aCert),
1355 mUsage(aUsage),
1356 mFlags(aFlags),
1357 mHostname(aHostname),
1358 mTime(aTime),
1359 mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(
1360 "nsICertVerificationCallback", aCallback)),
1361 mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE),
1362 mHasEVPolicy(false) {}
1364 private:
1365 virtual nsresult CalculateResult() override {
1366 nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
1367 if (!certDB) {
1368 return NS_ERROR_FAILURE;
1370 return VerifyCertAtTime(mCert, mUsage, mFlags, mHostname,
1371 mozilla::pkix::TimeFromEpochInSeconds(mTime),
1372 mVerifiedCertList, &mHasEVPolicy, &mPRErrorCode);
1375 virtual void CallCallback(nsresult rv) override {
1376 if (NS_FAILED(rv)) {
1377 nsTArray<RefPtr<nsIX509Cert>> tmp;
1378 Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE, tmp,
1379 false);
1380 } else {
1381 Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
1382 mHasEVPolicy);
1386 nsCOMPtr<nsIX509Cert> mCert;
1387 int64_t mUsage;
1388 uint32_t mFlags;
1389 nsCString mHostname;
1390 uint64_t mTime;
1391 nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
1392 int32_t mPRErrorCode;
1393 nsTArray<RefPtr<nsIX509Cert>> mVerifiedCertList;
1394 bool mHasEVPolicy;
1397 NS_IMETHODIMP
1398 nsNSSCertificateDB::AsyncVerifyCertAtTime(
1399 nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags,
1400 const nsACString& aHostname, uint64_t aTime,
1401 nsICertVerificationCallback* aCallback) {
1402 RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(
1403 aCert, aUsage, aFlags, aHostname, aTime, aCallback));
1404 return task->Dispatch();
1407 NS_IMETHODIMP
1408 nsNSSCertificateDB::ClearOCSPCache() {
1409 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1410 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1411 certVerifier->ClearOCSPCache();
1412 return NS_OK;