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 "SharedSSLState.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"
23 #include "nsArrayUtils.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsICertificateDialogs.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"
48 # include <winsock.h> // for ntohl
51 using namespace mozilla
;
52 using namespace mozilla::psm
;
54 extern LazyLogModule gPIPNSSLog
;
56 NS_IMPL_ISUPPORTS(nsNSSCertificateDB
, nsIX509CertDB
)
59 nsNSSCertificateDB::FindCertByDBKey(const nsACString
& aDBKey
,
60 /*out*/ nsIX509Cert
** _cert
) {
61 NS_ENSURE_ARG_POINTER(_cert
);
64 if (aDBKey
.IsEmpty()) {
65 return NS_ERROR_INVALID_ARG
;
68 nsresult rv
= BlockUntilLoadableCertsLoaded();
73 UniqueCERTCertificate cert
;
74 rv
= FindCertByDBKey(aDBKey
, cert
);
78 // If we can't find the certificate, that's not an error. Just return null.
82 nsCOMPtr
<nsIX509Cert
> nssCert
= new nsNSSCertificate(cert
.get());
83 nssCert
.forget(_cert
);
87 nsresult
nsNSSCertificateDB::FindCertByDBKey(const nsACString
& aDBKey
,
88 UniqueCERTCertificate
& cert
) {
89 static_assert(sizeof(uint64_t) == 8, "type size sanity check");
90 static_assert(sizeof(uint32_t) == 4, "type size sanity check");
91 // (From nsNSSCertificate::GetDbKey)
92 // The format of the key is the base64 encoding of the following:
93 // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
95 // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
97 // 4 bytes: <serial number length in big-endian order>
98 // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
99 // n bytes: <bytes of serial number>
100 // m bytes: <DER-encoded issuer distinguished name>
101 nsAutoCString decoded
;
102 nsAutoCString
tmpDBKey(aDBKey
);
103 // Filter out any whitespace for backwards compatibility.
104 tmpDBKey
.StripWhitespace();
105 nsresult rv
= Base64Decode(tmpDBKey
, decoded
);
109 if (decoded
.Length() < 16) {
110 return NS_ERROR_ILLEGAL_INPUT
;
112 const char* reader
= decoded
.BeginReading();
113 uint64_t zeroes
= *BitwiseCast
<const uint64_t*, const char*>(reader
);
115 return NS_ERROR_ILLEGAL_INPUT
;
117 reader
+= sizeof(uint64_t);
118 // Note: We surround the ntohl() argument with parentheses to stop the macro
119 // from thinking two arguments were passed.
120 uint32_t serialNumberLen
=
121 ntohl((*BitwiseCast
<const uint32_t*, const char*>(reader
)));
122 reader
+= sizeof(uint32_t);
124 ntohl((*BitwiseCast
<const uint32_t*, const char*>(reader
)));
125 reader
+= sizeof(uint32_t);
126 if (decoded
.Length() != 16ULL + serialNumberLen
+ issuerLen
) {
127 return NS_ERROR_ILLEGAL_INPUT
;
129 CERTIssuerAndSN issuerSN
;
130 issuerSN
.serialNumber
.len
= serialNumberLen
;
131 issuerSN
.serialNumber
.data
= BitwiseCast
<unsigned char*, const char*>(reader
);
132 reader
+= serialNumberLen
;
133 issuerSN
.derIssuer
.len
= issuerLen
;
134 issuerSN
.derIssuer
.data
= BitwiseCast
<unsigned char*, const char*>(reader
);
136 MOZ_ASSERT(reader
== decoded
.EndReading());
138 cert
.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN
));
142 SECStatus
collect_certs(void* arg
, SECItem
** certs
, int numcerts
) {
143 nsTArray
<nsTArray
<uint8_t>>* certsArray
=
144 reinterpret_cast<nsTArray
<nsTArray
<uint8_t>>*>(arg
);
147 nsTArray
<uint8_t> certArray
;
148 SECItem
* cert
= *certs
;
149 certArray
.AppendElements(cert
->data
, cert
->len
);
150 certsArray
->AppendElement(std::move(certArray
));
156 nsresult
nsNSSCertificateDB::getCertsFromPackage(
157 nsTArray
<nsTArray
<uint8_t>>& collectArgs
, uint8_t* data
, uint32_t length
) {
158 if (CERT_DecodeCertPackage(BitwiseCast
<char*, uint8_t*>(data
), length
,
159 collect_certs
, &collectArgs
) != SECSuccess
) {
160 return NS_ERROR_FAILURE
;
165 // When using the sql-backed softoken, trust settings are authenticated using a
166 // key in the secret database. Thus, if the user has a password, we need to
167 // authenticate to the token in order to be able to change trust settings.
168 SECStatus
ChangeCertTrustWithPossibleAuthentication(
169 const UniqueCERTCertificate
& cert
, CERTCertTrust
& trust
, void* ctx
) {
170 MOZ_ASSERT(cert
, "cert must be non-null");
172 PR_SetError(SEC_ERROR_LIBRARY_FAILURE
, 0);
175 // NSS ignores the first argument to CERT_ChangeCertTrust
176 SECStatus srv
= CERT_ChangeCertTrust(nullptr, cert
.get(), &trust
);
177 if (srv
== SECSuccess
|| PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN
) {
181 // If this certificate is on an external PKCS#11 token, we have to
182 // authenticate to that token.
183 srv
= PK11_Authenticate(cert
->slot
, PR_TRUE
, ctx
);
185 // Otherwise, the certificate is on the internal module.
186 UniquePK11SlotInfo
internalSlot(PK11_GetInternalKeySlot());
187 srv
= PK11_Authenticate(internalSlot
.get(), PR_TRUE
, ctx
);
189 if (srv
!= SECSuccess
) {
192 return CERT_ChangeCertTrust(nullptr, cert
.get(), &trust
);
195 static nsresult
ImportCertsIntoPermanentStorage(
196 const UniqueCERTCertList
& certChain
) {
197 bool encounteredFailure
= false;
198 PRErrorCode savedErrorCode
= 0;
199 UniquePK11SlotInfo
slot(PK11_GetInternalKeySlot());
200 for (CERTCertListNode
* chainNode
= CERT_LIST_HEAD(certChain
);
201 !CERT_LIST_END(chainNode
, certChain
);
202 chainNode
= CERT_LIST_NEXT(chainNode
)) {
203 UniquePORTString
nickname(CERT_MakeCANickname(chainNode
->cert
));
204 SECStatus srv
= PK11_ImportCert(slot
.get(), chainNode
->cert
,
205 CK_INVALID_HANDLE
, nickname
.get(),
206 false); // this parameter is ignored by NSS
207 if (srv
!= SECSuccess
) {
208 encounteredFailure
= true;
209 savedErrorCode
= PR_GetError();
213 if (encounteredFailure
) {
214 return GetXPCOMFromNSSError(savedErrorCode
);
220 nsresult
nsNSSCertificateDB::handleCACertDownload(NotNull
<nsIArray
*> x509Certs
,
221 nsIInterfaceRequestor
* ctx
) {
222 // First thing we have to do is figure out which certificate we're
223 // gonna present to the user. The CA may have sent down a list of
224 // certs which may or may not be a chained list of certs. Until
225 // the day we can design some solid UI for the general case, we'll
226 // code to the > 90% case. That case is where a CA sends down a
227 // list that is a hierarchy whose root is either the first or
228 // the last cert. What we're gonna do is compare the first
229 // 2 entries, if the second was signed by the first, we assume
230 // the root cert is the first cert and display it. Otherwise,
231 // we compare the last 2 entries, if the second to last cert was
232 // signed by the last cert, then we assume the last cert is the
233 // root and display it.
237 x509Certs
->GetLength(&numCerts
);
239 if (numCerts
== 0) return NS_OK
; // Nothing to import, so nothing to do.
241 nsCOMPtr
<nsIX509Cert
> certToShow
;
242 uint32_t selCertIndex
;
244 // There's only one cert, so let's show it.
246 certToShow
= do_QueryElementAt(x509Certs
, selCertIndex
);
248 nsCOMPtr
<nsIX509Cert
> cert0
; // first cert
249 nsCOMPtr
<nsIX509Cert
> cert1
; // second cert
250 nsCOMPtr
<nsIX509Cert
> certn_2
; // second to last cert
251 nsCOMPtr
<nsIX509Cert
> certn_1
; // last cert
253 cert0
= do_QueryElementAt(x509Certs
, 0);
254 cert1
= do_QueryElementAt(x509Certs
, 1);
255 certn_2
= do_QueryElementAt(x509Certs
, numCerts
- 2);
256 certn_1
= do_QueryElementAt(x509Certs
, numCerts
- 1);
258 nsAutoString cert0SubjectName
;
259 nsAutoString cert1IssuerName
;
260 nsAutoString certn_2IssuerName
;
261 nsAutoString certn_1SubjectName
;
263 cert0
->GetSubjectName(cert0SubjectName
);
264 cert1
->GetIssuerName(cert1IssuerName
);
265 certn_2
->GetIssuerName(certn_2IssuerName
);
266 certn_1
->GetSubjectName(certn_1SubjectName
);
268 if (cert1IssuerName
.Equals(cert0SubjectName
)) {
269 // In this case, the first cert in the list signed the second,
270 // so the first cert is the root. Let's display it.
273 } else if (certn_2IssuerName
.Equals(certn_1SubjectName
)) {
274 // In this case the last cert has signed the second to last cert.
275 // The last cert is the root, so let's display it.
276 selCertIndex
= numCerts
- 1;
277 certToShow
= certn_1
;
279 // It's not a chain, so let's just show the first one in the
286 if (!certToShow
) return NS_ERROR_FAILURE
;
288 nsCOMPtr
<nsICertificateDialogs
> dialogs
;
289 nsresult rv
= ::getNSSDialogs(getter_AddRefs(dialogs
),
290 NS_GET_IID(nsICertificateDialogs
),
291 NS_CERTIFICATEDIALOGS_CONTRACTID
);
296 UniqueCERTCertificate
tmpCert(certToShow
->GetCert());
298 return NS_ERROR_FAILURE
;
301 if (!CERT_IsCACert(tmpCert
.get(), nullptr)) {
302 DisplayCertificateAlert(ctx
, "NotACACert", certToShow
);
303 return NS_ERROR_FAILURE
;
306 if (tmpCert
->isperm
) {
307 DisplayCertificateAlert(ctx
, "CaCertExists", certToShow
);
308 return NS_ERROR_FAILURE
;
313 rv
= dialogs
->ConfirmDownloadCACert(ctx
, certToShow
, &trustBits
, &allows
);
314 if (NS_FAILED(rv
)) return rv
;
316 if (!allows
) return NS_ERROR_NOT_AVAILABLE
;
318 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
, ("trust is %d\n", trustBits
));
319 UniquePORTString
nickname(CERT_MakeCANickname(tmpCert
.get()));
321 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
322 ("Created nick \"%s\"\n", nickname
.get()));
324 nsNSSCertTrust trust
;
326 trust
.AddCATrust(!!(trustBits
& nsIX509CertDB::TRUSTED_SSL
),
327 !!(trustBits
& nsIX509CertDB::TRUSTED_EMAIL
));
329 UniquePK11SlotInfo
slot(PK11_GetInternalKeySlot());
330 SECStatus srv
= PK11_ImportCert(slot
.get(), tmpCert
.get(), CK_INVALID_HANDLE
,
332 false); // this parameter is ignored by NSS
333 if (srv
!= SECSuccess
) {
334 return MapSECStatus(srv
);
337 ChangeCertTrustWithPossibleAuthentication(tmpCert
, trust
.GetTrust(), ctx
);
338 if (srv
!= SECSuccess
) {
339 return MapSECStatus(srv
);
342 // Import additional delivered certificates that can be verified.
344 // build a CertList for filtering
345 UniqueCERTCertList
certList(CERT_NewCertList());
347 return NS_ERROR_FAILURE
;
350 // get all remaining certs into temp store
352 for (uint32_t i
= 0; i
< numCerts
; i
++) {
353 if (i
== selCertIndex
) {
354 // we already processed that one
358 nsCOMPtr
<nsIX509Cert
> remainingCert
= do_QueryElementAt(x509Certs
, i
);
359 if (!remainingCert
) {
363 UniqueCERTCertificate
tmpCert2(remainingCert
->GetCert());
365 continue; // Let's try to import the rest of 'em
368 if (CERT_AddCertToListTail(certList
.get(), tmpCert2
.get()) != SECSuccess
) {
372 Unused
<< tmpCert2
.release();
375 return ImportCertsIntoPermanentStorage(certList
);
378 nsresult
nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
379 const UniqueCERTCertList
& aCertListIn
,
380 nsTArray
<RefPtr
<nsIX509Cert
>>& aCertListOut
) {
381 if (!aCertListIn
.get()) {
382 return NS_ERROR_INVALID_ARG
;
385 for (CERTCertListNode
* node
= CERT_LIST_HEAD(aCertListIn
.get());
386 !CERT_LIST_END(node
, aCertListIn
.get()); node
= CERT_LIST_NEXT(node
)) {
387 RefPtr
<nsIX509Cert
> cert
= new nsNSSCertificate(node
->cert
);
388 aCertListOut
.AppendElement(cert
);
394 nsNSSCertificateDB::ImportCertificates(uint8_t* data
, uint32_t length
,
396 nsIInterfaceRequestor
* ctx
) {
397 // We currently only handle CA certificates.
398 if (type
!= nsIX509Cert::CA_CERT
) {
399 return NS_ERROR_FAILURE
;
402 nsTArray
<nsTArray
<uint8_t>> certsArray
;
403 nsresult rv
= getCertsFromPackage(certsArray
, data
, length
);
408 nsCOMPtr
<nsIMutableArray
> array
= nsArrayBase::Create();
410 return NS_ERROR_FAILURE
;
413 // Now let's create some certs to work with
414 for (nsTArray
<uint8_t>& certDER
: certsArray
) {
415 nsCOMPtr
<nsIX509Cert
> cert
= new nsNSSCertificate(std::move(certDER
));
416 nsresult rv
= array
->AppendElement(cert
);
422 return handleCACertDownload(WrapNotNull(array
), ctx
);
426 * Decodes a given array of DER-encoded certificates into temporary storage.
429 * Array in which the decoded certificates are stored as arrays of
431 * @param temporaryCerts
432 * List of decoded certificates.
434 static nsresult
ImportCertsIntoTempStorage(
435 nsTArray
<nsTArray
<uint8_t>>& certs
,
436 /*out*/ const UniqueCERTCertList
& temporaryCerts
) {
437 NS_ENSURE_ARG_POINTER(temporaryCerts
);
439 for (nsTArray
<uint8_t>& certDER
: certs
) {
440 CERTCertificate
* certificate
;
442 certItem
.len
= certDER
.Length();
443 certItem
.data
= certDER
.Elements();
444 certificate
= CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem
,
445 nullptr, false, true);
447 UniqueCERTCertificate
cert(certificate
);
452 if (CERT_AddCertToListTail(temporaryCerts
.get(), cert
.get()) ==
454 Unused
<< cert
.release();
462 nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data
, uint32_t length
,
463 nsIInterfaceRequestor
* ctx
) {
464 nsTArray
<nsTArray
<uint8_t>> certsArray
;
466 nsresult rv
= getCertsFromPackage(certsArray
, data
, length
);
471 UniqueCERTCertList
temporaryCerts(CERT_NewCertList());
472 if (!temporaryCerts
) {
473 return NS_ERROR_FAILURE
;
476 rv
= ImportCertsIntoTempStorage(certsArray
, temporaryCerts
);
481 return ImportCertsIntoPermanentStorage(temporaryCerts
);
484 nsresult
nsNSSCertificateDB::ImportCACerts(nsTArray
<nsTArray
<uint8_t>>& caCerts
,
485 nsIInterfaceRequestor
* ctx
) {
486 UniqueCERTCertList
temporaryCerts(CERT_NewCertList());
487 if (!temporaryCerts
) {
488 return NS_ERROR_FAILURE
;
491 nsresult rv
= ImportCertsIntoTempStorage(caCerts
, temporaryCerts
);
496 return ImportCertsIntoPermanentStorage(temporaryCerts
);
499 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor
* ctx
,
500 const char* stringID
,
501 nsIX509Cert
* certToShow
) {
502 if (!NS_IsMainThread()) {
504 "nsNSSCertificateDB::DisplayCertificateAlert called off the main "
509 nsCOMPtr
<nsIInterfaceRequestor
> my_ctx
= ctx
;
511 my_ctx
= new PipUIContext();
514 // This shall be replaced by embedding ovverridable prompts
515 // as discussed in bug 310446, and should make use of certToShow.
517 nsAutoString tmpMessage
;
518 GetPIPNSSBundleString(stringID
, tmpMessage
);
519 nsCOMPtr
<nsIPrompt
> prompt(do_GetInterface(my_ctx
));
524 prompt
->Alert(nullptr, tmpMessage
.get());
528 nsNSSCertificateDB::ImportUserCertificate(uint8_t* data
, uint32_t length
,
529 nsIInterfaceRequestor
* ctx
) {
530 if (!NS_IsMainThread()) {
532 "nsNSSCertificateDB::ImportUserCertificate called off the main thread");
533 return NS_ERROR_NOT_SAME_THREAD
;
536 nsTArray
<nsTArray
<uint8_t>> certsArray
;
538 nsresult rv
= getCertsFromPackage(certsArray
, data
, length
);
545 if (certsArray
.IsEmpty()) {
549 certItem
.len
= certsArray
.ElementAt(0).Length();
550 certItem
.data
= certsArray
.ElementAt(0).Elements();
552 UniqueCERTCertificate
cert(CERT_NewTempCertificate(
553 CERT_GetDefaultCertDB(), &certItem
, nullptr, false, true));
555 return NS_ERROR_FAILURE
;
558 UniquePK11SlotInfo
slot(PK11_KeyForCertExists(cert
.get(), nullptr, ctx
));
560 nsCOMPtr
<nsIX509Cert
> certToShow
= new nsNSSCertificate(cert
.get());
561 DisplayCertificateAlert(ctx
, "UserCertIgnoredNoPrivateKey", certToShow
);
562 return NS_ERROR_FAILURE
;
566 /* pick a nickname for the cert */
567 nsAutoCString nickname
;
568 if (cert
->nickname
) {
569 nickname
= cert
->nickname
;
571 get_default_nickname(cert
.get(), ctx
, nickname
);
574 /* user wants to import the cert */
575 slot
.reset(PK11_ImportCertForKey(cert
.get(), nickname
.get(), ctx
));
577 return NS_ERROR_FAILURE
;
582 nsCOMPtr
<nsIX509Cert
> certToShow
= new nsNSSCertificate(cert
.get());
583 DisplayCertificateAlert(ctx
, "UserCertImported", certToShow
);
587 if (!certsArray
.IsEmpty()) {
588 certsArray
.RemoveElementAt(0);
589 rv
= ImportCACerts(certsArray
, ctx
);
592 nsCOMPtr
<nsIObserverService
> observerService
=
593 mozilla::services::GetObserverService();
594 if (observerService
) {
595 observerService
->NotifyObservers(nullptr, "psm:user-certificate-added",
603 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert
* aCert
) {
604 NS_ENSURE_ARG_POINTER(aCert
);
605 UniqueCERTCertificate
cert(aCert
->GetCert());
607 return NS_ERROR_FAILURE
;
610 // Temporary certificates aren't on a slot and will go away when the
611 // nsIX509Cert is destructed.
614 nsresult rv
= aCert
->GetCertType(&certType
);
615 if (NS_WARN_IF(NS_FAILED(rv
))) {
618 if (certType
== nsIX509Cert::USER_CERT
) {
619 SECStatus srv
= PK11_Authenticate(cert
->slot
, true, nullptr);
620 if (srv
!= SECSuccess
) {
621 return NS_ERROR_FAILURE
;
623 srv
= PK11_DeleteTokenCertAndKey(cert
.get(), nullptr);
624 if (srv
!= SECSuccess
) {
625 return NS_ERROR_FAILURE
;
628 // For certificates that can't be deleted (e.g. built-in roots), un-set
630 nsNSSCertTrust
trust(0, 0);
631 SECStatus srv
= ChangeCertTrustWithPossibleAuthentication(
632 cert
, trust
.GetTrust(), nullptr);
633 if (srv
!= SECSuccess
) {
634 return NS_ERROR_FAILURE
;
636 if (!PK11_IsReadOnly(cert
->slot
)) {
637 srv
= SEC_DeletePermCertificate(cert
.get());
638 if (srv
!= SECSuccess
) {
639 return NS_ERROR_FAILURE
;
645 nsCOMPtr
<nsIObserverService
> observerService
=
646 mozilla::services::GetObserverService();
647 if (observerService
) {
648 observerService
->NotifyObservers(nullptr, "psm:user-certificate-deleted",
656 nsNSSCertificateDB::SetCertTrust(nsIX509Cert
* cert
, uint32_t type
,
658 NS_ENSURE_ARG_POINTER(cert
);
659 nsNSSCertTrust trust
;
661 case nsIX509Cert::CA_CERT
:
663 trust
.AddCATrust(!!(trusted
& nsIX509CertDB::TRUSTED_SSL
),
664 !!(trusted
& nsIX509CertDB::TRUSTED_EMAIL
));
666 case nsIX509Cert::SERVER_CERT
:
667 trust
.SetValidPeer();
668 trust
.AddPeerTrust(trusted
& nsIX509CertDB::TRUSTED_SSL
, false);
670 case nsIX509Cert::EMAIL_CERT
:
671 trust
.SetValidPeer();
672 trust
.AddPeerTrust(false, !!(trusted
& nsIX509CertDB::TRUSTED_EMAIL
));
675 // Ignore any other type of certificate (including invalid types).
679 UniqueCERTCertificate
nsscert(cert
->GetCert());
680 SECStatus srv
= ChangeCertTrustWithPossibleAuthentication(
681 nsscert
, trust
.GetTrust(), nullptr);
682 return MapSECStatus(srv
);
686 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert
* cert
, uint32_t certType
,
687 uint32_t trustType
, bool* _isTrusted
) {
688 NS_ENSURE_ARG_POINTER(_isTrusted
);
691 nsresult rv
= BlockUntilLoadableCertsLoaded();
697 UniqueCERTCertificate
nsscert(cert
->GetCert());
698 CERTCertTrust nsstrust
;
699 srv
= CERT_GetCertTrust(nsscert
.get(), &nsstrust
);
700 if (srv
!= SECSuccess
) {
701 // CERT_GetCertTrust returns SECFailure if given a temporary cert that
702 // doesn't have any trust information yet. This isn't an error.
706 nsNSSCertTrust
trust(&nsstrust
);
707 if (certType
== nsIX509Cert::CA_CERT
) {
708 if (trustType
& nsIX509CertDB::TRUSTED_SSL
) {
709 *_isTrusted
= trust
.HasTrustedCA(true, false);
710 } else if (trustType
& nsIX509CertDB::TRUSTED_EMAIL
) {
711 *_isTrusted
= trust
.HasTrustedCA(false, true);
713 return NS_ERROR_FAILURE
;
715 } else if (certType
== nsIX509Cert::SERVER_CERT
) {
716 if (trustType
& nsIX509CertDB::TRUSTED_SSL
) {
717 *_isTrusted
= trust
.HasTrustedPeer(true, false);
718 } else if (trustType
& nsIX509CertDB::TRUSTED_EMAIL
) {
719 *_isTrusted
= trust
.HasTrustedPeer(false, true);
721 return NS_ERROR_FAILURE
;
723 } else if (certType
== nsIX509Cert::EMAIL_CERT
) {
724 if (trustType
& nsIX509CertDB::TRUSTED_SSL
) {
725 *_isTrusted
= trust
.HasTrustedPeer(true, false);
726 } else if (trustType
& nsIX509CertDB::TRUSTED_EMAIL
) {
727 *_isTrusted
= trust
.HasTrustedPeer(false, true);
729 return NS_ERROR_FAILURE
;
736 nsNSSCertificateDB::ImportCertsFromFile(nsIFile
* aFile
, uint32_t aType
) {
737 NS_ENSURE_ARG(aFile
);
739 case nsIX509Cert::CA_CERT
:
740 case nsIX509Cert::EMAIL_CERT
:
745 // not supported (yet)
746 return NS_ERROR_FAILURE
;
749 PRFileDesc
* fd
= nullptr;
750 nsresult rv
= aFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, &fd
);
755 return NS_ERROR_FAILURE
;
759 if (PR_GetOpenFileInfo(fd
, &fileInfo
) != PR_SUCCESS
) {
760 return NS_ERROR_FAILURE
;
763 auto buf
= MakeUnique
<unsigned char[]>(fileInfo
.size
);
764 int32_t bytesObtained
= PR_Read(fd
, buf
.get(), fileInfo
.size
);
767 if (bytesObtained
!= fileInfo
.size
) {
768 return NS_ERROR_FAILURE
;
771 nsCOMPtr
<nsIInterfaceRequestor
> cxt
= new PipUIContext();
774 case nsIX509Cert::CA_CERT
:
775 return ImportCertificates(buf
.get(), bytesObtained
, aType
, cxt
);
776 case nsIX509Cert::EMAIL_CERT
:
777 return ImportEmailCertificate(buf
.get(), bytesObtained
, cxt
);
779 MOZ_ASSERT(false, "Unsupported type should have been filtered out");
783 return NS_ERROR_FAILURE
;
787 nsNSSCertificateDB::ImportPKCS12File(nsIFile
* aFile
, const nsAString
& aPassword
,
789 if (!NS_IsMainThread()) {
790 return NS_ERROR_NOT_SAME_THREAD
;
792 nsresult rv
= BlockUntilLoadableCertsLoaded();
797 NS_ENSURE_ARG(aFile
);
799 rv
= blob
.ImportFromFile(aFile
, aPassword
, *aError
);
800 nsCOMPtr
<nsIObserverService
> observerService
=
801 mozilla::services::GetObserverService();
802 if (NS_SUCCEEDED(rv
) && observerService
) {
803 observerService
->NotifyObservers(nullptr, "psm:user-certificate-added",
811 nsNSSCertificateDB::ExportPKCS12File(
812 nsIFile
* aFile
, const nsTArray
<RefPtr
<nsIX509Cert
>>& aCerts
,
813 const nsAString
& aPassword
, uint32_t* aError
) {
814 if (!NS_IsMainThread()) {
815 return NS_ERROR_NOT_SAME_THREAD
;
817 nsresult rv
= BlockUntilLoadableCertsLoaded();
822 NS_ENSURE_ARG(aFile
);
823 if (aCerts
.IsEmpty()) {
827 return blob
.ExportToFile(aFile
, aCerts
, aPassword
, *aError
);
831 nsNSSCertificateDB::ConstructX509FromBase64(const nsACString
& base64
,
832 /*out*/ nsIX509Cert
** _retval
) {
834 return NS_ERROR_INVALID_POINTER
;
837 // Base64Decode() doesn't consider a zero length input as an error, and just
838 // returns the empty string. We don't want this behavior, so the below check
839 // catches this case.
840 if (base64
.Length() < 1) {
841 return NS_ERROR_ILLEGAL_VALUE
;
844 nsAutoCString certDER
;
845 nsresult rv
= Base64Decode(base64
, certDER
);
850 return ConstructX509FromSpan(AsBytes(Span(certDER
)), _retval
);
854 nsNSSCertificateDB::ConstructX509(const nsTArray
<uint8_t>& certDER
,
855 nsIX509Cert
** _retval
) {
856 return ConstructX509FromSpan(Span(certDER
.Elements(), certDER
.Length()),
860 nsresult
nsNSSCertificateDB::ConstructX509FromSpan(
861 Span
<const uint8_t> aInputSpan
, nsIX509Cert
** _retval
) {
862 if (NS_WARN_IF(!_retval
)) {
863 return NS_ERROR_INVALID_POINTER
;
866 if (aInputSpan
.Length() > std::numeric_limits
<unsigned int>::max()) {
867 return NS_ERROR_ILLEGAL_VALUE
;
871 certData
.type
= siDERCertBuffer
;
872 certData
.data
= const_cast<unsigned char*>(
873 reinterpret_cast<const unsigned char*>(aInputSpan
.Elements()));
874 certData
.len
= aInputSpan
.Length();
876 UniqueCERTCertificate
cert(CERT_NewTempCertificate(
877 CERT_GetDefaultCertDB(), &certData
, nullptr, false, true));
879 return (PORT_GetError() == SEC_ERROR_NO_MEMORY
) ? NS_ERROR_OUT_OF_MEMORY
882 nsCOMPtr
<nsIX509Cert
> nssCert
= new nsNSSCertificate(cert
.get());
883 nssCert
.forget(_retval
);
887 void nsNSSCertificateDB::get_default_nickname(CERTCertificate
* cert
,
888 nsIInterfaceRequestor
* ctx
,
889 nsCString
& nickname
) {
892 CK_OBJECT_HANDLE keyHandle
;
894 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
898 CERTCertDBHandle
* defaultcertdb
= CERT_GetDefaultCertDB();
899 nsAutoCString username
;
900 UniquePORTString
tempCN(CERT_GetCommonName(&cert
->subject
));
902 username
= tempCN
.get();
905 nsAutoCString caname
;
906 UniquePORTString
tempIssuerOrg(CERT_GetOrgName(&cert
->issuer
));
908 caname
= tempIssuerOrg
.get();
911 nsAutoString tmpNickFmt
;
912 GetPIPNSSBundleString("nick_template", tmpNickFmt
);
913 NS_ConvertUTF16toUTF8
nickFmt(tmpNickFmt
);
915 nsAutoCString baseName
;
916 baseName
.AppendPrintf(nickFmt
.get(), username
.get(), caname
.get());
917 if (baseName
.IsEmpty()) {
924 * We need to see if the private key exists on a token, if it does
925 * then we need to check for nicknames that already exist on the smart
928 UniquePK11SlotInfo
slot(PK11_KeyForCertExists(cert
, &keyHandle
, ctx
));
931 if (!PK11_IsInternal(slot
.get())) {
933 tmp
.AppendPrintf("%s:%s", PK11_GetTokenName(slot
.get()), baseName
.get());
946 tmp
.AppendPrintf("%s #%d", baseName
.get(), count
);
954 UniqueCERTCertificate dummycert
;
956 if (PK11_IsInternal(slot
.get())) {
957 /* look up the nickname to make sure it isn't in use already */
958 dummycert
.reset(CERT_FindCertByNickname(defaultcertdb
, nickname
.get()));
960 // Check the cert against others that already live on the smart card.
961 dummycert
.reset(PK11_FindCertFromNickname(nickname
.get(), ctx
));
963 // Make sure the subject names are different.
964 if (CERT_CompareName(&cert
->subject
, &dummycert
->subject
) == SECEqual
) {
966 * There is another certificate with the same nickname and
967 * the same subject name on the smart card, so let's use this
982 nsNSSCertificateDB::AddCertFromBase64(const nsACString
& aBase64
,
983 const nsACString
& aTrust
,
984 nsIX509Cert
** addedCertificate
) {
985 // Base64Decode() doesn't consider a zero length input as an error, and just
986 // returns the empty string. We don't want this behavior, so the below check
987 // catches this case.
988 if (aBase64
.Length() < 1) {
989 return NS_ERROR_ILLEGAL_VALUE
;
992 nsAutoCString aCertDER
;
993 nsresult rv
= Base64Decode(aBase64
, aCertDER
);
997 return AddCert(aCertDER
, aTrust
, addedCertificate
);
1001 nsNSSCertificateDB::AddCert(const nsACString
& aCertDER
,
1002 const nsACString
& aTrust
,
1003 nsIX509Cert
** addedCertificate
) {
1004 MOZ_ASSERT(addedCertificate
);
1005 if (!addedCertificate
) {
1006 return NS_ERROR_INVALID_ARG
;
1008 *addedCertificate
= nullptr;
1010 nsNSSCertTrust trust
;
1011 if (CERT_DecodeTrustString(&trust
.GetTrust(),
1012 PromiseFlatCString(aTrust
).get()) != SECSuccess
) {
1013 return NS_ERROR_FAILURE
;
1016 nsCOMPtr
<nsIX509Cert
> newCert
;
1018 ConstructX509FromSpan(AsBytes(Span(aCertDER
)), getter_AddRefs(newCert
));
1019 if (NS_FAILED(rv
)) {
1023 UniqueCERTCertificate
tmpCert(newCert
->GetCert());
1025 return NS_ERROR_FAILURE
;
1028 // If there's already a certificate that matches this one in the database, we
1029 // still want to set its trust to the given value.
1030 if (tmpCert
->isperm
) {
1031 rv
= SetCertTrustFromString(newCert
, aTrust
);
1032 if (NS_FAILED(rv
)) {
1035 newCert
.forget(addedCertificate
);
1039 UniquePORTString
nickname(CERT_MakeCANickname(tmpCert
.get()));
1041 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1042 ("Created nick \"%s\"\n", nickname
.get()));
1044 UniquePK11SlotInfo
slot(PK11_GetInternalKeySlot());
1045 SECStatus srv
= PK11_ImportCert(slot
.get(), tmpCert
.get(), CK_INVALID_HANDLE
,
1047 false); // this parameter is ignored by NSS
1048 if (srv
!= SECSuccess
) {
1049 return MapSECStatus(srv
);
1051 srv
= ChangeCertTrustWithPossibleAuthentication(tmpCert
, trust
.GetTrust(),
1053 if (srv
!= SECSuccess
) {
1054 return MapSECStatus(srv
);
1056 newCert
.forget(addedCertificate
);
1061 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert
* cert
,
1062 const nsACString
& trustString
) {
1063 NS_ENSURE_ARG(cert
);
1065 CERTCertTrust trust
;
1067 CERT_DecodeTrustString(&trust
, PromiseFlatCString(trustString
).get());
1068 if (srv
!= SECSuccess
) {
1069 return MapSECStatus(srv
);
1071 UniqueCERTCertificate
nssCert(cert
->GetCert());
1073 srv
= ChangeCertTrustWithPossibleAuthentication(nssCert
, trust
, nullptr);
1074 return MapSECStatus(srv
);
1077 NS_IMETHODIMP
nsNSSCertificateDB::AsPKCS7Blob(
1078 const nsTArray
<RefPtr
<nsIX509Cert
>>& certList
, nsACString
& _retval
) {
1079 if (certList
.IsEmpty()) {
1080 return NS_ERROR_INVALID_ARG
;
1083 UniqueNSSCMSMessage
cmsg(NSS_CMSMessage_Create(nullptr));
1085 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1086 ("nsNSSCertificateDB::AsPKCS7Blob - can't create CMS message"));
1087 return NS_ERROR_OUT_OF_MEMORY
;
1090 UniqueNSSCMSSignedData
sigd(nullptr);
1091 for (const auto& cert
: certList
) {
1092 // We need an owning handle when calling nsIX509Cert::GetCert().
1093 UniqueCERTCertificate
nssCert(cert
->GetCert());
1096 NSS_CMSSignedData_CreateCertsOnly(cmsg
.get(), nssCert
.get(), false));
1098 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1099 ("nsNSSCertificateDB::AsPKCS7Blob - can't create SignedData"));
1100 return NS_ERROR_FAILURE
;
1102 } else if (NSS_CMSSignedData_AddCertificate(sigd
.get(), nssCert
.get()) !=
1104 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1105 ("nsNSSCertificateDB::AsPKCS7Blob - can't add cert"));
1106 return NS_ERROR_FAILURE
;
1110 NSSCMSContentInfo
* cinfo
= NSS_CMSMessage_GetContentInfo(cmsg
.get());
1111 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg
.get(), cinfo
, sigd
.get()) !=
1113 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1114 ("nsNSSCertificateDB::AsPKCS7Blob - can't attach SignedData"));
1115 return NS_ERROR_FAILURE
;
1117 // cmsg owns sigd now.
1118 Unused
<< sigd
.release();
1120 UniquePLArenaPool
arena(PORT_NewArena(1024));
1122 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1123 ("nsNSSCertificateDB::AsPKCS7Blob - out of memory"));
1124 return NS_ERROR_OUT_OF_MEMORY
;
1127 SECItem certP7
= {siBuffer
, nullptr, 0};
1128 NSSCMSEncoderContext
* ecx
= NSS_CMSEncoder_Start(
1129 cmsg
.get(), nullptr, nullptr, &certP7
, arena
.get(), nullptr, nullptr,
1130 nullptr, nullptr, nullptr, nullptr);
1132 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1133 ("nsNSSCertificateDB::AsPKCS7Blob - can't create encoder"));
1134 return NS_ERROR_FAILURE
;
1137 if (NSS_CMSEncoder_Finish(ecx
) != SECSuccess
) {
1138 MOZ_LOG(gPIPNSSLog
, LogLevel::Debug
,
1139 ("nsNSSCertificateDB::AsPKCS7Blob - failed to add encoded data"));
1140 return NS_ERROR_FAILURE
;
1143 _retval
.Assign(nsDependentCSubstring(
1144 reinterpret_cast<const char*>(certP7
.data
), certP7
.len
));
1149 nsNSSCertificateDB::GetCerts(nsTArray
<RefPtr
<nsIX509Cert
>>& _retval
) {
1150 nsresult rv
= BlockUntilLoadableCertsLoaded();
1151 if (NS_FAILED(rv
)) {
1155 rv
= CheckForSmartCardChanges();
1156 if (NS_FAILED(rv
)) {
1160 nsCOMPtr
<nsIInterfaceRequestor
> ctx
= new PipUIContext();
1161 UniqueCERTCertList
certList(PK11_ListCerts(PK11CertListUnique
, ctx
));
1163 return NS_ERROR_FAILURE
;
1165 return nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(certList
,
1170 nsNSSCertificateDB::AsyncHasThirdPartyRoots(nsIAsyncBoolCallback
* aCallback
) {
1171 NS_ENSURE_ARG_POINTER(aCallback
);
1172 nsMainThreadPtrHandle
<nsIAsyncBoolCallback
> callback(
1173 new nsMainThreadPtrHolder
<nsIAsyncBoolCallback
>("AsyncHasThirdPartyRoots",
1176 return NS_DispatchBackgroundTask(
1177 NS_NewRunnableFunction(
1178 "nsNSSCertificateDB::AsyncHasThirdPartyRoots",
1179 [cb
= std::move(callback
), self
= RefPtr
{this}] {
1180 bool hasThirdPartyRoots
= [self
]() -> bool {
1181 nsTArray
<RefPtr
<nsIX509Cert
>> certs
;
1182 nsresult rv
= self
->GetCerts(certs
);
1183 if (NS_FAILED(rv
)) {
1187 for (const auto& cert
: certs
) {
1188 bool isTrusted
= false;
1190 self
->IsCertTrusted(cert
, nsIX509Cert::CA_CERT
,
1191 nsIX509CertDB::TRUSTED_SSL
, &isTrusted
);
1192 if (NS_FAILED(rv
)) {
1200 bool isBuiltInRoot
= false;
1201 rv
= cert
->GetIsBuiltInRoot(&isBuiltInRoot
);
1202 if (NS_FAILED(rv
)) {
1206 if (!isBuiltInRoot
) {
1214 NS_DispatchToMainThread(NS_NewRunnableFunction(
1215 "nsNSSCertificateDB::AsyncHasThirdPartyRoots callback",
1216 [cb
, hasThirdPartyRoots
]() {
1217 cb
->OnResult(hasThirdPartyRoots
);
1220 NS_DISPATCH_EVENT_MAY_BLOCK
);
1223 nsresult
VerifyCertAtTime(nsIX509Cert
* aCert
,
1224 int64_t /*SECCertificateUsage*/ aUsage
,
1225 uint32_t aFlags
, const nsACString
& aHostname
,
1226 mozilla::pkix::Time aTime
,
1227 nsTArray
<RefPtr
<nsIX509Cert
>>& aVerifiedChain
,
1229 int32_t* /*PRErrorCode*/ _retval
) {
1230 NS_ENSURE_ARG_POINTER(aCert
);
1231 NS_ENSURE_ARG_POINTER(aHasEVPolicy
);
1232 NS_ENSURE_ARG_POINTER(_retval
);
1234 if (!aVerifiedChain
.IsEmpty()) {
1235 return NS_ERROR_INVALID_ARG
;
1238 *aHasEVPolicy
= false;
1239 *_retval
= PR_UNKNOWN_ERROR
;
1241 RefPtr
<SharedCertVerifier
> certVerifier(GetDefaultCertVerifier());
1242 NS_ENSURE_TRUE(certVerifier
, NS_ERROR_FAILURE
);
1244 nsTArray
<nsTArray
<uint8_t>> resultChain
;
1246 mozilla::pkix::Result result
;
1248 nsTArray
<uint8_t> certBytes
;
1249 nsresult nsrv
= aCert
->GetRawDER(certBytes
);
1250 if (NS_FAILED(nsrv
)) {
1254 if (!aHostname
.IsVoid() && aUsage
== certificateUsageSSLServer
) {
1256 certVerifier
->VerifySSLServerCert(certBytes
, aTime
,
1257 nullptr, // Assume no context
1258 aHostname
, resultChain
, aFlags
,
1259 Nothing(), // extraCertificates
1260 Nothing(), // stapledOCSPResponse
1261 Nothing(), // sctsFromTLSExtension
1262 Nothing(), // dcInfo
1263 OriginAttributes(), &evStatus
);
1265 const nsCString
& flatHostname
= PromiseFlatCString(aHostname
);
1266 result
= certVerifier
->VerifyCert(
1267 certBytes
, aUsage
, aTime
,
1268 nullptr, // Assume no context
1269 aHostname
.IsVoid() ? nullptr : flatHostname
.get(), resultChain
, aFlags
,
1270 Nothing(), // extraCertificates
1271 Nothing(), // stapledOCSPResponse
1272 Nothing(), // sctsFromTLSExtension
1273 OriginAttributes(), &evStatus
);
1276 if (result
== mozilla::pkix::Success
) {
1277 for (auto& certDER
: resultChain
) {
1278 RefPtr
<nsIX509Cert
> cert
= new nsNSSCertificate(std::move(certDER
));
1279 aVerifiedChain
.AppendElement(cert
);
1282 if (evStatus
== EVStatus::EV
) {
1283 *aHasEVPolicy
= true;
1287 *_retval
= mozilla::pkix::MapResultToPRErrorCode(result
);
1292 class VerifyCertAtTimeTask final
: public CryptoTask
{
1294 VerifyCertAtTimeTask(nsIX509Cert
* aCert
, int64_t aUsage
, uint32_t aFlags
,
1295 const nsACString
& aHostname
, uint64_t aTime
,
1296 nsICertVerificationCallback
* aCallback
)
1300 mHostname(aHostname
),
1302 mCallback(new nsMainThreadPtrHolder
<nsICertVerificationCallback
>(
1303 "nsICertVerificationCallback", aCallback
)),
1304 mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE
),
1305 mHasEVPolicy(false) {}
1308 virtual nsresult
CalculateResult() override
{
1309 nsCOMPtr
<nsIX509CertDB
> certDB
= do_GetService(NS_X509CERTDB_CONTRACTID
);
1311 return NS_ERROR_FAILURE
;
1313 return VerifyCertAtTime(mCert
, mUsage
, mFlags
, mHostname
,
1314 mozilla::pkix::TimeFromEpochInSeconds(mTime
),
1315 mVerifiedCertList
, &mHasEVPolicy
, &mPRErrorCode
);
1318 virtual void CallCallback(nsresult rv
) override
{
1319 if (NS_FAILED(rv
)) {
1320 nsTArray
<RefPtr
<nsIX509Cert
>> tmp
;
1321 Unused
<< mCallback
->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE
, tmp
,
1324 Unused
<< mCallback
->VerifyCertFinished(mPRErrorCode
, mVerifiedCertList
,
1329 nsCOMPtr
<nsIX509Cert
> mCert
;
1332 nsCString mHostname
;
1334 nsMainThreadPtrHandle
<nsICertVerificationCallback
> mCallback
;
1335 int32_t mPRErrorCode
;
1336 nsTArray
<RefPtr
<nsIX509Cert
>> mVerifiedCertList
;
1341 nsNSSCertificateDB::AsyncVerifyCertAtTime(
1342 nsIX509Cert
* aCert
, int64_t /*SECCertificateUsage*/ aUsage
, uint32_t aFlags
,
1343 const nsACString
& aHostname
, uint64_t aTime
,
1344 nsICertVerificationCallback
* aCallback
) {
1345 RefPtr
<VerifyCertAtTimeTask
> task(new VerifyCertAtTimeTask(
1346 aCert
, aUsage
, aFlags
, aHostname
, aTime
, aCallback
));
1347 return task
->Dispatch();
1351 nsNSSCertificateDB::ClearOCSPCache() {
1352 RefPtr
<SharedCertVerifier
> certVerifier(GetDefaultCertVerifier());
1353 NS_ENSURE_TRUE(certVerifier
, NS_ERROR_FAILURE
);
1354 certVerifier
->ClearOCSPCache();