Blob Blame History Raw
# HG changeset patch
# Parent  77c27a1e264305362865d6d64af201299e04dece

diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -1097,7 +1097,27 @@ InitializeNSS(const char* dir, bool read
   if (!loadPKCS11Modules) {
     flags |= NSS_INIT_NOMODDB;
   }
-  return ::NSS_Initialize(dir, "", "", SECMOD_DB, flags);
+  SECStatus srv = NSS_Initialize(dir, "", "", SECMOD_DB, flags);
+  if (srv != SECSuccess) {
+    return srv;
+  }
+
+  if (!readOnly) {
+    UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+    if (!slot) {
+      return SECFailure;
+    }
+    // If the key DB doesn't have a password set, PK11_NeedUserInit will return
+    // true. For the SQL DB, we need to set a password or we won't be able to
+    // import any certificates or change trust settings.
+    if (PK11_NeedUserInit(slot.get())) {
+      srv = PK11_InitPin(slot.get(), nullptr, nullptr);
+      MOZ_ASSERT(srv == SECSuccess);
+      Unused << srv;
+    }
+  }
+
+  return SECSuccess;
 }
 
 void
diff --git a/security/manager/ssl/nsIPK11Token.idl b/security/manager/ssl/nsIPK11Token.idl
--- a/security/manager/ssl/nsIPK11Token.idl
+++ b/security/manager/ssl/nsIPK11Token.idl
@@ -65,6 +65,14 @@ interface nsIPK11Token : nsISupports
   void setAskPasswordDefaults([const] in long askTimes, [const] in long timeout);
 
   /*
+   * True if a password has been configured for this token, and false otherwise.
+   * (Whether or not the user is currently logged in makes no difference.)
+   * In particular, this can be used to determine if a user has set a master
+   * password (if this is the internal key token).
+   */
+  readonly attribute boolean hasPassword;
+
+  /*
    * Other attributes
    */
   boolean isHardwareToken();
diff --git a/security/manager/ssl/nsNSSCertificateDB.cpp b/security/manager/ssl/nsNSSCertificateDB.cpp
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -56,31 +56,6 @@ using mozilla::psm::SharedSSLState;
 
 extern LazyLogModule gPIPNSSLog;
 
-static nsresult
-attemptToLogInWithDefaultPassword()
-{
-#ifdef NSS_DISABLE_DBM
-  // The SQL NSS DB requires the user to be authenticated to set certificate
-  // trust settings, even if the user's password is empty. To maintain
-  // compatibility with the DBM-based database, try to log in with the
-  // default empty password. This will allow, at least, tests that need to
-  // change certificate trust to pass on all platforms. TODO(bug 978120): Do
-  // proper testing and/or implement a better solution so that we are confident
-  // that this does the correct thing outside of xpcshell tests too.
-  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
-  if (!slot) {
-    return MapSECStatus(SECFailure);
-  }
-  if (PK11_NeedUserInit(slot.get())) {
-    // Ignore the return value. Presumably PK11_InitPin will fail if the user
-    // has a non-default password.
-    Unused << PK11_InitPin(slot.get(), nullptr, nullptr);
-  }
-#endif
-
-  return NS_OK;
-}
-
 NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)
 
 nsNSSCertificateDB::~nsNSSCertificateDB()
@@ -843,11 +818,6 @@ nsNSSCertificateDB::SetCertTrust(nsIX509
   nsresult rv;
   UniqueCERTCertificate nsscert(cert->GetCert());
 
-  rv = attemptToLogInWithDefaultPassword();
-  if (NS_WARN_IF(rv != NS_OK)) {
-    return rv;
-  }
-
   SECStatus srv;
   if (type == nsIX509Cert::CA_CERT) {
     // always start with untrusted and move up
@@ -1366,11 +1336,6 @@ nsNSSCertificateDB::AddCertFromBase64(co
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get()));
 
-  rv = attemptToLogInWithDefaultPassword();
-  if (NS_WARN_IF(rv != NS_OK)) {
-    return rv;
-  }
-
   SECStatus srv = CERT_AddTempCertToPerm(tmpCert.get(), nickname.get(),
                                          trust.GetTrust());
   return MapSECStatus(srv);
@@ -1400,11 +1365,6 @@ nsNSSCertificateDB::SetCertTrustFromStri
   }
   UniqueCERTCertificate nssCert(cert->GetCert());
 
-  nsresult rv = attemptToLogInWithDefaultPassword();
-  if (NS_WARN_IF(rv != NS_OK)) {
-    return rv;
-  }
-
   srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nssCert.get(), &trust);
   return MapSECStatus(srv);
 }
diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -2081,8 +2081,14 @@ NS_IMETHODIMP
 nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
                         const char16_t* someData)
 {
-  if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("receiving profile change topic\n"));
+  // In some tests, we don't receive a "profile-before-change" topic. However,
+  // we still have to shut down before the storage service shuts down, because
+  // closing the sql-backed softoken requires sqlite still be available. Thus,
+  // we observe "xpcom-shutdown" just in case.
+  if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0 ||
+      nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+            ("receiving profile change or XPCOM shutdown notification"));
     DoProfileBeforeChange();
   } else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
     nsNSSShutDownPreventionLock locker;
@@ -2234,6 +2240,7 @@ nsNSSComponent::RegisterObservers()
   // keep a strong reference to this component. As a result, this will live at
   // least as long as the observer service.
   observerService->AddObserver(this, PROFILE_BEFORE_CHANGE_TOPIC, false);
+  observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 
   return NS_OK;
 }
diff --git a/security/manager/ssl/nsPK11TokenDB.cpp b/security/manager/ssl/nsPK11TokenDB.cpp
--- a/security/manager/ssl/nsPK11TokenDB.cpp
+++ b/security/manager/ssl/nsPK11TokenDB.cpp
@@ -293,11 +293,24 @@ NS_IMETHODIMP
 nsPK11Token::InitPassword(const nsACString& initialPassword)
 {
   nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
+  if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
-  return MapSECStatus(
-    PK11_InitPin(mSlot.get(), "", PromiseFlatCString(initialPassword).get()));
+  const nsCString& passwordCStr = PromiseFlatCString(initialPassword);
+  // PSM initializes the sqlite-backed softoken with an empty password. The
+  // implementation considers this not to be a password (GetHasPassword returns
+  // false), but we can't actually call PK11_InitPin again. Instead, we call
+  // PK11_ChangePW with the empty password.
+  bool hasPassword;
+  nsresult rv = GetHasPassword(&hasPassword);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (!PK11_NeedUserInit(mSlot.get()) && !hasPassword) {
+    return MapSECStatus(PK11_ChangePW(mSlot.get(), "", passwordCStr.get()));
+  }
+  return MapSECStatus(PK11_InitPin(mSlot.get(), "", passwordCStr.get()));
 }
 
 NS_IMETHODIMP
@@ -359,6 +372,23 @@ nsPK11Token::ChangePassword(const nsACSt
 }
 
 NS_IMETHODIMP
+nsPK11Token::GetHasPassword(bool* hasPassword)
+{
+  NS_ENSURE_ARG_POINTER(hasPassword);
+
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // PK11_NeedLogin returns true if the token is currently configured to require
+  // the user to log in (whether or not the user is actually logged in makes no
+  // difference).
+  *hasPassword = PK11_NeedLogin(mSlot.get()) && !PK11_NeedUserInit(mSlot.get());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPK11Token::IsHardwareToken(bool* _retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -770,14 +770,6 @@ function add_prevented_cert_override_tes
   add_connection_test(aHost, aExpectedError);
 }
 
-function loginToDBWithDefaultPassword() {
-  let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
-                  .getService(Ci.nsIPK11TokenDB);
-  let token = tokenDB.getInternalKeyToken();
-  token.initPassword("");
-  token.login(/*force*/ false);
-}
-
 // Helper for asyncTestCertificateUsages.
 class CertVerificationResult {
   constructor(certName, usageString, successExpected, resolve) {
diff --git a/security/manager/ssl/tests/unit/test_certDB_import.js b/security/manager/ssl/tests/unit/test_certDB_import.js
--- a/security/manager/ssl/tests/unit/test_certDB_import.js
+++ b/security/manager/ssl/tests/unit/test_certDB_import.js
@@ -89,11 +89,6 @@ function testImportCACert() {
 }
 
 function run_test() {
-  // We have to set a password and login before we attempt to import anything.
-  // In particular, the SQL NSS DB requires the user to be authenticated to set
-  // certificate trust settings, which we do when we import CA certs.
-  loginToDBWithDefaultPassword();
-
   let certificateDialogsCID =
     MockRegistrar.register("@mozilla.org/nsCertificateDialogs;1",
                            gCertificateDialogs);
diff --git a/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js b/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
--- a/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
+++ b/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
@@ -73,9 +73,6 @@ function testImportPKCS12Cert() {
 }
 
 function run_test() {
-  // We have to set a password and login before we attempt to import anything.
-  loginToDBWithDefaultPassword();
-
   let certificateDialogsCID =
     MockRegistrar.register("@mozilla.org/nsCertificateDialogs;1",
                            gCertificateDialogs);
diff --git a/security/manager/ssl/tests/unit/test_sdr.js b/security/manager/ssl/tests/unit/test_sdr.js
--- a/security/manager/ssl/tests/unit/test_sdr.js
+++ b/security/manager/ssl/tests/unit/test_sdr.js
@@ -22,12 +22,6 @@ const gTokenPasswordDialogs = {
 };
 
 function run_test() {
-  // We have to set a password and login before we attempt to encrypt anything.
-  // In particular, failing to do so will cause the Encrypt() implementation to
-  // pop up a dialog asking for a password to be set. This won't work in the
-  // xpcshell environment and will lead to an assertion.
-  loginToDBWithDefaultPassword();
-
   let sdr = Cc["@mozilla.org/security/sdr;1"]
               .getService(Ci.nsISecretDecoderRing);