bug 783994 - use the sqlite-backed certificate and key DBs r=jcj
authorDavid Keeler <dkeeler@mozilla.com>
Fri, 22 Sep 2017 14:34:20 -0700
changeset 386815 b47f07ff93be090a09dd4b96f304a4d8dc6eb8a1
parent 386814 c035d1bdc3730c662828a123e8bf0bf6fa3b6e2b
child 386816 70ab0eb9afe2bdb59bdad402b91806b5a991033f
push id96311
push userarchaeopteryx@coole-files.de
push dateWed, 18 Oct 2017 09:52:02 +0000
treeherdermozilla-inbound@a8a1e8cc1980 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjcj
bugs783994
milestone58.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 783994 - use the sqlite-backed certificate and key DBs r=jcj MozReview-Commit-ID: 2K8JVGc0mAj
security/manager/ssl/PKCS11ModuleDB.cpp
security/manager/ssl/security-prefs.js
security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
security/manager/ssl/tests/unit/test_sdr_preexisting_with_password/key3.db
security/manager/ssl/tests/unit/xpcshell.ini
--- a/security/manager/ssl/PKCS11ModuleDB.cpp
+++ b/security/manager/ssl/PKCS11ModuleDB.cpp
@@ -109,16 +109,23 @@ PKCS11ModuleDB::AddModule(const nsAStrin
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (aModuleName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  // There appears to be a deadlock if we try to load modules concurrently, so
+  // just wait until the loadable roots module has been loaded.
+  nsresult rv = BlockUntilLoadableRootsLoaded();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   NS_ConvertUTF16toUTF8 moduleName(aModuleName);
   nsCString fullPath;
   // NSS doesn't support Unicode path.  Use native charset
   NS_CopyUnicodeToNative(aLibraryFullPath, fullPath);
   uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
   uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
   SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(),
                                       mechFlags, cipherFlags);
--- a/security/manager/ssl/security-prefs.js
+++ b/security/manager/ssl/security-prefs.js
@@ -39,17 +39,17 @@ pref("security.ask_for_password",       
 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);
+pref("security.use_sqldb", true);
 
 // 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);
 
copy from security/manager/ssl/tests/unit/test_sdr_preexisting.js
copy to security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
--- a/security/manager/ssl/tests/unit/test_sdr_preexisting.js
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
@@ -1,187 +1,68 @@
 // -*- 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 the SDR implementation is able to decrypt strings encrypted using
-// a preexisting NSS key database. Creating the database is straight-forward:
-// simply run Firefox (or xpcshell) and encrypt something using
-// nsISecretDecoderRing (e.g. by saving a password or directly using the
-// interface). The resulting key3.db file (in the profile directory) now
-// contains the private key used to encrypt the data.
-// "Upgrading" a key3.db with certutil to use on Android appears not to work.
-// Because the keys have to be the same for this test to work the way it does,
-// the key from key3.db must be extracted and added to a new key4.db. This can
-// be done with NSS' PK11_* APIs like so (although note that the following code
-// is not guaranteed to compile or work, but is more of a guideline for how to
-// do this in the future if necessary):
-//
-// #include <stdio.h>
-//
-// #include "nss.h"
-// #include "pk11pub.h"
-// #include "prerror.h"
-// #include "secerr.h"
-//
-// void printPRError(const char* message) {
-//   fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
-// }
-//
-// int main(int argc, char* argv[]) {
-//   if (NSS_Initialize(".", "", "", "", NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT)
-//         != SECSuccess) {
-//     printPRError("NSS_Initialize failed");
-//     return 1;
-//   }
-//
-//   PK11SlotInfo* slot = PK11_GetInternalKeySlot();
-//   if (!slot) {
-//     printPRError("PK11_GetInternalKeySlot failed");
-//     return 1;
-//   }
-//
-//   // Create a key to wrap the SDR key to export it.
-//   unsigned char wrappingKeyIDBytes[] = { 0 };
-//   SECItem wrappingKeyID = {
-//     siBuffer,
-//     wrappingKeyIDBytes,
-//     sizeof(wrappingKeyIDBytes)
-//   };
-//   PK11SymKey* wrappingKey = PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0,
-//                                              &wrappingKeyID, PR_FALSE, NULL);
-//   if (!wrappingKey) {
-//     printPRError("PK11_TokenKeyGen failed");
-//     return 1;
-//   }
-//
-//   // This is the magic identifier NSS uses for the SDR key.
-//   unsigned char sdrKeyIDBytes[] = {
-//     0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-//     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
-//   };
-//   SECItem sdrKeyID = { siBuffer, sdrKeyIDBytes, sizeof(sdrKeyIDBytes) };
-//   PK11SymKey* sdrKey = PK11_FindFixedKey(slot, CKM_DES3_CBC, &sdrKeyID,
-//                                          NULL);
-//   if (!sdrKey) {
-//     printPRError("PK11_FindFixedKey failed");
-//     return 1;
-//   }
-//
-//   // Wrap the SDR key.
-//   unsigned char wrappedKeyBuf[1024];
-//   SECItem wrapped = { siBuffer, wrappedKeyBuf, sizeof(wrappedKeyBuf) };
-//   if (PK11_WrapSymKey(CKM_DES3_ECB, NULL, wrappingKey, sdrKey, &wrapped)
-//         != SECSuccess) {
-//     printPRError("PK11_WrapSymKey failed");
-//     return 1;
-//   }
-//
-//   // Unwrap the SDR key (NSS considers the SDR key "sensitive" and so won't
-//   // just export it as raw key material - we have to export it and then
-//   // re-import it as non-sensitive to get that data.
-//   PK11SymKey* unwrapped = PK11_UnwrapSymKey(wrappingKey, CKM_DES3_ECB, NULL,
-//                                             &wrapped, CKM_DES3_CBC,
-//                                             CKA_ENCRYPT, 0);
-//   if (!unwrapped) {
-//     printPRError("PK11_UnwrapSymKey failed");
-//     return 1;
-//   }
-//   if (PK11_ExtractKeyValue(unwrapped) != SECSuccess) {
-//     printPRError("PK11_ExtractKeyValue failed");
-//     return 1;
-//   }
-//   SECItem* keyData = PK11_GetKeyData(unwrapped);
-//   if (!keyData) {
-//     printPRError("PK11_GetKeyData failed");
-//     return 1;
-//   }
-//   for (int i = 0; i < keyData->len; i++) {
-//     printf("0x%02hhx, ", keyData->data[i]);
-//   }
-//   printf("\n");
-//
-//   PK11_FreeSymKey(unwrapped);
-//   PK11_FreeSymKey(sdrKey);
-//   PK11_FreeSymKey(wrappingKey);
-//   PK11_FreeSlot(slot);
-//
-//   if (NSS_Shutdown() != SECSuccess) {
-//     printPRError("NSS_Shutdown failed");
-//     return 1;
-//   }
-//   return 0;
-// }
-//
-// The output of compiling and running the above should be the bytes of the SDR
-// key. Given that, create a key4.db with an empty password using
-// `certutil -N -d sql:.` and then compile and run the following:
-//
-// #include <stdio.h>
-//
-// #include "nss.h"
-// #include "pk11pub.h"
-// #include "prerror.h"
-// #include "secerr.h"
-// #include "secmod.h"
-//
-// void printPRError(const char* message) {
-//   fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
-// }
-//
-// int main(int argc, char* argv[]) {
-//   if (NSS_Initialize("sql:.", "", "", "",
-//                      NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT) != SECSuccess) {
-//     printPRError("NSS_Initialize failed");
-//     return 1;
-//   }
-//
-//   PK11SlotInfo* slot = PK11_GetInternalKeySlot();
-//   if (!slot) {
-//     printPRError("PK11_GetInternalKeySlot failed");
-//     return 1;
-//   }
-//
-//   // These are the bytes of the SDR key from the previous step:
-//   unsigned char keyBytes[] = {
-//     0x70, 0xab, 0xea, 0x1f, 0x8f, 0xe3, 0x4a, 0x7a, 0xb5, 0xb0, 0x43, 0xe5,
-//     0x51, 0x83, 0x86, 0xe5, 0xb3, 0x43, 0xa8, 0x1f, 0xc1, 0x57, 0x86, 0x46
-//   };
-//   SECItem keyItem = { siBuffer, keyBytes, sizeof(keyBytes) };
-//   PK11SymKey* key = PK11_ImportSymKey(slot, CKM_DES3_CBC, PK11_OriginUnwrap,
-//                                       CKA_ENCRYPT, &keyItem, NULL);
-//   if (!key) {
-//     printPRError("PK11_ImportSymKey failed");
-//     return 1;
-//   }
-//
-//   PK11_FreeSymKey(key);
-//   PK11_FreeSlot(slot);
-//
-//   if (NSS_Shutdown() != SECSuccess) {
-//     printPRError("NSS_Shutdown failed");
-//     return 1;
-//   }
-//   return 0;
-// }
-//
-// This should create a key4.db file with the SDR key. (Note however that this
-// does not set the magic key ID for the SDR key. Currently this is not a
-// problem because the NSS implementation that backs the SDR simply tries all
-// applicable keys it has when decrypting, so this still works.)
+// a preexisting NSS key database that a) has a password and b) is in the old
+// dbm format.
+// To create such a database, run a version Firefox (or xpcshell) where the
+// default database format is the old dbm format (i.e. pre-bug 783994), set a
+// master password, and then encrypt something using nsISecretDecoderRing.
+// This does not apply to Android as the dbm implementation was never enabled on
+// that platform.
+
+var gMockPrompter = {
+  passwordToTry: "password",
+  numPrompts: 0,
+
+  // This intentionally does not use arrow function syntax to avoid an issue
+  // where in the context of the arrow function, |this != gMockPrompter| due to
+  // how objects get wrapped when going across xpcom boundaries.
+  promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+    this.numPrompts++;
+    if (this.numPrompts > 1) { // don't keep retrying a bad password
+      return false;
+    }
+    equal(text,
+          "Please enter your master password.",
+          "password prompt text should be as expected");
+    equal(checkMsg, null, "checkMsg should be null");
+    ok(this.passwordToTry, "passwordToTry should be non-null");
+    password.value = this.passwordToTry;
+    return true;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+var gWindowWatcher = {
+  getNewPrompter: () => gMockPrompter,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher]),
+};
 
 function run_test() {
-  const isAndroid = AppConstants.platform == "android";
-  const keyDBName = isAndroid ? "key4.db" : "key3.db";
+  let windowWatcherCID =
+    MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
+                           gWindowWatcher);
+  do_register_cleanup(() => {
+    MockRegistrar.unregister(windowWatcherCID);
+  });
+
+  Services.prefs.setBoolPref("security.use_sqldb", true);
+
   let profile = do_get_profile();
-  let keyDBFile = do_get_file(`test_sdr_preexisting/${keyDBName}`);
-  keyDBFile.copyTo(profile, keyDBName);
+  let keyDBFile = do_get_file("test_sdr_preexisting_with_password/key3.db");
+  keyDBFile.copyTo(profile, "key3.db");
 
   let sdr = Cc["@mozilla.org/security/sdr;1"]
               .getService(Ci.nsISecretDecoderRing);
 
   let testcases = [
     // a full padding block
     { ciphertext: "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d",
       plaintext: "password" },
@@ -206,9 +87,11 @@ function run_test() {
       plaintext: "nnLbuwLRkhlongerandsotakesupmultipleblocks" },
   ];
 
   for (let testcase of testcases) {
     let decrypted = sdr.decryptString(testcase.ciphertext);
     equal(decrypted, testcase.plaintext,
           "decrypted ciphertext should match expected plaintext");
   }
+  equal(gMockPrompter.numPrompts, 1,
+        "Should have been prompted for a password once");
 }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cac0808ac32c35752b08d886eef4a3d362e56acd
GIT binary patch
literal 16384
zc%1Fn&r8!`90%~v^ZkAsjxC;{3lwj|!`d*up@JQBOv=&_Y72uXXsP*YY_bU(N~E9`
zu_0({6tN%~9)=bbtq~nc^dLbtgw&JZNxSS)Zt$(S+z$2!=n&p7c=LI~b9kQX^C8tK
z2N4NGQj$oQtE44jln<2@icpy1{@gzz>0b}I?rl+?P{@%hVa2!=00000007|lP*H}b
z`3`^2V?4~qbIAe#000000Kn0hotAtJ7j=e)wV4`M?8;b^W_6|8>AvG|R(l=JK~c}n
z-5B|+9c@kxDU-p4rAVW4?XitCrin`vyC<3U1QRV!%hu=o_GsrLtNiV<Wq82h_4?eN
z&gzbyu8w}Oz02e6b-M&g@mt)UOR+Z*000000002Tlt_%^(SU4J9sHQf6_Vb5Hn}~#
zZt8o!ee+}H)v{b#)s_($lWzCcD<dtOCEms^)c^Jet*yGo2UE$V`BZD<s(JFRrXsFC
z6ZqP5-?*`8_~bu-J^IG@<Hc*WIuv~7QZ%RIKPy8|BFe^Rv#;)#vDVhVS(<43Q2sru
efB5Cy%YnIY`u`WQKgHg3000000002Mq1yvX%-vf6
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -25,16 +25,17 @@ support-files =
   test_keysize_ev/**
   test_missing_intermediate/**
   test_name_constraints/**
   test_ocsp_fetch_method/**
   test_ocsp_url/**
   test_onecrl/**
   test_pinning_dynamic/**
   test_sdr_preexisting/**
+  test_sdr_preexisting_with_password/**
   test_signed_apps/**
   test_signed_dir/**
   test_startcom_wosign/**
   test_validity/**
   tlsserver/**
 
 [test_add_preexisting_cert.js]
 [test_baseline_requirements_subject_common_name.js]
@@ -137,16 +138,19 @@ run-sequentially = hardcoded ports
 run-sequentially = hardcoded ports
 # This test can take longer than 300 seconds on B2G emulator debug builds, so
 # give it enough time to finish. See bug 1081128.
 requesttimeoutfactor = 2
 [test_pinning_dynamic.js]
 [test_pinning_header_parsing.js]
 [test_sdr.js]
 [test_sdr_preexisting.js]
+[test_sdr_preexisting_with_password.js]
+# Not relevant to Android. See the comment in the test.
+skip-if = toolkit == 'android'
 [test_session_resumption.js]
 run-sequentially = hardcoded ports
 [test_signed_apps.js]
 [test_signed_dir.js]
 tags = addons psm
 [test_sss_enumerate.js]
 [test_sss_eviction.js]
 [test_sss_originAttributes.js]