bug 1398932 - add a preference for enabling the sqlite-backed NSS databases r?Cykesiopka,jcj draft
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 06 Sep 2017 14:31:27 -0700
changeset 664381 2f31c09edc85ce1f5054196be94872f4709c14d1
parent 663831 1888ec2f277f6bb26271b8808e08914a21db9efe
child 731442 88f2b931d67eb0600f4257e7f8e141b1ba0e42da
push id79694
push userbmo:dkeeler@mozilla.com
push dateWed, 13 Sep 2017 23:02:06 +0000
reviewersCykesiopka, jcj
bugs1398932, 1377940
milestone57.0a1
bug 1398932 - add a preference for enabling the sqlite-backed NSS databases r?Cykesiopka,jcj In the future, bug 1377940 will make the sqlite-backed databases the default, but until we're sure this will stick we want to be able to control this with a Firefox-only change. The use of a preference to configure which format to use will hopefully allow us to restore the old behavior quickly and relatively safely if necessary. Note that doing this should be done with care; any changes made in the sqlite databases after upgrade migration will not be reflected if we need to go back to the old database format. Thus, user data (imported CAs, client certificates, and keys) can be lost. MozReview-Commit-ID: tkovdiCU9v
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/certverifier/moz.build
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/security-prefs.js
security/manager/ssl/tests/unit/test_db_format_pref_new.js
security/manager/ssl/tests/unit/test_db_format_pref_old.js
security/manager/ssl/tests/unit/xpcshell.ini
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -14,21 +14,23 @@
 #include "OCSPVerificationTrustDomain.h"
 #include "PublicKeyPinningService.h"
 #include "cert.h"
 #include "certdb.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "nsCRTGlue.h"
 #include "nsNSSCertificate.h"
 #include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
 #include "nss.h"
 #include "pk11pub.h"
 #include "pkix/Result.h"
 #include "pkix/pkix.h"
 #include "pkix/pkixnss.h"
 #include "prerror.h"
 #include "secerr.h"
 
@@ -1223,33 +1225,46 @@ NSSCertDBTrustDomain::NoteAuxiliaryExten
   }
   if (out) {
     SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
     out->reset(SECITEM_DupItem(&extensionDataItem));
   }
 }
 
 SECStatus
-InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules)
+InitializeNSS(const nsACString& dir, bool readOnly, bool loadPKCS11Modules)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
   // module by NSS_Initialize because we will load it in InstallLoadableRoots
   // later.  It also allows us to work around a bug in the system NSS in
   // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
   // "/usr/lib/nss/libnssckbi.so".
   uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
   if (readOnly) {
     flags |= NSS_INIT_READONLY;
   }
   if (!loadPKCS11Modules) {
     flags |= NSS_INIT_NOMODDB;
   }
+  bool useSQLDB = Preferences::GetBool("security.use_sqldb", false);
+  nsAutoCString dbTypeAndDirectory;
+  // Don't change any behavior if the user has specified an alternative database
+  // location with MOZPSM_NSSDBDIR_OVERRIDE.
+  const char* dbDirOverride = getenv("MOZPSM_NSSDBDIR_OVERRIDE");
+  if (useSQLDB && (!dbDirOverride || strlen(dbDirOverride) == 0)) {
+    dbTypeAndDirectory.Append("sql:");
+  }
+  dbTypeAndDirectory.Append(dir);
   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
-          ("InitializeNSS(%s, %d, %d)", dir, readOnly, loadPKCS11Modules));
-  SECStatus srv = NSS_Initialize(dir, "", "", SECMOD_DB, flags);
+          ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(), readOnly,
+           loadPKCS11Modules));
+  SECStatus srv = NSS_Initialize(dbTypeAndDirectory.get(), "", "",
+                                 SECMOD_DB, flags);
   if (srv != SECSuccess) {
     return srv;
   }
 
   if (!readOnly) {
     UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
     if (!slot) {
       return SECFailure;
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -32,17 +32,18 @@ enum class ValidityCheckingMode {
 // * Never match: the OID is never considered equivalent to serverAuth
 enum class NetscapeStepUpPolicy : uint32_t {
   AlwaysMatch = 0,
   MatchBefore23August2016 = 1,
   MatchBefore23August2015 = 2,
   NeverMatch = 3,
 };
 
-SECStatus InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules);
+SECStatus InitializeNSS(const nsACString& dir, bool readOnly,
+                        bool loadPKCS11Modules);
 
 void DisableMD5();
 
 /**
  * Loads root certificates from a module.
  *
  * @param dir
  *        The path to the directory containing the NSS builtin roots module.
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -78,16 +78,21 @@ if CONFIG['_MSC_VER']:
                    # constructor required
         '-wd4619', # pragma warning: there is no warning 'warning'
         '-wd4623', # default constructor could not be generated because a base
                    # class default constructor is inaccessible or deleted
         '-wd4625', # copy constructor could not be generated because a base
                    # class copy constructor is inaccessible or deleted
         '-wd4626', # assignment operator could not be generated because a base
                    # class assignment operator is inaccessible or deleted
+        '-wd4628', # digraphs not supported with -Ze (nsThreadUtils.h includes
+                   # what would be the digraph "<:" in the expression
+                   # "mozilla::EnableIf<::detail::...". Since we don't want it
+                   # interpreted as a digraph anyway, we can disable the
+                   # warning.)
         '-wd4640', # construction of local static object is not thread-safe
         '-wd4710', # 'function': function not inlined
         '-wd4711', # function 'function' selected for inline expansion
         '-wd4820', # 'bytes' bytes padding added after construct 'member_name'
     ]
 
     # MSVC 2010's headers trigger these
     CXXFLAGS += [
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1881,33 +1881,31 @@ InitializeNSSWithFallbacks(const nsACStr
   if (nocertdb || profilePath.IsEmpty()) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("nocertdb mode or empty profile path -> NSS_NoDB_Init"));
     SECStatus srv = NSS_NoDB_Init(nullptr);
     return srv == SECSuccess ? NS_OK : NS_ERROR_FAILURE;
   }
 
 
-  const nsCString& profilePathCStr = PromiseFlatCString(profilePath);
   // Try read/write mode. If we're in safeMode, we won't load PKCS#11 modules.
 #ifndef ANDROID
   PRErrorCode savedPRErrorCode1;
 #endif // ifndef ANDROID
-  SECStatus srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), false,
-                                                !safeMode);
+  SECStatus srv = ::mozilla::psm::InitializeNSS(profilePath, false, !safeMode);
   if (srv == SECSuccess) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized NSS in r/w mode"));
     return NS_OK;
   }
 #ifndef ANDROID
   savedPRErrorCode1 = PR_GetError();
   PRErrorCode savedPRErrorCode2;
 #endif // ifndef ANDROID
   // That failed. Try read-only mode.
-  srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), true, !safeMode);
+  srv = ::mozilla::psm::InitializeNSS(profilePath, true, !safeMode);
   if (srv == SECSuccess) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized NSS in r-o mode"));
     return NS_OK;
   }
 #ifndef ANDROID
   savedPRErrorCode2 = PR_GetError();
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
@@ -1924,38 +1922,38 @@ InitializeNSSWithFallbacks(const nsACStr
                     savedPRErrorCode1 == SEC_ERROR_PKCS11_DEVICE_ERROR ||
                     savedPRErrorCode2 == SEC_ERROR_PKCS11_DEVICE_ERROR)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("attempting no-module db init"));
     // It would make sense to initialize NSS in read-only mode here since this
     // is just a test to see if the PKCS#11 module DB being in FIPS mode is the
     // problem, but for some reason the combination of read-only and no-moddb
     // flags causes NSS initialization to fail, so unfortunately we have to use
     // read-write mode.
-    srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), false, false);
+    srv = ::mozilla::psm::InitializeNSS(profilePath, false, false);
     if (srv == SECSuccess) {
       MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("FIPS may be the problem"));
       // Unload NSS so we can attempt to fix this situation for the user.
       srv = NSS_Shutdown();
       if (srv != SECSuccess) {
         return NS_ERROR_FAILURE;
       }
       MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trying to rename module db"));
       // If this fails non-catastrophically, we'll attempt to initialize NSS
       // again in r/w then r-o mode (both of which will fail), and then we'll
       // fall back to NSS_NoDB_Init, which is the behavior we want.
       nsresult rv = AttemptToRenameBothPKCS11ModuleDBVersions(profilePath);
       if (NS_FAILED(rv)) {
         return rv;
       }
-      srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), false, true);
+      srv = ::mozilla::psm::InitializeNSS(profilePath, false, true);
       if (srv == SECSuccess) {
         MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized in r/w mode"));
         return NS_OK;
       }
-      srv = ::mozilla::psm::InitializeNSS(profilePathCStr.get(), true, true);
+      srv = ::mozilla::psm::InitializeNSS(profilePath, true, true);
       if (srv == SECSuccess) {
         MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("initialized in r-o mode"));
         return NS_OK;
       }
     }
   }
 #endif
 
--- a/security/manager/ssl/security-prefs.js
+++ b/security/manager/ssl/security-prefs.js
@@ -38,16 +38,24 @@ pref("security.ssl3.rsa_des_ede3_sha", f
 pref("security.content.signature.root_hash",
      "97:E8:BA:9C:F1:2F:B3:DE:53:CC:42:A4:E6:57:7E:D6:4D:F4:93:C2:47:B4:14:FE:A0:36:81:8D:38:23:56:0E");
 
 pref("security.default_personal_cert",   "Ask Every Time");
 pref("security.remember_cert_checkbox_default_setting", true);
 pref("security.ask_for_password",        0);
 pref("security.password_lifetime",       30);
 
+// If true, use the modern sqlite-backed certificate and key databases in NSS.
+// If false, use the default format. Currently the default in NSS is the old
+// BerkeleyDB format, but this will change in bug 1377940.
+// Changing this requires a restart to take effect.
+// Note that the environment variable MOZPSM_NSSDBDIR_OVERRIDE can override both
+// the behavior of this preference and the NSS default.
+pref("security.use_sqldb", false);
+
 // The supported values of this pref are:
 // 0: disable detecting Family Safety mode and importing the root
 // 1: only attempt to detect Family Safety mode (don't import the root)
 // 2: detect Family Safety mode and import the root
 // (This is only relevant to Windows 8.1)
 pref("security.family_safety.mode", 2);
 
 pref("security.enterprise_roots.enabled", false);
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_db_format_pref_new.js
@@ -0,0 +1,23 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// 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/.
+"use strict";
+
+// Tests that if "security.use_sqldb" is set to true when PSM initializes,
+// we create the sqlite-backed certificate and key databases.
+
+function run_test() {
+  let profileDir = do_get_profile();
+  Services.prefs.setBoolPref("security.use_sqldb", true);
+  let certificateDBFile = profileDir.clone();
+  certificateDBFile.append("cert9.db");
+  ok(!certificateDBFile.exists(), "cert9.db should not exist beforehand");
+  let keyDBFile = profileDir.clone();
+  keyDBFile.append("key4.db");
+  ok(!keyDBFile.exists(), "key4.db should not exist beforehand");
+  // This should start PSM.
+  Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+  ok(certificateDBFile.exists(), "cert9.db should exist in the profile");
+  ok(keyDBFile.exists(), "key4.db should exist in the profile");
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_db_format_pref_old.js
@@ -0,0 +1,24 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// 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/.
+"use strict";
+
+// Tests that if "security.use_sqldb" is set to false when PSM initializes,
+// we create the system-default certificate and key databases, which currently
+// use the old BerkeleyDB format. This will change in bug 1377940.
+
+function run_test() {
+  let profileDir = do_get_profile();
+  Services.prefs.setBoolPref("security.use_sqldb", false);
+  let certificateDBFile = profileDir.clone();
+  certificateDBFile.append("cert8.db");
+  ok(!certificateDBFile.exists(), "cert8.db should not exist beforehand");
+  let keyDBFile = profileDir.clone();
+  keyDBFile.append("key3.db");
+  ok(!keyDBFile.exists(), "key3.db should not exist beforehand");
+  // This should start PSM.
+  Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+  ok(certificateDBFile.exists(), "cert8.db should exist in the profile");
+  ok(keyDBFile.exists(), "key3.db should exist in the profile");
+}
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -68,16 +68,22 @@ run-sequentially = hardcoded ports
 skip-if = toolkit == 'android'
 [test_constructX509FromBase64.js]
 [test_content_signing.js]
 [test_ct.js]
 # Requires hard-coded debug-only data
 skip-if = !debug
 run-sequentially = hardcoded ports
 [test_datasignatureverifier.js]
+# Android always has and always will use the new format, so
+# these two tests don't apply.
+[test_db_format_pref_new.js]
+skip-if = toolkit == 'android'
+[test_db_format_pref_old.js]
+skip-if = toolkit == 'android'
 [test_der.js]
 [test_enterprise_roots.js]
 skip-if = os != 'win' # tests a Windows-specific feature
 [test_ev_certs.js]
 tags = blocklist
 run-sequentially = hardcoded ports
 [test_forget_about_site_security_headers.js]
 skip-if = toolkit == 'android'