Bug 1914115 - Ensure UPLOAD_PATH is set before using it for profile logs r=perftest...
[gecko.git] / security / manager / ssl / nsNSSCertificateDB.cpp
blob3a8b106348976e5578e46dbc68678c3a9b38cc5b
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;
193 // NSS ignores the first argument to CERT_ChangeCertTrust
194 SECStatus srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
195 if (srv == SECSuccess || PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
196 return srv;
198 if (cert->slot) {
199 // If this certificate is on an external PKCS#11 token, we have to
200 // authenticate to that token.
201 srv = PK11_Authenticate(cert->slot, PR_TRUE, ctx);
202 } else {
203 // Otherwise, the certificate is on the internal module.
204 UniquePK11SlotInfo internalSlot(PK11_GetInternalKeySlot());
205 srv = PK11_Authenticate(internalSlot.get(), PR_TRUE, ctx);
207 if (srv != SECSuccess) {
208 return srv;
210 return CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
213 static nsresult ImportCertsIntoPermanentStorage(
214 const UniqueCERTCertList& certChain) {
215 bool encounteredFailure = false;
216 PRErrorCode savedErrorCode = 0;
217 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
218 for (CERTCertListNode* chainNode = CERT_LIST_HEAD(certChain);
219 !CERT_LIST_END(chainNode, certChain);
220 chainNode = CERT_LIST_NEXT(chainNode)) {
221 UniquePORTString nickname(CERT_MakeCANickname(chainNode->cert));
222 SECStatus srv = PK11_ImportCert(slot.get(), chainNode->cert,
223 CK_INVALID_HANDLE, nickname.get(),
224 false); // this parameter is ignored by NSS
225 if (srv != SECSuccess) {
226 encounteredFailure = true;
227 savedErrorCode = PR_GetError();
231 if (encounteredFailure) {
232 return GetXPCOMFromNSSError(savedErrorCode);
235 return NS_OK;
238 nsresult nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
239 nsIInterfaceRequestor* ctx) {
240 // First thing we have to do is figure out which certificate we're
241 // gonna present to the user. The CA may have sent down a list of
242 // certs which may or may not be a chained list of certs. Until
243 // the day we can design some solid UI for the general case, we'll
244 // code to the > 90% case. That case is where a CA sends down a
245 // list that is a hierarchy whose root is either the first or
246 // the last cert. What we're gonna do is compare the first
247 // 2 entries, if the second was signed by the first, we assume
248 // the root cert is the first cert and display it. Otherwise,
249 // we compare the last 2 entries, if the second to last cert was
250 // signed by the last cert, then we assume the last cert is the
251 // root and display it.
253 uint32_t numCerts;
255 x509Certs->GetLength(&numCerts);
257 if (numCerts == 0) return NS_OK; // Nothing to import, so nothing to do.
259 nsCOMPtr<nsIX509Cert> certToShow;
260 uint32_t selCertIndex;
261 if (numCerts == 1) {
262 // There's only one cert, so let's show it.
263 selCertIndex = 0;
264 certToShow = do_QueryElementAt(x509Certs, selCertIndex);
265 } else {
266 nsCOMPtr<nsIX509Cert> cert0; // first cert
267 nsCOMPtr<nsIX509Cert> cert1; // second cert
268 nsCOMPtr<nsIX509Cert> certn_2; // second to last cert
269 nsCOMPtr<nsIX509Cert> certn_1; // last cert
271 cert0 = do_QueryElementAt(x509Certs, 0);
272 cert1 = do_QueryElementAt(x509Certs, 1);
273 certn_2 = do_QueryElementAt(x509Certs, numCerts - 2);
274 certn_1 = do_QueryElementAt(x509Certs, numCerts - 1);
276 nsAutoString cert0SubjectName;
277 nsAutoString cert1IssuerName;
278 nsAutoString certn_2IssuerName;
279 nsAutoString certn_1SubjectName;
281 cert0->GetSubjectName(cert0SubjectName);
282 cert1->GetIssuerName(cert1IssuerName);
283 certn_2->GetIssuerName(certn_2IssuerName);
284 certn_1->GetSubjectName(certn_1SubjectName);
286 if (cert1IssuerName.Equals(cert0SubjectName)) {
287 // In this case, the first cert in the list signed the second,
288 // so the first cert is the root. Let's display it.
289 selCertIndex = 0;
290 certToShow = cert0;
291 } else if (certn_2IssuerName.Equals(certn_1SubjectName)) {
292 // In this case the last cert has signed the second to last cert.
293 // The last cert is the root, so let's display it.
294 selCertIndex = numCerts - 1;
295 certToShow = certn_1;
296 } else {
297 // It's not a chain, so let's just show the first one in the
298 // downloaded list.
299 selCertIndex = 0;
300 certToShow = cert0;
304 if (!certToShow) return NS_ERROR_FAILURE;
306 nsCOMPtr<nsICertificateDialogs> dialogs;
307 nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
308 NS_GET_IID(nsICertificateDialogs),
309 NS_CERTIFICATEDIALOGS_CONTRACTID);
310 if (NS_FAILED(rv)) {
311 return rv;
314 UniqueCERTCertificate tmpCert(certToShow->GetCert());
315 if (!tmpCert) {
316 return NS_ERROR_FAILURE;
319 if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
320 DisplayCertificateAlert(ctx, "NotACACert", certToShow);
321 return NS_ERROR_FAILURE;
324 if (tmpCert->isperm) {
325 DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
326 return NS_ERROR_FAILURE;
329 uint32_t trustBits;
330 bool allows;
331 rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
332 if (NS_FAILED(rv)) return rv;
334 if (!allows) return NS_ERROR_NOT_AVAILABLE;
336 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
337 UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
339 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
340 ("Created nick \"%s\"\n", nickname.get()));
342 nsNSSCertTrust trust;
343 trust.SetValidCA();
344 trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
345 !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL));
347 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
348 SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
349 nickname.get(),
350 false); // this parameter is ignored by NSS
351 if (srv != SECSuccess) {
352 return MapSECStatus(srv);
354 srv =
355 ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(), ctx);
356 if (srv != SECSuccess) {
357 return MapSECStatus(srv);
360 // Import additional delivered certificates that can be verified.
362 // build a CertList for filtering
363 UniqueCERTCertList certList(CERT_NewCertList());
364 if (!certList) {
365 return NS_ERROR_FAILURE;
368 // get all remaining certs into temp store
370 for (uint32_t i = 0; i < numCerts; i++) {
371 if (i == selCertIndex) {
372 // we already processed that one
373 continue;
376 nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
377 if (!remainingCert) {
378 continue;
381 UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
382 if (!tmpCert2) {
383 continue; // Let's try to import the rest of 'em
386 if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
387 continue;
390 Unused << tmpCert2.release();
393 return ImportCertsIntoPermanentStorage(certList);
396 nsresult nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
397 const UniqueCERTCertList& aCertListIn,
398 nsTArray<RefPtr<nsIX509Cert>>& aCertListOut) {
399 if (!aCertListIn.get()) {
400 return NS_ERROR_INVALID_ARG;
403 for (CERTCertListNode* node = CERT_LIST_HEAD(aCertListIn.get());
404 !CERT_LIST_END(node, aCertListIn.get()); node = CERT_LIST_NEXT(node)) {
405 RefPtr<nsIX509Cert> cert = new nsNSSCertificate(node->cert);
406 aCertListOut.AppendElement(cert);
408 return NS_OK;
411 NS_IMETHODIMP
412 nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
413 uint32_t type,
414 nsIInterfaceRequestor* ctx) {
415 // We currently only handle CA certificates.
416 if (type != nsIX509Cert::CA_CERT) {
417 return NS_ERROR_FAILURE;
420 nsTArray<nsTArray<uint8_t>> certsArray;
421 nsresult rv = getCertsFromPackage(certsArray, data, length);
422 if (NS_FAILED(rv)) {
423 return rv;
426 nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
427 if (!array) {
428 return NS_ERROR_FAILURE;
431 // Now let's create some certs to work with
432 for (nsTArray<uint8_t>& certDER : certsArray) {
433 nsCOMPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
434 nsresult rv = array->AppendElement(cert);
435 if (NS_FAILED(rv)) {
436 return rv;
440 return handleCACertDownload(WrapNotNull(array), ctx);
444 * Decodes a given array of DER-encoded certificates into temporary storage.
446 * @param certs
447 * Array in which the decoded certificates are stored as arrays of
448 * unsigned chars.
449 * @param temporaryCerts
450 * List of decoded certificates.
452 static nsresult ImportCertsIntoTempStorage(
453 nsTArray<nsTArray<uint8_t>>& certs,
454 /*out*/ const UniqueCERTCertList& temporaryCerts) {
455 NS_ENSURE_ARG_POINTER(temporaryCerts);
457 for (nsTArray<uint8_t>& certDER : certs) {
458 CERTCertificate* certificate;
459 SECItem certItem;
460 certItem.len = certDER.Length();
461 certItem.data = certDER.Elements();
462 certificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem,
463 nullptr, false, true);
465 UniqueCERTCertificate cert(certificate);
466 if (!cert) {
467 continue;
470 if (CERT_AddCertToListTail(temporaryCerts.get(), cert.get()) ==
471 SECSuccess) {
472 Unused << cert.release();
476 return NS_OK;
479 NS_IMETHODIMP
480 nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
481 nsIInterfaceRequestor* ctx) {
482 nsTArray<nsTArray<uint8_t>> certsArray;
484 nsresult rv = getCertsFromPackage(certsArray, data, length);
485 if (NS_FAILED(rv)) {
486 return rv;
489 UniqueCERTCertList temporaryCerts(CERT_NewCertList());
490 if (!temporaryCerts) {
491 return NS_ERROR_FAILURE;
494 rv = ImportCertsIntoTempStorage(certsArray, temporaryCerts);
495 if (NS_FAILED(rv)) {
496 return rv;
499 return ImportCertsIntoPermanentStorage(temporaryCerts);
502 nsresult nsNSSCertificateDB::ImportCACerts(nsTArray<nsTArray<uint8_t>>& caCerts,
503 nsIInterfaceRequestor* ctx) {
504 UniqueCERTCertList temporaryCerts(CERT_NewCertList());
505 if (!temporaryCerts) {
506 return NS_ERROR_FAILURE;
509 nsresult rv = ImportCertsIntoTempStorage(caCerts, temporaryCerts);
510 if (NS_FAILED(rv)) {
511 return rv;
514 return ImportCertsIntoPermanentStorage(temporaryCerts);
517 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor* ctx,
518 const char* stringID,
519 nsIX509Cert* certToShow) {
520 if (!NS_IsMainThread()) {
521 NS_ERROR(
522 "nsNSSCertificateDB::DisplayCertificateAlert called off the main "
523 "thread");
524 return;
527 nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
528 if (!my_ctx) {
529 my_ctx = new PipUIContext();
532 // This shall be replaced by embedding ovverridable prompts
533 // as discussed in bug 310446, and should make use of certToShow.
535 nsAutoString tmpMessage;
536 GetPIPNSSBundleString(stringID, tmpMessage);
537 nsCOMPtr<nsIPrompt> prompt(do_GetInterface(my_ctx));
538 if (!prompt) {
539 return;
542 prompt->Alert(nullptr, tmpMessage.get());
545 NS_IMETHODIMP
546 nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
547 nsIInterfaceRequestor* ctx) {
548 if (!NS_IsMainThread()) {
549 NS_ERROR(
550 "nsNSSCertificateDB::ImportUserCertificate called off the main thread");
551 return NS_ERROR_NOT_SAME_THREAD;
554 nsTArray<nsTArray<uint8_t>> certsArray;
556 nsresult rv = getCertsFromPackage(certsArray, data, length);
557 if (NS_FAILED(rv)) {
558 return rv;
561 SECItem certItem;
563 if (certsArray.IsEmpty()) {
564 return NS_OK;
567 certItem.len = certsArray.ElementAt(0).Length();
568 certItem.data = certsArray.ElementAt(0).Elements();
570 UniqueCERTCertificate cert(CERT_NewTempCertificate(
571 CERT_GetDefaultCertDB(), &certItem, nullptr, false, true));
572 if (!cert) {
573 return NS_ERROR_FAILURE;
576 UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
577 if (!slot) {
578 nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
579 DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow);
580 return NS_ERROR_FAILURE;
582 slot = nullptr;
584 /* pick a nickname for the cert */
585 nsAutoCString nickname;
586 if (cert->nickname) {
587 nickname = cert->nickname;
588 } else {
589 get_default_nickname(cert.get(), ctx, nickname);
592 /* user wants to import the cert */
593 slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
594 if (!slot) {
595 return NS_ERROR_FAILURE;
597 slot = nullptr;
600 nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
601 DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
604 rv = NS_OK;
605 if (!certsArray.IsEmpty()) {
606 certsArray.RemoveElementAt(0);
607 rv = ImportCACerts(certsArray, ctx);
610 nsCOMPtr<nsIObserverService> observerService =
611 mozilla::services::GetObserverService();
612 if (observerService) {
613 observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
614 nullptr);
617 return rv;
620 NS_IMETHODIMP
621 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert* aCert) {
622 NS_ENSURE_ARG_POINTER(aCert);
623 UniqueCERTCertificate cert(aCert->GetCert());
624 if (!cert) {
625 return NS_ERROR_FAILURE;
628 // Temporary certificates aren't on a slot and will go away when the
629 // nsIX509Cert is destructed.
630 if (cert->slot) {
631 uint32_t certType;
632 nsresult rv = aCert->GetCertType(&certType);
633 if (NS_WARN_IF(NS_FAILED(rv))) {
634 return rv;
636 if (certType == nsIX509Cert::USER_CERT) {
637 SECStatus srv = PK11_Authenticate(cert->slot, true, nullptr);
638 if (srv != SECSuccess) {
639 return NS_ERROR_FAILURE;
641 srv = PK11_DeleteTokenCertAndKey(cert.get(), nullptr);
642 if (srv != SECSuccess) {
643 return NS_ERROR_FAILURE;
645 } else {
646 // For certificates that can't be deleted (e.g. built-in roots), un-set
647 // all trust bits.
648 nsNSSCertTrust trust(0, 0);
649 SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
650 cert, trust.GetTrust(), nullptr);
651 if (srv != SECSuccess) {
652 return NS_ERROR_FAILURE;
654 if (!PK11_IsReadOnly(cert->slot)) {
655 srv = SEC_DeletePermCertificate(cert.get());
656 if (srv != SECSuccess) {
657 return NS_ERROR_FAILURE;
663 nsCOMPtr<nsIObserverService> observerService =
664 mozilla::services::GetObserverService();
665 if (observerService) {
666 observerService->NotifyObservers(nullptr, "psm:user-certificate-deleted",
667 nullptr);
670 return NS_OK;
673 NS_IMETHODIMP
674 nsNSSCertificateDB::SetCertTrust(nsIX509Cert* cert, uint32_t type,
675 uint32_t trusted) {
676 NS_ENSURE_ARG_POINTER(cert);
677 nsNSSCertTrust trust;
678 switch (type) {
679 case nsIX509Cert::CA_CERT:
680 trust.SetValidCA();
681 trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
682 !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
683 break;
684 case nsIX509Cert::SERVER_CERT:
685 trust.SetValidPeer();
686 trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false);
687 break;
688 case nsIX509Cert::EMAIL_CERT:
689 trust.SetValidPeer();
690 trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
691 break;
692 default:
693 // Ignore any other type of certificate (including invalid types).
694 return NS_OK;
697 UniqueCERTCertificate nsscert(cert->GetCert());
698 SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
699 nsscert, trust.GetTrust(), nullptr);
700 return MapSECStatus(srv);
703 NS_IMETHODIMP
704 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert* cert, uint32_t certType,
705 uint32_t trustType, bool* _isTrusted) {
706 NS_ENSURE_ARG_POINTER(_isTrusted);
707 *_isTrusted = false;
709 nsresult rv = BlockUntilLoadableCertsLoaded();
710 if (NS_FAILED(rv)) {
711 return rv;
714 SECStatus srv;
715 UniqueCERTCertificate nsscert(cert->GetCert());
716 CERTCertTrust nsstrust;
717 srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
718 if (srv != SECSuccess) {
719 // CERT_GetCertTrust returns SECFailure if given a temporary cert that
720 // doesn't have any trust information yet. This isn't an error.
721 return NS_OK;
724 nsNSSCertTrust trust(&nsstrust);
725 if (certType == nsIX509Cert::CA_CERT) {
726 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
727 *_isTrusted = trust.HasTrustedCA(true, false);
728 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
729 *_isTrusted = trust.HasTrustedCA(false, true);
730 } else {
731 return NS_ERROR_FAILURE;
733 } else if (certType == nsIX509Cert::SERVER_CERT) {
734 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
735 *_isTrusted = trust.HasTrustedPeer(true, false);
736 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
737 *_isTrusted = trust.HasTrustedPeer(false, true);
738 } else {
739 return NS_ERROR_FAILURE;
741 } else if (certType == nsIX509Cert::EMAIL_CERT) {
742 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
743 *_isTrusted = trust.HasTrustedPeer(true, false);
744 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
745 *_isTrusted = trust.HasTrustedPeer(false, true);
746 } else {
747 return NS_ERROR_FAILURE;
749 } /* user: ignore */
750 return NS_OK;
753 NS_IMETHODIMP
754 nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) {
755 NS_ENSURE_ARG(aFile);
756 switch (aType) {
757 case nsIX509Cert::CA_CERT:
758 case nsIX509Cert::EMAIL_CERT:
759 // good
760 break;
762 default:
763 // not supported (yet)
764 return NS_ERROR_FAILURE;
767 PRFileDesc* fd = nullptr;
768 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
769 if (NS_FAILED(rv)) {
770 return rv;
772 if (!fd) {
773 return NS_ERROR_FAILURE;
776 PRFileInfo fileInfo;
777 if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
778 return NS_ERROR_FAILURE;
781 auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
782 int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
783 PR_Close(fd);
785 if (bytesObtained != fileInfo.size) {
786 return NS_ERROR_FAILURE;
789 nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
791 switch (aType) {
792 case nsIX509Cert::CA_CERT:
793 return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
794 case nsIX509Cert::EMAIL_CERT:
795 return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
796 default:
797 MOZ_ASSERT(false, "Unsupported type should have been filtered out");
798 break;
801 return NS_ERROR_FAILURE;
804 NS_IMETHODIMP
805 nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile, const nsAString& aPassword,
806 uint32_t* aError) {
807 if (!NS_IsMainThread()) {
808 return NS_ERROR_NOT_SAME_THREAD;
810 nsresult rv = BlockUntilLoadableCertsLoaded();
811 if (NS_FAILED(rv)) {
812 return rv;
815 NS_ENSURE_ARG(aFile);
816 nsPKCS12Blob blob;
817 rv = blob.ImportFromFile(aFile, aPassword, *aError);
818 nsCOMPtr<nsIObserverService> observerService =
819 mozilla::services::GetObserverService();
820 if (NS_SUCCEEDED(rv) && observerService) {
821 observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
822 nullptr);
825 return rv;
828 NS_IMETHODIMP
829 nsNSSCertificateDB::ExportPKCS12File(
830 nsIFile* aFile, const nsTArray<RefPtr<nsIX509Cert>>& aCerts,
831 const nsAString& aPassword, uint32_t* aError) {
832 if (!NS_IsMainThread()) {
833 return NS_ERROR_NOT_SAME_THREAD;
835 nsresult rv = BlockUntilLoadableCertsLoaded();
836 if (NS_FAILED(rv)) {
837 return rv;
840 NS_ENSURE_ARG(aFile);
841 if (aCerts.IsEmpty()) {
842 return NS_OK;
844 nsPKCS12Blob blob;
845 return blob.ExportToFile(aFile, aCerts, aPassword, *aError);
848 NS_IMETHODIMP
849 nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
850 /*out*/ nsIX509Cert** _retval) {
851 if (!_retval) {
852 return NS_ERROR_INVALID_POINTER;
855 // Base64Decode() doesn't consider a zero length input as an error, and just
856 // returns the empty string. We don't want this behavior, so the below check
857 // catches this case.
858 if (base64.Length() < 1) {
859 return NS_ERROR_ILLEGAL_VALUE;
862 nsAutoCString certDER;
863 nsresult rv = Base64Decode(base64, certDER);
864 if (NS_FAILED(rv)) {
865 return rv;
868 return ConstructX509FromSpan(AsBytes(Span(certDER)), _retval);
871 NS_IMETHODIMP
872 nsNSSCertificateDB::ConstructX509(const nsTArray<uint8_t>& certDER,
873 nsIX509Cert** _retval) {
874 return ConstructX509FromSpan(Span(certDER.Elements(), certDER.Length()),
875 _retval);
878 nsresult nsNSSCertificateDB::ConstructX509FromSpan(
879 Span<const uint8_t> aInputSpan, nsIX509Cert** _retval) {
880 if (NS_WARN_IF(!_retval)) {
881 return NS_ERROR_INVALID_POINTER;
884 if (aInputSpan.Length() > std::numeric_limits<unsigned int>::max()) {
885 return NS_ERROR_ILLEGAL_VALUE;
888 SECItem certData;
889 certData.type = siDERCertBuffer;
890 certData.data = const_cast<unsigned char*>(
891 reinterpret_cast<const unsigned char*>(aInputSpan.Elements()));
892 certData.len = aInputSpan.Length();
894 UniqueCERTCertificate cert(CERT_NewTempCertificate(
895 CERT_GetDefaultCertDB(), &certData, nullptr, false, true));
896 if (!cert)
897 return (PORT_GetError() == SEC_ERROR_NO_MEMORY) ? NS_ERROR_OUT_OF_MEMORY
898 : NS_ERROR_FAILURE;
900 nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
901 nssCert.forget(_retval);
902 return NS_OK;
905 void nsNSSCertificateDB::get_default_nickname(CERTCertificate* cert,
906 nsIInterfaceRequestor* ctx,
907 nsCString& nickname) {
908 nickname.Truncate();
910 CK_OBJECT_HANDLE keyHandle;
912 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
913 return;
916 CERTCertDBHandle* defaultcertdb = CERT_GetDefaultCertDB();
917 nsAutoCString username;
918 UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
919 if (tempCN) {
920 username = tempCN.get();
923 nsAutoCString caname;
924 UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
925 if (tempIssuerOrg) {
926 caname = tempIssuerOrg.get();
929 nsAutoString tmpNickFmt;
930 GetPIPNSSBundleString("nick_template", tmpNickFmt);
931 NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
933 nsAutoCString baseName;
934 baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
935 if (baseName.IsEmpty()) {
936 return;
939 nickname = baseName;
942 * We need to see if the private key exists on a token, if it does
943 * then we need to check for nicknames that already exist on the smart
944 * card.
946 UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
947 if (!slot) return;
949 if (!PK11_IsInternal(slot.get())) {
950 nsAutoCString tmp;
951 tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
952 if (tmp.IsEmpty()) {
953 nickname.Truncate();
954 return;
956 baseName = tmp;
957 nickname = baseName;
960 int count = 1;
961 while (true) {
962 if (count > 1) {
963 nsAutoCString tmp;
964 tmp.AppendPrintf("%s #%d", baseName.get(), count);
965 if (tmp.IsEmpty()) {
966 nickname.Truncate();
967 return;
969 nickname = tmp;
972 UniqueCERTCertificate dummycert;
974 if (PK11_IsInternal(slot.get())) {
975 /* look up the nickname to make sure it isn't in use already */
976 dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
977 } else {
978 // Check the cert against others that already live on the smart card.
979 dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
980 if (dummycert) {
981 // Make sure the subject names are different.
982 if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) {
984 * There is another certificate with the same nickname and
985 * the same subject name on the smart card, so let's use this
986 * nickname.
988 dummycert = nullptr;
992 if (!dummycert) {
993 break;
995 count++;
999 NS_IMETHODIMP
1000 nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
1001 const nsACString& aTrust,
1002 nsIX509Cert** addedCertificate) {
1003 // Base64Decode() doesn't consider a zero length input as an error, and just
1004 // returns the empty string. We don't want this behavior, so the below check
1005 // catches this case.
1006 if (aBase64.Length() < 1) {
1007 return NS_ERROR_ILLEGAL_VALUE;
1010 nsAutoCString aCertDER;
1011 nsresult rv = Base64Decode(aBase64, aCertDER);
1012 if (NS_FAILED(rv)) {
1013 return rv;
1015 return AddCert(aCertDER, aTrust, addedCertificate);
1018 NS_IMETHODIMP
1019 nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
1020 const nsACString& aTrust,
1021 nsIX509Cert** addedCertificate) {
1022 MOZ_ASSERT(addedCertificate);
1023 if (!addedCertificate) {
1024 return NS_ERROR_INVALID_ARG;
1026 *addedCertificate = nullptr;
1028 nsNSSCertTrust trust;
1029 if (CERT_DecodeTrustString(&trust.GetTrust(),
1030 PromiseFlatCString(aTrust).get()) != SECSuccess) {
1031 return NS_ERROR_FAILURE;
1034 nsCOMPtr<nsIX509Cert> newCert;
1035 nsresult rv =
1036 ConstructX509FromSpan(AsBytes(Span(aCertDER)), getter_AddRefs(newCert));
1037 if (NS_FAILED(rv)) {
1038 return rv;
1041 UniqueCERTCertificate tmpCert(newCert->GetCert());
1042 if (!tmpCert) {
1043 return NS_ERROR_FAILURE;
1046 // If there's already a certificate that matches this one in the database, we
1047 // still want to set its trust to the given value.
1048 if (tmpCert->isperm) {
1049 rv = SetCertTrustFromString(newCert, aTrust);
1050 if (NS_FAILED(rv)) {
1051 return rv;
1053 newCert.forget(addedCertificate);
1054 return NS_OK;
1057 UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
1059 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1060 ("Created nick \"%s\"\n", nickname.get()));
1062 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1063 SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
1064 nickname.get(),
1065 false); // this parameter is ignored by NSS
1066 if (srv != SECSuccess) {
1067 return MapSECStatus(srv);
1069 srv = ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(),
1070 nullptr);
1071 if (srv != SECSuccess) {
1072 return MapSECStatus(srv);
1074 newCert.forget(addedCertificate);
1075 return NS_OK;
1078 NS_IMETHODIMP
1079 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
1080 const nsACString& trustString) {
1081 NS_ENSURE_ARG(cert);
1083 CERTCertTrust trust;
1084 SECStatus srv =
1085 CERT_DecodeTrustString(&trust, PromiseFlatCString(trustString).get());
1086 if (srv != SECSuccess) {
1087 return MapSECStatus(srv);
1089 UniqueCERTCertificate nssCert(cert->GetCert());
1091 srv = ChangeCertTrustWithPossibleAuthentication(nssCert, trust, nullptr);
1092 return MapSECStatus(srv);
1095 NS_IMETHODIMP nsNSSCertificateDB::AsPKCS7Blob(
1096 const nsTArray<RefPtr<nsIX509Cert>>& certList, nsACString& _retval) {
1097 if (certList.IsEmpty()) {
1098 return NS_ERROR_INVALID_ARG;
1101 UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
1102 if (!cmsg) {
1103 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1104 ("nsNSSCertificateDB::AsPKCS7Blob - can't create CMS message"));
1105 return NS_ERROR_OUT_OF_MEMORY;
1108 UniqueNSSCMSSignedData sigd(nullptr);
1109 for (const auto& cert : certList) {
1110 // We need an owning handle when calling nsIX509Cert::GetCert().
1111 UniqueCERTCertificate nssCert(cert->GetCert());
1112 if (!sigd) {
1113 sigd.reset(
1114 NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), false));
1115 if (!sigd) {
1116 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1117 ("nsNSSCertificateDB::AsPKCS7Blob - can't create SignedData"));
1118 return NS_ERROR_FAILURE;
1120 } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) !=
1121 SECSuccess) {
1122 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1123 ("nsNSSCertificateDB::AsPKCS7Blob - can't add cert"));
1124 return NS_ERROR_FAILURE;
1128 NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get());
1129 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) !=
1130 SECSuccess) {
1131 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1132 ("nsNSSCertificateDB::AsPKCS7Blob - can't attach SignedData"));
1133 return NS_ERROR_FAILURE;
1135 // cmsg owns sigd now.
1136 Unused << sigd.release();
1138 UniquePLArenaPool arena(PORT_NewArena(1024));
1139 if (!arena) {
1140 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1141 ("nsNSSCertificateDB::AsPKCS7Blob - out of memory"));
1142 return NS_ERROR_OUT_OF_MEMORY;
1145 SECItem certP7 = {siBuffer, nullptr, 0};
1146 NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(
1147 cmsg.get(), nullptr, nullptr, &certP7, arena.get(), nullptr, nullptr,
1148 nullptr, nullptr, nullptr, nullptr);
1149 if (!ecx) {
1150 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1151 ("nsNSSCertificateDB::AsPKCS7Blob - can't create encoder"));
1152 return NS_ERROR_FAILURE;
1155 if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
1156 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1157 ("nsNSSCertificateDB::AsPKCS7Blob - failed to add encoded data"));
1158 return NS_ERROR_FAILURE;
1161 _retval.Assign(nsDependentCSubstring(
1162 reinterpret_cast<const char*>(certP7.data), certP7.len));
1163 return NS_OK;
1166 NS_IMETHODIMP
1167 nsNSSCertificateDB::GetCerts(nsTArray<RefPtr<nsIX509Cert>>& _retval) {
1168 nsresult rv = BlockUntilLoadableCertsLoaded();
1169 if (NS_FAILED(rv)) {
1170 return rv;
1173 rv = CheckForSmartCardChanges();
1174 if (NS_FAILED(rv)) {
1175 return rv;
1178 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1179 UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
1180 if (!certList) {
1181 return NS_ERROR_FAILURE;
1183 return nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(certList,
1184 _retval);
1187 nsresult IsCertBuiltInRoot(const RefPtr<nsIX509Cert>& cert,
1188 bool& isBuiltInRoot) {
1189 nsTArray<uint8_t> der;
1190 nsresult rv = cert->GetRawDER(der);
1191 if (NS_FAILED(rv)) {
1192 return rv;
1194 pkix::Input certInput;
1195 pkix::Result result = certInput.Init(der.Elements(), der.Length());
1196 if (result != pkix::Result::Success) {
1197 return NS_ERROR_FAILURE;
1199 result = IsCertBuiltInRoot(certInput, isBuiltInRoot);
1200 if (result != pkix::Result::Success) {
1201 return NS_ERROR_FAILURE;
1203 return NS_OK;
1206 NS_IMETHODIMP
1207 nsNSSCertificateDB::AsyncHasThirdPartyRoots(nsIAsyncBoolCallback* aCallback) {
1208 NS_ENSURE_ARG_POINTER(aCallback);
1209 nsMainThreadPtrHandle<nsIAsyncBoolCallback> callback(
1210 new nsMainThreadPtrHolder<nsIAsyncBoolCallback>("AsyncHasThirdPartyRoots",
1211 aCallback));
1213 return NS_DispatchBackgroundTask(
1214 NS_NewRunnableFunction(
1215 "nsNSSCertificateDB::AsyncHasThirdPartyRoots",
1216 [cb = std::move(callback), self = RefPtr{this}] {
1217 bool hasThirdPartyRoots = [self]() -> bool {
1218 nsTArray<RefPtr<nsIX509Cert>> certs;
1219 nsresult rv = self->GetCerts(certs);
1220 if (NS_FAILED(rv)) {
1221 return false;
1224 for (const auto& cert : certs) {
1225 bool isTrusted = false;
1226 nsresult rv =
1227 self->IsCertTrusted(cert, nsIX509Cert::CA_CERT,
1228 nsIX509CertDB::TRUSTED_SSL, &isTrusted);
1229 if (NS_FAILED(rv)) {
1230 return false;
1233 if (!isTrusted) {
1234 continue;
1237 bool isBuiltInRoot = false;
1238 rv = IsCertBuiltInRoot(cert, isBuiltInRoot);
1239 if (NS_FAILED(rv)) {
1240 return false;
1243 if (!isBuiltInRoot) {
1244 return true;
1248 return false;
1249 }();
1251 NS_DispatchToMainThread(NS_NewRunnableFunction(
1252 "nsNSSCertificateDB::AsyncHasThirdPartyRoots callback",
1253 [cb, hasThirdPartyRoots]() {
1254 cb->OnResult(hasThirdPartyRoots);
1255 }));
1257 NS_DISPATCH_EVENT_MAY_BLOCK);
1260 nsresult VerifyCertAtTime(nsIX509Cert* aCert,
1261 int64_t /*SECCertificateUsage*/ aUsage,
1262 uint32_t aFlags, const nsACString& aHostname,
1263 mozilla::pkix::Time aTime,
1264 nsTArray<RefPtr<nsIX509Cert>>& aVerifiedChain,
1265 bool* aHasEVPolicy,
1266 int32_t* /*PRErrorCode*/ _retval) {
1267 NS_ENSURE_ARG_POINTER(aCert);
1268 NS_ENSURE_ARG_POINTER(aHasEVPolicy);
1269 NS_ENSURE_ARG_POINTER(_retval);
1271 if (!aVerifiedChain.IsEmpty()) {
1272 return NS_ERROR_INVALID_ARG;
1275 *aHasEVPolicy = false;
1276 *_retval = PR_UNKNOWN_ERROR;
1278 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1279 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1281 nsTArray<nsTArray<uint8_t>> resultChain;
1282 EVStatus evStatus;
1283 mozilla::pkix::Result result;
1285 nsTArray<uint8_t> certBytes;
1286 nsresult nsrv = aCert->GetRawDER(certBytes);
1287 if (NS_FAILED(nsrv)) {
1288 return nsrv;
1291 if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
1292 result =
1293 certVerifier->VerifySSLServerCert(certBytes, aTime,
1294 nullptr, // Assume no context
1295 aHostname, resultChain, aFlags,
1296 Nothing(), // extraCertificates
1297 Nothing(), // stapledOCSPResponse
1298 Nothing(), // sctsFromTLSExtension
1299 Nothing(), // dcInfo
1300 OriginAttributes(), &evStatus);
1301 } else {
1302 const nsCString& flatHostname = PromiseFlatCString(aHostname);
1303 result = certVerifier->VerifyCert(
1304 certBytes, aUsage, aTime,
1305 nullptr, // Assume no context
1306 aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
1307 Nothing(), // extraCertificates
1308 Nothing(), // stapledOCSPResponse
1309 Nothing(), // sctsFromTLSExtension
1310 OriginAttributes(), &evStatus);
1313 if (result == mozilla::pkix::Success) {
1314 for (auto& certDER : resultChain) {
1315 RefPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
1316 aVerifiedChain.AppendElement(cert);
1319 if (evStatus == EVStatus::EV) {
1320 *aHasEVPolicy = true;
1324 *_retval = mozilla::pkix::MapResultToPRErrorCode(result);
1326 return NS_OK;
1329 class VerifyCertAtTimeTask final : public CryptoTask {
1330 public:
1331 VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
1332 const nsACString& aHostname, uint64_t aTime,
1333 nsICertVerificationCallback* aCallback)
1334 : mCert(aCert),
1335 mUsage(aUsage),
1336 mFlags(aFlags),
1337 mHostname(aHostname),
1338 mTime(aTime),
1339 mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(
1340 "nsICertVerificationCallback", aCallback)),
1341 mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE),
1342 mHasEVPolicy(false) {}
1344 private:
1345 virtual nsresult CalculateResult() override {
1346 nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
1347 if (!certDB) {
1348 return NS_ERROR_FAILURE;
1350 return VerifyCertAtTime(mCert, mUsage, mFlags, mHostname,
1351 mozilla::pkix::TimeFromEpochInSeconds(mTime),
1352 mVerifiedCertList, &mHasEVPolicy, &mPRErrorCode);
1355 virtual void CallCallback(nsresult rv) override {
1356 if (NS_FAILED(rv)) {
1357 nsTArray<RefPtr<nsIX509Cert>> tmp;
1358 Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE, tmp,
1359 false);
1360 } else {
1361 Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
1362 mHasEVPolicy);
1366 nsCOMPtr<nsIX509Cert> mCert;
1367 int64_t mUsage;
1368 uint32_t mFlags;
1369 nsCString mHostname;
1370 uint64_t mTime;
1371 nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
1372 int32_t mPRErrorCode;
1373 nsTArray<RefPtr<nsIX509Cert>> mVerifiedCertList;
1374 bool mHasEVPolicy;
1377 NS_IMETHODIMP
1378 nsNSSCertificateDB::AsyncVerifyCertAtTime(
1379 nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags,
1380 const nsACString& aHostname, uint64_t aTime,
1381 nsICertVerificationCallback* aCallback) {
1382 RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(
1383 aCert, aUsage, aFlags, aHostname, aTime, aCallback));
1384 return task->Dispatch();
1387 NS_IMETHODIMP
1388 nsNSSCertificateDB::ClearOCSPCache() {
1389 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1390 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1391 certVerifier->ClearOCSPCache();
1392 return NS_OK;