security/manager/ssl/src/nsNSSCertificateDB.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 // XXX: This must be done prior to including cert.h (directly or indirectly).
michael@0 6 // CERT_AddTempCertToPerm is exposed as __CERT_AddTempCertToPerm, but it is
michael@0 7 // only exported so PSM can use it for this specific purpose.
michael@0 8 #define CERT_AddTempCertToPerm __CERT_AddTempCertToPerm
michael@0 9
michael@0 10 #include "nsNSSComponent.h"
michael@0 11 #include "nsNSSCertificateDB.h"
michael@0 12
michael@0 13 #include "CertVerifier.h"
michael@0 14 #include "ExtendedValidation.h"
michael@0 15 #include "NSSCertDBTrustDomain.h"
michael@0 16 #include "pkix/pkixtypes.h"
michael@0 17 #include "nsNSSComponent.h"
michael@0 18 #include "mozilla/Base64.h"
michael@0 19 #include "nsCOMPtr.h"
michael@0 20 #include "nsNSSCertificate.h"
michael@0 21 #include "nsNSSHelper.h"
michael@0 22 #include "nsNSSCertHelper.h"
michael@0 23 #include "nsNSSCertCache.h"
michael@0 24 #include "nsCRT.h"
michael@0 25 #include "nsICertificateDialogs.h"
michael@0 26 #include "nsNSSCertTrust.h"
michael@0 27 #include "nsIFile.h"
michael@0 28 #include "nsPKCS12Blob.h"
michael@0 29 #include "nsPK11TokenDB.h"
michael@0 30 #include "nsReadableUtils.h"
michael@0 31 #include "nsIMutableArray.h"
michael@0 32 #include "nsArrayUtils.h"
michael@0 33 #include "nsNSSShutDown.h"
michael@0 34 #include "nsIPrefService.h"
michael@0 35 #include "nsIPrefBranch.h"
michael@0 36 #include "nsComponentManagerUtils.h"
michael@0 37 #include "nsIPrompt.h"
michael@0 38 #include "nsThreadUtils.h"
michael@0 39 #include "nsIObserverService.h"
michael@0 40 #include "nsRecentBadCerts.h"
michael@0 41 #include "SharedSSLState.h"
michael@0 42
michael@0 43 #include "nspr.h"
michael@0 44 #include "certdb.h"
michael@0 45 #include "secerr.h"
michael@0 46 #include "nssb64.h"
michael@0 47 #include "secasn1.h"
michael@0 48 #include "secder.h"
michael@0 49 #include "ssl.h"
michael@0 50 #include "ocsp.h"
michael@0 51 #include "plbase64.h"
michael@0 52
michael@0 53 using namespace mozilla;
michael@0 54 using namespace mozilla::psm;
michael@0 55 using mozilla::psm::SharedSSLState;
michael@0 56
michael@0 57 #ifdef PR_LOGGING
michael@0 58 extern PRLogModuleInfo* gPIPNSSLog;
michael@0 59 #endif
michael@0 60
michael@0 61 static nsresult
michael@0 62 attemptToLogInWithDefaultPassword()
michael@0 63 {
michael@0 64 #ifdef NSS_DISABLE_DBM
michael@0 65 // The SQL NSS DB requires the user to be authenticated to set certificate
michael@0 66 // trust settings, even if the user's password is empty. To maintain
michael@0 67 // compatibility with the DBM-based database, try to log in with the
michael@0 68 // default empty password. This will allow, at least, tests that need to
michael@0 69 // change certificate trust to pass on all platforms. TODO(bug 978120): Do
michael@0 70 // proper testing and/or implement a better solution so that we are confident
michael@0 71 // that this does the correct thing outside of xpcshell tests too.
michael@0 72 ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
michael@0 73 if (!slot) {
michael@0 74 return MapSECStatus(SECFailure);
michael@0 75 }
michael@0 76 if (PK11_NeedUserInit(slot)) {
michael@0 77 // Ignore the return value. Presumably PK11_InitPin will fail if the user
michael@0 78 // has a non-default password.
michael@0 79 (void) PK11_InitPin(slot, nullptr, nullptr);
michael@0 80 }
michael@0 81 #endif
michael@0 82
michael@0 83 return NS_OK;
michael@0 84 }
michael@0 85
michael@0 86 NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB, nsIX509CertDB2)
michael@0 87
michael@0 88 nsNSSCertificateDB::nsNSSCertificateDB()
michael@0 89 : mBadCertsLock("nsNSSCertificateDB::mBadCertsLock")
michael@0 90 {
michael@0 91 SharedSSLState::NoteCertDBServiceInstantiated();
michael@0 92 }
michael@0 93
michael@0 94 nsNSSCertificateDB::~nsNSSCertificateDB()
michael@0 95 {
michael@0 96 nsNSSShutDownPreventionLock locker;
michael@0 97 if (isAlreadyShutDown()) {
michael@0 98 return;
michael@0 99 }
michael@0 100
michael@0 101 shutdown(calledFromObject);
michael@0 102 }
michael@0 103
michael@0 104 NS_IMETHODIMP
michael@0 105 nsNSSCertificateDB::FindCertByNickname(nsISupports *aToken,
michael@0 106 const nsAString &nickname,
michael@0 107 nsIX509Cert **_rvCert)
michael@0 108 {
michael@0 109 NS_ENSURE_ARG_POINTER(_rvCert);
michael@0 110 *_rvCert = nullptr;
michael@0 111
michael@0 112 nsNSSShutDownPreventionLock locker;
michael@0 113 if (isAlreadyShutDown()) {
michael@0 114 return NS_ERROR_NOT_AVAILABLE;
michael@0 115 }
michael@0 116 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 117 char *asciiname = nullptr;
michael@0 118 NS_ConvertUTF16toUTF8 aUtf8Nickname(nickname);
michael@0 119 asciiname = const_cast<char*>(aUtf8Nickname.get());
michael@0 120 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting \"%s\"\n", asciiname));
michael@0 121 cert = PK11_FindCertFromNickname(asciiname, nullptr);
michael@0 122 if (!cert) {
michael@0 123 cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname);
michael@0 124 }
michael@0 125 if (cert) {
michael@0 126 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("got it\n"));
michael@0 127 nsCOMPtr<nsIX509Cert> pCert = nsNSSCertificate::Create(cert.get());
michael@0 128 if (pCert) {
michael@0 129 pCert.forget(_rvCert);
michael@0 130 return NS_OK;
michael@0 131 }
michael@0 132 }
michael@0 133 return NS_ERROR_FAILURE;
michael@0 134 }
michael@0 135
michael@0 136 NS_IMETHODIMP
michael@0 137 nsNSSCertificateDB::FindCertByDBKey(const char *aDBkey, nsISupports *aToken,
michael@0 138 nsIX509Cert **_cert)
michael@0 139 {
michael@0 140 NS_ENSURE_ARG_POINTER(aDBkey);
michael@0 141 NS_ENSURE_ARG(aDBkey[0]);
michael@0 142 NS_ENSURE_ARG_POINTER(_cert);
michael@0 143 *_cert = nullptr;
michael@0 144
michael@0 145 nsNSSShutDownPreventionLock locker;
michael@0 146 if (isAlreadyShutDown()) {
michael@0 147 return NS_ERROR_NOT_AVAILABLE;
michael@0 148 }
michael@0 149
michael@0 150 SECItem keyItem = {siBuffer, nullptr, 0};
michael@0 151 SECItem *dummy;
michael@0 152 CERTIssuerAndSN issuerSN;
michael@0 153 //unsigned long moduleID,slotID;
michael@0 154
michael@0 155 dummy = NSSBase64_DecodeBuffer(nullptr, &keyItem, aDBkey,
michael@0 156 (uint32_t)strlen(aDBkey));
michael@0 157 if (!dummy || keyItem.len < NS_NSS_LONG*4) {
michael@0 158 PR_FREEIF(keyItem.data);
michael@0 159 return NS_ERROR_INVALID_ARG;
michael@0 160 }
michael@0 161
michael@0 162 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 163 // someday maybe we can speed up the search using the moduleID and slotID
michael@0 164 // moduleID = NS_NSS_GET_LONG(keyItem.data);
michael@0 165 // slotID = NS_NSS_GET_LONG(&keyItem.data[NS_NSS_LONG]);
michael@0 166
michael@0 167 // build the issuer/SN structure
michael@0 168 issuerSN.serialNumber.len = NS_NSS_GET_LONG(&keyItem.data[NS_NSS_LONG*2]);
michael@0 169 issuerSN.derIssuer.len = NS_NSS_GET_LONG(&keyItem.data[NS_NSS_LONG*3]);
michael@0 170 if (issuerSN.serialNumber.len == 0 || issuerSN.derIssuer.len == 0
michael@0 171 || issuerSN.serialNumber.len + issuerSN.derIssuer.len
michael@0 172 != keyItem.len - NS_NSS_LONG*4) {
michael@0 173 PR_FREEIF(keyItem.data);
michael@0 174 return NS_ERROR_INVALID_ARG;
michael@0 175 }
michael@0 176 issuerSN.serialNumber.data= &keyItem.data[NS_NSS_LONG*4];
michael@0 177 issuerSN.derIssuer.data= &keyItem.data[NS_NSS_LONG*4+
michael@0 178 issuerSN.serialNumber.len];
michael@0 179
michael@0 180 cert = CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN);
michael@0 181 PR_FREEIF(keyItem.data);
michael@0 182 if (cert) {
michael@0 183 nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
michael@0 184 if (!nssCert)
michael@0 185 return NS_ERROR_OUT_OF_MEMORY;
michael@0 186 nssCert.forget(_cert);
michael@0 187 }
michael@0 188 return NS_OK;
michael@0 189 }
michael@0 190
michael@0 191 NS_IMETHODIMP
michael@0 192 nsNSSCertificateDB::FindCertNicknames(nsISupports *aToken,
michael@0 193 uint32_t aType,
michael@0 194 uint32_t *_count,
michael@0 195 char16_t ***_certNames)
michael@0 196 {
michael@0 197 nsNSSShutDownPreventionLock locker;
michael@0 198 if (isAlreadyShutDown()) {
michael@0 199 return NS_ERROR_NOT_AVAILABLE;
michael@0 200 }
michael@0 201
michael@0 202 nsresult rv = NS_ERROR_FAILURE;
michael@0 203 /*
michael@0 204 * obtain the cert list from NSS
michael@0 205 */
michael@0 206 mozilla::pkix::ScopedCERTCertList certList;
michael@0 207 certList = PK11_ListCerts(PK11CertListUnique, nullptr);
michael@0 208 if (!certList)
michael@0 209 goto cleanup;
michael@0 210 /*
michael@0 211 * get list of cert names from list of certs
michael@0 212 * XXX also cull the list (NSS only distinguishes based on user/non-user
michael@0 213 */
michael@0 214 getCertNames(certList.get(), aType, _count, _certNames, locker);
michael@0 215 rv = NS_OK;
michael@0 216 /*
michael@0 217 * finish up
michael@0 218 */
michael@0 219 cleanup:
michael@0 220 return rv;
michael@0 221 }
michael@0 222
michael@0 223 SECStatus
michael@0 224 collect_certs(void *arg, SECItem **certs, int numcerts)
michael@0 225 {
michael@0 226 CERTDERCerts *collectArgs;
michael@0 227 SECItem *cert;
michael@0 228 SECStatus rv;
michael@0 229
michael@0 230 collectArgs = (CERTDERCerts *)arg;
michael@0 231
michael@0 232 collectArgs->numcerts = numcerts;
michael@0 233 collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc(collectArgs->arena,
michael@0 234 sizeof(SECItem) * numcerts);
michael@0 235 if (!collectArgs->rawCerts)
michael@0 236 return(SECFailure);
michael@0 237
michael@0 238 cert = collectArgs->rawCerts;
michael@0 239
michael@0 240 while ( numcerts-- ) {
michael@0 241 rv = SECITEM_CopyItem(collectArgs->arena, cert, *certs);
michael@0 242 if ( rv == SECFailure )
michael@0 243 return(SECFailure);
michael@0 244 cert++;
michael@0 245 certs++;
michael@0 246 }
michael@0 247
michael@0 248 return (SECSuccess);
michael@0 249 }
michael@0 250
michael@0 251 CERTDERCerts*
michael@0 252 nsNSSCertificateDB::getCertsFromPackage(PLArenaPool *arena, uint8_t *data,
michael@0 253 uint32_t length,
michael@0 254 const nsNSSShutDownPreventionLock &/*proofOfLock*/)
michael@0 255 {
michael@0 256 CERTDERCerts *collectArgs =
michael@0 257 (CERTDERCerts *)PORT_ArenaZAlloc(arena, sizeof(CERTDERCerts));
michael@0 258 if (!collectArgs)
michael@0 259 return nullptr;
michael@0 260
michael@0 261 collectArgs->arena = arena;
michael@0 262 SECStatus sec_rv = CERT_DecodeCertPackage(reinterpret_cast<char *>(data),
michael@0 263 length, collect_certs,
michael@0 264 (void *)collectArgs);
michael@0 265 if (sec_rv != SECSuccess)
michael@0 266 return nullptr;
michael@0 267
michael@0 268 return collectArgs;
michael@0 269 }
michael@0 270
michael@0 271 nsresult
michael@0 272 nsNSSCertificateDB::handleCACertDownload(nsIArray *x509Certs,
michael@0 273 nsIInterfaceRequestor *ctx,
michael@0 274 const nsNSSShutDownPreventionLock &proofOfLock)
michael@0 275 {
michael@0 276 // First thing we have to do is figure out which certificate we're
michael@0 277 // gonna present to the user. The CA may have sent down a list of
michael@0 278 // certs which may or may not be a chained list of certs. Until
michael@0 279 // the day we can design some solid UI for the general case, we'll
michael@0 280 // code to the > 90% case. That case is where a CA sends down a
michael@0 281 // list that is a hierarchy whose root is either the first or
michael@0 282 // the last cert. What we're gonna do is compare the first
michael@0 283 // 2 entries, if the second was signed by the first, we assume
michael@0 284 // the root cert is the first cert and display it. Otherwise,
michael@0 285 // we compare the last 2 entries, if the second to last cert was
michael@0 286 // signed by the last cert, then we assume the last cert is the
michael@0 287 // root and display it.
michael@0 288
michael@0 289 uint32_t numCerts;
michael@0 290
michael@0 291 x509Certs->GetLength(&numCerts);
michael@0 292 NS_ASSERTION(numCerts > 0, "Didn't get any certs to import.");
michael@0 293 if (numCerts == 0)
michael@0 294 return NS_OK; // Nothing to import, so nothing to do.
michael@0 295
michael@0 296 nsCOMPtr<nsIX509Cert> certToShow;
michael@0 297 nsCOMPtr<nsISupports> isupports;
michael@0 298 uint32_t selCertIndex;
michael@0 299 if (numCerts == 1) {
michael@0 300 // There's only one cert, so let's show it.
michael@0 301 selCertIndex = 0;
michael@0 302 certToShow = do_QueryElementAt(x509Certs, selCertIndex);
michael@0 303 } else {
michael@0 304 nsCOMPtr<nsIX509Cert> cert0; // first cert
michael@0 305 nsCOMPtr<nsIX509Cert> cert1; // second cert
michael@0 306 nsCOMPtr<nsIX509Cert> certn_2; // second to last cert
michael@0 307 nsCOMPtr<nsIX509Cert> certn_1; // last cert
michael@0 308
michael@0 309 cert0 = do_QueryElementAt(x509Certs, 0);
michael@0 310 cert1 = do_QueryElementAt(x509Certs, 1);
michael@0 311 certn_2 = do_QueryElementAt(x509Certs, numCerts-2);
michael@0 312 certn_1 = do_QueryElementAt(x509Certs, numCerts-1);
michael@0 313
michael@0 314 nsXPIDLString cert0SubjectName;
michael@0 315 nsXPIDLString cert1IssuerName;
michael@0 316 nsXPIDLString certn_2IssuerName;
michael@0 317 nsXPIDLString certn_1SubjectName;
michael@0 318
michael@0 319 cert0->GetSubjectName(cert0SubjectName);
michael@0 320 cert1->GetIssuerName(cert1IssuerName);
michael@0 321 certn_2->GetIssuerName(certn_2IssuerName);
michael@0 322 certn_1->GetSubjectName(certn_1SubjectName);
michael@0 323
michael@0 324 if (cert1IssuerName.Equals(cert0SubjectName)) {
michael@0 325 // In this case, the first cert in the list signed the second,
michael@0 326 // so the first cert is the root. Let's display it.
michael@0 327 selCertIndex = 0;
michael@0 328 certToShow = cert0;
michael@0 329 } else
michael@0 330 if (certn_2IssuerName.Equals(certn_1SubjectName)) {
michael@0 331 // In this case the last cert has signed the second to last cert.
michael@0 332 // The last cert is the root, so let's display it.
michael@0 333 selCertIndex = numCerts-1;
michael@0 334 certToShow = certn_1;
michael@0 335 } else {
michael@0 336 // It's not a chain, so let's just show the first one in the
michael@0 337 // downloaded list.
michael@0 338 selCertIndex = 0;
michael@0 339 certToShow = cert0;
michael@0 340 }
michael@0 341 }
michael@0 342
michael@0 343 if (!certToShow)
michael@0 344 return NS_ERROR_FAILURE;
michael@0 345
michael@0 346 nsCOMPtr<nsICertificateDialogs> dialogs;
michael@0 347 nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
michael@0 348 NS_GET_IID(nsICertificateDialogs),
michael@0 349 NS_CERTIFICATEDIALOGS_CONTRACTID);
michael@0 350
michael@0 351 if (NS_FAILED(rv))
michael@0 352 return rv;
michael@0 353
michael@0 354 SECItem der;
michael@0 355 rv=certToShow->GetRawDER(&der.len, (uint8_t **)&der.data);
michael@0 356
michael@0 357 if (NS_FAILED(rv))
michael@0 358 return rv;
michael@0 359
michael@0 360 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));
michael@0 361 mozilla::pkix::ScopedCERTCertificate tmpCert;
michael@0 362 CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
michael@0 363 tmpCert = CERT_FindCertByDERCert(certdb, &der);
michael@0 364 if (!tmpCert) {
michael@0 365 tmpCert = CERT_NewTempCertificate(certdb, &der,
michael@0 366 nullptr, false, true);
michael@0 367 }
michael@0 368 nsMemory::Free(der.data);
michael@0 369 der.data = nullptr;
michael@0 370 der.len = 0;
michael@0 371
michael@0 372 if (!tmpCert) {
michael@0 373 NS_ERROR("Couldn't create cert from DER blob");
michael@0 374 return NS_ERROR_FAILURE;
michael@0 375 }
michael@0 376
michael@0 377 if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
michael@0 378 DisplayCertificateAlert(ctx, "NotACACert", certToShow, proofOfLock);
michael@0 379 return NS_ERROR_FAILURE;
michael@0 380 }
michael@0 381
michael@0 382 if (tmpCert->isperm) {
michael@0 383 DisplayCertificateAlert(ctx, "CaCertExists", certToShow, proofOfLock);
michael@0 384 return NS_ERROR_FAILURE;
michael@0 385 }
michael@0 386
michael@0 387 uint32_t trustBits;
michael@0 388 bool allows;
michael@0 389 rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
michael@0 390 if (NS_FAILED(rv))
michael@0 391 return rv;
michael@0 392
michael@0 393 if (!allows)
michael@0 394 return NS_ERROR_NOT_AVAILABLE;
michael@0 395
michael@0 396 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("trust is %d\n", trustBits));
michael@0 397 nsXPIDLCString nickname;
michael@0 398 nickname.Adopt(CERT_MakeCANickname(tmpCert.get()));
michael@0 399
michael@0 400 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nickname.get()));
michael@0 401
michael@0 402 nsNSSCertTrust trust;
michael@0 403 trust.SetValidCA();
michael@0 404 trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
michael@0 405 !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL),
michael@0 406 !!(trustBits & nsIX509CertDB::TRUSTED_OBJSIGN));
michael@0 407
michael@0 408 SECStatus srv = __CERT_AddTempCertToPerm(tmpCert.get(),
michael@0 409 const_cast<char*>(nickname.get()),
michael@0 410 trust.GetTrust());
michael@0 411
michael@0 412 if (srv != SECSuccess)
michael@0 413 return NS_ERROR_FAILURE;
michael@0 414
michael@0 415 // Import additional delivered certificates that can be verified.
michael@0 416
michael@0 417 // build a CertList for filtering
michael@0 418 mozilla::pkix::ScopedCERTCertList certList(CERT_NewCertList());
michael@0 419 if (!certList) {
michael@0 420 return NS_ERROR_FAILURE;
michael@0 421 }
michael@0 422
michael@0 423 // get all remaining certs into temp store
michael@0 424
michael@0 425 for (uint32_t i=0; i<numCerts; i++) {
michael@0 426 if (i == selCertIndex) {
michael@0 427 // we already processed that one
michael@0 428 continue;
michael@0 429 }
michael@0 430
michael@0 431 certToShow = do_QueryElementAt(x509Certs, i);
michael@0 432 certToShow->GetRawDER(&der.len, (uint8_t **)&der.data);
michael@0 433
michael@0 434 CERTCertificate *tmpCert2 =
michael@0 435 CERT_NewTempCertificate(certdb, &der, nullptr, false, true);
michael@0 436
michael@0 437 nsMemory::Free(der.data);
michael@0 438 der.data = nullptr;
michael@0 439 der.len = 0;
michael@0 440
michael@0 441 if (!tmpCert2) {
michael@0 442 NS_ERROR("Couldn't create temp cert from DER blob");
michael@0 443 continue; // Let's try to import the rest of 'em
michael@0 444 }
michael@0 445
michael@0 446 CERT_AddCertToListTail(certList.get(), tmpCert2);
michael@0 447 }
michael@0 448
michael@0 449 return ImportValidCACertsInList(certList.get(), ctx, proofOfLock);
michael@0 450 }
michael@0 451
michael@0 452 /*
michael@0 453 * [noscript] void importCertificates(in charPtr data, in unsigned long length,
michael@0 454 * in unsigned long type,
michael@0 455 * in nsIInterfaceRequestor ctx);
michael@0 456 */
michael@0 457 NS_IMETHODIMP
michael@0 458 nsNSSCertificateDB::ImportCertificates(uint8_t * data, uint32_t length,
michael@0 459 uint32_t type,
michael@0 460 nsIInterfaceRequestor *ctx)
michael@0 461
michael@0 462 {
michael@0 463 nsNSSShutDownPreventionLock locker;
michael@0 464 if (isAlreadyShutDown()) {
michael@0 465 return NS_ERROR_NOT_AVAILABLE;
michael@0 466 }
michael@0 467
michael@0 468 nsresult nsrv;
michael@0 469
michael@0 470 PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 471 if (!arena)
michael@0 472 return NS_ERROR_OUT_OF_MEMORY;
michael@0 473
michael@0 474 CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
michael@0 475 if (!certCollection) {
michael@0 476 PORT_FreeArena(arena, false);
michael@0 477 return NS_ERROR_FAILURE;
michael@0 478 }
michael@0 479 nsCOMPtr<nsIMutableArray> array =
michael@0 480 do_CreateInstance(NS_ARRAY_CONTRACTID, &nsrv);
michael@0 481 if (NS_FAILED(nsrv)) {
michael@0 482 PORT_FreeArena(arena, false);
michael@0 483 return nsrv;
michael@0 484 }
michael@0 485
michael@0 486 // Now let's create some certs to work with
michael@0 487 nsCOMPtr<nsIX509Cert> x509Cert;
michael@0 488 nsNSSCertificate *nssCert;
michael@0 489 SECItem *currItem;
michael@0 490 for (int i=0; i<certCollection->numcerts; i++) {
michael@0 491 currItem = &certCollection->rawCerts[i];
michael@0 492 nssCert = nsNSSCertificate::ConstructFromDER((char*)currItem->data, currItem->len);
michael@0 493 if (!nssCert)
michael@0 494 return NS_ERROR_FAILURE;
michael@0 495 x509Cert = do_QueryInterface((nsIX509Cert*)nssCert);
michael@0 496 array->AppendElement(x509Cert, false);
michael@0 497 }
michael@0 498 switch (type) {
michael@0 499 case nsIX509Cert::CA_CERT:
michael@0 500 nsrv = handleCACertDownload(array, ctx, locker);
michael@0 501 break;
michael@0 502 default:
michael@0 503 // We only deal with import CA certs in this method currently.
michael@0 504 nsrv = NS_ERROR_FAILURE;
michael@0 505 break;
michael@0 506 }
michael@0 507 PORT_FreeArena(arena, false);
michael@0 508 return nsrv;
michael@0 509 }
michael@0 510
michael@0 511 static
michael@0 512 SECStatus
michael@0 513 ImportCertsIntoPermanentStorage(
michael@0 514 const mozilla::pkix::ScopedCERTCertList& certChain,
michael@0 515 const SECCertUsage usage, const PRBool caOnly)
michael@0 516 {
michael@0 517 CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
michael@0 518
michael@0 519 int chainLen = 0;
michael@0 520 for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain);
michael@0 521 !CERT_LIST_END(chainNode, certChain);
michael@0 522 chainNode = CERT_LIST_NEXT(chainNode)) {
michael@0 523 chainLen++;
michael@0 524 }
michael@0 525
michael@0 526 SECItem **rawArray;
michael@0 527 rawArray = (SECItem **) PORT_Alloc(chainLen * sizeof(SECItem *));
michael@0 528 if (!rawArray) {
michael@0 529 return SECFailure;
michael@0 530 }
michael@0 531
michael@0 532 int i = 0;
michael@0 533 for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain);
michael@0 534 !CERT_LIST_END(chainNode, certChain);
michael@0 535 chainNode = CERT_LIST_NEXT(chainNode), i++) {
michael@0 536 rawArray[i] = &chainNode->cert->derCert;
michael@0 537 }
michael@0 538 SECStatus srv = CERT_ImportCerts(certdb, usage, chainLen, rawArray,
michael@0 539 nullptr, true, caOnly, nullptr);
michael@0 540
michael@0 541 PORT_Free(rawArray);
michael@0 542 return srv;
michael@0 543 }
michael@0 544
michael@0 545
michael@0 546 /*
michael@0 547 * [noscript] void importEmailCertificates(in charPtr data, in unsigned long length,
michael@0 548 * in nsIInterfaceRequestor ctx);
michael@0 549 */
michael@0 550 NS_IMETHODIMP
michael@0 551 nsNSSCertificateDB::ImportEmailCertificate(uint8_t * data, uint32_t length,
michael@0 552 nsIInterfaceRequestor *ctx)
michael@0 553
michael@0 554 {
michael@0 555 nsNSSShutDownPreventionLock locker;
michael@0 556 if (isAlreadyShutDown()) {
michael@0 557 return NS_ERROR_NOT_AVAILABLE;
michael@0 558 }
michael@0 559
michael@0 560 SECStatus srv = SECFailure;
michael@0 561 nsresult nsrv = NS_OK;
michael@0 562 CERTCertDBHandle *certdb;
michael@0 563 CERTCertificate **certArray = nullptr;
michael@0 564 mozilla::pkix::ScopedCERTCertList certList;
michael@0 565 CERTCertListNode *node;
michael@0 566 SECItem **rawArray;
michael@0 567 int numcerts;
michael@0 568 int i;
michael@0 569
michael@0 570 PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 571 if (!arena)
michael@0 572 return NS_ERROR_OUT_OF_MEMORY;
michael@0 573
michael@0 574 CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
michael@0 575 if (!certCollection) {
michael@0 576 PORT_FreeArena(arena, false);
michael@0 577 return NS_ERROR_FAILURE;
michael@0 578 }
michael@0 579
michael@0 580 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
michael@0 581 NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
michael@0 582
michael@0 583 certdb = CERT_GetDefaultCertDB();
michael@0 584 const PRTime now = PR_Now();
michael@0 585
michael@0 586 numcerts = certCollection->numcerts;
michael@0 587
michael@0 588 rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numcerts);
michael@0 589 if ( !rawArray ) {
michael@0 590 nsrv = NS_ERROR_FAILURE;
michael@0 591 goto loser;
michael@0 592 }
michael@0 593
michael@0 594 for (i=0; i < numcerts; i++) {
michael@0 595 rawArray[i] = &certCollection->rawCerts[i];
michael@0 596 }
michael@0 597
michael@0 598 srv = CERT_ImportCerts(certdb, certUsageEmailRecipient, numcerts, rawArray,
michael@0 599 &certArray, false, false, nullptr);
michael@0 600
michael@0 601 PORT_Free(rawArray);
michael@0 602 rawArray = nullptr;
michael@0 603
michael@0 604 if (srv != SECSuccess) {
michael@0 605 nsrv = NS_ERROR_FAILURE;
michael@0 606 goto loser;
michael@0 607 }
michael@0 608
michael@0 609 // build a CertList for filtering
michael@0 610 certList = CERT_NewCertList();
michael@0 611 if (!certList) {
michael@0 612 nsrv = NS_ERROR_FAILURE;
michael@0 613 goto loser;
michael@0 614 }
michael@0 615 for (i=0; i < numcerts; i++) {
michael@0 616 CERTCertificate *cert = certArray[i];
michael@0 617 if (cert)
michael@0 618 cert = CERT_DupCertificate(cert);
michael@0 619 if (cert)
michael@0 620 CERT_AddCertToListTail(certList.get(), cert);
michael@0 621 }
michael@0 622
michael@0 623 /* go down the remaining list of certs and verify that they have
michael@0 624 * valid chains, then import them.
michael@0 625 */
michael@0 626
michael@0 627 for (node = CERT_LIST_HEAD(certList);
michael@0 628 !CERT_LIST_END(node,certList);
michael@0 629 node = CERT_LIST_NEXT(node)) {
michael@0 630
michael@0 631 if (!node->cert) {
michael@0 632 continue;
michael@0 633 }
michael@0 634
michael@0 635 mozilla::pkix::ScopedCERTCertList certChain;
michael@0 636
michael@0 637 SECStatus rv = certVerifier->VerifyCert(node->cert,
michael@0 638 certificateUsageEmailRecipient,
michael@0 639 now, ctx, nullptr, 0,
michael@0 640 nullptr, &certChain);
michael@0 641
michael@0 642 if (rv != SECSuccess) {
michael@0 643 nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
michael@0 644 DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
michael@0 645 continue;
michael@0 646 }
michael@0 647 rv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient,
michael@0 648 false);
michael@0 649 if (rv != SECSuccess) {
michael@0 650 goto loser;
michael@0 651 }
michael@0 652 CERT_SaveSMimeProfile(node->cert, nullptr, nullptr);
michael@0 653
michael@0 654 }
michael@0 655
michael@0 656 loser:
michael@0 657 if (certArray) {
michael@0 658 CERT_DestroyCertArray(certArray, numcerts);
michael@0 659 }
michael@0 660 if (arena)
michael@0 661 PORT_FreeArena(arena, true);
michael@0 662 return nsrv;
michael@0 663 }
michael@0 664
michael@0 665 NS_IMETHODIMP
michael@0 666 nsNSSCertificateDB::ImportServerCertificate(uint8_t * data, uint32_t length,
michael@0 667 nsIInterfaceRequestor *ctx)
michael@0 668
michael@0 669 {
michael@0 670 nsNSSShutDownPreventionLock locker;
michael@0 671 if (isAlreadyShutDown()) {
michael@0 672 return NS_ERROR_NOT_AVAILABLE;
michael@0 673 }
michael@0 674
michael@0 675 SECStatus srv = SECFailure;
michael@0 676 nsresult nsrv = NS_OK;
michael@0 677 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 678 SECItem **rawCerts = nullptr;
michael@0 679 int numcerts;
michael@0 680 int i;
michael@0 681 nsNSSCertTrust trust;
michael@0 682 char *serverNickname = nullptr;
michael@0 683
michael@0 684 PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 685 if (!arena)
michael@0 686 return NS_ERROR_OUT_OF_MEMORY;
michael@0 687
michael@0 688 CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
michael@0 689 if (!certCollection) {
michael@0 690 PORT_FreeArena(arena, false);
michael@0 691 return NS_ERROR_FAILURE;
michael@0 692 }
michael@0 693 cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certCollection->rawCerts,
michael@0 694 nullptr, false, true);
michael@0 695 if (!cert) {
michael@0 696 nsrv = NS_ERROR_FAILURE;
michael@0 697 goto loser;
michael@0 698 }
michael@0 699 numcerts = certCollection->numcerts;
michael@0 700 rawCerts = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numcerts);
michael@0 701 if ( !rawCerts ) {
michael@0 702 nsrv = NS_ERROR_FAILURE;
michael@0 703 goto loser;
michael@0 704 }
michael@0 705
michael@0 706 for ( i = 0; i < numcerts; i++ ) {
michael@0 707 rawCerts[i] = &certCollection->rawCerts[i];
michael@0 708 }
michael@0 709
michael@0 710 serverNickname = DefaultServerNicknameForCert(cert.get());
michael@0 711 srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageSSLServer,
michael@0 712 numcerts, rawCerts, nullptr, true, false,
michael@0 713 serverNickname);
michael@0 714 PR_FREEIF(serverNickname);
michael@0 715 if ( srv != SECSuccess ) {
michael@0 716 nsrv = NS_ERROR_FAILURE;
michael@0 717 goto loser;
michael@0 718 }
michael@0 719
michael@0 720 trust.SetValidServerPeer();
michael@0 721 srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert.get(),
michael@0 722 trust.GetTrust());
michael@0 723 if ( srv != SECSuccess ) {
michael@0 724 nsrv = NS_ERROR_FAILURE;
michael@0 725 goto loser;
michael@0 726 }
michael@0 727 loser:
michael@0 728 PORT_Free(rawCerts);
michael@0 729 if (arena)
michael@0 730 PORT_FreeArena(arena, true);
michael@0 731 return nsrv;
michael@0 732 }
michael@0 733
michael@0 734 nsresult
michael@0 735 nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem *CACerts, nsIInterfaceRequestor *ctx, const nsNSSShutDownPreventionLock &proofOfLock)
michael@0 736 {
michael@0 737 ScopedCERTCertList certList;
michael@0 738 SECItem **rawArray;
michael@0 739
michael@0 740 // build a CertList for filtering
michael@0 741 certList = CERT_NewCertList();
michael@0 742 if (!certList) {
michael@0 743 return NS_ERROR_FAILURE;
michael@0 744 }
michael@0 745
michael@0 746 // get all certs into temp store
michael@0 747 SECStatus srv = SECFailure;
michael@0 748 CERTCertificate **certArray = nullptr;
michael@0 749
michael@0 750 rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numCACerts);
michael@0 751 if ( !rawArray ) {
michael@0 752 return NS_ERROR_FAILURE;
michael@0 753 }
michael@0 754
michael@0 755 for (int i=0; i < numCACerts; i++) {
michael@0 756 rawArray[i] = &CACerts[i];
michael@0 757 }
michael@0 758
michael@0 759 srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageAnyCA, numCACerts, rawArray,
michael@0 760 &certArray, false, true, nullptr);
michael@0 761
michael@0 762 PORT_Free(rawArray);
michael@0 763 rawArray = nullptr;
michael@0 764
michael@0 765 if (srv != SECSuccess) {
michael@0 766 return NS_ERROR_FAILURE;
michael@0 767 }
michael@0 768
michael@0 769 for (int i2=0; i2 < numCACerts; i2++) {
michael@0 770 CERTCertificate *cacert = certArray[i2];
michael@0 771 if (cacert)
michael@0 772 cacert = CERT_DupCertificate(cacert);
michael@0 773 if (cacert)
michael@0 774 CERT_AddCertToListTail(certList, cacert);
michael@0 775 }
michael@0 776
michael@0 777 CERT_DestroyCertArray(certArray, numCACerts);
michael@0 778
michael@0 779 return ImportValidCACertsInList(certList, ctx, proofOfLock);
michael@0 780 }
michael@0 781
michael@0 782 nsresult
michael@0 783 nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx,
michael@0 784 const nsNSSShutDownPreventionLock &proofOfLock)
michael@0 785 {
michael@0 786 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
michael@0 787 if (!certVerifier)
michael@0 788 return NS_ERROR_UNEXPECTED;
michael@0 789
michael@0 790 /* filter out the certs we don't want */
michael@0 791 SECStatus srv = CERT_FilterCertListByUsage(certList, certUsageAnyCA, true);
michael@0 792 if (srv != SECSuccess) {
michael@0 793 return NS_ERROR_FAILURE;
michael@0 794 }
michael@0 795
michael@0 796 /* go down the remaining list of certs and verify that they have
michael@0 797 * valid chains, if yes, then import.
michael@0 798 */
michael@0 799 CERTCertListNode *node;
michael@0 800
michael@0 801 for (node = CERT_LIST_HEAD(certList);
michael@0 802 !CERT_LIST_END(node,certList);
michael@0 803 node = CERT_LIST_NEXT(node)) {
michael@0 804 mozilla::pkix::ScopedCERTCertList certChain;
michael@0 805 SECStatus rv = certVerifier->VerifyCert(node->cert,
michael@0 806 certificateUsageVerifyCA,
michael@0 807 PR_Now(), ctx, nullptr, 0, nullptr,
michael@0 808 &certChain);
michael@0 809 if (rv != SECSuccess) {
michael@0 810 nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
michael@0 811 DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
michael@0 812 continue;
michael@0 813 }
michael@0 814
michael@0 815 rv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
michael@0 816 if (rv != SECSuccess) {
michael@0 817 return NS_ERROR_FAILURE;
michael@0 818 }
michael@0 819 }
michael@0 820
michael@0 821 return NS_OK;
michael@0 822 }
michael@0 823
michael@0 824 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor *ctx,
michael@0 825 const char *stringID,
michael@0 826 nsIX509Cert *certToShow,
michael@0 827 const nsNSSShutDownPreventionLock &/*proofOfLock*/)
michael@0 828 {
michael@0 829 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
michael@0 830
michael@0 831 if (!NS_IsMainThread()) {
michael@0 832 NS_ERROR("nsNSSCertificateDB::DisplayCertificateAlert called off the main thread");
michael@0 833 return;
michael@0 834 }
michael@0 835
michael@0 836 nsPSMUITracker tracker;
michael@0 837 if (!tracker.isUIForbidden()) {
michael@0 838
michael@0 839 nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
michael@0 840 if (!my_ctx)
michael@0 841 my_ctx = new PipUIContext();
michael@0 842
michael@0 843 // This shall be replaced by embedding ovverridable prompts
michael@0 844 // as discussed in bug 310446, and should make use of certToShow.
michael@0 845
michael@0 846 nsresult rv;
michael@0 847 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
michael@0 848 if (NS_SUCCEEDED(rv)) {
michael@0 849 nsAutoString tmpMessage;
michael@0 850 nssComponent->GetPIPNSSBundleString(stringID, tmpMessage);
michael@0 851
michael@0 852 nsCOMPtr<nsIPrompt> prompt (do_GetInterface(my_ctx));
michael@0 853 if (!prompt)
michael@0 854 return;
michael@0 855
michael@0 856 prompt->Alert(nullptr, tmpMessage.get());
michael@0 857 }
michael@0 858 }
michael@0 859 }
michael@0 860
michael@0 861
michael@0 862 NS_IMETHODIMP
michael@0 863 nsNSSCertificateDB::ImportUserCertificate(uint8_t *data, uint32_t length, nsIInterfaceRequestor *ctx)
michael@0 864 {
michael@0 865 if (!NS_IsMainThread()) {
michael@0 866 NS_ERROR("nsNSSCertificateDB::ImportUserCertificate called off the main thread");
michael@0 867 return NS_ERROR_NOT_SAME_THREAD;
michael@0 868 }
michael@0 869
michael@0 870 nsNSSShutDownPreventionLock locker;
michael@0 871 if (isAlreadyShutDown()) {
michael@0 872 return NS_ERROR_NOT_AVAILABLE;
michael@0 873 }
michael@0 874
michael@0 875 ScopedPK11SlotInfo slot;
michael@0 876 nsAutoCString nickname;
michael@0 877 nsresult rv = NS_ERROR_FAILURE;
michael@0 878 int numCACerts;
michael@0 879 SECItem *CACerts;
michael@0 880 CERTDERCerts * collectArgs;
michael@0 881 PLArenaPool *arena;
michael@0 882 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 883
michael@0 884 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 885 if (!arena) {
michael@0 886 goto loser;
michael@0 887 }
michael@0 888
michael@0 889 collectArgs = getCertsFromPackage(arena, data, length, locker);
michael@0 890 if (!collectArgs) {
michael@0 891 goto loser;
michael@0 892 }
michael@0 893
michael@0 894 cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts,
michael@0 895 nullptr, false, true);
michael@0 896 if (!cert) {
michael@0 897 goto loser;
michael@0 898 }
michael@0 899
michael@0 900 slot = PK11_KeyForCertExists(cert.get(), nullptr, ctx);
michael@0 901 if (!slot) {
michael@0 902 nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
michael@0 903 DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow, locker);
michael@0 904 goto loser;
michael@0 905 }
michael@0 906 slot = nullptr;
michael@0 907
michael@0 908 /* pick a nickname for the cert */
michael@0 909 if (cert->nickname) {
michael@0 910 /* sigh, we need a call to look up other certs with this subject and
michael@0 911 * identify nicknames from them. We can no longer walk down internal
michael@0 912 * database structures rjr */
michael@0 913 nickname = cert->nickname;
michael@0 914 }
michael@0 915 else {
michael@0 916 get_default_nickname(cert.get(), ctx, nickname, locker);
michael@0 917 }
michael@0 918
michael@0 919 /* user wants to import the cert */
michael@0 920 {
michael@0 921 char *cast_const_away = const_cast<char*>(nickname.get());
michael@0 922 slot = PK11_ImportCertForKey(cert.get(), cast_const_away, ctx);
michael@0 923 }
michael@0 924 if (!slot) {
michael@0 925 goto loser;
michael@0 926 }
michael@0 927 slot = nullptr;
michael@0 928
michael@0 929 {
michael@0 930 nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
michael@0 931 DisplayCertificateAlert(ctx, "UserCertImported", certToShow, locker);
michael@0 932 }
michael@0 933 rv = NS_OK;
michael@0 934
michael@0 935 numCACerts = collectArgs->numcerts - 1;
michael@0 936 if (numCACerts) {
michael@0 937 CACerts = collectArgs->rawCerts+1;
michael@0 938 rv = ImportValidCACerts(numCACerts, CACerts, ctx, locker);
michael@0 939 }
michael@0 940
michael@0 941 loser:
michael@0 942 if (arena) {
michael@0 943 PORT_FreeArena(arena, false);
michael@0 944 }
michael@0 945 return rv;
michael@0 946 }
michael@0 947
michael@0 948 /*
michael@0 949 * void deleteCertificate(in nsIX509Cert aCert);
michael@0 950 */
michael@0 951 NS_IMETHODIMP
michael@0 952 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert)
michael@0 953 {
michael@0 954 nsNSSShutDownPreventionLock locker;
michael@0 955 if (isAlreadyShutDown()) {
michael@0 956 return NS_ERROR_NOT_AVAILABLE;
michael@0 957 }
michael@0 958 nsCOMPtr<nsIX509Cert2> nssCert = do_QueryInterface(aCert);
michael@0 959 mozilla::pkix::ScopedCERTCertificate cert(nssCert->GetCert());
michael@0 960 if (!cert) return NS_ERROR_FAILURE;
michael@0 961 SECStatus srv = SECSuccess;
michael@0 962
michael@0 963 uint32_t certType;
michael@0 964 nssCert->GetCertType(&certType);
michael@0 965 if (NS_FAILED(nssCert->MarkForPermDeletion()))
michael@0 966 {
michael@0 967 return NS_ERROR_FAILURE;
michael@0 968 }
michael@0 969
michael@0 970 if (cert->slot && certType != nsIX509Cert::USER_CERT) {
michael@0 971 // To delete a cert of a slot (builtin, most likely), mark it as
michael@0 972 // completely untrusted. This way we keep a copy cached in the
michael@0 973 // local database, and next time we try to load it off of the
michael@0 974 // external token/slot, we'll know not to trust it. We don't
michael@0 975 // want to do that with user certs, because a user may re-store
michael@0 976 // the cert onto the card again at which point we *will* want to
michael@0 977 // trust that cert if it chains up properly.
michael@0 978 nsNSSCertTrust trust(0, 0, 0);
michael@0 979 srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
michael@0 980 cert.get(), trust.GetTrust());
michael@0 981 }
michael@0 982 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("cert deleted: %d", srv));
michael@0 983 return (srv) ? NS_ERROR_FAILURE : NS_OK;
michael@0 984 }
michael@0 985
michael@0 986 /*
michael@0 987 * void setCertTrust(in nsIX509Cert cert,
michael@0 988 * in unsigned long type,
michael@0 989 * in unsigned long trust);
michael@0 990 */
michael@0 991 NS_IMETHODIMP
michael@0 992 nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert,
michael@0 993 uint32_t type,
michael@0 994 uint32_t trusted)
michael@0 995 {
michael@0 996 nsNSSShutDownPreventionLock locker;
michael@0 997 if (isAlreadyShutDown()) {
michael@0 998 return NS_ERROR_NOT_AVAILABLE;
michael@0 999 }
michael@0 1000 nsNSSCertTrust trust;
michael@0 1001 nsresult rv;
michael@0 1002 nsCOMPtr<nsIX509Cert2> pipCert = do_QueryInterface(cert, &rv);
michael@0 1003 if (!pipCert) {
michael@0 1004 return rv;
michael@0 1005 }
michael@0 1006 mozilla::pkix::ScopedCERTCertificate nsscert(pipCert->GetCert());
michael@0 1007
michael@0 1008 rv = attemptToLogInWithDefaultPassword();
michael@0 1009 if (NS_WARN_IF(rv != NS_OK)) {
michael@0 1010 return rv;
michael@0 1011 }
michael@0 1012
michael@0 1013 SECStatus srv;
michael@0 1014 if (type == nsIX509Cert::CA_CERT) {
michael@0 1015 // always start with untrusted and move up
michael@0 1016 trust.SetValidCA();
michael@0 1017 trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
michael@0 1018 !!(trusted & nsIX509CertDB::TRUSTED_EMAIL),
michael@0 1019 !!(trusted & nsIX509CertDB::TRUSTED_OBJSIGN));
michael@0 1020 srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
michael@0 1021 nsscert.get(),
michael@0 1022 trust.GetTrust());
michael@0 1023 } else if (type == nsIX509Cert::SERVER_CERT) {
michael@0 1024 // always start with untrusted and move up
michael@0 1025 trust.SetValidPeer();
michael@0 1026 trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, 0, 0);
michael@0 1027 srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
michael@0 1028 nsscert.get(),
michael@0 1029 trust.GetTrust());
michael@0 1030 } else if (type == nsIX509Cert::EMAIL_CERT) {
michael@0 1031 // always start with untrusted and move up
michael@0 1032 trust.SetValidPeer();
michael@0 1033 trust.AddPeerTrust(0, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL), 0);
michael@0 1034 srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
michael@0 1035 nsscert.get(),
michael@0 1036 trust.GetTrust());
michael@0 1037 } else {
michael@0 1038 // ignore user certs
michael@0 1039 return NS_OK;
michael@0 1040 }
michael@0 1041 return MapSECStatus(srv);
michael@0 1042 }
michael@0 1043
michael@0 1044 NS_IMETHODIMP
michael@0 1045 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert *cert,
michael@0 1046 uint32_t certType,
michael@0 1047 uint32_t trustType,
michael@0 1048 bool *_isTrusted)
michael@0 1049 {
michael@0 1050 NS_ENSURE_ARG_POINTER(_isTrusted);
michael@0 1051 *_isTrusted = false;
michael@0 1052
michael@0 1053 nsNSSShutDownPreventionLock locker;
michael@0 1054 if (isAlreadyShutDown()) {
michael@0 1055 return NS_ERROR_NOT_AVAILABLE;
michael@0 1056 }
michael@0 1057 SECStatus srv;
michael@0 1058 nsCOMPtr<nsIX509Cert2> pipCert = do_QueryInterface(cert);
michael@0 1059 mozilla::pkix::ScopedCERTCertificate nsscert(pipCert->GetCert());
michael@0 1060 CERTCertTrust nsstrust;
michael@0 1061 srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
michael@0 1062 if (srv != SECSuccess)
michael@0 1063 return NS_ERROR_FAILURE;
michael@0 1064
michael@0 1065 nsNSSCertTrust trust(&nsstrust);
michael@0 1066 if (certType == nsIX509Cert::CA_CERT) {
michael@0 1067 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
michael@0 1068 *_isTrusted = trust.HasTrustedCA(true, false, false);
michael@0 1069 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
michael@0 1070 *_isTrusted = trust.HasTrustedCA(false, true, false);
michael@0 1071 } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
michael@0 1072 *_isTrusted = trust.HasTrustedCA(false, false, true);
michael@0 1073 } else {
michael@0 1074 return NS_ERROR_FAILURE;
michael@0 1075 }
michael@0 1076 } else if (certType == nsIX509Cert::SERVER_CERT) {
michael@0 1077 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
michael@0 1078 *_isTrusted = trust.HasTrustedPeer(true, false, false);
michael@0 1079 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
michael@0 1080 *_isTrusted = trust.HasTrustedPeer(false, true, false);
michael@0 1081 } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
michael@0 1082 *_isTrusted = trust.HasTrustedPeer(false, false, true);
michael@0 1083 } else {
michael@0 1084 return NS_ERROR_FAILURE;
michael@0 1085 }
michael@0 1086 } else if (certType == nsIX509Cert::EMAIL_CERT) {
michael@0 1087 if (trustType & nsIX509CertDB::TRUSTED_SSL) {
michael@0 1088 *_isTrusted = trust.HasTrustedPeer(true, false, false);
michael@0 1089 } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
michael@0 1090 *_isTrusted = trust.HasTrustedPeer(false, true, false);
michael@0 1091 } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
michael@0 1092 *_isTrusted = trust.HasTrustedPeer(false, false, true);
michael@0 1093 } else {
michael@0 1094 return NS_ERROR_FAILURE;
michael@0 1095 }
michael@0 1096 } /* user: ignore */
michael@0 1097 return NS_OK;
michael@0 1098 }
michael@0 1099
michael@0 1100
michael@0 1101 NS_IMETHODIMP
michael@0 1102 nsNSSCertificateDB::ImportCertsFromFile(nsISupports *aToken,
michael@0 1103 nsIFile *aFile,
michael@0 1104 uint32_t aType)
michael@0 1105 {
michael@0 1106 nsNSSShutDownPreventionLock locker;
michael@0 1107 if (isAlreadyShutDown()) {
michael@0 1108 return NS_ERROR_NOT_AVAILABLE;
michael@0 1109 }
michael@0 1110
michael@0 1111 NS_ENSURE_ARG(aFile);
michael@0 1112 switch (aType) {
michael@0 1113 case nsIX509Cert::CA_CERT:
michael@0 1114 case nsIX509Cert::EMAIL_CERT:
michael@0 1115 case nsIX509Cert::SERVER_CERT:
michael@0 1116 // good
michael@0 1117 break;
michael@0 1118
michael@0 1119 default:
michael@0 1120 // not supported (yet)
michael@0 1121 return NS_ERROR_FAILURE;
michael@0 1122 }
michael@0 1123
michael@0 1124 nsresult rv;
michael@0 1125 PRFileDesc *fd = nullptr;
michael@0 1126
michael@0 1127 rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
michael@0 1128
michael@0 1129 if (NS_FAILED(rv))
michael@0 1130 return rv;
michael@0 1131
michael@0 1132 if (!fd)
michael@0 1133 return NS_ERROR_FAILURE;
michael@0 1134
michael@0 1135 PRFileInfo file_info;
michael@0 1136 if (PR_SUCCESS != PR_GetOpenFileInfo(fd, &file_info))
michael@0 1137 return NS_ERROR_FAILURE;
michael@0 1138
michael@0 1139 unsigned char *buf = new unsigned char[file_info.size];
michael@0 1140
michael@0 1141 int32_t bytes_obtained = PR_Read(fd, buf, file_info.size);
michael@0 1142 PR_Close(fd);
michael@0 1143
michael@0 1144 if (bytes_obtained != file_info.size)
michael@0 1145 rv = NS_ERROR_FAILURE;
michael@0 1146 else {
michael@0 1147 nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
michael@0 1148
michael@0 1149 switch (aType) {
michael@0 1150 case nsIX509Cert::CA_CERT:
michael@0 1151 rv = ImportCertificates(buf, bytes_obtained, aType, cxt);
michael@0 1152 break;
michael@0 1153
michael@0 1154 case nsIX509Cert::SERVER_CERT:
michael@0 1155 rv = ImportServerCertificate(buf, bytes_obtained, cxt);
michael@0 1156 break;
michael@0 1157
michael@0 1158 case nsIX509Cert::EMAIL_CERT:
michael@0 1159 rv = ImportEmailCertificate(buf, bytes_obtained, cxt);
michael@0 1160 break;
michael@0 1161
michael@0 1162 default:
michael@0 1163 break;
michael@0 1164 }
michael@0 1165 }
michael@0 1166
michael@0 1167 delete [] buf;
michael@0 1168 return rv;
michael@0 1169 }
michael@0 1170
michael@0 1171 NS_IMETHODIMP
michael@0 1172 nsNSSCertificateDB::ImportPKCS12File(nsISupports *aToken,
michael@0 1173 nsIFile *aFile)
michael@0 1174 {
michael@0 1175 nsNSSShutDownPreventionLock locker;
michael@0 1176 if (isAlreadyShutDown()) {
michael@0 1177 return NS_ERROR_NOT_AVAILABLE;
michael@0 1178 }
michael@0 1179
michael@0 1180 NS_ENSURE_ARG(aFile);
michael@0 1181 nsPKCS12Blob blob;
michael@0 1182 nsCOMPtr<nsIPK11Token> token = do_QueryInterface(aToken);
michael@0 1183 if (token) {
michael@0 1184 blob.SetToken(token);
michael@0 1185 }
michael@0 1186 return blob.ImportFromFile(aFile);
michael@0 1187 }
michael@0 1188
michael@0 1189 NS_IMETHODIMP
michael@0 1190 nsNSSCertificateDB::ExportPKCS12File(nsISupports *aToken,
michael@0 1191 nsIFile *aFile,
michael@0 1192 uint32_t count,
michael@0 1193 nsIX509Cert **certs)
michael@0 1194 //const char16_t **aCertNames)
michael@0 1195 {
michael@0 1196 nsNSSShutDownPreventionLock locker;
michael@0 1197 if (isAlreadyShutDown()) {
michael@0 1198 return NS_ERROR_NOT_AVAILABLE;
michael@0 1199 }
michael@0 1200
michael@0 1201 NS_ENSURE_ARG(aFile);
michael@0 1202 nsPKCS12Blob blob;
michael@0 1203 if (count == 0) return NS_OK;
michael@0 1204 nsCOMPtr<nsIPK11Token> localRef;
michael@0 1205 if (!aToken) {
michael@0 1206 ScopedPK11SlotInfo keySlot(PK11_GetInternalKeySlot());
michael@0 1207 NS_ASSERTION(keySlot,"Failed to get the internal key slot");
michael@0 1208 localRef = new nsPK11Token(keySlot);
michael@0 1209 }
michael@0 1210 else {
michael@0 1211 localRef = do_QueryInterface(aToken);
michael@0 1212 }
michael@0 1213 blob.SetToken(localRef);
michael@0 1214 //blob.LoadCerts(aCertNames, count);
michael@0 1215 //return blob.ExportToFile(aFile);
michael@0 1216 return blob.ExportToFile(aFile, certs, count);
michael@0 1217 }
michael@0 1218
michael@0 1219 /*
michael@0 1220 * NSS Helper Routines (private to nsNSSCertificateDB)
michael@0 1221 */
michael@0 1222
michael@0 1223 #define DELIM '\001'
michael@0 1224
michael@0 1225 /*
michael@0 1226 * GetSortedNameList
michael@0 1227 *
michael@0 1228 * Converts a CERTCertList to a list of certificate names
michael@0 1229 */
michael@0 1230 void
michael@0 1231 nsNSSCertificateDB::getCertNames(CERTCertList *certList,
michael@0 1232 uint32_t type,
michael@0 1233 uint32_t *_count,
michael@0 1234 char16_t ***_certNames,
michael@0 1235 const nsNSSShutDownPreventionLock &/*proofOfLock*/)
michael@0 1236 {
michael@0 1237 CERTCertListNode *node;
michael@0 1238 uint32_t numcerts = 0, i=0;
michael@0 1239 char16_t **tmpArray = nullptr;
michael@0 1240
michael@0 1241 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("List of certs %d:\n", type));
michael@0 1242 for (node = CERT_LIST_HEAD(certList);
michael@0 1243 !CERT_LIST_END(node, certList);
michael@0 1244 node = CERT_LIST_NEXT(node)) {
michael@0 1245 if (getCertType(node->cert) == type) {
michael@0 1246 numcerts++;
michael@0 1247 }
michael@0 1248 }
michael@0 1249 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("num certs: %d\n", numcerts));
michael@0 1250 int nc = (numcerts == 0) ? 1 : numcerts;
michael@0 1251 tmpArray = (char16_t **)nsMemory::Alloc(sizeof(char16_t *) * nc);
michael@0 1252 if (numcerts == 0) goto finish;
michael@0 1253 for (node = CERT_LIST_HEAD(certList);
michael@0 1254 !CERT_LIST_END(node, certList);
michael@0 1255 node = CERT_LIST_NEXT(node)) {
michael@0 1256 if (getCertType(node->cert) == type) {
michael@0 1257 nsNSSCertificate pipCert(node->cert);
michael@0 1258 char *dbkey = nullptr;
michael@0 1259 char *namestr = nullptr;
michael@0 1260 nsAutoString certstr;
michael@0 1261 pipCert.GetDbKey(&dbkey);
michael@0 1262 nsAutoString keystr = NS_ConvertASCIItoUTF16(dbkey);
michael@0 1263 PR_FREEIF(dbkey);
michael@0 1264 if (type == nsIX509Cert::EMAIL_CERT) {
michael@0 1265 namestr = node->cert->emailAddr;
michael@0 1266 } else {
michael@0 1267 namestr = node->cert->nickname;
michael@0 1268 if (namestr) {
michael@0 1269 char *sc = strchr(namestr, ':');
michael@0 1270 if (sc) *sc = DELIM;
michael@0 1271 }
michael@0 1272 }
michael@0 1273 nsAutoString certname = NS_ConvertASCIItoUTF16(namestr ? namestr : "");
michael@0 1274 certstr.Append(char16_t(DELIM));
michael@0 1275 certstr += certname;
michael@0 1276 certstr.Append(char16_t(DELIM));
michael@0 1277 certstr += keystr;
michael@0 1278 tmpArray[i++] = ToNewUnicode(certstr);
michael@0 1279 }
michael@0 1280 }
michael@0 1281 finish:
michael@0 1282 *_count = numcerts;
michael@0 1283 *_certNames = tmpArray;
michael@0 1284 }
michael@0 1285
michael@0 1286 /* nsIX509Cert getDefaultEmailEncryptionCert (); */
michael@0 1287 NS_IMETHODIMP
michael@0 1288 nsNSSCertificateDB::FindEmailEncryptionCert(const nsAString &aNickname, nsIX509Cert **_retval)
michael@0 1289 {
michael@0 1290 NS_ENSURE_ARG_POINTER(_retval);
michael@0 1291 *_retval = nullptr;
michael@0 1292
michael@0 1293 if (aNickname.IsEmpty())
michael@0 1294 return NS_OK;
michael@0 1295
michael@0 1296 nsNSSShutDownPreventionLock locker;
michael@0 1297 if (isAlreadyShutDown()) {
michael@0 1298 return NS_ERROR_NOT_AVAILABLE;
michael@0 1299 }
michael@0 1300
michael@0 1301 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
michael@0 1302 char *asciiname = nullptr;
michael@0 1303 NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
michael@0 1304 asciiname = const_cast<char*>(aUtf8Nickname.get());
michael@0 1305
michael@0 1306 /* Find a good cert in the user's database */
michael@0 1307 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 1308 cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname,
michael@0 1309 certUsageEmailRecipient, true, ctx);
michael@0 1310 if (!cert) {
michael@0 1311 return NS_OK;
michael@0 1312 }
michael@0 1313
michael@0 1314 nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
michael@0 1315 if (!nssCert) {
michael@0 1316 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1317 }
michael@0 1318 nssCert.forget(_retval);
michael@0 1319 return NS_OK;
michael@0 1320 }
michael@0 1321
michael@0 1322 /* nsIX509Cert getDefaultEmailSigningCert (); */
michael@0 1323 NS_IMETHODIMP
michael@0 1324 nsNSSCertificateDB::FindEmailSigningCert(const nsAString &aNickname, nsIX509Cert **_retval)
michael@0 1325 {
michael@0 1326 NS_ENSURE_ARG_POINTER(_retval);
michael@0 1327 *_retval = nullptr;
michael@0 1328
michael@0 1329 if (aNickname.IsEmpty())
michael@0 1330 return NS_OK;
michael@0 1331
michael@0 1332 nsNSSShutDownPreventionLock locker;
michael@0 1333 if (isAlreadyShutDown()) {
michael@0 1334 return NS_ERROR_NOT_AVAILABLE;
michael@0 1335 }
michael@0 1336
michael@0 1337 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 1338 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
michael@0 1339 char *asciiname = nullptr;
michael@0 1340 NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
michael@0 1341 asciiname = const_cast<char*>(aUtf8Nickname.get());
michael@0 1342
michael@0 1343 /* Find a good cert in the user's database */
michael@0 1344 cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname,
michael@0 1345 certUsageEmailSigner, true, ctx);
michael@0 1346 if (!cert) {
michael@0 1347 return NS_OK;
michael@0 1348 }
michael@0 1349
michael@0 1350 nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
michael@0 1351 if (!nssCert) {
michael@0 1352 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1353 }
michael@0 1354 nssCert.forget(_retval);
michael@0 1355 return NS_OK;
michael@0 1356 }
michael@0 1357
michael@0 1358 NS_IMETHODIMP
michael@0 1359 nsNSSCertificateDB::FindCertByEmailAddress(nsISupports *aToken, const char *aEmailAddress, nsIX509Cert **_retval)
michael@0 1360 {
michael@0 1361 nsNSSShutDownPreventionLock locker;
michael@0 1362 if (isAlreadyShutDown()) {
michael@0 1363 return NS_ERROR_NOT_AVAILABLE;
michael@0 1364 }
michael@0 1365
michael@0 1366 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
michael@0 1367 NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
michael@0 1368
michael@0 1369 ScopedCERTCertList certlist(
michael@0 1370 PK11_FindCertsFromEmailAddress(aEmailAddress, nullptr));
michael@0 1371 if (!certlist)
michael@0 1372 return NS_ERROR_FAILURE;
michael@0 1373
michael@0 1374 // certlist now contains certificates with the right email address,
michael@0 1375 // but they might not have the correct usage or might even be invalid
michael@0 1376
michael@0 1377 if (CERT_LIST_END(CERT_LIST_HEAD(certlist), certlist))
michael@0 1378 return NS_ERROR_FAILURE; // no certs found
michael@0 1379
michael@0 1380 CERTCertListNode *node;
michael@0 1381 // search for a valid certificate
michael@0 1382 for (node = CERT_LIST_HEAD(certlist);
michael@0 1383 !CERT_LIST_END(node, certlist);
michael@0 1384 node = CERT_LIST_NEXT(node)) {
michael@0 1385
michael@0 1386 SECStatus srv = certVerifier->VerifyCert(node->cert,
michael@0 1387 certificateUsageEmailRecipient,
michael@0 1388 PR_Now(), nullptr /*XXX pinarg*/,
michael@0 1389 nullptr /*hostname*/);
michael@0 1390 if (srv == SECSuccess) {
michael@0 1391 break;
michael@0 1392 }
michael@0 1393 }
michael@0 1394
michael@0 1395 if (CERT_LIST_END(node, certlist)) {
michael@0 1396 // no valid cert found
michael@0 1397 return NS_ERROR_FAILURE;
michael@0 1398 }
michael@0 1399
michael@0 1400 // node now contains the first valid certificate with correct usage
michael@0 1401 nsNSSCertificate *nssCert = nsNSSCertificate::Create(node->cert);
michael@0 1402 if (!nssCert)
michael@0 1403 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1404
michael@0 1405 NS_ADDREF(nssCert);
michael@0 1406 *_retval = static_cast<nsIX509Cert*>(nssCert);
michael@0 1407 return NS_OK;
michael@0 1408 }
michael@0 1409
michael@0 1410 /* nsIX509Cert constructX509FromBase64 (in string base64); */
michael@0 1411 NS_IMETHODIMP
michael@0 1412 nsNSSCertificateDB::ConstructX509FromBase64(const char *base64,
michael@0 1413 nsIX509Cert **_retval)
michael@0 1414 {
michael@0 1415 nsNSSShutDownPreventionLock locker;
michael@0 1416 if (isAlreadyShutDown()) {
michael@0 1417 return NS_ERROR_NOT_AVAILABLE;
michael@0 1418 }
michael@0 1419 if (NS_WARN_IF(!_retval)) {
michael@0 1420 return NS_ERROR_INVALID_POINTER;
michael@0 1421 }
michael@0 1422
michael@0 1423 // sure would be nice to have a smart pointer class for PL_ allocations
michael@0 1424 // unfortunately, we cannot distinguish out-of-memory from bad-input here
michael@0 1425 uint32_t len = base64 ? strlen(base64) : 0;
michael@0 1426 char *certDER = PL_Base64Decode(base64, len, nullptr);
michael@0 1427 if (!certDER)
michael@0 1428 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1429 if (!*certDER) {
michael@0 1430 PL_strfree(certDER);
michael@0 1431 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1432 }
michael@0 1433
michael@0 1434 // If we get to this point, we know we had well-formed base64 input;
michael@0 1435 // therefore the input string cannot have been less than two
michael@0 1436 // characters long. Compute the unpadded length of the decoded data.
michael@0 1437 uint32_t lengthDER = (len * 3) / 4;
michael@0 1438 if (base64[len-1] == '=') {
michael@0 1439 lengthDER--;
michael@0 1440 if (base64[len-2] == '=')
michael@0 1441 lengthDER--;
michael@0 1442 }
michael@0 1443
michael@0 1444 nsresult rv = ConstructX509(certDER, lengthDER, _retval);
michael@0 1445 PL_strfree(certDER);
michael@0 1446 return rv;
michael@0 1447 }
michael@0 1448
michael@0 1449 /* nsIX509Cert constructX509 (in string certDER, unsigned long len); */
michael@0 1450 NS_IMETHODIMP
michael@0 1451 nsNSSCertificateDB::ConstructX509(const char* certDER,
michael@0 1452 uint32_t lengthDER,
michael@0 1453 nsIX509Cert** _retval)
michael@0 1454 {
michael@0 1455 nsNSSShutDownPreventionLock locker;
michael@0 1456 if (isAlreadyShutDown()) {
michael@0 1457 return NS_ERROR_NOT_AVAILABLE;
michael@0 1458 }
michael@0 1459 if (NS_WARN_IF(!_retval)) {
michael@0 1460 return NS_ERROR_INVALID_POINTER;
michael@0 1461 }
michael@0 1462
michael@0 1463 SECItem secitem_cert;
michael@0 1464 secitem_cert.type = siDERCertBuffer;
michael@0 1465 secitem_cert.data = (unsigned char*)certDER;
michael@0 1466 secitem_cert.len = lengthDER;
michael@0 1467
michael@0 1468 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 1469 cert =
michael@0 1470 CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &secitem_cert,
michael@0 1471 nullptr, false, true);
michael@0 1472 if (!cert)
michael@0 1473 return (PORT_GetError() == SEC_ERROR_NO_MEMORY)
michael@0 1474 ? NS_ERROR_OUT_OF_MEMORY : NS_ERROR_FAILURE;
michael@0 1475
michael@0 1476 nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
michael@0 1477 if (!nssCert) {
michael@0 1478 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1479 }
michael@0 1480 nssCert.forget(_retval);
michael@0 1481 return NS_OK;
michael@0 1482 }
michael@0 1483
michael@0 1484 void
michael@0 1485 nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert,
michael@0 1486 nsIInterfaceRequestor* ctx,
michael@0 1487 nsCString &nickname,
michael@0 1488 const nsNSSShutDownPreventionLock &/*proofOfLock*/)
michael@0 1489 {
michael@0 1490 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
michael@0 1491
michael@0 1492 nickname.Truncate();
michael@0 1493
michael@0 1494 nsresult rv;
michael@0 1495 CK_OBJECT_HANDLE keyHandle;
michael@0 1496
michael@0 1497 CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB();
michael@0 1498 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
michael@0 1499 if (NS_FAILED(rv))
michael@0 1500 return;
michael@0 1501
michael@0 1502 nsAutoCString username;
michael@0 1503 char *temp_un = CERT_GetCommonName(&cert->subject);
michael@0 1504 if (temp_un) {
michael@0 1505 username = temp_un;
michael@0 1506 PORT_Free(temp_un);
michael@0 1507 temp_un = nullptr;
michael@0 1508 }
michael@0 1509
michael@0 1510 nsAutoCString caname;
michael@0 1511 char *temp_ca = CERT_GetOrgName(&cert->issuer);
michael@0 1512 if (temp_ca) {
michael@0 1513 caname = temp_ca;
michael@0 1514 PORT_Free(temp_ca);
michael@0 1515 temp_ca = nullptr;
michael@0 1516 }
michael@0 1517
michael@0 1518 nsAutoString tmpNickFmt;
michael@0 1519 nssComponent->GetPIPNSSBundleString("nick_template", tmpNickFmt);
michael@0 1520 NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
michael@0 1521
michael@0 1522 nsAutoCString baseName;
michael@0 1523 char *temp_nn = PR_smprintf(nickFmt.get(), username.get(), caname.get());
michael@0 1524 if (!temp_nn) {
michael@0 1525 return;
michael@0 1526 } else {
michael@0 1527 baseName = temp_nn;
michael@0 1528 PR_smprintf_free(temp_nn);
michael@0 1529 temp_nn = nullptr;
michael@0 1530 }
michael@0 1531
michael@0 1532 nickname = baseName;
michael@0 1533
michael@0 1534 /*
michael@0 1535 * We need to see if the private key exists on a token, if it does
michael@0 1536 * then we need to check for nicknames that already exist on the smart
michael@0 1537 * card.
michael@0 1538 */
michael@0 1539 ScopedPK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
michael@0 1540 if (!slot)
michael@0 1541 return;
michael@0 1542
michael@0 1543 if (!PK11_IsInternal(slot)) {
michael@0 1544 char *tmp = PR_smprintf("%s:%s", PK11_GetTokenName(slot), baseName.get());
michael@0 1545 if (!tmp) {
michael@0 1546 nickname.Truncate();
michael@0 1547 return;
michael@0 1548 }
michael@0 1549 baseName = tmp;
michael@0 1550 PR_smprintf_free(tmp);
michael@0 1551
michael@0 1552 nickname = baseName;
michael@0 1553 }
michael@0 1554
michael@0 1555 int count = 1;
michael@0 1556 while (true) {
michael@0 1557 if ( count > 1 ) {
michael@0 1558 char *tmp = PR_smprintf("%s #%d", baseName.get(), count);
michael@0 1559 if (!tmp) {
michael@0 1560 nickname.Truncate();
michael@0 1561 return;
michael@0 1562 }
michael@0 1563 nickname = tmp;
michael@0 1564 PR_smprintf_free(tmp);
michael@0 1565 }
michael@0 1566
michael@0 1567 mozilla::pkix::ScopedCERTCertificate dummycert;
michael@0 1568
michael@0 1569 if (PK11_IsInternal(slot)) {
michael@0 1570 /* look up the nickname to make sure it isn't in use already */
michael@0 1571 dummycert = CERT_FindCertByNickname(defaultcertdb, nickname.get());
michael@0 1572
michael@0 1573 } else {
michael@0 1574 /*
michael@0 1575 * Check the cert against others that already live on the smart
michael@0 1576 * card.
michael@0 1577 */
michael@0 1578 dummycert = PK11_FindCertFromNickname(nickname.get(), ctx);
michael@0 1579 if (dummycert) {
michael@0 1580 /*
michael@0 1581 * Make sure the subject names are different.
michael@0 1582 */
michael@0 1583 if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual)
michael@0 1584 {
michael@0 1585 /*
michael@0 1586 * There is another certificate with the same nickname and
michael@0 1587 * the same subject name on the smart card, so let's use this
michael@0 1588 * nickname.
michael@0 1589 */
michael@0 1590 dummycert = nullptr;
michael@0 1591 }
michael@0 1592 }
michael@0 1593 }
michael@0 1594 if (!dummycert)
michael@0 1595 break;
michael@0 1596
michael@0 1597 count++;
michael@0 1598 }
michael@0 1599 }
michael@0 1600
michael@0 1601 NS_IMETHODIMP nsNSSCertificateDB::AddCertFromBase64(const char *aBase64, const char *aTrust, const char *aName)
michael@0 1602 {
michael@0 1603 NS_ENSURE_ARG_POINTER(aBase64);
michael@0 1604 nsCOMPtr <nsIX509Cert> newCert;
michael@0 1605
michael@0 1606 nsNSSShutDownPreventionLock locker;
michael@0 1607 if (isAlreadyShutDown()) {
michael@0 1608 return NS_ERROR_NOT_AVAILABLE;
michael@0 1609 }
michael@0 1610
michael@0 1611 nsNSSCertTrust trust;
michael@0 1612
michael@0 1613 // need to calculate the trust bits from the aTrust string.
michael@0 1614 SECStatus stat = CERT_DecodeTrustString(trust.GetTrust(),
michael@0 1615 /* this is const, but not declared that way */(char *) aTrust);
michael@0 1616 NS_ENSURE_STATE(stat == SECSuccess); // if bad trust passed in, return error.
michael@0 1617
michael@0 1618
michael@0 1619 nsresult rv = ConstructX509FromBase64(aBase64, getter_AddRefs(newCert));
michael@0 1620 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1621
michael@0 1622 SECItem der;
michael@0 1623 rv = newCert->GetRawDER(&der.len, (uint8_t **)&der.data);
michael@0 1624 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1625
michael@0 1626 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));
michael@0 1627 CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
michael@0 1628 mozilla::pkix::ScopedCERTCertificate tmpCert(CERT_FindCertByDERCert(certdb, &der));
michael@0 1629 if (!tmpCert)
michael@0 1630 tmpCert = CERT_NewTempCertificate(certdb, &der,
michael@0 1631 nullptr, false, true);
michael@0 1632 nsMemory::Free(der.data);
michael@0 1633 der.data = nullptr;
michael@0 1634 der.len = 0;
michael@0 1635
michael@0 1636 if (!tmpCert) {
michael@0 1637 NS_ERROR("Couldn't create cert from DER blob");
michael@0 1638 return MapSECStatus(SECFailure);
michael@0 1639 }
michael@0 1640
michael@0 1641 if (tmpCert->isperm) {
michael@0 1642 return NS_OK;
michael@0 1643 }
michael@0 1644
michael@0 1645 nsXPIDLCString nickname;
michael@0 1646 nickname.Adopt(CERT_MakeCANickname(tmpCert.get()));
michael@0 1647
michael@0 1648 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nickname.get()));
michael@0 1649
michael@0 1650 rv = attemptToLogInWithDefaultPassword();
michael@0 1651 if (NS_WARN_IF(rv != NS_OK)) {
michael@0 1652 return rv;
michael@0 1653 }
michael@0 1654
michael@0 1655 SECStatus srv = __CERT_AddTempCertToPerm(tmpCert.get(),
michael@0 1656 const_cast<char*>(nickname.get()),
michael@0 1657 trust.GetTrust());
michael@0 1658 return MapSECStatus(srv);
michael@0 1659 }
michael@0 1660
michael@0 1661 NS_IMETHODIMP
michael@0 1662 nsNSSCertificateDB::AddCert(const nsACString & aCertDER, const char *aTrust,
michael@0 1663 const char *aName)
michael@0 1664 {
michael@0 1665 nsCString base64;
michael@0 1666 nsresult rv = Base64Encode(aCertDER, base64);
michael@0 1667 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1668 return AddCertFromBase64(base64.get(), aTrust, aName);
michael@0 1669 }
michael@0 1670
michael@0 1671 NS_IMETHODIMP
michael@0 1672 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert3* cert,
michael@0 1673 const char* trustString)
michael@0 1674 {
michael@0 1675 CERTCertTrust trust;
michael@0 1676
michael@0 1677 // need to calculate the trust bits from the aTrust string.
michael@0 1678 SECStatus srv = CERT_DecodeTrustString(&trust,
michael@0 1679 const_cast<char *>(trustString));
michael@0 1680 if (srv != SECSuccess) {
michael@0 1681 return MapSECStatus(SECFailure);
michael@0 1682 }
michael@0 1683 mozilla::pkix::ScopedCERTCertificate nssCert(cert->GetCert());
michael@0 1684
michael@0 1685 nsresult rv = attemptToLogInWithDefaultPassword();
michael@0 1686 if (NS_WARN_IF(rv != NS_OK)) {
michael@0 1687 return rv;
michael@0 1688 }
michael@0 1689
michael@0 1690 srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nssCert.get(), &trust);
michael@0 1691 return MapSECStatus(srv);
michael@0 1692 }
michael@0 1693
michael@0 1694 NS_IMETHODIMP
michael@0 1695 nsNSSCertificateDB::GetCerts(nsIX509CertList **_retval)
michael@0 1696 {
michael@0 1697 nsNSSShutDownPreventionLock locker;
michael@0 1698 if (isAlreadyShutDown()) {
michael@0 1699 return NS_ERROR_NOT_AVAILABLE;
michael@0 1700 }
michael@0 1701
michael@0 1702 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
michael@0 1703 nsCOMPtr<nsIX509CertList> nssCertList;
michael@0 1704 mozilla::pkix::ScopedCERTCertList certList(
michael@0 1705 PK11_ListCerts(PK11CertListUnique, ctx));
michael@0 1706
michael@0 1707 // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine.
michael@0 1708 // (returns an empty list)
michael@0 1709 nssCertList = new nsNSSCertList(certList, locker);
michael@0 1710
michael@0 1711 *_retval = nssCertList;
michael@0 1712 NS_ADDREF(*_retval);
michael@0 1713 return NS_OK;
michael@0 1714 }
michael@0 1715
michael@0 1716 NS_IMETHODIMP
michael@0 1717 nsNSSCertificateDB::GetRecentBadCerts(bool isPrivate, nsIRecentBadCerts** result)
michael@0 1718 {
michael@0 1719 nsNSSShutDownPreventionLock locker;
michael@0 1720 if (isAlreadyShutDown()) {
michael@0 1721 return NS_ERROR_NOT_AVAILABLE;
michael@0 1722 }
michael@0 1723
michael@0 1724 MutexAutoLock lock(mBadCertsLock);
michael@0 1725 if (isPrivate) {
michael@0 1726 if (!mPrivateRecentBadCerts) {
michael@0 1727 mPrivateRecentBadCerts = new nsRecentBadCerts;
michael@0 1728 }
michael@0 1729 NS_ADDREF(*result = mPrivateRecentBadCerts);
michael@0 1730 } else {
michael@0 1731 if (!mPublicRecentBadCerts) {
michael@0 1732 mPublicRecentBadCerts = new nsRecentBadCerts;
michael@0 1733 }
michael@0 1734 NS_ADDREF(*result = mPublicRecentBadCerts);
michael@0 1735 }
michael@0 1736 return NS_OK;
michael@0 1737 }
michael@0 1738
michael@0 1739 NS_IMETHODIMP
michael@0 1740 nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
michael@0 1741 int64_t /*SECCertificateUsage*/ aUsage,
michael@0 1742 uint32_t aFlags,
michael@0 1743 nsIX509CertList** verifiedChain,
michael@0 1744 bool* aHasEVPolicy,
michael@0 1745 int32_t* /*PRErrorCode*/ _retval )
michael@0 1746 {
michael@0 1747 NS_ENSURE_ARG_POINTER(aCert);
michael@0 1748 NS_ENSURE_ARG_POINTER(aHasEVPolicy);
michael@0 1749 NS_ENSURE_ARG_POINTER(verifiedChain);
michael@0 1750 NS_ENSURE_ARG_POINTER(_retval);
michael@0 1751
michael@0 1752 *verifiedChain = nullptr;
michael@0 1753 *aHasEVPolicy = false;
michael@0 1754 *_retval = PR_UNKNOWN_ERROR;
michael@0 1755
michael@0 1756 nsNSSShutDownPreventionLock locker;
michael@0 1757 if (isAlreadyShutDown()) {
michael@0 1758 return NS_ERROR_NOT_AVAILABLE;
michael@0 1759 }
michael@0 1760
michael@0 1761 #ifndef MOZ_NO_EV_CERTS
michael@0 1762 EnsureIdentityInfoLoaded();
michael@0 1763 #endif
michael@0 1764
michael@0 1765 nsCOMPtr<nsIX509Cert2> x509Cert = do_QueryInterface(aCert);
michael@0 1766 if (!x509Cert) {
michael@0 1767 return NS_ERROR_INVALID_ARG;
michael@0 1768 }
michael@0 1769 ScopedCERTCertificate nssCert(x509Cert->GetCert());
michael@0 1770
michael@0 1771 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
michael@0 1772 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
michael@0 1773
michael@0 1774 mozilla::pkix::ScopedCERTCertList resultChain;
michael@0 1775 SECOidTag evOidPolicy;
michael@0 1776 SECStatus srv;
michael@0 1777
michael@0 1778 srv = certVerifier->VerifyCert(nssCert,
michael@0 1779 aUsage, PR_Now(),
michael@0 1780 nullptr, // Assume no context
michael@0 1781 nullptr, // hostname
michael@0 1782 aFlags,
michael@0 1783 nullptr, // stapledOCSPResponse
michael@0 1784 &resultChain,
michael@0 1785 &evOidPolicy);
michael@0 1786
michael@0 1787 PRErrorCode error = PR_GetError();
michael@0 1788
michael@0 1789 nsCOMPtr<nsIX509CertList> nssCertList;
michael@0 1790 // This adopts the list
michael@0 1791 nssCertList = new nsNSSCertList(resultChain, locker);
michael@0 1792 NS_ENSURE_TRUE(nssCertList, NS_ERROR_FAILURE);
michael@0 1793
michael@0 1794 if (srv == SECSuccess) {
michael@0 1795 if (evOidPolicy != SEC_OID_UNKNOWN) {
michael@0 1796 *aHasEVPolicy = true;
michael@0 1797 }
michael@0 1798 *_retval = 0;
michael@0 1799 } else {
michael@0 1800 NS_ENSURE_TRUE(evOidPolicy == SEC_OID_UNKNOWN, NS_ERROR_FAILURE);
michael@0 1801 NS_ENSURE_TRUE(error != 0, NS_ERROR_FAILURE);
michael@0 1802 *_retval = error;
michael@0 1803 }
michael@0 1804 nssCertList.forget(verifiedChain);
michael@0 1805
michael@0 1806 return NS_OK;
michael@0 1807 }
michael@0 1808
michael@0 1809 NS_IMETHODIMP
michael@0 1810 nsNSSCertificateDB::ClearOCSPCache()
michael@0 1811 {
michael@0 1812 nsNSSShutDownPreventionLock locker;
michael@0 1813 if (isAlreadyShutDown()) {
michael@0 1814 return NS_ERROR_NOT_AVAILABLE;
michael@0 1815 }
michael@0 1816
michael@0 1817 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
michael@0 1818 NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
michael@0 1819 if (certVerifier->mImplementation == CertVerifier::mozillapkix) {
michael@0 1820 certVerifier->ClearOCSPCache();
michael@0 1821 } else {
michael@0 1822 SECStatus srv = CERT_ClearOCSPCache();
michael@0 1823 if (srv != SECSuccess) {
michael@0 1824 return MapSECStatus(srv);
michael@0 1825 }
michael@0 1826 }
michael@0 1827
michael@0 1828 return NS_OK;
michael@0 1829 }

mercurial