Line data Source code
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/Unused.h"
17 : #include "nsArray.h"
18 : #include "nsArrayUtils.h"
19 : #include "nsCOMPtr.h"
20 : #include "nsComponentManagerUtils.h"
21 : #include "nsICertificateDialogs.h"
22 : #include "nsIFile.h"
23 : #include "nsIMutableArray.h"
24 : #include "nsIObserverService.h"
25 : #include "nsIPrefBranch.h"
26 : #include "nsIPrefService.h"
27 : #include "nsIPrompt.h"
28 : #include "nsNSSCertHelper.h"
29 : #include "nsNSSCertTrust.h"
30 : #include "nsNSSCertificate.h"
31 : #include "nsNSSComponent.h"
32 : #include "nsNSSHelper.h"
33 : #include "nsNSSShutDown.h"
34 : #include "nsPKCS12Blob.h"
35 : #include "nsPromiseFlatString.h"
36 : #include "nsProxyRelease.h"
37 : #include "nsReadableUtils.h"
38 : #include "nsThreadUtils.h"
39 : #include "nspr.h"
40 : #include "pkix/Time.h"
41 : #include "pkix/pkixnss.h"
42 : #include "pkix/pkixtypes.h"
43 : #include "secasn1.h"
44 : #include "secder.h"
45 : #include "secerr.h"
46 : #include "ssl.h"
47 :
48 : #ifdef XP_WIN
49 : #include <winsock.h> // for ntohl
50 : #endif
51 :
52 : using namespace mozilla;
53 : using namespace mozilla::psm;
54 : using mozilla::psm::SharedSSLState;
55 :
56 : extern LazyLogModule gPIPNSSLog;
57 :
58 : static nsresult
59 0 : attemptToLogInWithDefaultPassword()
60 : {
61 : #ifdef NSS_DISABLE_DBM
62 : // The SQL NSS DB requires the user to be authenticated to set certificate
63 : // trust settings, even if the user's password is empty. To maintain
64 : // compatibility with the DBM-based database, try to log in with the
65 : // default empty password. This will allow, at least, tests that need to
66 : // change certificate trust to pass on all platforms. TODO(bug 978120): Do
67 : // proper testing and/or implement a better solution so that we are confident
68 : // that this does the correct thing outside of xpcshell tests too.
69 : UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
70 : if (!slot) {
71 : return MapSECStatus(SECFailure);
72 : }
73 : if (PK11_NeedUserInit(slot.get())) {
74 : // Ignore the return value. Presumably PK11_InitPin will fail if the user
75 : // has a non-default password.
76 : Unused << PK11_InitPin(slot.get(), nullptr, nullptr);
77 : }
78 : #endif
79 :
80 0 : return NS_OK;
81 : }
82 :
83 0 : NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)
84 :
85 0 : nsNSSCertificateDB::~nsNSSCertificateDB()
86 : {
87 0 : nsNSSShutDownPreventionLock locker;
88 0 : if (isAlreadyShutDown()) {
89 0 : return;
90 : }
91 :
92 0 : shutdown(ShutdownCalledFrom::Object);
93 0 : }
94 :
95 : NS_IMETHODIMP
96 0 : nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
97 : /*out*/ nsIX509Cert** _cert)
98 : {
99 0 : NS_ENSURE_ARG_POINTER(_cert);
100 0 : *_cert = nullptr;
101 :
102 0 : if (aDBKey.IsEmpty()) {
103 0 : return NS_ERROR_INVALID_ARG;
104 : }
105 :
106 0 : nsNSSShutDownPreventionLock locker;
107 0 : if (isAlreadyShutDown()) {
108 0 : return NS_ERROR_NOT_AVAILABLE;
109 : }
110 :
111 0 : UniqueCERTCertificate cert;
112 0 : nsresult rv = FindCertByDBKey(aDBKey, cert);
113 0 : if (NS_FAILED(rv)) {
114 0 : return rv;
115 : }
116 : // If we can't find the certificate, that's not an error. Just return null.
117 0 : if (!cert) {
118 0 : return NS_OK;
119 : }
120 0 : nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
121 0 : if (!nssCert) {
122 0 : return NS_ERROR_OUT_OF_MEMORY;
123 : }
124 0 : nssCert.forget(_cert);
125 0 : return NS_OK;
126 : }
127 :
128 : nsresult
129 0 : nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
130 : UniqueCERTCertificate& cert)
131 : {
132 : static_assert(sizeof(uint64_t) == 8, "type size sanity check");
133 : static_assert(sizeof(uint32_t) == 4, "type size sanity check");
134 : // (From nsNSSCertificate::GetDbKey)
135 : // The format of the key is the base64 encoding of the following:
136 : // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
137 : // never implemented)
138 : // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
139 : // never implemented)
140 : // 4 bytes: <serial number length in big-endian order>
141 : // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
142 : // n bytes: <bytes of serial number>
143 : // m bytes: <DER-encoded issuer distinguished name>
144 0 : nsAutoCString decoded;
145 0 : nsAutoCString tmpDBKey(aDBKey);
146 : // Filter out any whitespace for backwards compatibility.
147 0 : tmpDBKey.StripWhitespace();
148 0 : nsresult rv = Base64Decode(tmpDBKey, decoded);
149 0 : if (NS_FAILED(rv)) {
150 0 : return rv;
151 : }
152 0 : if (decoded.Length() < 16) {
153 0 : return NS_ERROR_ILLEGAL_INPUT;
154 : }
155 0 : const char* reader = decoded.BeginReading();
156 0 : uint64_t zeroes = *BitwiseCast<const uint64_t*, const char*>(reader);
157 0 : if (zeroes != 0) {
158 0 : return NS_ERROR_ILLEGAL_INPUT;
159 : }
160 0 : reader += sizeof(uint64_t);
161 : // Note: We surround the ntohl() argument with parentheses to stop the macro
162 : // from thinking two arguments were passed.
163 0 : uint32_t serialNumberLen = ntohl(
164 0 : (*BitwiseCast<const uint32_t*, const char*>(reader)));
165 0 : reader += sizeof(uint32_t);
166 0 : uint32_t issuerLen = ntohl(
167 0 : (*BitwiseCast<const uint32_t*, const char*>(reader)));
168 0 : reader += sizeof(uint32_t);
169 0 : if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) {
170 0 : return NS_ERROR_ILLEGAL_INPUT;
171 : }
172 : CERTIssuerAndSN issuerSN;
173 0 : issuerSN.serialNumber.len = serialNumberLen;
174 0 : issuerSN.serialNumber.data = BitwiseCast<unsigned char*, const char*>(reader);
175 0 : reader += serialNumberLen;
176 0 : issuerSN.derIssuer.len = issuerLen;
177 0 : issuerSN.derIssuer.data = BitwiseCast<unsigned char*, const char*>(reader);
178 0 : reader += issuerLen;
179 0 : MOZ_ASSERT(reader == decoded.EndReading());
180 :
181 0 : cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN));
182 0 : return NS_OK;
183 : }
184 :
185 : SECStatus
186 0 : collect_certs(void *arg, SECItem **certs, int numcerts)
187 : {
188 : CERTDERCerts *collectArgs;
189 : SECItem *cert;
190 : SECStatus rv;
191 :
192 0 : collectArgs = (CERTDERCerts *)arg;
193 :
194 0 : collectArgs->numcerts = numcerts;
195 0 : collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc(collectArgs->arena,
196 : sizeof(SECItem) * numcerts);
197 0 : if (!collectArgs->rawCerts)
198 0 : return(SECFailure);
199 :
200 0 : cert = collectArgs->rawCerts;
201 :
202 0 : while ( numcerts-- ) {
203 0 : rv = SECITEM_CopyItem(collectArgs->arena, cert, *certs);
204 0 : if ( rv == SECFailure )
205 0 : return(SECFailure);
206 0 : cert++;
207 0 : certs++;
208 : }
209 :
210 0 : return (SECSuccess);
211 : }
212 :
213 : CERTDERCerts*
214 0 : nsNSSCertificateDB::getCertsFromPackage(const UniquePLArenaPool& arena,
215 : uint8_t* data, uint32_t length,
216 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
217 : {
218 0 : CERTDERCerts* collectArgs = PORT_ArenaZNew(arena.get(), CERTDERCerts);
219 0 : if (!collectArgs) {
220 0 : return nullptr;
221 : }
222 :
223 0 : collectArgs->arena = arena.get();
224 0 : if (CERT_DecodeCertPackage(BitwiseCast<char*, uint8_t*>(data), length,
225 : collect_certs, collectArgs) != SECSuccess) {
226 0 : return nullptr;
227 : }
228 :
229 0 : return collectArgs;
230 : }
231 :
232 : nsresult
233 0 : nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
234 : nsIInterfaceRequestor *ctx,
235 : const nsNSSShutDownPreventionLock &proofOfLock)
236 : {
237 : // First thing we have to do is figure out which certificate we're
238 : // gonna present to the user. The CA may have sent down a list of
239 : // certs which may or may not be a chained list of certs. Until
240 : // the day we can design some solid UI for the general case, we'll
241 : // code to the > 90% case. That case is where a CA sends down a
242 : // list that is a hierarchy whose root is either the first or
243 : // the last cert. What we're gonna do is compare the first
244 : // 2 entries, if the second was signed by the first, we assume
245 : // the root cert is the first cert and display it. Otherwise,
246 : // we compare the last 2 entries, if the second to last cert was
247 : // signed by the last cert, then we assume the last cert is the
248 : // root and display it.
249 :
250 : uint32_t numCerts;
251 :
252 0 : x509Certs->GetLength(&numCerts);
253 0 : MOZ_ASSERT(numCerts > 0, "Didn't get any certs to import.");
254 0 : if (numCerts == 0)
255 0 : return NS_OK; // Nothing to import, so nothing to do.
256 :
257 0 : nsCOMPtr<nsIX509Cert> certToShow;
258 : uint32_t selCertIndex;
259 0 : if (numCerts == 1) {
260 : // There's only one cert, so let's show it.
261 0 : selCertIndex = 0;
262 0 : certToShow = do_QueryElementAt(x509Certs, selCertIndex);
263 : } else {
264 0 : nsCOMPtr<nsIX509Cert> cert0; // first cert
265 0 : nsCOMPtr<nsIX509Cert> cert1; // second cert
266 0 : nsCOMPtr<nsIX509Cert> certn_2; // second to last cert
267 0 : nsCOMPtr<nsIX509Cert> certn_1; // last cert
268 :
269 0 : cert0 = do_QueryElementAt(x509Certs, 0);
270 0 : cert1 = do_QueryElementAt(x509Certs, 1);
271 0 : certn_2 = do_QueryElementAt(x509Certs, numCerts-2);
272 0 : certn_1 = do_QueryElementAt(x509Certs, numCerts-1);
273 :
274 0 : nsXPIDLString cert0SubjectName;
275 0 : nsXPIDLString cert1IssuerName;
276 0 : nsXPIDLString certn_2IssuerName;
277 0 : nsXPIDLString certn_1SubjectName;
278 :
279 0 : cert0->GetSubjectName(cert0SubjectName);
280 0 : cert1->GetIssuerName(cert1IssuerName);
281 0 : certn_2->GetIssuerName(certn_2IssuerName);
282 0 : certn_1->GetSubjectName(certn_1SubjectName);
283 :
284 0 : if (cert1IssuerName.Equals(cert0SubjectName)) {
285 : // In this case, the first cert in the list signed the second,
286 : // so the first cert is the root. Let's display it.
287 0 : selCertIndex = 0;
288 0 : certToShow = cert0;
289 : } else
290 0 : if (certn_2IssuerName.Equals(certn_1SubjectName)) {
291 : // In this case the last cert has signed the second to last cert.
292 : // The last cert is the root, so let's display it.
293 0 : selCertIndex = numCerts-1;
294 0 : certToShow = certn_1;
295 : } else {
296 : // It's not a chain, so let's just show the first one in the
297 : // downloaded list.
298 0 : selCertIndex = 0;
299 0 : certToShow = cert0;
300 : }
301 : }
302 :
303 0 : if (!certToShow)
304 0 : return NS_ERROR_FAILURE;
305 :
306 0 : nsCOMPtr<nsICertificateDialogs> dialogs;
307 0 : nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
308 : NS_GET_IID(nsICertificateDialogs),
309 0 : NS_CERTIFICATEDIALOGS_CONTRACTID);
310 0 : if (NS_FAILED(rv)) {
311 0 : return rv;
312 : }
313 :
314 0 : UniqueCERTCertificate tmpCert(certToShow->GetCert());
315 0 : if (!tmpCert) {
316 0 : return NS_ERROR_FAILURE;
317 : }
318 :
319 0 : if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
320 0 : DisplayCertificateAlert(ctx, "NotACACert", certToShow, proofOfLock);
321 0 : return NS_ERROR_FAILURE;
322 : }
323 :
324 0 : if (tmpCert->isperm) {
325 0 : DisplayCertificateAlert(ctx, "CaCertExists", certToShow, proofOfLock);
326 0 : return NS_ERROR_FAILURE;
327 : }
328 :
329 : uint32_t trustBits;
330 : bool allows;
331 0 : rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
332 0 : if (NS_FAILED(rv))
333 0 : return rv;
334 :
335 0 : if (!allows)
336 0 : return NS_ERROR_NOT_AVAILABLE;
337 :
338 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
339 0 : UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
340 :
341 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get()));
342 :
343 0 : nsNSSCertTrust trust;
344 0 : trust.SetValidCA();
345 0 : trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
346 0 : !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL),
347 0 : !!(trustBits & nsIX509CertDB::TRUSTED_OBJSIGN));
348 :
349 0 : UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
350 0 : SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
351 0 : nickname.get(),
352 0 : false); // this parameter is ignored by NSS
353 0 : if (srv != SECSuccess) {
354 0 : return MapSECStatus(srv);
355 : }
356 : // NSS ignores the first argument to CERT_ChangeCertTrust
357 0 : srv = CERT_ChangeCertTrust(nullptr, tmpCert.get(), trust.GetTrust());
358 0 : if (srv != SECSuccess) {
359 0 : return MapSECStatus(srv);
360 : }
361 :
362 : // Import additional delivered certificates that can be verified.
363 :
364 : // build a CertList for filtering
365 0 : UniqueCERTCertList certList(CERT_NewCertList());
366 0 : if (!certList) {
367 0 : return NS_ERROR_FAILURE;
368 : }
369 :
370 : // get all remaining certs into temp store
371 :
372 0 : for (uint32_t i=0; i<numCerts; i++) {
373 0 : if (i == selCertIndex) {
374 : // we already processed that one
375 0 : continue;
376 : }
377 :
378 0 : nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
379 0 : if (!remainingCert) {
380 0 : continue;
381 : }
382 :
383 0 : UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
384 0 : if (!tmpCert2) {
385 0 : continue; // Let's try to import the rest of 'em
386 : }
387 :
388 0 : if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
389 0 : continue;
390 : }
391 :
392 0 : Unused << tmpCert2.release();
393 : }
394 :
395 0 : return ImportValidCACertsInList(certList, ctx, proofOfLock);
396 : }
397 :
398 : NS_IMETHODIMP
399 0 : nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
400 : uint32_t type,
401 : nsIInterfaceRequestor* ctx)
402 : {
403 0 : nsNSSShutDownPreventionLock locker;
404 0 : if (isAlreadyShutDown()) {
405 0 : return NS_ERROR_NOT_AVAILABLE;
406 : }
407 :
408 : // We currently only handle CA certificates.
409 0 : if (type != nsIX509Cert::CA_CERT) {
410 0 : return NS_ERROR_FAILURE;
411 : }
412 :
413 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
414 0 : if (!arena) {
415 0 : return NS_ERROR_OUT_OF_MEMORY;
416 : }
417 :
418 : CERTDERCerts* certCollection = getCertsFromPackage(arena, data, length,
419 0 : locker);
420 0 : if (!certCollection) {
421 0 : return NS_ERROR_FAILURE;
422 : }
423 :
424 0 : nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
425 0 : if (!array) {
426 0 : return NS_ERROR_FAILURE;
427 : }
428 :
429 : // Now let's create some certs to work with
430 0 : for (int i = 0; i < certCollection->numcerts; i++) {
431 0 : SECItem* currItem = &certCollection->rawCerts[i];
432 0 : nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::ConstructFromDER(
433 0 : BitwiseCast<char*, unsigned char*>(currItem->data), currItem->len);
434 0 : if (!cert) {
435 0 : return NS_ERROR_FAILURE;
436 : }
437 0 : nsresult rv = array->AppendElement(cert, false);
438 0 : if (NS_FAILED(rv)) {
439 0 : return rv;
440 : }
441 : }
442 :
443 0 : return handleCACertDownload(WrapNotNull(array), ctx, locker);
444 : }
445 :
446 : /**
447 : * Filters an array of certs by usage and imports them into temporary storage.
448 : *
449 : * @param numcerts
450 : * Size of the |certs| array.
451 : * @param certs
452 : * Pointer to array of certs to import.
453 : * @param usage
454 : * Usage the certs should be filtered on.
455 : * @param caOnly
456 : * Whether to import only CA certs.
457 : * @param filteredCerts
458 : * List of certs that weren't filtered out and were successfully imported.
459 : */
460 : static nsresult
461 0 : ImportCertsIntoTempStorage(int numcerts, SECItem* certs,
462 : const SECCertUsage usage, const bool caOnly,
463 : const nsNSSShutDownPreventionLock& /*proofOfLock*/,
464 : /*out*/ const UniqueCERTCertList& filteredCerts)
465 : {
466 0 : NS_ENSURE_ARG_MIN(numcerts, 1);
467 0 : NS_ENSURE_ARG_POINTER(certs);
468 0 : NS_ENSURE_ARG_POINTER(filteredCerts.get());
469 :
470 : // CERT_ImportCerts() expects an array of *pointers* to SECItems, so we have
471 : // to convert |certs| to such a format first.
472 : SECItem** ptrArray =
473 0 : static_cast<SECItem**>(PORT_Alloc(sizeof(SECItem*) * numcerts));
474 0 : if (!ptrArray) {
475 0 : return NS_ERROR_OUT_OF_MEMORY;
476 : }
477 :
478 0 : for (int i = 0; i < numcerts; i++) {
479 0 : ptrArray[i] = &certs[i];
480 : }
481 :
482 0 : CERTCertificate** importedCerts = nullptr;
483 0 : SECStatus srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), usage,
484 : numcerts, ptrArray, &importedCerts, false,
485 0 : caOnly, nullptr);
486 0 : PORT_Free(ptrArray);
487 0 : ptrArray = nullptr;
488 0 : if (srv != SECSuccess) {
489 0 : return NS_ERROR_FAILURE;
490 : }
491 :
492 0 : for (int i = 0; i < numcerts; i++) {
493 0 : if (!importedCerts[i]) {
494 0 : continue;
495 : }
496 :
497 0 : UniqueCERTCertificate cert(CERT_DupCertificate(importedCerts[i]));
498 0 : if (!cert) {
499 0 : continue;
500 : }
501 :
502 0 : if (CERT_AddCertToListTail(filteredCerts.get(), cert.get()) == SECSuccess) {
503 0 : Unused << cert.release();
504 : }
505 : }
506 :
507 0 : CERT_DestroyCertArray(importedCerts, numcerts);
508 :
509 : // CERT_ImportCerts() ignores its |usage| parameter, so we have to manually
510 : // filter out unwanted certs.
511 0 : if (CERT_FilterCertListByUsage(filteredCerts.get(), usage, caOnly)
512 : != SECSuccess) {
513 0 : return NS_ERROR_FAILURE;
514 : }
515 :
516 0 : return NS_OK;
517 : }
518 :
519 : static nsresult
520 0 : ImportCertsIntoPermanentStorage(const UniqueCERTCertList& certChain)
521 : {
522 0 : bool encounteredFailure = false;
523 0 : PRErrorCode savedErrorCode = 0;
524 0 : UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
525 0 : for (CERTCertListNode* chainNode = CERT_LIST_HEAD(certChain);
526 0 : !CERT_LIST_END(chainNode, certChain);
527 0 : chainNode = CERT_LIST_NEXT(chainNode)) {
528 0 : UniquePORTString nickname(CERT_MakeCANickname(chainNode->cert));
529 0 : SECStatus srv = PK11_ImportCert(slot.get(), chainNode->cert,
530 0 : CK_INVALID_HANDLE, nickname.get(),
531 0 : false); // this parameter is ignored by NSS
532 0 : if (srv != SECSuccess) {
533 0 : encounteredFailure = true;
534 0 : savedErrorCode = PR_GetError();
535 : }
536 : }
537 :
538 0 : if (encounteredFailure) {
539 0 : return GetXPCOMFromNSSError(savedErrorCode);
540 : }
541 :
542 0 : return NS_OK;
543 : }
544 :
545 : NS_IMETHODIMP
546 0 : nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
547 : nsIInterfaceRequestor* ctx)
548 : {
549 0 : nsNSSShutDownPreventionLock locker;
550 0 : if (isAlreadyShutDown()) {
551 0 : return NS_ERROR_NOT_AVAILABLE;
552 : }
553 :
554 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
555 0 : if (!arena) {
556 0 : return NS_ERROR_OUT_OF_MEMORY;
557 : }
558 :
559 0 : CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
560 0 : if (!certCollection) {
561 0 : return NS_ERROR_FAILURE;
562 : }
563 :
564 0 : UniqueCERTCertList filteredCerts(CERT_NewCertList());
565 0 : if (!filteredCerts) {
566 0 : return NS_ERROR_FAILURE;
567 : }
568 :
569 0 : nsresult rv = ImportCertsIntoTempStorage(certCollection->numcerts,
570 : certCollection->rawCerts,
571 : certUsageEmailRecipient,
572 0 : false, locker, filteredCerts);
573 0 : if (NS_FAILED(rv)) {
574 0 : return rv;
575 : }
576 :
577 0 : RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
578 0 : if (!certVerifier) {
579 0 : return NS_ERROR_UNEXPECTED;
580 : }
581 :
582 : // Iterate through the filtered cert list and import verified certs into
583 : // permanent storage.
584 : // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004.
585 0 : for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get());
586 0 : !CERT_LIST_END(node, filteredCerts.get());
587 0 : node = CERT_LIST_NEXT(node)) {
588 0 : if (!node->cert) {
589 0 : continue;
590 : }
591 :
592 0 : UniqueCERTCertList certChain;
593 : mozilla::pkix::Result result =
594 0 : certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient,
595 0 : mozilla::pkix::Now(), ctx, nullptr, certChain);
596 0 : if (result != mozilla::pkix::Success) {
597 0 : nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
598 0 : DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
599 0 : continue;
600 : }
601 0 : rv = ImportCertsIntoPermanentStorage(certChain);
602 0 : if (NS_FAILED(rv)) {
603 0 : return rv;
604 : }
605 0 : CERT_SaveSMimeProfile(node->cert, nullptr, nullptr);
606 : }
607 :
608 0 : return NS_OK;
609 : }
610 :
611 : nsresult
612 0 : nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem* caCerts,
613 : nsIInterfaceRequestor* ctx,
614 : const nsNSSShutDownPreventionLock& proofOfLock)
615 : {
616 0 : UniqueCERTCertList filteredCerts(CERT_NewCertList());
617 0 : if (!filteredCerts) {
618 0 : return NS_ERROR_FAILURE;
619 : }
620 :
621 : nsresult rv = ImportCertsIntoTempStorage(numCACerts, caCerts, certUsageAnyCA,
622 0 : true, proofOfLock, filteredCerts);
623 0 : if (NS_FAILED(rv)) {
624 0 : return rv;
625 : }
626 :
627 0 : return ImportValidCACertsInList(filteredCerts, ctx, proofOfLock);
628 : }
629 :
630 : nsresult
631 0 : nsNSSCertificateDB::ImportValidCACertsInList(const UniqueCERTCertList& filteredCerts,
632 : nsIInterfaceRequestor* ctx,
633 : const nsNSSShutDownPreventionLock& proofOfLock)
634 : {
635 0 : RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
636 0 : if (!certVerifier) {
637 0 : return NS_ERROR_UNEXPECTED;
638 : }
639 :
640 : // Iterate through the filtered cert list and import verified certs into
641 : // permanent storage.
642 : // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004.
643 0 : for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get());
644 0 : !CERT_LIST_END(node, filteredCerts.get());
645 0 : node = CERT_LIST_NEXT(node)) {
646 0 : UniqueCERTCertList certChain;
647 : mozilla::pkix::Result result =
648 0 : certVerifier->VerifyCert(node->cert, certificateUsageVerifyCA,
649 0 : mozilla::pkix::Now(), ctx, nullptr, certChain);
650 0 : if (result != mozilla::pkix::Success) {
651 0 : nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
652 0 : DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
653 0 : continue;
654 : }
655 :
656 0 : nsresult rv = ImportCertsIntoPermanentStorage(certChain);
657 0 : if (NS_FAILED(rv)) {
658 0 : return rv;
659 : }
660 : }
661 :
662 0 : return NS_OK;
663 : }
664 :
665 0 : void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor *ctx,
666 : const char *stringID,
667 : nsIX509Cert *certToShow,
668 : const nsNSSShutDownPreventionLock &/*proofOfLock*/)
669 : {
670 : static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
671 :
672 0 : if (!NS_IsMainThread()) {
673 0 : NS_ERROR("nsNSSCertificateDB::DisplayCertificateAlert called off the main thread");
674 0 : return;
675 : }
676 :
677 0 : nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
678 0 : if (!my_ctx) {
679 0 : my_ctx = new PipUIContext();
680 : }
681 :
682 : // This shall be replaced by embedding ovverridable prompts
683 : // as discussed in bug 310446, and should make use of certToShow.
684 :
685 : nsresult rv;
686 0 : nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
687 0 : if (NS_SUCCEEDED(rv)) {
688 0 : nsAutoString tmpMessage;
689 0 : nssComponent->GetPIPNSSBundleString(stringID, tmpMessage);
690 :
691 0 : nsCOMPtr<nsIPrompt> prompt (do_GetInterface(my_ctx));
692 0 : if (!prompt) {
693 0 : return;
694 : }
695 :
696 0 : prompt->Alert(nullptr, tmpMessage.get());
697 : }
698 : }
699 :
700 : NS_IMETHODIMP
701 0 : nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
702 : nsIInterfaceRequestor* ctx)
703 : {
704 0 : if (!NS_IsMainThread()) {
705 0 : NS_ERROR("nsNSSCertificateDB::ImportUserCertificate called off the main thread");
706 0 : return NS_ERROR_NOT_SAME_THREAD;
707 : }
708 :
709 0 : nsNSSShutDownPreventionLock locker;
710 0 : if (isAlreadyShutDown()) {
711 0 : return NS_ERROR_NOT_AVAILABLE;
712 : }
713 :
714 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
715 0 : if (!arena) {
716 0 : return NS_ERROR_OUT_OF_MEMORY;
717 : }
718 :
719 0 : CERTDERCerts* collectArgs = getCertsFromPackage(arena, data, length, locker);
720 0 : if (!collectArgs) {
721 0 : return NS_ERROR_FAILURE;
722 : }
723 :
724 : UniqueCERTCertificate cert(
725 : CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts,
726 0 : nullptr, false, true));
727 0 : if (!cert) {
728 0 : return NS_ERROR_FAILURE;
729 : }
730 :
731 0 : UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
732 0 : if (!slot) {
733 0 : nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
734 0 : DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow, locker);
735 0 : return NS_ERROR_FAILURE;
736 : }
737 0 : slot = nullptr;
738 :
739 : /* pick a nickname for the cert */
740 0 : nsAutoCString nickname;
741 0 : if (cert->nickname) {
742 0 : nickname = cert->nickname;
743 : } else {
744 0 : get_default_nickname(cert.get(), ctx, nickname, locker);
745 : }
746 :
747 : /* user wants to import the cert */
748 0 : slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
749 0 : if (!slot) {
750 0 : return NS_ERROR_FAILURE;
751 : }
752 0 : slot = nullptr;
753 :
754 : {
755 0 : nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
756 0 : DisplayCertificateAlert(ctx, "UserCertImported", certToShow, locker);
757 : }
758 :
759 0 : int numCACerts = collectArgs->numcerts - 1;
760 0 : if (numCACerts) {
761 0 : SECItem* caCerts = collectArgs->rawCerts + 1;
762 0 : return ImportValidCACerts(numCACerts, caCerts, ctx, locker);
763 : }
764 :
765 0 : return NS_OK;
766 : }
767 :
768 : NS_IMETHODIMP
769 0 : nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert)
770 : {
771 0 : NS_ENSURE_ARG_POINTER(aCert);
772 0 : nsNSSShutDownPreventionLock locker;
773 0 : if (isAlreadyShutDown()) {
774 0 : return NS_ERROR_NOT_AVAILABLE;
775 : }
776 0 : UniqueCERTCertificate cert(aCert->GetCert());
777 0 : if (!cert) {
778 0 : return NS_ERROR_FAILURE;
779 : }
780 0 : SECStatus srv = SECSuccess;
781 :
782 : uint32_t certType;
783 0 : aCert->GetCertType(&certType);
784 0 : if (NS_FAILED(aCert->MarkForPermDeletion()))
785 : {
786 0 : return NS_ERROR_FAILURE;
787 : }
788 :
789 0 : if (cert->slot && certType != nsIX509Cert::USER_CERT) {
790 : // To delete a cert of a slot (builtin, most likely), mark it as
791 : // completely untrusted. This way we keep a copy cached in the
792 : // local database, and next time we try to load it off of the
793 : // external token/slot, we'll know not to trust it. We don't
794 : // want to do that with user certs, because a user may re-store
795 : // the cert onto the card again at which point we *will* want to
796 : // trust that cert if it chains up properly.
797 0 : nsNSSCertTrust trust(0, 0, 0);
798 0 : srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
799 0 : cert.get(), trust.GetTrust());
800 : }
801 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("cert deleted: %d", srv));
802 0 : return (srv) ? NS_ERROR_FAILURE : NS_OK;
803 : }
804 :
805 : NS_IMETHODIMP
806 0 : nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert,
807 : uint32_t type,
808 : uint32_t trusted)
809 : {
810 0 : NS_ENSURE_ARG_POINTER(cert);
811 0 : nsNSSShutDownPreventionLock locker;
812 0 : if (isAlreadyShutDown()) {
813 0 : return NS_ERROR_NOT_AVAILABLE;
814 : }
815 0 : nsNSSCertTrust trust;
816 : nsresult rv;
817 0 : UniqueCERTCertificate nsscert(cert->GetCert());
818 :
819 0 : rv = attemptToLogInWithDefaultPassword();
820 0 : if (NS_WARN_IF(rv != NS_OK)) {
821 0 : return rv;
822 : }
823 :
824 : SECStatus srv;
825 0 : if (type == nsIX509Cert::CA_CERT) {
826 : // always start with untrusted and move up
827 0 : trust.SetValidCA();
828 0 : trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
829 0 : !!(trusted & nsIX509CertDB::TRUSTED_EMAIL),
830 0 : !!(trusted & nsIX509CertDB::TRUSTED_OBJSIGN));
831 0 : srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
832 : nsscert.get(),
833 0 : trust.GetTrust());
834 0 : } else if (type == nsIX509Cert::SERVER_CERT) {
835 : // always start with untrusted and move up
836 0 : trust.SetValidPeer();
837 0 : trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, 0, 0);
838 0 : srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
839 : nsscert.get(),
840 0 : trust.GetTrust());
841 0 : } else if (type == nsIX509Cert::EMAIL_CERT) {
842 : // always start with untrusted and move up
843 0 : trust.SetValidPeer();
844 0 : trust.AddPeerTrust(0, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL), 0);
845 0 : srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
846 : nsscert.get(),
847 0 : trust.GetTrust());
848 : } else {
849 : // ignore user certs
850 0 : return NS_OK;
851 : }
852 0 : return MapSECStatus(srv);
853 : }
854 :
855 : NS_IMETHODIMP
856 0 : nsNSSCertificateDB::IsCertTrusted(nsIX509Cert *cert,
857 : uint32_t certType,
858 : uint32_t trustType,
859 : bool *_isTrusted)
860 : {
861 0 : NS_ENSURE_ARG_POINTER(_isTrusted);
862 0 : *_isTrusted = false;
863 :
864 0 : nsNSSShutDownPreventionLock locker;
865 0 : if (isAlreadyShutDown()) {
866 0 : return NS_ERROR_NOT_AVAILABLE;
867 : }
868 : SECStatus srv;
869 0 : UniqueCERTCertificate nsscert(cert->GetCert());
870 : CERTCertTrust nsstrust;
871 0 : srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
872 0 : if (srv != SECSuccess)
873 0 : return NS_ERROR_FAILURE;
874 :
875 0 : nsNSSCertTrust trust(&nsstrust);
876 0 : if (certType == nsIX509Cert::CA_CERT) {
877 0 : if (trustType & nsIX509CertDB::TRUSTED_SSL) {
878 0 : *_isTrusted = trust.HasTrustedCA(true, false, false);
879 0 : } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
880 0 : *_isTrusted = trust.HasTrustedCA(false, true, false);
881 0 : } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
882 0 : *_isTrusted = trust.HasTrustedCA(false, false, true);
883 : } else {
884 0 : return NS_ERROR_FAILURE;
885 : }
886 0 : } else if (certType == nsIX509Cert::SERVER_CERT) {
887 0 : if (trustType & nsIX509CertDB::TRUSTED_SSL) {
888 0 : *_isTrusted = trust.HasTrustedPeer(true, false, false);
889 0 : } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
890 0 : *_isTrusted = trust.HasTrustedPeer(false, true, false);
891 0 : } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
892 0 : *_isTrusted = trust.HasTrustedPeer(false, false, true);
893 : } else {
894 0 : return NS_ERROR_FAILURE;
895 : }
896 0 : } else if (certType == nsIX509Cert::EMAIL_CERT) {
897 0 : if (trustType & nsIX509CertDB::TRUSTED_SSL) {
898 0 : *_isTrusted = trust.HasTrustedPeer(true, false, false);
899 0 : } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
900 0 : *_isTrusted = trust.HasTrustedPeer(false, true, false);
901 0 : } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
902 0 : *_isTrusted = trust.HasTrustedPeer(false, false, true);
903 : } else {
904 0 : return NS_ERROR_FAILURE;
905 : }
906 : } /* user: ignore */
907 0 : return NS_OK;
908 : }
909 :
910 :
911 : NS_IMETHODIMP
912 0 : nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType)
913 : {
914 0 : nsNSSShutDownPreventionLock locker;
915 0 : if (isAlreadyShutDown()) {
916 0 : return NS_ERROR_NOT_AVAILABLE;
917 : }
918 :
919 0 : NS_ENSURE_ARG(aFile);
920 0 : switch (aType) {
921 : case nsIX509Cert::CA_CERT:
922 : case nsIX509Cert::EMAIL_CERT:
923 : // good
924 0 : break;
925 :
926 : default:
927 : // not supported (yet)
928 0 : return NS_ERROR_FAILURE;
929 : }
930 :
931 0 : PRFileDesc* fd = nullptr;
932 0 : nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
933 0 : if (NS_FAILED(rv)) {
934 0 : return rv;
935 : }
936 0 : if (!fd) {
937 0 : return NS_ERROR_FAILURE;
938 : }
939 :
940 : PRFileInfo fileInfo;
941 0 : if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
942 0 : return NS_ERROR_FAILURE;
943 : }
944 :
945 0 : auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
946 0 : int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
947 0 : PR_Close(fd);
948 :
949 0 : if (bytesObtained != fileInfo.size) {
950 0 : return NS_ERROR_FAILURE;
951 : }
952 :
953 0 : nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
954 :
955 0 : switch (aType) {
956 : case nsIX509Cert::CA_CERT:
957 0 : return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
958 : case nsIX509Cert::EMAIL_CERT:
959 0 : return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
960 : default:
961 0 : MOZ_ASSERT(false, "Unsupported type should have been filtered out");
962 : break;
963 : }
964 :
965 : return NS_ERROR_FAILURE;
966 : }
967 :
968 : NS_IMETHODIMP
969 0 : nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile)
970 : {
971 0 : if (!NS_IsMainThread()) {
972 0 : return NS_ERROR_NOT_SAME_THREAD;
973 : }
974 0 : nsNSSShutDownPreventionLock locker;
975 0 : if (isAlreadyShutDown()) {
976 0 : return NS_ERROR_NOT_AVAILABLE;
977 : }
978 :
979 0 : NS_ENSURE_ARG(aFile);
980 0 : nsPKCS12Blob blob;
981 0 : return blob.ImportFromFile(aFile);
982 : }
983 :
984 : NS_IMETHODIMP
985 0 : nsNSSCertificateDB::ExportPKCS12File(nsIFile* aFile, uint32_t count,
986 : nsIX509Cert** certs)
987 : {
988 0 : if (!NS_IsMainThread()) {
989 0 : return NS_ERROR_NOT_SAME_THREAD;
990 : }
991 0 : nsNSSShutDownPreventionLock locker;
992 0 : if (isAlreadyShutDown()) {
993 0 : return NS_ERROR_NOT_AVAILABLE;
994 : }
995 :
996 0 : NS_ENSURE_ARG(aFile);
997 0 : if (count == 0) {
998 0 : return NS_OK;
999 : }
1000 0 : nsPKCS12Blob blob;
1001 0 : return blob.ExportToFile(aFile, certs, count);
1002 : }
1003 :
1004 : NS_IMETHODIMP
1005 0 : nsNSSCertificateDB::FindCertByEmailAddress(const nsACString& aEmailAddress,
1006 : nsIX509Cert** _retval)
1007 : {
1008 0 : nsNSSShutDownPreventionLock locker;
1009 0 : if (isAlreadyShutDown()) {
1010 0 : return NS_ERROR_NOT_AVAILABLE;
1011 : }
1012 :
1013 0 : RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1014 0 : NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
1015 :
1016 0 : const nsCString& flatEmailAddress = PromiseFlatCString(aEmailAddress);
1017 : UniqueCERTCertList certlist(
1018 0 : PK11_FindCertsFromEmailAddress(flatEmailAddress.get(), nullptr));
1019 0 : if (!certlist)
1020 0 : return NS_ERROR_FAILURE;
1021 :
1022 : // certlist now contains certificates with the right email address,
1023 : // but they might not have the correct usage or might even be invalid
1024 :
1025 0 : if (CERT_LIST_END(CERT_LIST_HEAD(certlist), certlist))
1026 0 : return NS_ERROR_FAILURE; // no certs found
1027 :
1028 : CERTCertListNode *node;
1029 : // search for a valid certificate
1030 0 : for (node = CERT_LIST_HEAD(certlist);
1031 0 : !CERT_LIST_END(node, certlist);
1032 0 : node = CERT_LIST_NEXT(node)) {
1033 :
1034 0 : UniqueCERTCertList unusedCertChain;
1035 : mozilla::pkix::Result result =
1036 0 : certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient,
1037 : mozilla::pkix::Now(),
1038 : nullptr /*XXX pinarg*/,
1039 : nullptr /*hostname*/,
1040 0 : unusedCertChain);
1041 0 : if (result == mozilla::pkix::Success) {
1042 0 : break;
1043 : }
1044 : }
1045 :
1046 0 : if (CERT_LIST_END(node, certlist)) {
1047 : // no valid cert found
1048 0 : return NS_ERROR_FAILURE;
1049 : }
1050 :
1051 : // node now contains the first valid certificate with correct usage
1052 0 : RefPtr<nsNSSCertificate> nssCert = nsNSSCertificate::Create(node->cert);
1053 0 : if (!nssCert)
1054 0 : return NS_ERROR_OUT_OF_MEMORY;
1055 :
1056 0 : nssCert.forget(_retval);
1057 0 : return NS_OK;
1058 : }
1059 :
1060 : NS_IMETHODIMP
1061 0 : nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
1062 : /*out*/ nsIX509Cert** _retval)
1063 : {
1064 0 : nsNSSShutDownPreventionLock locker;
1065 0 : if (isAlreadyShutDown()) {
1066 0 : return NS_ERROR_NOT_AVAILABLE;
1067 : }
1068 0 : if (!_retval) {
1069 0 : return NS_ERROR_INVALID_POINTER;
1070 : }
1071 :
1072 : // Base64Decode() doesn't consider a zero length input as an error, and just
1073 : // returns the empty string. We don't want this behavior, so the below check
1074 : // catches this case.
1075 0 : if (base64.Length() < 1) {
1076 0 : return NS_ERROR_ILLEGAL_VALUE;
1077 : }
1078 :
1079 0 : nsAutoCString certDER;
1080 0 : nsresult rv = Base64Decode(base64, certDER);
1081 0 : if (NS_FAILED(rv)) {
1082 0 : return rv;
1083 : }
1084 :
1085 0 : return ConstructX509(certDER, _retval);
1086 : }
1087 :
1088 : NS_IMETHODIMP
1089 0 : nsNSSCertificateDB::ConstructX509(const nsACString& certDER,
1090 : nsIX509Cert** _retval)
1091 : {
1092 0 : nsNSSShutDownPreventionLock locker;
1093 0 : if (isAlreadyShutDown()) {
1094 0 : return NS_ERROR_NOT_AVAILABLE;
1095 : }
1096 0 : if (NS_WARN_IF(!_retval)) {
1097 0 : return NS_ERROR_INVALID_POINTER;
1098 : }
1099 :
1100 : SECItem certData;
1101 0 : certData.type = siDERCertBuffer;
1102 0 : certData.data = BitwiseCast<unsigned char*, const char*>(certDER.BeginReading());
1103 0 : certData.len = certDER.Length();
1104 :
1105 : UniqueCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
1106 : &certData, nullptr,
1107 0 : false, true));
1108 0 : if (!cert)
1109 0 : return (PORT_GetError() == SEC_ERROR_NO_MEMORY)
1110 0 : ? NS_ERROR_OUT_OF_MEMORY : NS_ERROR_FAILURE;
1111 :
1112 0 : nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
1113 0 : if (!nssCert) {
1114 0 : return NS_ERROR_OUT_OF_MEMORY;
1115 : }
1116 0 : nssCert.forget(_retval);
1117 0 : return NS_OK;
1118 : }
1119 :
1120 : void
1121 0 : nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert,
1122 : nsIInterfaceRequestor* ctx,
1123 : nsCString &nickname,
1124 : const nsNSSShutDownPreventionLock &/*proofOfLock*/)
1125 : {
1126 : static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
1127 :
1128 0 : nickname.Truncate();
1129 :
1130 : nsresult rv;
1131 : CK_OBJECT_HANDLE keyHandle;
1132 :
1133 0 : CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB();
1134 0 : nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
1135 0 : if (NS_FAILED(rv))
1136 0 : return;
1137 :
1138 0 : nsAutoCString username;
1139 0 : UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
1140 0 : if (tempCN) {
1141 0 : username = tempCN.get();
1142 : }
1143 :
1144 0 : nsAutoCString caname;
1145 0 : UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
1146 0 : if (tempIssuerOrg) {
1147 0 : caname = tempIssuerOrg.get();
1148 : }
1149 :
1150 0 : nsAutoString tmpNickFmt;
1151 0 : nssComponent->GetPIPNSSBundleString("nick_template", tmpNickFmt);
1152 0 : NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
1153 :
1154 0 : nsAutoCString baseName;
1155 0 : baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
1156 0 : if (baseName.IsEmpty()) {
1157 0 : return;
1158 : }
1159 :
1160 0 : nickname = baseName;
1161 :
1162 : /*
1163 : * We need to see if the private key exists on a token, if it does
1164 : * then we need to check for nicknames that already exist on the smart
1165 : * card.
1166 : */
1167 0 : UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
1168 0 : if (!slot)
1169 0 : return;
1170 :
1171 0 : if (!PK11_IsInternal(slot.get())) {
1172 0 : nsAutoCString tmp;
1173 0 : tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
1174 0 : if (tmp.IsEmpty()) {
1175 0 : nickname.Truncate();
1176 0 : return;
1177 : }
1178 0 : baseName = tmp;
1179 0 : nickname = baseName;
1180 : }
1181 :
1182 0 : int count = 1;
1183 : while (true) {
1184 0 : if ( count > 1 ) {
1185 0 : nsAutoCString tmp;
1186 0 : tmp.AppendPrintf("%s #%d", baseName.get(), count);
1187 0 : if (tmp.IsEmpty()) {
1188 0 : nickname.Truncate();
1189 0 : return;
1190 : }
1191 0 : nickname = tmp;
1192 : }
1193 :
1194 0 : UniqueCERTCertificate dummycert;
1195 :
1196 0 : if (PK11_IsInternal(slot.get())) {
1197 : /* look up the nickname to make sure it isn't in use already */
1198 0 : dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
1199 : } else {
1200 : // Check the cert against others that already live on the smart card.
1201 0 : dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
1202 0 : if (dummycert) {
1203 : // Make sure the subject names are different.
1204 0 : if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual)
1205 : {
1206 : /*
1207 : * There is another certificate with the same nickname and
1208 : * the same subject name on the smart card, so let's use this
1209 : * nickname.
1210 : */
1211 0 : dummycert = nullptr;
1212 : }
1213 : }
1214 : }
1215 0 : if (!dummycert) {
1216 0 : break;
1217 : }
1218 0 : count++;
1219 0 : }
1220 : }
1221 :
1222 : NS_IMETHODIMP
1223 0 : nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
1224 : const nsACString& aTrust,
1225 : nsIX509Cert** addedCertificate)
1226 : {
1227 0 : MOZ_ASSERT(addedCertificate);
1228 0 : if (!addedCertificate) {
1229 0 : return NS_ERROR_INVALID_ARG;
1230 : }
1231 0 : *addedCertificate = nullptr;
1232 :
1233 0 : nsNSSShutDownPreventionLock locker;
1234 0 : if (isAlreadyShutDown()) {
1235 0 : return NS_ERROR_NOT_AVAILABLE;
1236 : }
1237 :
1238 0 : nsNSSCertTrust trust;
1239 0 : if (CERT_DecodeTrustString(trust.GetTrust(), PromiseFlatCString(aTrust).get())
1240 : != SECSuccess) {
1241 0 : return NS_ERROR_FAILURE;
1242 : }
1243 :
1244 0 : nsCOMPtr<nsIX509Cert> newCert;
1245 0 : nsresult rv = ConstructX509FromBase64(aBase64, getter_AddRefs(newCert));
1246 0 : if (NS_FAILED(rv)) {
1247 0 : return rv;
1248 : }
1249 :
1250 0 : UniqueCERTCertificate tmpCert(newCert->GetCert());
1251 0 : if (!tmpCert) {
1252 0 : return NS_ERROR_FAILURE;
1253 : }
1254 :
1255 : // If there's already a certificate that matches this one in the database, we
1256 : // still want to set its trust to the given value.
1257 0 : if (tmpCert->isperm) {
1258 0 : rv = SetCertTrustFromString(newCert, aTrust);
1259 0 : if (NS_FAILED(rv)) {
1260 0 : return rv;
1261 : }
1262 0 : newCert.forget(addedCertificate);
1263 0 : return NS_OK;
1264 : }
1265 :
1266 0 : UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
1267 :
1268 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get()));
1269 :
1270 0 : rv = attemptToLogInWithDefaultPassword();
1271 0 : if (NS_WARN_IF(rv != NS_OK)) {
1272 0 : return rv;
1273 : }
1274 :
1275 0 : UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1276 0 : SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
1277 0 : nickname.get(),
1278 0 : false); // this parameter is ignored by NSS
1279 0 : if (srv != SECSuccess) {
1280 0 : return MapSECStatus(srv);
1281 : }
1282 : // NSS ignores the first argument to CERT_ChangeCertTrust
1283 0 : srv = CERT_ChangeCertTrust(nullptr, tmpCert.get(), trust.GetTrust());
1284 0 : if (srv != SECSuccess) {
1285 0 : return MapSECStatus(srv);
1286 : }
1287 0 : newCert.forget(addedCertificate);
1288 0 : return NS_OK;
1289 : }
1290 :
1291 : NS_IMETHODIMP
1292 0 : nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
1293 : const nsACString& aTrust,
1294 : nsIX509Cert** addedCertificate)
1295 : {
1296 0 : nsCString base64;
1297 0 : nsresult rv = Base64Encode(aCertDER, base64);
1298 0 : NS_ENSURE_SUCCESS(rv, rv);
1299 0 : return AddCertFromBase64(base64, aTrust, addedCertificate);
1300 : }
1301 :
1302 : NS_IMETHODIMP
1303 0 : nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
1304 : const nsACString& trustString)
1305 : {
1306 0 : NS_ENSURE_ARG(cert);
1307 :
1308 : CERTCertTrust trust;
1309 0 : SECStatus srv = CERT_DecodeTrustString(&trust,
1310 0 : PromiseFlatCString(trustString).get());
1311 0 : if (srv != SECSuccess) {
1312 0 : return MapSECStatus(srv);
1313 : }
1314 0 : UniqueCERTCertificate nssCert(cert->GetCert());
1315 :
1316 0 : nsresult rv = attemptToLogInWithDefaultPassword();
1317 0 : if (NS_WARN_IF(rv != NS_OK)) {
1318 0 : return rv;
1319 : }
1320 :
1321 0 : srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nssCert.get(), &trust);
1322 0 : return MapSECStatus(srv);
1323 : }
1324 :
1325 : NS_IMETHODIMP
1326 0 : nsNSSCertificateDB::GetCerts(nsIX509CertList **_retval)
1327 : {
1328 0 : nsNSSShutDownPreventionLock locker;
1329 0 : if (isAlreadyShutDown()) {
1330 0 : return NS_ERROR_NOT_AVAILABLE;
1331 : }
1332 :
1333 0 : nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1334 0 : nsCOMPtr<nsIX509CertList> nssCertList;
1335 0 : UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
1336 :
1337 : // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine.
1338 : // (returns an empty list)
1339 0 : nssCertList = new nsNSSCertList(Move(certList), locker);
1340 :
1341 0 : nssCertList.forget(_retval);
1342 0 : return NS_OK;
1343 : }
1344 :
1345 : NS_IMETHODIMP
1346 0 : nsNSSCertificateDB::GetEnterpriseRoots(nsIX509CertList** enterpriseRoots)
1347 : {
1348 0 : MOZ_ASSERT(NS_IsMainThread());
1349 0 : if (!NS_IsMainThread()) {
1350 0 : return NS_ERROR_NOT_SAME_THREAD;
1351 : }
1352 :
1353 0 : NS_ENSURE_ARG_POINTER(enterpriseRoots);
1354 :
1355 0 : nsNSSShutDownPreventionLock locker;
1356 0 : if (isAlreadyShutDown()) {
1357 0 : return NS_ERROR_NOT_AVAILABLE;
1358 : }
1359 :
1360 : #ifdef XP_WIN
1361 : nsCOMPtr<nsINSSComponent> psm(do_GetService(PSM_COMPONENT_CONTRACTID));
1362 : if (!psm) {
1363 : return NS_ERROR_FAILURE;
1364 : }
1365 : return psm->GetEnterpriseRoots(enterpriseRoots);
1366 : #else
1367 0 : return NS_ERROR_NOT_IMPLEMENTED;
1368 : #endif
1369 : }
1370 :
1371 : nsresult
1372 0 : VerifyCertAtTime(nsIX509Cert* aCert,
1373 : int64_t /*SECCertificateUsage*/ aUsage,
1374 : uint32_t aFlags,
1375 : const nsACString& aHostname,
1376 : mozilla::pkix::Time aTime,
1377 : nsIX509CertList** aVerifiedChain,
1378 : bool* aHasEVPolicy,
1379 : int32_t* /*PRErrorCode*/ _retval,
1380 : const nsNSSShutDownPreventionLock& locker)
1381 : {
1382 0 : NS_ENSURE_ARG_POINTER(aCert);
1383 0 : NS_ENSURE_ARG_POINTER(aHasEVPolicy);
1384 0 : NS_ENSURE_ARG_POINTER(aVerifiedChain);
1385 0 : NS_ENSURE_ARG_POINTER(_retval);
1386 :
1387 0 : *aVerifiedChain = nullptr;
1388 0 : *aHasEVPolicy = false;
1389 0 : *_retval = PR_UNKNOWN_ERROR;
1390 :
1391 0 : UniqueCERTCertificate nssCert(aCert->GetCert());
1392 0 : if (!nssCert) {
1393 0 : return NS_ERROR_INVALID_ARG;
1394 : }
1395 :
1396 0 : RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1397 0 : NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1398 :
1399 0 : UniqueCERTCertList resultChain;
1400 : SECOidTag evOidPolicy;
1401 : mozilla::pkix::Result result;
1402 :
1403 0 : if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
1404 0 : result = certVerifier->VerifySSLServerCert(nssCert,
1405 : nullptr, // stapledOCSPResponse
1406 : nullptr, // sctsFromTLSExtension
1407 : aTime,
1408 : nullptr, // Assume no context
1409 : aHostname,
1410 : resultChain,
1411 : nullptr, // no peerCertChain
1412 : false, // don't save intermediates
1413 : aFlags,
1414 0 : OriginAttributes(),
1415 0 : &evOidPolicy);
1416 : } else {
1417 0 : const nsCString& flatHostname = PromiseFlatCString(aHostname);
1418 0 : result = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime,
1419 : nullptr, // Assume no context
1420 0 : aHostname.IsVoid() ? nullptr
1421 : : flatHostname.get(),
1422 : resultChain,
1423 : nullptr, // no peerCertChain
1424 : aFlags,
1425 : nullptr, // stapledOCSPResponse
1426 : nullptr, // sctsFromTLSExtension
1427 0 : OriginAttributes(),
1428 0 : &evOidPolicy);
1429 : }
1430 :
1431 0 : nsCOMPtr<nsIX509CertList> nssCertList;
1432 : // This adopts the list
1433 0 : nssCertList = new nsNSSCertList(Move(resultChain), locker);
1434 0 : NS_ENSURE_TRUE(nssCertList, NS_ERROR_FAILURE);
1435 :
1436 0 : *_retval = mozilla::pkix::MapResultToPRErrorCode(result);
1437 0 : if (result == mozilla::pkix::Success && evOidPolicy != SEC_OID_UNKNOWN) {
1438 0 : *aHasEVPolicy = true;
1439 : }
1440 0 : nssCertList.forget(aVerifiedChain);
1441 :
1442 0 : return NS_OK;
1443 : }
1444 :
1445 : NS_IMETHODIMP
1446 0 : nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
1447 : int64_t /*SECCertificateUsage*/ aUsage,
1448 : uint32_t aFlags,
1449 : const nsACString& aHostname,
1450 : nsIX509CertList** aVerifiedChain,
1451 : bool* aHasEVPolicy,
1452 : int32_t* /*PRErrorCode*/ _retval)
1453 : {
1454 0 : nsNSSShutDownPreventionLock locker;
1455 0 : if (isAlreadyShutDown()) {
1456 0 : return NS_ERROR_NOT_AVAILABLE;
1457 : }
1458 :
1459 0 : return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname,
1460 : mozilla::pkix::Now(),
1461 0 : aVerifiedChain, aHasEVPolicy, _retval, locker);
1462 : }
1463 :
1464 : NS_IMETHODIMP
1465 0 : nsNSSCertificateDB::VerifyCertAtTime(nsIX509Cert* aCert,
1466 : int64_t /*SECCertificateUsage*/ aUsage,
1467 : uint32_t aFlags,
1468 : const nsACString& aHostname,
1469 : uint64_t aTime,
1470 : nsIX509CertList** aVerifiedChain,
1471 : bool* aHasEVPolicy,
1472 : int32_t* /*PRErrorCode*/ _retval)
1473 : {
1474 0 : nsNSSShutDownPreventionLock locker;
1475 0 : if (isAlreadyShutDown()) {
1476 0 : return NS_ERROR_NOT_AVAILABLE;
1477 : }
1478 :
1479 0 : return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname,
1480 : mozilla::pkix::TimeFromEpochInSeconds(aTime),
1481 0 : aVerifiedChain, aHasEVPolicy, _retval, locker);
1482 : }
1483 :
1484 0 : class VerifyCertAtTimeTask final : public CryptoTask
1485 : {
1486 : public:
1487 0 : VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
1488 : const nsACString& aHostname, uint64_t aTime,
1489 : nsICertVerificationCallback* aCallback)
1490 0 : : mCert(aCert)
1491 : , mUsage(aUsage)
1492 : , mFlags(aFlags)
1493 : , mHostname(aHostname)
1494 : , mTime(aTime)
1495 : , mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(
1496 0 : "nsICertVerificationCallback", aCallback))
1497 : , mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE)
1498 : , mVerifiedCertList(nullptr)
1499 0 : , mHasEVPolicy(false)
1500 : {
1501 0 : }
1502 :
1503 : private:
1504 0 : virtual nsresult CalculateResult() override
1505 : {
1506 0 : nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
1507 0 : if (!certDB) {
1508 0 : return NS_ERROR_FAILURE;
1509 : }
1510 0 : return certDB->VerifyCertAtTime(mCert, mUsage, mFlags, mHostname, mTime,
1511 0 : getter_AddRefs(mVerifiedCertList),
1512 0 : &mHasEVPolicy, &mPRErrorCode);
1513 : }
1514 :
1515 : // No NSS resources are directly held, so there is nothing to release.
1516 0 : virtual void ReleaseNSSResources() override { }
1517 :
1518 0 : virtual void CallCallback(nsresult rv) override
1519 : {
1520 0 : if (NS_FAILED(rv)) {
1521 0 : Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE,
1522 0 : nullptr, false);
1523 : } else {
1524 0 : Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
1525 0 : mHasEVPolicy);
1526 : }
1527 0 : }
1528 :
1529 : nsCOMPtr<nsIX509Cert> mCert;
1530 : int64_t mUsage;
1531 : uint32_t mFlags;
1532 : nsCString mHostname;
1533 : uint64_t mTime;
1534 : nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
1535 : int32_t mPRErrorCode;
1536 : nsCOMPtr<nsIX509CertList> mVerifiedCertList;
1537 : bool mHasEVPolicy;
1538 : };
1539 :
1540 : NS_IMETHODIMP
1541 0 : nsNSSCertificateDB::AsyncVerifyCertAtTime(nsIX509Cert* aCert,
1542 : int64_t /*SECCertificateUsage*/ aUsage,
1543 : uint32_t aFlags,
1544 : const nsACString& aHostname,
1545 : uint64_t aTime,
1546 : nsICertVerificationCallback* aCallback)
1547 : {
1548 0 : nsNSSShutDownPreventionLock locker;
1549 0 : if (isAlreadyShutDown()) {
1550 0 : return NS_ERROR_NOT_AVAILABLE;
1551 : }
1552 : RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(aCert, aUsage,
1553 : aFlags, aHostname,
1554 0 : aTime, aCallback));
1555 0 : return task->Dispatch("VerifyCert");
1556 : }
1557 :
1558 : NS_IMETHODIMP
1559 0 : nsNSSCertificateDB::ClearOCSPCache()
1560 : {
1561 0 : nsNSSShutDownPreventionLock locker;
1562 0 : if (isAlreadyShutDown()) {
1563 0 : return NS_ERROR_NOT_AVAILABLE;
1564 : }
1565 :
1566 0 : RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1567 0 : NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1568 0 : certVerifier->ClearOCSPCache();
1569 0 : return NS_OK;
1570 9 : }
|