Bug 911336: nsNSSCertificateDB does not lock nssShutdown correctly. r=keeler.
authorCamilo Viecco <cviecco@mozilla.com>
Thu, 29 Aug 2013 16:15:35 -0700
changeset 165315 f78f52c8c9ff3d45106deee263e1710f74da4805
parent 165314 5a0801040b54c10f0fab3cc51d04f4ea34ec2fc9
child 165316 0052a204cf836ed6df8ede11adba3030850ac81f
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs911336
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 911336: nsNSSCertificateDB does not lock nssShutdown correctly. r=keeler.
security/manager/ssl/src/nsCrypto.cpp
security/manager/ssl/src/nsNSSCertificateDB.cpp
security/manager/ssl/src/nsNSSCertificateDB.h
--- a/security/manager/ssl/src/nsCrypto.cpp
+++ b/security/manager/ssl/src/nsCrypto.cpp
@@ -2346,17 +2346,17 @@ nsCrypto::ImportUserCertificates(const n
     }
     // Let's figure out which nickname to give the cert.  If 
     // a certificate with the same subject name already exists,
     // then just use that one, otherwise, get the default nickname.
     if (currCert->nickname) {
       localNick = currCert->nickname;
     }
     else if (!nickname || nickname[0] == '\0') {
-      nsNSSCertificateDB::get_default_nickname(currCert, ctx, localNick);
+      nsNSSCertificateDB::get_default_nickname(currCert, ctx, localNick, locker);
     } else {
       //This is the case where we're getting a brand new
       //cert that doesn't have the same subjectName as a cert
       //that already exists in our db and the CA page has 
       //designated a nickname to use for the newly issued cert.
       localNick = nickname;
     }
     {
@@ -2402,17 +2402,17 @@ nsCrypto::ImportUserCertificates(const n
         rv = NS_ERROR_OUT_OF_MEMORY;
         goto loser;
       }
       for (node = CERT_LIST_HEAD(caPubs), i=0; 
            !CERT_LIST_END(node, caPubs);
            node = CERT_LIST_NEXT(node), i++) {
         derCerts[i] = node->cert->derCert;
       }
-      nsNSSCertificateDB::ImportValidCACerts(numCAs, derCerts, ctx);
+      nsNSSCertificateDB::ImportValidCACerts(numCAs, derCerts, ctx, locker);
       nsMemory::Free(derCerts);
     }
   }
  }
 
   if (aDoForcedBackup) {
     // I can't pop up a file picker from the depths of JavaScript,
     // so I'll just post an event on the UI queue to do the backups
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -74,16 +74,19 @@ NS_IMETHODIMP
 nsNSSCertificateDB::FindCertByNickname(nsISupports *aToken,
                                       const nsAString &nickname,
                                       nsIX509Cert **_rvCert)
 {
   NS_ENSURE_ARG_POINTER(_rvCert);
   *_rvCert = nullptr;
 
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   ScopedCERTCertificate cert;
   char *asciiname = nullptr;
   NS_ConvertUTF16toUTF8 aUtf8Nickname(nickname);
   asciiname = const_cast<char*>(aUtf8Nickname.get());
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting \"%s\"\n", asciiname));
 #if 0
   // what it should be, but for now...
   if (aToken) {
@@ -112,16 +115,20 @@ nsNSSCertificateDB::FindCertByDBKey(cons
                                    nsIX509Cert **_cert)
 {
   NS_ENSURE_ARG_POINTER(aDBkey);
   NS_ENSURE_ARG(aDBkey[0]);
   NS_ENSURE_ARG_POINTER(_cert);
   *_cert = nullptr;
 
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   SECItem keyItem = {siBuffer, nullptr, 0};
   SECItem *dummy;
   CERTIssuerAndSN issuerSN;
   //unsigned long moduleID,slotID;
 
   dummy = NSSBase64_DecodeBuffer(nullptr, &keyItem, aDBkey,
                                  (uint32_t)strlen(aDBkey)); 
   if (!dummy || keyItem.len < NS_NSS_LONG*4) {
@@ -160,16 +167,20 @@ nsNSSCertificateDB::FindCertByDBKey(cons
 
 NS_IMETHODIMP 
 nsNSSCertificateDB::FindCertNicknames(nsISupports *aToken, 
                                      uint32_t      aType,
                                      uint32_t     *_count,
                                      PRUnichar  ***_certNames)
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsresult rv = NS_ERROR_FAILURE;
   /*
    * obtain the cert list from NSS
    */
   ScopedCERTCertList certList;
   PK11CertListType pk11type;
 #if 0
   // this would seem right, but it didn't work...
@@ -181,17 +192,17 @@ nsNSSCertificateDB::FindCertNicknames(ns
     pk11type = PK11CertListUnique;
   certList = PK11_ListCerts(pk11type, nullptr);
   if (!certList)
     goto cleanup;
   /*
    * get list of cert names from list of certs
    * XXX also cull the list (NSS only distinguishes based on user/non-user
    */
-  getCertNames(certList, aType, _count, _certNames);
+  getCertNames(certList, aType, _count, _certNames, locker);
   rv = NS_OK;
   /*
    * finish up
    */
 cleanup:
   return rv;
 }
 
@@ -220,19 +231,19 @@ collect_certs(void *arg, SECItem **certs
     certs++;
   }
 
   return (SECSuccess);
 }
 
 CERTDERCerts*
 nsNSSCertificateDB::getCertsFromPackage(PLArenaPool *arena, uint8_t *data, 
-                                        uint32_t length)
+                                        uint32_t length,
+                                        const nsNSSShutDownPreventionLock &/*proofOfLock*/)
 {
-  nsNSSShutDownPreventionLock locker;
   CERTDERCerts *collectArgs = 
                (CERTDERCerts *)PORT_ArenaZAlloc(arena, sizeof(CERTDERCerts));
   if (!collectArgs)
     return nullptr;
 
   collectArgs->arena = arena;
   SECStatus sec_rv = CERT_DecodeCertPackage(reinterpret_cast<char *>(data), 
                                             length, collect_certs, 
@@ -240,33 +251,32 @@ nsNSSCertificateDB::getCertsFromPackage(
   if (sec_rv != SECSuccess)
     return nullptr;
 
   return collectArgs;
 }
 
 nsresult
 nsNSSCertificateDB::handleCACertDownload(nsIArray *x509Certs,
-                                         nsIInterfaceRequestor *ctx)
+                                         nsIInterfaceRequestor *ctx,
+                                         const nsNSSShutDownPreventionLock &proofOfLock)
 {
   // First thing we have to do is figure out which certificate we're 
   // gonna present to the user.  The CA may have sent down a list of 
   // certs which may or may not be a chained list of certs.  Until
   // the day we can design some solid UI for the general case, we'll
   // code to the > 90% case.  That case is where a CA sends down a
   // list that is a hierarchy whose root is either the first or 
   // the last cert.  What we're gonna do is compare the first 
   // 2 entries, if the second was signed by the first, we assume
   // the root cert is the first cert and display it.  Otherwise,
   // we compare the last 2 entries, if the second to last cert was
   // signed by the last cert, then we assume the last cert is the
   // root and display it.
 
-  nsNSSShutDownPreventionLock locker;
-
   uint32_t numCerts;
 
   x509Certs->GetLength(&numCerts);
   NS_ASSERTION(numCerts > 0, "Didn't get any certs to import.");
   if (numCerts == 0)
     return NS_OK; // Nothing to import, so nothing to do.
 
   nsCOMPtr<nsIX509Cert> certToShow;
@@ -346,22 +356,22 @@ nsNSSCertificateDB::handleCACertDownload
   der.len = 0;
   
   if (!tmpCert) {
     NS_ERROR("Couldn't create cert from DER blob");
     return NS_ERROR_FAILURE;
   }
 
   if (!CERT_IsCACert(tmpCert, nullptr)) {
-    DisplayCertificateAlert(ctx, "NotACACert", certToShow);
+    DisplayCertificateAlert(ctx, "NotACACert", certToShow, proofOfLock);
     return NS_ERROR_FAILURE;
   }
 
   if (tmpCert->isperm) {
-    DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
+    DisplayCertificateAlert(ctx, "CaCertExists", certToShow, proofOfLock);
     return NS_ERROR_FAILURE;
   }
 
   uint32_t trustBits;
   bool allows;
   rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
   if (NS_FAILED(rv))
     return rv;
@@ -417,38 +427,42 @@ nsNSSCertificateDB::handleCACertDownload
     if (!tmpCert2) {
       NS_ERROR("Couldn't create temp cert from DER blob");
       continue;  // Let's try to import the rest of 'em
     }
     
     CERT_AddCertToListTail(certList, tmpCert2);
   }
 
-  return ImportValidCACertsInList(certList, ctx);
+  return ImportValidCACertsInList(certList, ctx, proofOfLock);
 }
 
 /*
  *  [noscript] void importCertificates(in charPtr data, in unsigned long length,
  *                                     in unsigned long type, 
  *                                     in nsIInterfaceRequestor ctx);
  */
 NS_IMETHODIMP 
 nsNSSCertificateDB::ImportCertificates(uint8_t * data, uint32_t length, 
                                        uint32_t type, 
                                        nsIInterfaceRequestor *ctx)
 
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsresult nsrv;
 
   PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   if (!arena)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length);
+  CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
   if (!certCollection) {
     PORT_FreeArena(arena, false);
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIMutableArray> array =
     do_CreateInstance(NS_ARRAY_CONTRACTID, &nsrv);
   if (NS_FAILED(nsrv)) {
     PORT_FreeArena(arena, false);
@@ -464,17 +478,17 @@ nsNSSCertificateDB::ImportCertificates(u
      nssCert = nsNSSCertificate::ConstructFromDER((char*)currItem->data, currItem->len);
      if (!nssCert)
        return NS_ERROR_FAILURE;
      x509Cert = do_QueryInterface((nsIX509Cert*)nssCert);
      array->AppendElement(x509Cert, false);
   }
   switch (type) {
   case nsIX509Cert::CA_CERT:
-    nsrv = handleCACertDownload(array, ctx);
+    nsrv = handleCACertDownload(array, ctx, locker);
     break;
   default:
     // We only deal with import CA certs in this method currently.
      nsrv = NS_ERROR_FAILURE;
      break;
   }  
   PORT_FreeArena(arena, false);
   return nsrv;
@@ -519,31 +533,35 @@ ImportCertsIntoPermanentStorage(const Sc
  *                                     in nsIInterfaceRequestor ctx);
  */
 NS_IMETHODIMP
 nsNSSCertificateDB::ImportEmailCertificate(uint8_t * data, uint32_t length, 
                                        nsIInterfaceRequestor *ctx)
 
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   SECStatus srv = SECFailure;
   nsresult nsrv = NS_OK;
   CERTCertDBHandle *certdb;
   CERTCertificate **certArray = nullptr;
   ScopedCERTCertList certList;
   CERTCertListNode *node;
   SECItem **rawArray;
   int numcerts;
   int i;
 
   PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   if (!arena)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length);
+  CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
   if (!certCollection) {
     PORT_FreeArena(arena, false);
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<CertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
@@ -604,17 +622,17 @@ nsNSSCertificateDB::ImportEmailCertifica
     SECStatus rv = certVerifier->VerifyCert(node->cert,
                                             certificateUsageEmailRecipient,
                                             now, ctx, 0, &verifyCertChain);
 
     ScopedCERTCertList certChain(verifyCertChain);
 
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
-      DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow);
+      DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
       continue;
     }
     rv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient, false);
     if (rv != SECSuccess) {
       goto loser;
     } 
     CERT_SaveSMimeProfile(node->cert, nullptr, nullptr);
 
@@ -630,30 +648,34 @@ loser:
 }
 
 NS_IMETHODIMP
 nsNSSCertificateDB::ImportServerCertificate(uint8_t * data, uint32_t length, 
                                             nsIInterfaceRequestor *ctx)
 
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   SECStatus srv = SECFailure;
   nsresult nsrv = NS_OK;
   ScopedCERTCertificate cert;
   SECItem **rawCerts = nullptr;
   int numcerts;
   int i;
   nsNSSCertTrust trust;
   char *serverNickname = nullptr;
  
   PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   if (!arena)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length);
+  CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
   if (!certCollection) {
     PORT_FreeArena(arena, false);
     return NS_ERROR_FAILURE;
   }
   cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certCollection->rawCerts,
                                  nullptr, false, true);
   if (!cert) {
     nsrv = NS_ERROR_FAILURE;
@@ -689,17 +711,17 @@ nsNSSCertificateDB::ImportServerCertific
 loser:
   PORT_Free(rawCerts);
   if (arena) 
     PORT_FreeArena(arena, true);
   return nsrv;
 }
 
 nsresult
-nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem *CACerts, nsIInterfaceRequestor *ctx)
+nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem *CACerts, nsIInterfaceRequestor *ctx,  const nsNSSShutDownPreventionLock &proofOfLock)
 {
   ScopedCERTCertList certList;
   SECItem **rawArray;
 
   // build a CertList for filtering
   certList = CERT_NewCertList();
   if (!certList) {
     return NS_ERROR_FAILURE;
@@ -733,21 +755,22 @@ nsNSSCertificateDB::ImportValidCACerts(i
     if (cacert)
       cacert = CERT_DupCertificate(cacert);
     if (cacert)
       CERT_AddCertToListTail(certList, cacert);
   }
 
   CERT_DestroyCertArray(certArray, numCACerts);
 
-  return ImportValidCACertsInList(certList, ctx);
+  return ImportValidCACertsInList(certList, ctx, proofOfLock);
 }
 
 nsresult
-nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx)
+nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx,
+                                             const nsNSSShutDownPreventionLock &proofOfLock)
 {
   RefPtr<CertVerifier> certVerifier(GetDefaultCertVerifier());
   if (!certVerifier)
     return NS_ERROR_UNEXPECTED;
 
   /* filter out the certs we don't want */
   SECStatus srv = CERT_FilterCertListByUsage(certList, certUsageAnyCA, true);
   if (srv != SECSuccess) {
@@ -768,29 +791,30 @@ nsNSSCertificateDB::ImportValidCACertsIn
 
     SECStatus rv = certVerifier->VerifyCert(node->cert, certificateUsageVerifyCA,
                                             PR_Now(), ctx, 0, &verifyCertChain);
 
     ScopedCERTCertList certChain(verifyCertChain);
 
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
-      DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow);
+      DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
       continue;
     }
 
     ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
   }
   
   return NS_OK;
 }
 
 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor *ctx, 
                                                  const char *stringID, 
-                                                 nsIX509Cert *certToShow)
+                                                 nsIX509Cert *certToShow,
+                                                 const nsNSSShutDownPreventionLock &/*proofOfLock*/)
 {
   if (!NS_IsMainThread()) {
     NS_ERROR("nsNSSCertificateDB::DisplayCertificateAlert called off the main thread");
     return;
   }
 
   nsPSMUITracker tracker;
   if (!tracker.isUIForbidden()) {
@@ -822,96 +846,103 @@ NS_IMETHODIMP
 nsNSSCertificateDB::ImportUserCertificate(uint8_t *data, uint32_t length, nsIInterfaceRequestor *ctx)
 {
   if (!NS_IsMainThread()) {
     NS_ERROR("nsNSSCertificateDB::ImportUserCertificate called off the main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
   
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   ScopedPK11SlotInfo slot;
   nsAutoCString nickname;
   nsresult rv = NS_ERROR_FAILURE;
   int numCACerts;
   SECItem *CACerts;
   CERTDERCerts * collectArgs;
   PLArenaPool *arena;
   ScopedCERTCertificate cert;
 
   arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   if (!arena) {
     goto loser;
   }
 
-  collectArgs = getCertsFromPackage(arena, data, length);
+  collectArgs = getCertsFromPackage(arena, data, length, locker);
   if (!collectArgs) {
     goto loser;
   }
 
   cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts,
                                  nullptr, false, true);
   if (!cert) {
     goto loser;
   }
 
   slot = PK11_KeyForCertExists(cert, nullptr, ctx);
   if (!slot) {
     nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert);
-    DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow);
+    DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow, locker);
     goto loser;
   }
   slot = nullptr;
 
   /* pick a nickname for the cert */
   if (cert->nickname) {
 	/* sigh, we need a call to look up other certs with this subject and
 	 * identify nicknames from them. We can no longer walk down internal
 	 * database structures  rjr */
   	nickname = cert->nickname;
   }
   else {
-    get_default_nickname(cert, ctx, nickname);
+    get_default_nickname(cert, ctx, nickname, locker);
   }
 
   /* user wants to import the cert */
   {
     char *cast_const_away = const_cast<char*>(nickname.get());
     slot = PK11_ImportCertForKey(cert, cast_const_away, ctx);
   }
   if (!slot) {
     goto loser;
   }
   slot = nullptr;
 
   {
     nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert);
-    DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
+    DisplayCertificateAlert(ctx, "UserCertImported", certToShow, locker);
   }
   rv = NS_OK;
 
   numCACerts = collectArgs->numcerts - 1;
   if (numCACerts) {
     CACerts = collectArgs->rawCerts+1;
-    rv = ImportValidCACerts(numCACerts, CACerts, ctx);
+    rv = ImportValidCACerts(numCACerts, CACerts, ctx, locker);
   }
   
 loser:
   if (arena) {
     PORT_FreeArena(arena, false);
   }
   return rv;
 }
 
 /*
  * void deleteCertificate(in nsIX509Cert aCert);
  */
 NS_IMETHODIMP 
 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert)
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   nsCOMPtr<nsIX509Cert2> nssCert = do_QueryInterface(aCert);
   ScopedCERTCertificate cert(nssCert->GetCert());
   if (!cert) return NS_ERROR_FAILURE;
   SECStatus srv = SECSuccess;
 
   uint32_t certType;
   nssCert->GetCertType(&certType);
   if (NS_FAILED(nssCert->MarkForPermDeletion()))
@@ -941,16 +972,19 @@ nsNSSCertificateDB::DeleteCertificate(ns
  *                   in unsigned long trust);
  */
 NS_IMETHODIMP 
 nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert, 
                                  uint32_t type,
                                  uint32_t trusted)
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   SECStatus srv;
   nsNSSCertTrust trust;
   nsCOMPtr<nsIX509Cert2> pipCert = do_QueryInterface(cert);
   if (!pipCert)
     return NS_ERROR_FAILURE;
   ScopedCERTCertificate nsscert(pipCert->GetCert());
 
   if (type == nsIX509Cert::CA_CERT) {
@@ -988,16 +1022,19 @@ nsNSSCertificateDB::IsCertTrusted(nsIX50
                                   uint32_t certType,
                                   uint32_t trustType,
                                   bool *_isTrusted)
 {
   NS_ENSURE_ARG_POINTER(_isTrusted);
   *_isTrusted = false;
 
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   SECStatus srv;
   nsCOMPtr<nsIX509Cert2> pipCert = do_QueryInterface(cert);
   ScopedCERTCertificate nsscert(pipCert->GetCert());
   CERTCertTrust nsstrust;
   srv = CERT_GetCertTrust(nsscert, &nsstrust);
   if (srv != SECSuccess)
     return NS_ERROR_FAILURE;
 
@@ -1037,16 +1074,21 @@ nsNSSCertificateDB::IsCertTrusted(nsIX50
 }
 
 
 NS_IMETHODIMP 
 nsNSSCertificateDB::ImportCertsFromFile(nsISupports *aToken, 
                                         nsIFile *aFile,
                                         uint32_t aType)
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   NS_ENSURE_ARG(aFile);
   switch (aType) {
     case nsIX509Cert::CA_CERT:
     case nsIX509Cert::EMAIL_CERT:
     case nsIX509Cert::SERVER_CERT:
       // good
       break;
     
@@ -1101,16 +1143,21 @@ nsNSSCertificateDB::ImportCertsFromFile(
   delete [] buf;
   return rv;  
 }
 
 NS_IMETHODIMP 
 nsNSSCertificateDB::ImportPKCS12File(nsISupports *aToken, 
                                      nsIFile *aFile)
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   NS_ENSURE_ARG(aFile);
   nsPKCS12Blob blob;
   nsCOMPtr<nsIPK11Token> token = do_QueryInterface(aToken);
   if (token) {
     blob.SetToken(token);
   }
   return blob.ImportFromFile(aFile);
 }
@@ -1118,16 +1165,20 @@ nsNSSCertificateDB::ImportPKCS12File(nsI
 NS_IMETHODIMP 
 nsNSSCertificateDB::ExportPKCS12File(nsISupports     *aToken, 
                                      nsIFile          *aFile,
                                      uint32_t          count,
                                      nsIX509Cert     **certs)
                                      //const PRUnichar **aCertNames)
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   NS_ENSURE_ARG(aFile);
   nsPKCS12Blob blob;
   if (count == 0) return NS_OK;
   nsCOMPtr<nsIPK11Token> localRef;
   if (!aToken) {
     ScopedPK11SlotInfo keySlot(PK11_GetInternalKeySlot());
     NS_ASSERTION(keySlot,"Failed to get the internal key slot");
     localRef = new nsPK11Token(keySlot);
@@ -1151,22 +1202,23 @@ nsNSSCertificateDB::ExportPKCS12File(nsI
  * GetSortedNameList
  *
  * Converts a CERTCertList to a list of certificate names
  */
 void
 nsNSSCertificateDB::getCertNames(CERTCertList *certList,
                                  uint32_t      type, 
                                  uint32_t     *_count,
-                                 PRUnichar  ***_certNames)
+                                 PRUnichar  ***_certNames,
+                                 const nsNSSShutDownPreventionLock &/*proofOfLock*/)
 {
-  nsNSSShutDownPreventionLock locker;
   CERTCertListNode *node;
   uint32_t numcerts = 0, i=0;
   PRUnichar **tmpArray = nullptr;
+
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("List of certs %d:\n", type));
   for (node = CERT_LIST_HEAD(certList);
        !CERT_LIST_END(node, certList);
        node = CERT_LIST_NEXT(node)) {
     if (getCertType(node->cert) == type) {
       numcerts++;
     }
   }
@@ -1213,16 +1265,20 @@ nsNSSCertificateDB::FindEmailEncryptionC
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = nullptr;
 
   if (aNickname.IsEmpty())
     return NS_OK;
 
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   char *asciiname = nullptr;
   NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
   asciiname = const_cast<char*>(aUtf8Nickname.get());
 
   /* Find a good cert in the user's database */
   ScopedCERTCertificate cert;
   cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname, 
@@ -1245,16 +1301,20 @@ nsNSSCertificateDB::FindEmailSigningCert
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = nullptr;
 
   if (aNickname.IsEmpty())
     return NS_OK;
 
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   ScopedCERTCertificate cert;
   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   char *asciiname = nullptr;
   NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
   asciiname = const_cast<char*>(aUtf8Nickname.get());
 
   /* Find a good cert in the user's database */
   cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname, 
@@ -1270,16 +1330,19 @@ nsNSSCertificateDB::FindEmailSigningCert
   nssCert.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateDB::FindCertByEmailAddress(nsISupports *aToken, const char *aEmailAddress, nsIX509Cert **_retval)
 {
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   
   RefPtr<CertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   ScopedCERTCertList certlist(
       PK11_FindCertsFromEmailAddress(aEmailAddress, nullptr));
   if (!certlist)
     return NS_ERROR_FAILURE;  
@@ -1343,16 +1406,20 @@ nsNSSCertificateDB::ConstructX509FromBas
   uint32_t lengthDER = (len * 3) / 4;
   if (base64[len-1] == '=') {
     lengthDER--;
     if (base64[len-2] == '=')
       lengthDER--;
   }
 
   nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   SECItem secitem_cert;
   secitem_cert.type = siDERCertBuffer;
   secitem_cert.data = (unsigned char*)certDER;
   secitem_cert.len = lengthDER;
 
   ScopedCERTCertificate cert;
   cert =
     CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &secitem_cert,
@@ -1369,21 +1436,21 @@ nsNSSCertificateDB::ConstructX509FromBas
   }
   nssCert.forget(_retval);
   return NS_OK;
 }
 
 void
 nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert, 
                                          nsIInterfaceRequestor* ctx,
-                                         nsCString &nickname)
+                                         nsCString &nickname,
+                                         const nsNSSShutDownPreventionLock &/*proofOfLock*/)
 {
   nickname.Truncate();
 
-  nsNSSShutDownPreventionLock locker;
   nsresult rv;
   CK_OBJECT_HANDLE keyHandle;
 
   CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB();
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
   if (NS_FAILED(rv))
     return;
 
@@ -1486,16 +1553,21 @@ nsNSSCertificateDB::get_default_nickname
   }
 }
 
 NS_IMETHODIMP nsNSSCertificateDB::AddCertFromBase64(const char *aBase64, const char *aTrust, const char *aName)
 {
   NS_ENSURE_ARG_POINTER(aBase64);
   nsCOMPtr <nsIX509Cert> newCert;
 
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsNSSCertTrust trust;
 
   // need to calculate the trust bits from the aTrust string.
   SECStatus stat = CERT_DecodeTrustString(trust.GetTrust(),
     /* this is const, but not declared that way */(char *) aTrust);
   NS_ENSURE_STATE(stat == SECSuccess); // if bad trust passed in, return error.
 
 
@@ -1546,16 +1618,21 @@ nsNSSCertificateDB::AddCert(const nsACSt
   nsresult rv = Base64Encode(aCertDER, base64);
   NS_ENSURE_SUCCESS(rv, rv);
   return AddCertFromBase64(base64.get(), aTrust, aName);
 }
 
 NS_IMETHODIMP 
 nsNSSCertificateDB::GetCerts(nsIX509CertList **_retval)
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   CERTCertList *certList;
 
   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   nsCOMPtr<nsIX509CertList> nssCertList;
   certList = PK11_ListCerts(PK11CertListUnique, ctx);
 
   // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine.
   // (returns an empty list) 
@@ -1564,16 +1641,21 @@ nsNSSCertificateDB::GetCerts(nsIX509Cert
   *_retval = nssCertList;
   NS_ADDREF(*_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateDB::GetRecentBadCerts(bool isPrivate, nsIRecentBadCerts** result)
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   MutexAutoLock lock(mBadCertsLock);
   if (isPrivate) {
     if (!mPrivateRecentBadCerts) {
       mPrivateRecentBadCerts = new nsRecentBadCerts;
     }
     NS_ADDREF(*result = mPrivateRecentBadCerts);
   } else {
     if (!mPublicRecentBadCerts) {
--- a/security/manager/ssl/src/nsNSSCertificateDB.h
+++ b/security/manager/ssl/src/nsNSSCertificateDB.h
@@ -2,64 +2,78 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __NSNSSCERTIFICATEDB_H__
 #define __NSNSSCERTIFICATEDB_H__
 
 #include "nsIX509CertDB.h"
 #include "nsIX509CertDB2.h"
+#include "nsNSSShutDown.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Mutex.h"
 #include "certt.h"
 
 class nsCString;
 class nsIArray;
 class nsRecentBadCerts;
 
-class nsNSSCertificateDB : public nsIX509CertDB, public nsIX509CertDB2
+class nsNSSCertificateDB : public nsIX509CertDB
+                         , public nsIX509CertDB2
+                         , public nsNSSShutDownObject
+
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIX509CERTDB
   NS_DECL_NSIX509CERTDB2
 
   nsNSSCertificateDB(); 
   virtual ~nsNSSCertificateDB();
 
   // Use this function to generate a default nickname for a user
   // certificate that is to be imported onto a token.
   static void
   get_default_nickname(CERTCertificate *cert, nsIInterfaceRequestor* ctx,
-                       nsCString &nickname);
+                       nsCString &nickname,
+                       const nsNSSShutDownPreventionLock &proofOfLock);
 
   static nsresult 
-  ImportValidCACerts(int numCACerts, SECItem *CACerts, nsIInterfaceRequestor *ctx);
+  ImportValidCACerts(int numCACerts, SECItem *CACerts, nsIInterfaceRequestor *ctx,
+                     const nsNSSShutDownPreventionLock &proofOfLock);
 
 private:
 
   static nsresult
-  ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx);
+  ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx,
+                           const nsNSSShutDownPreventionLock &proofOfLock);
 
   static void DisplayCertificateAlert(nsIInterfaceRequestor *ctx, 
-                                      const char *stringID, nsIX509Cert *certToShow);
+                                      const char *stringID, nsIX509Cert *certToShow,
+                                      const nsNSSShutDownPreventionLock &proofOfLock);
 
   void getCertNames(CERTCertList *certList,
                     uint32_t      type, 
                     uint32_t     *_count,
-                    PRUnichar  ***_certNameList);
+                    PRUnichar  ***_certNameList,
+                    const nsNSSShutDownPreventionLock &proofOfLock);
 
   CERTDERCerts *getCertsFromPackage(PLArenaPool *arena, uint8_t *data, 
-                                    uint32_t length);
+                                    uint32_t length,
+                                    const nsNSSShutDownPreventionLock &proofOfLock);
   nsresult handleCACertDownload(nsIArray *x509Certs, 
-                                nsIInterfaceRequestor *ctx);
+                                nsIInterfaceRequestor *ctx,
+                                const nsNSSShutDownPreventionLock &proofOfLock);
 
   mozilla::Mutex mBadCertsLock;
   mozilla::RefPtr<nsRecentBadCerts> mPublicRecentBadCerts;
   mozilla::RefPtr<nsRecentBadCerts> mPrivateRecentBadCerts;
+
+  // We don't own any NSS objects here, so no need to clean up
+  virtual void virtualDestroyNSSReference() { };
 };
 
 #define NS_X509CERTDB_CID { /* fb0bbc5c-452e-4783-b32c-80124693d871 */ \
     0xfb0bbc5c,                                                        \
     0x452e,                                                            \
     0x4783,                                                            \
     {0xb3, 0x2c, 0x80, 0x12, 0x46, 0x93, 0xd8, 0x71}                   \
   }