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/. */
4
5 #include "nsNSSCertificateDB.h"
6
7 #include "CertVerifier.h"
8 #include "CryptoTask.h"
9 #include "ExtendedValidation.h"
10 #include "NSSCertDBTrustDomain.h"
11 #include "SharedSSLState.h"
12 #include "certdb.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"
46
47 #ifdef XP_WIN
48 # include <winsock.h> // for ntohl
49 #endif
50
51 using namespace mozilla;
52 using namespace mozilla::psm;
53
54 extern LazyLogModule gPIPNSSLog;
55
NS_IMPL_ISUPPORTS(nsNSSCertificateDB,nsIX509CertDB)56 NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)
57
58 NS_IMETHODIMP
59 nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
60 /*out*/ nsIX509Cert** _cert) {
61 NS_ENSURE_ARG_POINTER(_cert);
62 *_cert = nullptr;
63
64 if (aDBKey.IsEmpty()) {
65 return NS_ERROR_INVALID_ARG;
66 }
67
68 nsresult rv = BlockUntilLoadableCertsLoaded();
69 if (NS_FAILED(rv)) {
70 return rv;
71 }
72
73 UniqueCERTCertificate cert;
74 rv = FindCertByDBKey(aDBKey, cert);
75 if (NS_FAILED(rv)) {
76 return rv;
77 }
78 // If we can't find the certificate, that's not an error. Just return null.
79 if (!cert) {
80 return NS_OK;
81 }
82 nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
83 if (!nssCert) {
84 return NS_ERROR_OUT_OF_MEMORY;
85 }
86 nssCert.forget(_cert);
87 return NS_OK;
88 }
89
FindCertByDBKey(const nsACString & aDBKey,UniqueCERTCertificate & cert)90 nsresult nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
91 UniqueCERTCertificate& cert) {
92 static_assert(sizeof(uint64_t) == 8, "type size sanity check");
93 static_assert(sizeof(uint32_t) == 4, "type size sanity check");
94 // (From nsNSSCertificate::GetDbKey)
95 // The format of the key is the base64 encoding of the following:
96 // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
97 // never implemented)
98 // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
99 // never implemented)
100 // 4 bytes: <serial number length in big-endian order>
101 // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
102 // n bytes: <bytes of serial number>
103 // m bytes: <DER-encoded issuer distinguished name>
104 nsAutoCString decoded;
105 nsAutoCString tmpDBKey(aDBKey);
106 // Filter out any whitespace for backwards compatibility.
107 tmpDBKey.StripWhitespace();
108 nsresult rv = Base64Decode(tmpDBKey, decoded);
109 if (NS_FAILED(rv)) {
110 return rv;
111 }
112 if (decoded.Length() < 16) {
113 return NS_ERROR_ILLEGAL_INPUT;
114 }
115 const char* reader = decoded.BeginReading();
116 uint64_t zeroes = *BitwiseCast<const uint64_t*, const char*>(reader);
117 if (zeroes != 0) {
118 return NS_ERROR_ILLEGAL_INPUT;
119 }
120 reader += sizeof(uint64_t);
121 // Note: We surround the ntohl() argument with parentheses to stop the macro
122 // from thinking two arguments were passed.
123 uint32_t serialNumberLen =
124 ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
125 reader += sizeof(uint32_t);
126 uint32_t issuerLen =
127 ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
128 reader += sizeof(uint32_t);
129 if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) {
130 return NS_ERROR_ILLEGAL_INPUT;
131 }
132 CERTIssuerAndSN issuerSN;
133 issuerSN.serialNumber.len = serialNumberLen;
134 issuerSN.serialNumber.data = BitwiseCast<unsigned char*, const char*>(reader);
135 reader += serialNumberLen;
136 issuerSN.derIssuer.len = issuerLen;
137 issuerSN.derIssuer.data = BitwiseCast<unsigned char*, const char*>(reader);
138 reader += issuerLen;
139 MOZ_ASSERT(reader == decoded.EndReading());
140
141 cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN));
142 return NS_OK;
143 }
144
collect_certs(void * arg,SECItem ** certs,int numcerts)145 SECStatus collect_certs(void* arg, SECItem** certs, int numcerts) {
146 nsTArray<nsTArray<uint8_t>>* certsArray =
147 reinterpret_cast<nsTArray<nsTArray<uint8_t>>*>(arg);
148
149 while (numcerts--) {
150 nsTArray<uint8_t> certArray;
151 SECItem* cert = *certs;
152 certArray.AppendElements(cert->data, cert->len);
153 certsArray->AppendElement(std::move(certArray));
154 certs++;
155 }
156 return (SECSuccess);
157 }
158
getCertsFromPackage(nsTArray<nsTArray<uint8_t>> & collectArgs,uint8_t * data,uint32_t length)159 nsresult nsNSSCertificateDB::getCertsFromPackage(
160 nsTArray<nsTArray<uint8_t>>& collectArgs, uint8_t* data, uint32_t length) {
161 if (CERT_DecodeCertPackage(BitwiseCast<char*, uint8_t*>(data), length,
162 collect_certs, &collectArgs) != SECSuccess) {
163 return NS_ERROR_FAILURE;
164 }
165 return NS_OK;
166 }
167
168 // When using the sql-backed softoken, trust settings are authenticated using a
169 // key in the secret database. Thus, if the user has a password, we need to
170 // authenticate to the token in order to be able to change trust settings.
ChangeCertTrustWithPossibleAuthentication(const UniqueCERTCertificate & cert,CERTCertTrust & trust,void * ctx)171 SECStatus ChangeCertTrustWithPossibleAuthentication(
172 const UniqueCERTCertificate& cert, CERTCertTrust& trust, void* ctx) {
173 MOZ_ASSERT(cert, "cert must be non-null");
174 if (!cert) {
175 PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
176 return SECFailure;
177 }
178 // NSS ignores the first argument to CERT_ChangeCertTrust
179 SECStatus srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
180 if (srv == SECSuccess || PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
181 return srv;
182 }
183 if (cert->slot) {
184 // If this certificate is on an external PKCS#11 token, we have to
185 // authenticate to that token.
186 srv = PK11_Authenticate(cert->slot, PR_TRUE, ctx);
187 } else {
188 // Otherwise, the certificate is on the internal module.
189 UniquePK11SlotInfo internalSlot(PK11_GetInternalKeySlot());
190 srv = PK11_Authenticate(internalSlot.get(), PR_TRUE, ctx);
191 }
192 if (srv != SECSuccess) {
193 return srv;
194 }
195 return CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
196 }
197
ImportCertsIntoPermanentStorage(const UniqueCERTCertList & certChain)198 static nsresult ImportCertsIntoPermanentStorage(
199 const UniqueCERTCertList& certChain) {
200 bool encounteredFailure = false;
201 PRErrorCode savedErrorCode = 0;
202 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
203 for (CERTCertListNode* chainNode = CERT_LIST_HEAD(certChain);
204 !CERT_LIST_END(chainNode, certChain);
205 chainNode = CERT_LIST_NEXT(chainNode)) {
206 UniquePORTString nickname(CERT_MakeCANickname(chainNode->cert));
207 SECStatus srv = PK11_ImportCert(slot.get(), chainNode->cert,
208 CK_INVALID_HANDLE, nickname.get(),
209 false); // this parameter is ignored by NSS
210 if (srv != SECSuccess) {
211 encounteredFailure = true;
212 savedErrorCode = PR_GetError();
213 }
214 }
215
216 if (encounteredFailure) {
217 return GetXPCOMFromNSSError(savedErrorCode);
218 }
219
220 return NS_OK;
221 }
222
handleCACertDownload(NotNull<nsIArray * > x509Certs,nsIInterfaceRequestor * ctx)223 nsresult nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
224 nsIInterfaceRequestor* ctx) {
225 // First thing we have to do is figure out which certificate we're
226 // gonna present to the user. The CA may have sent down a list of
227 // certs which may or may not be a chained list of certs. Until
228 // the day we can design some solid UI for the general case, we'll
229 // code to the > 90% case. That case is where a CA sends down a
230 // list that is a hierarchy whose root is either the first or
231 // the last cert. What we're gonna do is compare the first
232 // 2 entries, if the second was signed by the first, we assume
233 // the root cert is the first cert and display it. Otherwise,
234 // we compare the last 2 entries, if the second to last cert was
235 // signed by the last cert, then we assume the last cert is the
236 // root and display it.
237
238 uint32_t numCerts;
239
240 x509Certs->GetLength(&numCerts);
241
242 if (numCerts == 0) return NS_OK; // Nothing to import, so nothing to do.
243
244 nsCOMPtr<nsIX509Cert> certToShow;
245 uint32_t selCertIndex;
246 if (numCerts == 1) {
247 // There's only one cert, so let's show it.
248 selCertIndex = 0;
249 certToShow = do_QueryElementAt(x509Certs, selCertIndex);
250 } else {
251 nsCOMPtr<nsIX509Cert> cert0; // first cert
252 nsCOMPtr<nsIX509Cert> cert1; // second cert
253 nsCOMPtr<nsIX509Cert> certn_2; // second to last cert
254 nsCOMPtr<nsIX509Cert> certn_1; // last cert
255
256 cert0 = do_QueryElementAt(x509Certs, 0);
257 cert1 = do_QueryElementAt(x509Certs, 1);
258 certn_2 = do_QueryElementAt(x509Certs, numCerts - 2);
259 certn_1 = do_QueryElementAt(x509Certs, numCerts - 1);
260
261 nsAutoString cert0SubjectName;
262 nsAutoString cert1IssuerName;
263 nsAutoString certn_2IssuerName;
264 nsAutoString certn_1SubjectName;
265
266 cert0->GetSubjectName(cert0SubjectName);
267 cert1->GetIssuerName(cert1IssuerName);
268 certn_2->GetIssuerName(certn_2IssuerName);
269 certn_1->GetSubjectName(certn_1SubjectName);
270
271 if (cert1IssuerName.Equals(cert0SubjectName)) {
272 // In this case, the first cert in the list signed the second,
273 // so the first cert is the root. Let's display it.
274 selCertIndex = 0;
275 certToShow = cert0;
276 } else if (certn_2IssuerName.Equals(certn_1SubjectName)) {
277 // In this case the last cert has signed the second to last cert.
278 // The last cert is the root, so let's display it.
279 selCertIndex = numCerts - 1;
280 certToShow = certn_1;
281 } else {
282 // It's not a chain, so let's just show the first one in the
283 // downloaded list.
284 selCertIndex = 0;
285 certToShow = cert0;
286 }
287 }
288
289 if (!certToShow) return NS_ERROR_FAILURE;
290
291 nsCOMPtr<nsICertificateDialogs> dialogs;
292 nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
293 NS_GET_IID(nsICertificateDialogs),
294 NS_CERTIFICATEDIALOGS_CONTRACTID);
295 if (NS_FAILED(rv)) {
296 return rv;
297 }
298
299 UniqueCERTCertificate tmpCert(certToShow->GetCert());
300 if (!tmpCert) {
301 return NS_ERROR_FAILURE;
302 }
303
304 if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
305 DisplayCertificateAlert(ctx, "NotACACert", certToShow);
306 return NS_ERROR_FAILURE;
307 }
308
309 if (tmpCert->isperm) {
310 DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
311 return NS_ERROR_FAILURE;
312 }
313
314 uint32_t trustBits;
315 bool allows;
316 rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
317 if (NS_FAILED(rv)) return rv;
318
319 if (!allows) return NS_ERROR_NOT_AVAILABLE;
320
321 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
322 UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
323
324 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
325 ("Created nick \"%s\"\n", nickname.get()));
326
327 nsNSSCertTrust trust;
328 trust.SetValidCA();
329 trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
330 !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL));
331
332 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
333 SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
334 nickname.get(),
335 false); // this parameter is ignored by NSS
336 if (srv != SECSuccess) {
337 return MapSECStatus(srv);
338 }
339 srv =
340 ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(), ctx);
341 if (srv != SECSuccess) {
342 return MapSECStatus(srv);
343 }
344
345 // Import additional delivered certificates that can be verified.
346
347 // build a CertList for filtering
348 UniqueCERTCertList certList(CERT_NewCertList());
349 if (!certList) {
350 return NS_ERROR_FAILURE;
351 }
352
353 // get all remaining certs into temp store
354
355 for (uint32_t i = 0; i < numCerts; i++) {
356 if (i == selCertIndex) {
357 // we already processed that one
358 continue;
359 }
360
361 nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
362 if (!remainingCert) {
363 continue;
364 }
365
366 UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
367 if (!tmpCert2) {
368 continue; // Let's try to import the rest of 'em
369 }
370
371 if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
372 continue;
373 }
374
375 Unused << tmpCert2.release();
376 }
377
378 return ImportCertsIntoPermanentStorage(certList);
379 }
380
ConstructCertArrayFromUniqueCertList(const UniqueCERTCertList & aCertListIn,nsTArray<RefPtr<nsIX509Cert>> & aCertListOut)381 nsresult nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
382 const UniqueCERTCertList& aCertListIn,
383 nsTArray<RefPtr<nsIX509Cert>>& aCertListOut) {
384 if (!aCertListIn.get()) {
385 return NS_ERROR_INVALID_ARG;
386 }
387
388 for (CERTCertListNode* node = CERT_LIST_HEAD(aCertListIn.get());
389 !CERT_LIST_END(node, aCertListIn.get()); node = CERT_LIST_NEXT(node)) {
390 RefPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
391 if (!cert) {
392 return NS_ERROR_OUT_OF_MEMORY;
393 }
394 aCertListOut.AppendElement(cert);
395 }
396 return NS_OK;
397 }
398
399 NS_IMETHODIMP
ImportCertificates(uint8_t * data,uint32_t length,uint32_t type,nsIInterfaceRequestor * ctx)400 nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
401 uint32_t type,
402 nsIInterfaceRequestor* ctx) {
403 // We currently only handle CA certificates.
404 if (type != nsIX509Cert::CA_CERT) {
405 return NS_ERROR_FAILURE;
406 }
407
408 nsTArray<nsTArray<uint8_t>> certsArray;
409
410 nsresult rv = getCertsFromPackage(certsArray, data, length);
411 if (NS_FAILED(rv)) {
412 return rv;
413 }
414
415 nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
416 if (!array) {
417 return NS_ERROR_FAILURE;
418 }
419
420 // Now let's create some certs to work with
421 for (nsTArray<uint8_t>& certDER : certsArray) {
422 nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::ConstructFromDER(
423 BitwiseCast<char*, uint8_t*>(certDER.Elements()), certDER.Length());
424 if (!cert) {
425 return NS_ERROR_FAILURE;
426 }
427 nsresult rv = array->AppendElement(cert);
428 if (NS_FAILED(rv)) {
429 return rv;
430 }
431 }
432
433 return handleCACertDownload(WrapNotNull(array), ctx);
434 }
435
436 /**
437 * Decodes a given array of DER-encoded certificates into temporary storage.
438 *
439 * @param certs
440 * Array in which the decoded certificates are stored as arrays of
441 * unsigned chars.
442 * @param temporaryCerts
443 * List of decoded certificates.
444 */
ImportCertsIntoTempStorage(nsTArray<nsTArray<uint8_t>> & certs,const UniqueCERTCertList & temporaryCerts)445 static nsresult ImportCertsIntoTempStorage(
446 nsTArray<nsTArray<uint8_t>>& certs,
447 /*out*/ const UniqueCERTCertList& temporaryCerts) {
448 NS_ENSURE_ARG_POINTER(temporaryCerts);
449
450 for (nsTArray<uint8_t>& certDER : certs) {
451 CERTCertificate* certificate;
452 SECItem certItem;
453 certItem.len = certDER.Length();
454 certItem.data = certDER.Elements();
455 certificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem,
456 nullptr, false, true);
457
458 UniqueCERTCertificate cert(certificate);
459 if (!cert) {
460 continue;
461 }
462
463 if (CERT_AddCertToListTail(temporaryCerts.get(), cert.get()) ==
464 SECSuccess) {
465 Unused << cert.release();
466 }
467 }
468
469 return NS_OK;
470 }
471
472 NS_IMETHODIMP
ImportEmailCertificate(uint8_t * data,uint32_t length,nsIInterfaceRequestor * ctx)473 nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
474 nsIInterfaceRequestor* ctx) {
475 nsTArray<nsTArray<uint8_t>> certsArray;
476
477 nsresult rv = getCertsFromPackage(certsArray, data, length);
478 if (NS_FAILED(rv)) {
479 return rv;
480 }
481
482 UniqueCERTCertList temporaryCerts(CERT_NewCertList());
483 if (!temporaryCerts) {
484 return NS_ERROR_FAILURE;
485 }
486
487 rv = ImportCertsIntoTempStorage(certsArray, temporaryCerts);
488 if (NS_FAILED(rv)) {
489 return rv;
490 }
491
492 return ImportCertsIntoPermanentStorage(temporaryCerts);
493 }
494
ImportCACerts(nsTArray<nsTArray<uint8_t>> & caCerts,nsIInterfaceRequestor * ctx)495 nsresult nsNSSCertificateDB::ImportCACerts(nsTArray<nsTArray<uint8_t>>& caCerts,
496 nsIInterfaceRequestor* ctx) {
497 UniqueCERTCertList temporaryCerts(CERT_NewCertList());
498 if (!temporaryCerts) {
499 return NS_ERROR_FAILURE;
500 }
501
502 nsresult rv = ImportCertsIntoTempStorage(caCerts, temporaryCerts);
503 if (NS_FAILED(rv)) {
504 return rv;
505 }
506
507 return ImportCertsIntoPermanentStorage(temporaryCerts);
508 }
509
DisplayCertificateAlert(nsIInterfaceRequestor * ctx,const char * stringID,nsIX509Cert * certToShow)510 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor* ctx,
511 const char* stringID,
512 nsIX509Cert* certToShow) {
513 if (!NS_IsMainThread()) {
514 NS_ERROR(
515 "nsNSSCertificateDB::DisplayCertificateAlert called off the main "
516 "thread");
517 return;
518 }
519
520 nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
521 if (!my_ctx) {
522 my_ctx = new PipUIContext();
523 }
524
525 // This shall be replaced by embedding ovverridable prompts
526 // as discussed in bug 310446, and should make use of certToShow.
527
528 nsAutoString tmpMessage;
529 GetPIPNSSBundleString(stringID, tmpMessage);
530 nsCOMPtr<nsIPrompt> prompt(do_GetInterface(my_ctx));
531 if (!prompt) {
532 return;
533 }
534
535 prompt->Alert(nullptr, tmpMessage.get());
536 }
537
538 NS_IMETHODIMP
ImportUserCertificate(uint8_t * data,uint32_t length,nsIInterfaceRequestor * ctx)539 nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
540 nsIInterfaceRequestor* ctx) {
541 if (!NS_IsMainThread()) {
542 NS_ERROR(
543 "nsNSSCertificateDB::ImportUserCertificate called off the main thread");
544 return NS_ERROR_NOT_SAME_THREAD;
545 }
546
547 nsTArray<nsTArray<uint8_t>> certsArray;
548
549 nsresult rv = getCertsFromPackage(certsArray, data, length);
550 if (NS_FAILED(rv)) {
551 return rv;
552 }
553
554 SECItem certItem;
555
556 if (certsArray.IsEmpty()) {
557 return NS_OK;
558 }
559
560 certItem.len = certsArray.ElementAt(0).Length();
561 certItem.data = certsArray.ElementAt(0).Elements();
562
563 UniqueCERTCertificate cert(CERT_NewTempCertificate(
564 CERT_GetDefaultCertDB(), &certItem, nullptr, false, true));
565 if (!cert) {
566 return NS_ERROR_FAILURE;
567 }
568
569 UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
570 if (!slot) {
571 nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
572 DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow);
573 return NS_ERROR_FAILURE;
574 }
575 slot = nullptr;
576
577 /* pick a nickname for the cert */
578 nsAutoCString nickname;
579 if (cert->nickname) {
580 nickname = cert->nickname;
581 } else {
582 get_default_nickname(cert.get(), ctx, nickname);
583 }
584
585 /* user wants to import the cert */
586 slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
587 if (!slot) {
588 return NS_ERROR_FAILURE;
589 }
590 slot = nullptr;
591
592 {
593 nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
594 DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
595 }
596
597 rv = NS_OK;
598 if (!certsArray.IsEmpty()) {
599 certsArray.RemoveElementAt(0);
600 rv = ImportCACerts(certsArray, ctx);
601 }
602
603 nsCOMPtr<nsIObserverService> observerService =
604 mozilla::services::GetObserverService();
605 if (observerService) {
606 observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
607 nullptr);
608 }
609
610 return rv;
611 }
612
613 NS_IMETHODIMP
DeleteCertificate(nsIX509Cert * aCert)614 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert* aCert) {
615 NS_ENSURE_ARG_POINTER(aCert);
616 UniqueCERTCertificate cert(aCert->GetCert());
617 if (!cert) {
618 return NS_ERROR_FAILURE;
619 }
620
621 // Temporary certificates aren't on a slot and will go away when the
622 // nsIX509Cert is destructed.
623 if (cert->slot) {
624 uint32_t certType;
625 nsresult rv = aCert->GetCertType(&certType);
626 if (NS_WARN_IF(NS_FAILED(rv))) {
627 return rv;
628 }
629 if (certType == nsIX509Cert::USER_CERT) {
630 SECStatus srv = PK11_Authenticate(cert->slot, true, nullptr);
631 if (srv != SECSuccess) {
632 return NS_ERROR_FAILURE;
633 }
634 srv = PK11_DeleteTokenCertAndKey(cert.get(), nullptr);
635 if (srv != SECSuccess) {
636 return NS_ERROR_FAILURE;
637 }
638 } else {
639 // For certificates that can't be deleted (e.g. built-in roots), un-set
640 // all trust bits.
641 nsNSSCertTrust trust(0, 0);
642 SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
643 cert, trust.GetTrust(), nullptr);
644 if (srv != SECSuccess) {
645 return NS_ERROR_FAILURE;
646 }
647 if (!PK11_IsReadOnly(cert->slot)) {
648 srv = SEC_DeletePermCertificate(cert.get());
649 if (srv != SECSuccess) {
650 return NS_ERROR_FAILURE;
651 }
652 }
653 }
654 }
655
656 nsCOMPtr<nsIObserverService> observerService =
657 mozilla::services::GetObserverService();
658 if (observerService) {
659 observerService->NotifyObservers(nullptr, "psm:user-certificate-deleted",
660 nullptr);
661 }
662
663 return NS_OK;
664 }
665
666 NS_IMETHODIMP
SetCertTrust(nsIX509Cert * cert,uint32_t type,uint32_t trusted)667 nsNSSCertificateDB::SetCertTrust(nsIX509Cert* cert, uint32_t type,
668 uint32_t trusted) {
669 NS_ENSURE_ARG_POINTER(cert);
670 nsNSSCertTrust trust;
671 switch (type) {
672 case nsIX509Cert::CA_CERT:
673 trust.SetValidCA();
674 trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
675 !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
676 break;
677 case nsIX509Cert::SERVER_CERT:
678 trust.SetValidPeer();
679 trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false);
680 break;
681 case nsIX509Cert::EMAIL_CERT:
682 trust.SetValidPeer();
683 trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
684 break;
685 default:
686 // Ignore any other type of certificate (including invalid types).
687 return NS_OK;
688 }
689
690 UniqueCERTCertificate nsscert(cert->GetCert());
691 SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
692 nsscert, trust.GetTrust(), nullptr);
693 return MapSECStatus(srv);
694 }
695
696 NS_IMETHODIMP
IsCertTrusted(nsIX509Cert * cert,uint32_t certType,uint32_t trustType,bool * _isTrusted)697 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert* cert, uint32_t certType,
698 uint32_t trustType, bool* _isTrusted) {
699 NS_ENSURE_ARG_POINTER(_isTrusted);
700 *_isTrusted = false;
701
702 nsresult rv = BlockUntilLoadableCertsLoaded();
703 if (NS_FAILED(rv)) {
704 return rv;
705 }
706
707 SECStatus srv;
708 UniqueCERTCertificate nsscert(cert->GetCert());
709 CERTCertTrust nsstrust;
710 srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
711 if (srv != SECSuccess) {
712 // CERT_GetCertTrust returns SECFailure if given a temporary cert that
713 // doesn't have any trust information yet. This isn't an error.
714 return NS_OK;
715 }
716
717 nsNSSCertTrust trust(&nsstrust);
718 if (certType == nsIX509Cert::CA_CERT) {
719 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
720 *_isTrusted = trust.HasTrustedCA(true, false);
721 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
722 *_isTrusted = trust.HasTrustedCA(false, true);
723 } else {
724 return NS_ERROR_FAILURE;
725 }
726 } else if (certType == nsIX509Cert::SERVER_CERT) {
727 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
728 *_isTrusted = trust.HasTrustedPeer(true, false);
729 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
730 *_isTrusted = trust.HasTrustedPeer(false, true);
731 } else {
732 return NS_ERROR_FAILURE;
733 }
734 } else if (certType == nsIX509Cert::EMAIL_CERT) {
735 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
736 *_isTrusted = trust.HasTrustedPeer(true, false);
737 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
738 *_isTrusted = trust.HasTrustedPeer(false, true);
739 } else {
740 return NS_ERROR_FAILURE;
741 }
742 } /* user: ignore */
743 return NS_OK;
744 }
745
746 NS_IMETHODIMP
ImportCertsFromFile(nsIFile * aFile,uint32_t aType)747 nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) {
748 NS_ENSURE_ARG(aFile);
749 switch (aType) {
750 case nsIX509Cert::CA_CERT:
751 case nsIX509Cert::EMAIL_CERT:
752 // good
753 break;
754
755 default:
756 // not supported (yet)
757 return NS_ERROR_FAILURE;
758 }
759
760 PRFileDesc* fd = nullptr;
761 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
762 if (NS_FAILED(rv)) {
763 return rv;
764 }
765 if (!fd) {
766 return NS_ERROR_FAILURE;
767 }
768
769 PRFileInfo fileInfo;
770 if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
771 return NS_ERROR_FAILURE;
772 }
773
774 auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
775 int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
776 PR_Close(fd);
777
778 if (bytesObtained != fileInfo.size) {
779 return NS_ERROR_FAILURE;
780 }
781
782 nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
783
784 switch (aType) {
785 case nsIX509Cert::CA_CERT:
786 return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
787 case nsIX509Cert::EMAIL_CERT:
788 return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
789 default:
790 MOZ_ASSERT(false, "Unsupported type should have been filtered out");
791 break;
792 }
793
794 return NS_ERROR_FAILURE;
795 }
796
797 NS_IMETHODIMP
ImportPKCS12File(nsIFile * aFile,const nsAString & aPassword,uint32_t * aError)798 nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile, const nsAString& aPassword,
799 uint32_t* aError) {
800 if (!NS_IsMainThread()) {
801 return NS_ERROR_NOT_SAME_THREAD;
802 }
803 nsresult rv = BlockUntilLoadableCertsLoaded();
804 if (NS_FAILED(rv)) {
805 return rv;
806 }
807
808 NS_ENSURE_ARG(aFile);
809 nsPKCS12Blob blob;
810 rv = blob.ImportFromFile(aFile, aPassword, *aError);
811 nsCOMPtr<nsIObserverService> observerService =
812 mozilla::services::GetObserverService();
813 if (NS_SUCCEEDED(rv) && observerService) {
814 observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
815 nullptr);
816 }
817
818 return rv;
819 }
820
821 NS_IMETHODIMP
ExportPKCS12File(nsIFile * aFile,const nsTArray<RefPtr<nsIX509Cert>> & aCerts,const nsAString & aPassword,uint32_t * aError)822 nsNSSCertificateDB::ExportPKCS12File(
823 nsIFile* aFile, const nsTArray<RefPtr<nsIX509Cert>>& aCerts,
824 const nsAString& aPassword, uint32_t* aError) {
825 if (!NS_IsMainThread()) {
826 return NS_ERROR_NOT_SAME_THREAD;
827 }
828 nsresult rv = BlockUntilLoadableCertsLoaded();
829 if (NS_FAILED(rv)) {
830 return rv;
831 }
832
833 NS_ENSURE_ARG(aFile);
834 if (aCerts.IsEmpty()) {
835 return NS_OK;
836 }
837 nsPKCS12Blob blob;
838 return blob.ExportToFile(aFile, aCerts, aPassword, *aError);
839 }
840
841 NS_IMETHODIMP
ConstructX509FromBase64(const nsACString & base64,nsIX509Cert ** _retval)842 nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
843 /*out*/ nsIX509Cert** _retval) {
844 if (!_retval) {
845 return NS_ERROR_INVALID_POINTER;
846 }
847
848 // Base64Decode() doesn't consider a zero length input as an error, and just
849 // returns the empty string. We don't want this behavior, so the below check
850 // catches this case.
851 if (base64.Length() < 1) {
852 return NS_ERROR_ILLEGAL_VALUE;
853 }
854
855 nsAutoCString certDER;
856 nsresult rv = Base64Decode(base64, certDER);
857 if (NS_FAILED(rv)) {
858 return rv;
859 }
860
861 return ConstructX509FromSpan(AsBytes(Span(certDER)), _retval);
862 }
863
864 NS_IMETHODIMP
ConstructX509(const nsTArray<uint8_t> & certDER,nsIX509Cert ** _retval)865 nsNSSCertificateDB::ConstructX509(const nsTArray<uint8_t>& certDER,
866 nsIX509Cert** _retval) {
867 return ConstructX509FromSpan(Span(certDER.Elements(), certDER.Length()),
868 _retval);
869 }
870
ConstructX509FromSpan(Span<const uint8_t> aInputSpan,nsIX509Cert ** _retval)871 nsresult nsNSSCertificateDB::ConstructX509FromSpan(
872 Span<const uint8_t> aInputSpan, nsIX509Cert** _retval) {
873 if (NS_WARN_IF(!_retval)) {
874 return NS_ERROR_INVALID_POINTER;
875 }
876
877 if (aInputSpan.Length() > std::numeric_limits<unsigned int>::max()) {
878 return NS_ERROR_ILLEGAL_VALUE;
879 }
880
881 SECItem certData;
882 certData.type = siDERCertBuffer;
883 certData.data = const_cast<unsigned char*>(
884 reinterpret_cast<const unsigned char*>(aInputSpan.Elements()));
885 certData.len = aInputSpan.Length();
886
887 UniqueCERTCertificate cert(CERT_NewTempCertificate(
888 CERT_GetDefaultCertDB(), &certData, nullptr, false, true));
889 if (!cert)
890 return (PORT_GetError() == SEC_ERROR_NO_MEMORY) ? NS_ERROR_OUT_OF_MEMORY
891 : NS_ERROR_FAILURE;
892
893 nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
894 if (!nssCert) {
895 return NS_ERROR_OUT_OF_MEMORY;
896 }
897 nssCert.forget(_retval);
898 return NS_OK;
899 }
900
get_default_nickname(CERTCertificate * cert,nsIInterfaceRequestor * ctx,nsCString & nickname)901 void nsNSSCertificateDB::get_default_nickname(CERTCertificate* cert,
902 nsIInterfaceRequestor* ctx,
903 nsCString& nickname) {
904 nickname.Truncate();
905
906 CK_OBJECT_HANDLE keyHandle;
907
908 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
909 return;
910 }
911
912 CERTCertDBHandle* defaultcertdb = CERT_GetDefaultCertDB();
913 nsAutoCString username;
914 UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
915 if (tempCN) {
916 username = tempCN.get();
917 }
918
919 nsAutoCString caname;
920 UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
921 if (tempIssuerOrg) {
922 caname = tempIssuerOrg.get();
923 }
924
925 nsAutoString tmpNickFmt;
926 GetPIPNSSBundleString("nick_template", tmpNickFmt);
927 NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
928
929 nsAutoCString baseName;
930 baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
931 if (baseName.IsEmpty()) {
932 return;
933 }
934
935 nickname = baseName;
936
937 /*
938 * We need to see if the private key exists on a token, if it does
939 * then we need to check for nicknames that already exist on the smart
940 * card.
941 */
942 UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
943 if (!slot) return;
944
945 if (!PK11_IsInternal(slot.get())) {
946 nsAutoCString tmp;
947 tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
948 if (tmp.IsEmpty()) {
949 nickname.Truncate();
950 return;
951 }
952 baseName = tmp;
953 nickname = baseName;
954 }
955
956 int count = 1;
957 while (true) {
958 if (count > 1) {
959 nsAutoCString tmp;
960 tmp.AppendPrintf("%s #%d", baseName.get(), count);
961 if (tmp.IsEmpty()) {
962 nickname.Truncate();
963 return;
964 }
965 nickname = tmp;
966 }
967
968 UniqueCERTCertificate dummycert;
969
970 if (PK11_IsInternal(slot.get())) {
971 /* look up the nickname to make sure it isn't in use already */
972 dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
973 } else {
974 // Check the cert against others that already live on the smart card.
975 dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
976 if (dummycert) {
977 // Make sure the subject names are different.
978 if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) {
979 /*
980 * There is another certificate with the same nickname and
981 * the same subject name on the smart card, so let's use this
982 * nickname.
983 */
984 dummycert = nullptr;
985 }
986 }
987 }
988 if (!dummycert) {
989 break;
990 }
991 count++;
992 }
993 }
994
995 NS_IMETHODIMP
AddCertFromBase64(const nsACString & aBase64,const nsACString & aTrust,nsIX509Cert ** addedCertificate)996 nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
997 const nsACString& aTrust,
998 nsIX509Cert** addedCertificate) {
999 // Base64Decode() doesn't consider a zero length input as an error, and just
1000 // returns the empty string. We don't want this behavior, so the below check
1001 // catches this case.
1002 if (aBase64.Length() < 1) {
1003 return NS_ERROR_ILLEGAL_VALUE;
1004 }
1005
1006 nsAutoCString aCertDER;
1007 nsresult rv = Base64Decode(aBase64, aCertDER);
1008 if (NS_FAILED(rv)) {
1009 return rv;
1010 }
1011 return AddCert(aCertDER, aTrust, addedCertificate);
1012 }
1013
1014 NS_IMETHODIMP
AddCert(const nsACString & aCertDER,const nsACString & aTrust,nsIX509Cert ** addedCertificate)1015 nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
1016 const nsACString& aTrust,
1017 nsIX509Cert** addedCertificate) {
1018 MOZ_ASSERT(addedCertificate);
1019 if (!addedCertificate) {
1020 return NS_ERROR_INVALID_ARG;
1021 }
1022 *addedCertificate = nullptr;
1023
1024 nsNSSCertTrust trust;
1025 if (CERT_DecodeTrustString(&trust.GetTrust(),
1026 PromiseFlatCString(aTrust).get()) != SECSuccess) {
1027 return NS_ERROR_FAILURE;
1028 }
1029
1030 nsCOMPtr<nsIX509Cert> newCert;
1031 nsresult rv =
1032 ConstructX509FromSpan(AsBytes(Span(aCertDER)), getter_AddRefs(newCert));
1033 if (NS_FAILED(rv)) {
1034 return rv;
1035 }
1036
1037 UniqueCERTCertificate tmpCert(newCert->GetCert());
1038 if (!tmpCert) {
1039 return NS_ERROR_FAILURE;
1040 }
1041
1042 // If there's already a certificate that matches this one in the database, we
1043 // still want to set its trust to the given value.
1044 if (tmpCert->isperm) {
1045 rv = SetCertTrustFromString(newCert, aTrust);
1046 if (NS_FAILED(rv)) {
1047 return rv;
1048 }
1049 newCert.forget(addedCertificate);
1050 return NS_OK;
1051 }
1052
1053 UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
1054
1055 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1056 ("Created nick \"%s\"\n", nickname.get()));
1057
1058 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1059 SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
1060 nickname.get(),
1061 false); // this parameter is ignored by NSS
1062 if (srv != SECSuccess) {
1063 return MapSECStatus(srv);
1064 }
1065 srv = ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(),
1066 nullptr);
1067 if (srv != SECSuccess) {
1068 return MapSECStatus(srv);
1069 }
1070 newCert.forget(addedCertificate);
1071 return NS_OK;
1072 }
1073
1074 NS_IMETHODIMP
SetCertTrustFromString(nsIX509Cert * cert,const nsACString & trustString)1075 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
1076 const nsACString& trustString) {
1077 NS_ENSURE_ARG(cert);
1078
1079 CERTCertTrust trust;
1080 SECStatus srv =
1081 CERT_DecodeTrustString(&trust, PromiseFlatCString(trustString).get());
1082 if (srv != SECSuccess) {
1083 return MapSECStatus(srv);
1084 }
1085 UniqueCERTCertificate nssCert(cert->GetCert());
1086
1087 srv = ChangeCertTrustWithPossibleAuthentication(nssCert, trust, nullptr);
1088 return MapSECStatus(srv);
1089 }
1090
AsPKCS7Blob(const nsTArray<RefPtr<nsIX509Cert>> & certList,nsACString & _retval)1091 NS_IMETHODIMP nsNSSCertificateDB::AsPKCS7Blob(
1092 const nsTArray<RefPtr<nsIX509Cert>>& certList, nsACString& _retval) {
1093 if (certList.IsEmpty()) {
1094 return NS_ERROR_INVALID_ARG;
1095 }
1096
1097 UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
1098 if (!cmsg) {
1099 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1100 ("nsNSSCertificateDB::AsPKCS7Blob - can't create CMS message"));
1101 return NS_ERROR_OUT_OF_MEMORY;
1102 }
1103
1104 UniqueNSSCMSSignedData sigd(nullptr);
1105 for (const auto& cert : certList) {
1106 // We need an owning handle when calling nsIX509Cert::GetCert().
1107 UniqueCERTCertificate nssCert(cert->GetCert());
1108 if (!sigd) {
1109 sigd.reset(
1110 NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), false));
1111 if (!sigd) {
1112 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1113 ("nsNSSCertificateDB::AsPKCS7Blob - can't create SignedData"));
1114 return NS_ERROR_FAILURE;
1115 }
1116 } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) !=
1117 SECSuccess) {
1118 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1119 ("nsNSSCertificateDB::AsPKCS7Blob - can't add cert"));
1120 return NS_ERROR_FAILURE;
1121 }
1122 }
1123
1124 NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get());
1125 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) !=
1126 SECSuccess) {
1127 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1128 ("nsNSSCertificateDB::AsPKCS7Blob - can't attach SignedData"));
1129 return NS_ERROR_FAILURE;
1130 }
1131 // cmsg owns sigd now.
1132 Unused << sigd.release();
1133
1134 UniquePLArenaPool arena(PORT_NewArena(1024));
1135 if (!arena) {
1136 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1137 ("nsNSSCertificateDB::AsPKCS7Blob - out of memory"));
1138 return NS_ERROR_OUT_OF_MEMORY;
1139 }
1140
1141 SECItem certP7 = {siBuffer, nullptr, 0};
1142 NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(
1143 cmsg.get(), nullptr, nullptr, &certP7, arena.get(), nullptr, nullptr,
1144 nullptr, nullptr, nullptr, nullptr);
1145 if (!ecx) {
1146 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1147 ("nsNSSCertificateDB::AsPKCS7Blob - can't create encoder"));
1148 return NS_ERROR_FAILURE;
1149 }
1150
1151 if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
1152 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1153 ("nsNSSCertificateDB::AsPKCS7Blob - failed to add encoded data"));
1154 return NS_ERROR_FAILURE;
1155 }
1156
1157 _retval.Assign(nsDependentCSubstring(
1158 reinterpret_cast<const char*>(certP7.data), certP7.len));
1159 return NS_OK;
1160 }
1161
1162 NS_IMETHODIMP
GetCerts(nsTArray<RefPtr<nsIX509Cert>> & _retval)1163 nsNSSCertificateDB::GetCerts(nsTArray<RefPtr<nsIX509Cert>>& _retval) {
1164 nsresult rv = BlockUntilLoadableCertsLoaded();
1165 if (NS_FAILED(rv)) {
1166 return rv;
1167 }
1168
1169 rv = CheckForSmartCardChanges();
1170 if (NS_FAILED(rv)) {
1171 return rv;
1172 }
1173
1174 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1175 UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
1176 if (!certList) {
1177 return NS_ERROR_FAILURE;
1178 }
1179 return nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(certList,
1180 _retval);
1181 }
1182
1183 NS_IMETHODIMP
AsyncHasThirdPartyRoots(nsIAsyncBoolCallback * aCallback)1184 nsNSSCertificateDB::AsyncHasThirdPartyRoots(nsIAsyncBoolCallback* aCallback) {
1185 NS_ENSURE_ARG_POINTER(aCallback);
1186 nsMainThreadPtrHandle<nsIAsyncBoolCallback> callback(
1187 new nsMainThreadPtrHolder<nsIAsyncBoolCallback>("AsyncHasThirdPartyRoots",
1188 aCallback));
1189
1190 return NS_DispatchBackgroundTask(
1191 NS_NewRunnableFunction(
1192 "nsNSSCertificateDB::AsyncHasThirdPartyRoots",
1193 [cb = std::move(callback), self = RefPtr{this}] {
1194 bool hasThirdPartyRoots = [self]() -> bool {
1195 nsTArray<RefPtr<nsIX509Cert>> certs;
1196 nsresult rv = self->GetCerts(certs);
1197 if (NS_FAILED(rv)) {
1198 return false;
1199 }
1200
1201 for (const auto& cert : certs) {
1202 bool isTrusted = false;
1203 nsresult rv =
1204 self->IsCertTrusted(cert, nsIX509Cert::CA_CERT,
1205 nsIX509CertDB::TRUSTED_SSL, &isTrusted);
1206 if (NS_FAILED(rv)) {
1207 return false;
1208 }
1209
1210 if (!isTrusted) {
1211 continue;
1212 }
1213
1214 bool isBuiltInRoot = false;
1215 rv = cert->GetIsBuiltInRoot(&isBuiltInRoot);
1216 if (NS_FAILED(rv)) {
1217 return false;
1218 }
1219
1220 if (!isBuiltInRoot) {
1221 return true;
1222 }
1223 }
1224
1225 return false;
1226 }();
1227
1228 NS_DispatchToMainThread(NS_NewRunnableFunction(
1229 "nsNSSCertificateDB::AsyncHasThirdPartyRoots callback",
1230 [cb, hasThirdPartyRoots]() {
1231 cb->OnResult(hasThirdPartyRoots);
1232 }));
1233 }),
1234 NS_DISPATCH_EVENT_MAY_BLOCK);
1235
1236 return NS_OK;
1237 }
1238
VerifyCertAtTime(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const nsACString & aHostname,mozilla::pkix::Time aTime,nsTArray<RefPtr<nsIX509Cert>> & aVerifiedChain,bool * aHasEVPolicy,int32_t * _retval)1239 nsresult VerifyCertAtTime(nsIX509Cert* aCert,
1240 int64_t /*SECCertificateUsage*/ aUsage,
1241 uint32_t aFlags, const nsACString& aHostname,
1242 mozilla::pkix::Time aTime,
1243 nsTArray<RefPtr<nsIX509Cert>>& aVerifiedChain,
1244 bool* aHasEVPolicy,
1245 int32_t* /*PRErrorCode*/ _retval) {
1246 NS_ENSURE_ARG_POINTER(aCert);
1247 NS_ENSURE_ARG_POINTER(aHasEVPolicy);
1248 NS_ENSURE_ARG_POINTER(_retval);
1249
1250 if (!aVerifiedChain.IsEmpty()) {
1251 return NS_ERROR_INVALID_ARG;
1252 }
1253
1254 *aHasEVPolicy = false;
1255 *_retval = PR_UNKNOWN_ERROR;
1256
1257 UniqueCERTCertificate nssCert(aCert->GetCert());
1258 if (!nssCert) {
1259 return NS_ERROR_INVALID_ARG;
1260 }
1261
1262 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1263 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1264
1265 UniqueCERTCertList resultChain;
1266 EVStatus evStatus;
1267 mozilla::pkix::Result result;
1268
1269 if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
1270 result =
1271 certVerifier->VerifySSLServerCert(nssCert, aTime,
1272 nullptr, // Assume no context
1273 aHostname, resultChain, aFlags,
1274 Nothing(), // extraCertificates
1275 Nothing(), // stapledOCSPResponse
1276 Nothing(), // sctsFromTLSExtension
1277 Nothing(), // dcInfo
1278 OriginAttributes(), &evStatus);
1279 } else {
1280 const nsCString& flatHostname = PromiseFlatCString(aHostname);
1281 result = certVerifier->VerifyCert(
1282 nssCert.get(), aUsage, aTime,
1283 nullptr, // Assume no context
1284 aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
1285 Nothing(), // extraCertificates
1286 Nothing(), // stapledOCSPResponse
1287 Nothing(), // sctsFromTLSExtension
1288 OriginAttributes(), &evStatus);
1289 }
1290
1291 if (result == mozilla::pkix::Success) {
1292 nsresult rv = nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
1293 resultChain, aVerifiedChain);
1294
1295 if (NS_FAILED(rv)) {
1296 return rv;
1297 }
1298
1299 if (evStatus == EVStatus::EV) {
1300 *aHasEVPolicy = true;
1301 }
1302 }
1303
1304 *_retval = mozilla::pkix::MapResultToPRErrorCode(result);
1305
1306 return NS_OK;
1307 }
1308
1309 class VerifyCertAtTimeTask final : public CryptoTask {
1310 public:
VerifyCertAtTimeTask(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const nsACString & aHostname,uint64_t aTime,nsICertVerificationCallback * aCallback)1311 VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
1312 const nsACString& aHostname, uint64_t aTime,
1313 nsICertVerificationCallback* aCallback)
1314 : mCert(aCert),
1315 mUsage(aUsage),
1316 mFlags(aFlags),
1317 mHostname(aHostname),
1318 mTime(aTime),
1319 mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(
1320 "nsICertVerificationCallback", aCallback)),
1321 mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE),
1322 mHasEVPolicy(false) {}
1323
1324 private:
CalculateResult()1325 virtual nsresult CalculateResult() override {
1326 nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
1327 if (!certDB) {
1328 return NS_ERROR_FAILURE;
1329 }
1330 return VerifyCertAtTime(mCert, mUsage, mFlags, mHostname,
1331 mozilla::pkix::TimeFromEpochInSeconds(mTime),
1332 mVerifiedCertList, &mHasEVPolicy, &mPRErrorCode);
1333 }
1334
CallCallback(nsresult rv)1335 virtual void CallCallback(nsresult rv) override {
1336 if (NS_FAILED(rv)) {
1337 nsTArray<RefPtr<nsIX509Cert>> tmp;
1338 Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE, tmp,
1339 false);
1340 } else {
1341 Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
1342 mHasEVPolicy);
1343 }
1344 }
1345
1346 nsCOMPtr<nsIX509Cert> mCert;
1347 int64_t mUsage;
1348 uint32_t mFlags;
1349 nsCString mHostname;
1350 uint64_t mTime;
1351 nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
1352 int32_t mPRErrorCode;
1353 nsTArray<RefPtr<nsIX509Cert>> mVerifiedCertList;
1354 bool mHasEVPolicy;
1355 };
1356
1357 NS_IMETHODIMP
AsyncVerifyCertAtTime(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const nsACString & aHostname,uint64_t aTime,nsICertVerificationCallback * aCallback)1358 nsNSSCertificateDB::AsyncVerifyCertAtTime(
1359 nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags,
1360 const nsACString& aHostname, uint64_t aTime,
1361 nsICertVerificationCallback* aCallback) {
1362 RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(
1363 aCert, aUsage, aFlags, aHostname, aTime, aCallback));
1364 return task->Dispatch();
1365 }
1366
1367 NS_IMETHODIMP
ClearOCSPCache()1368 nsNSSCertificateDB::ClearOCSPCache() {
1369 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1370 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1371 certVerifier->ClearOCSPCache();
1372 return NS_OK;
1373 }
1374