Bug 1562671 - Add environment variables to control Master Password KDF iteration count. Disable iteration count for legacy DBM storage by default. r=rrelyea
authorKai Engert <kaie@kuix.de>
Fri, 01 Nov 2019 10:46:52 +0100
changeset 15367 ced91a705aa399e63e2084c608a31faf947a06a4
parent 15366 6619bb43d746307df1db57bdd5bbaa17aaec6337
child 15368 c8b490583b86a752b585fd11c20fcea05a8a7878
push id3561
push userkaie@kuix.de
push dateFri, 01 Nov 2019 09:57:47 +0000
reviewersrrelyea
bugs1562671
Bug 1562671 - Add environment variables to control Master Password KDF iteration count. Disable iteration count for legacy DBM storage by default. r=rrelyea
lib/softoken/lgglue.c
lib/softoken/sftkdb.c
lib/softoken/sftkdb.h
lib/softoken/sftkdbti.h
lib/softoken/sftkpwd.c
--- a/lib/softoken/lgglue.c
+++ b/lib/softoken/lgglue.c
@@ -189,22 +189,25 @@ sftkdb_encrypt_stub(PLArenaPool *arena, 
     }
 
     PZ_Lock(handle->passwordLock);
     if (handle->passwordKey.data == NULL) {
         PZ_Unlock(handle->passwordLock);
         /* PORT_SetError */
         return SECFailure;
     }
-    if (handle->newKey) {
-        key = handle->newKey;
-        iterationCount = handle->newDefaultIterationCount;
+    key = handle->newKey ? handle->newKey : &handle->passwordKey;
+    if (sftk_isLegacyIterationCountAllowed()) {
+        if (handle->newKey) {
+            iterationCount = handle->newDefaultIterationCount;
+        } else {
+            iterationCount = handle->defaultIterationCount;
+        }
     } else {
-        key = &handle->passwordKey;
-        iterationCount = handle->defaultIterationCount;
+        iterationCount = 1;
     }
 
     rv = sftkdb_EncryptAttribute(arena, key, iterationCount,
                                  plainText, cipherText);
     PZ_Unlock(handle->passwordLock);
 
     return rv;
 }
--- a/lib/softoken/sftkdb.c
+++ b/lib/softoken/sftkdb.c
@@ -2446,28 +2446,29 @@ sftk_getDBForTokenObject(SFTKSlot *slot,
     PZ_Unlock(slot->slotLock);
     return dbHandle;
 }
 
 /*
  * initialize a new database handle
  */
 static SFTKDBHandle *
-sftk_NewDBHandle(SDB *sdb, int type)
+sftk_NewDBHandle(SDB *sdb, int type, PRBool legacy)
 {
     SFTKDBHandle *handle = PORT_New(SFTKDBHandle);
     handle->ref = 1;
     handle->db = sdb;
     handle->update = NULL;
     handle->peerDB = NULL;
     handle->newKey = NULL;
     handle->oldKey = NULL;
     handle->updatePasswordKey = NULL;
     handle->updateID = NULL;
     handle->type = type;
+    handle->usesLegacyStorage = legacy;
     handle->passwordKey.data = NULL;
     handle->passwordKey.len = 0;
     handle->passwordLock = NULL;
     if (type == SFTK_KEYDB_TYPE) {
         handle->passwordLock = PZ_NewLock(nssILockAttribute);
     }
     sdb->app_private = handle;
     return handle;
@@ -2617,16 +2618,17 @@ sftk_DBInit(const char *configdir, const
     NSSDBType dbType = NSS_DB_TYPE_NONE;
     char *appName = NULL;
     SDB *keySDB, *certSDB;
     CK_RV crv = CKR_OK;
     int flags = SDB_RDONLY;
     PRBool newInit = PR_FALSE;
     PRBool needUpdate = PR_FALSE;
     char *nconfdir = NULL;
+    PRBool legacy = PR_TRUE;
 
     if (!readOnly) {
         flags = SDB_CREATE;
     }
     if (isFIPS) {
         flags |= SDB_FIPS;
     }
 
@@ -2649,22 +2651,24 @@ sftk_DBInit(const char *configdir, const
         case NSS_DB_TYPE_MULTIACCESS:
             crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags,
                                   noCertDB ? NULL : &certSDB, noKeyDB ? NULL : &keySDB);
             break;
         case NSS_DB_TYPE_SQL:
         case NSS_DB_TYPE_EXTERN: /* SHOULD open a loadable db */
             crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags,
                          noCertDB ? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit);
+            legacy = PR_FALSE;
 
             /*
              * if we failed to open the DB's read only, use the old ones if
              * the exists.
              */
             if (crv != CKR_OK) {
+                legacy = PR_TRUE;
                 if ((flags & SDB_RDONLY) == SDB_RDONLY) {
                     nconfdir = sftk_legacyPathFromSDBPath(confdir);
                 }
                 if (nconfdir &&
                     sftk_hasLegacyDB(nconfdir, certPrefix, keyPrefix, 8, 3)) {
                     /* we have legacy databases, if we failed to open the new format
                      * DB's read only, just use the legacy ones */
                     crv = sftkdbCall_open(nconfdir, certPrefix,
@@ -2708,22 +2712,22 @@ sftk_DBInit(const char *configdir, const
             crv = CKR_GENERAL_ERROR; /* can't happen, EvaluationConfigDir MUST
                                       * return one of the types we already
                                       * specified. */
     }
     if (crv != CKR_OK) {
         goto done;
     }
     if (!noCertDB) {
-        *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE);
+        *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE, legacy);
     } else {
         *certDB = NULL;
     }
     if (!noKeyDB) {
-        *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE);
+        *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE, legacy);
     } else {
         *keyDB = NULL;
     }
 
     /* link them together */
     if (*certDB) {
         (*certDB)->peerDB = *keyDB;
     }
--- a/lib/softoken/sftkdb.h
+++ b/lib/softoken/sftkdb.h
@@ -65,8 +65,10 @@ CK_RV sftk_DBInit(const char *configdir,
                   SFTKDBHandle **certDB, SFTKDBHandle **keyDB);
 CK_RV sftkdb_Shutdown(void);
 
 SFTKDBHandle *sftk_getCertDB(SFTKSlot *slot);
 SFTKDBHandle *sftk_getKeyDB(SFTKSlot *slot);
 SFTKDBHandle *sftk_getDBForTokenObject(SFTKSlot *slot,
                                        CK_OBJECT_HANDLE objectID);
 void sftk_freeDB(SFTKDBHandle *certHandle);
+
+PRBool sftk_isLegacyIterationCountAllowed(void);
--- a/lib/softoken/sftkdbti.h
+++ b/lib/softoken/sftkdbti.h
@@ -18,16 +18,17 @@ struct SFTKDBHandleStr {
     int newDefaultIterationCount;
     SECItem *oldKey;
     SECItem *updatePasswordKey;
     PZLock *passwordLock;
     SFTKDBHandle *peerDB;
     SDB *update;
     char *updateID;
     PRBool updateDBIsInit;
+    PRBool usesLegacyStorage;
 };
 
 #define SFTK_KEYDB_TYPE 0x40000000
 #define SFTK_CERTDB_TYPE 0x00000000
 #define SFTK_OBJ_TYPE_MASK 0xc0000000
 #define SFTK_OBJ_ID_MASK (~SFTK_OBJ_TYPE_MASK)
 #define SFTK_TOKEN_TYPE 0x80000000
 
--- a/lib/softoken/sftkpwd.c
+++ b/lib/softoken/sftkpwd.c
@@ -29,16 +29,51 @@
 #include "secoid.h"
 #include "lowpbe.h"
 #include "secdert.h"
 #include "prsystem.h"
 #include "lgglue.h"
 #include "secerr.h"
 #include "softoken.h"
 
+static const int NSS_MP_PBE_ITERATION_COUNT = 10000;
+
+static int
+getPBEIterationCount(void)
+{
+    int c = NSS_MP_PBE_ITERATION_COUNT;
+
+    char *val = getenv("NSS_MIN_MP_PBE_ITERATION_COUNT");
+    if (val) {
+        int minimum = atoi(val);
+        if (c < minimum) {
+            c = minimum;
+        }
+    }
+
+    val = getenv("NSS_MAX_MP_PBE_ITERATION_COUNT");
+    if (val) {
+        int maximum = atoi(val);
+        if (c > maximum) {
+            c = maximum;
+        }
+    }
+
+    return c;
+}
+
+PRBool
+sftk_isLegacyIterationCountAllowed(void)
+{
+    static const char *legacyCountEnvVar =
+        "NSS_ALLOW_LEGACY_DBM_ITERATION_COUNT";
+    char *iterEnv = getenv(legacyCountEnvVar);
+    return (iterEnv && strcmp("0", iterEnv) != 0);
+}
+
 /******************************************************************
  *
  * Key DB password handling functions
  *
  * These functions manage the key db password (set, reset, initialize, use).
  *
  * The key is managed on 'this side' of the database. All private data is
  * encrypted before it is sent to the database itself. Besides PBE's, the
@@ -231,16 +266,17 @@ sftkdb_DecryptAttribute(SECItem *passKey
     SECStatus rv;
     sftkCipherValue cipherValue;
 
     /* First get the cipher type */
     rv = sftkdb_decodeCipherText(cipherText, &cipherValue);
     if (rv != SECSuccess) {
         goto loser;
     }
+    /* fprintf(stderr, "sftkdb_DecryptAttribute iteration: %d\n", cipherValue.param->iter); */
 
     *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value,
                                  PR_FALSE, NULL);
     if (*plain == NULL) {
         rv = SECFailure;
         goto loser;
     }
 
@@ -669,17 +705,17 @@ sftkdb_HasPasswordSet(SFTKDBHandle *keyd
 SECStatus
 sftkdb_finishPasswordCheck(SFTKDBHandle *keydb, SECItem *key,
                            const char *pw, SECItem *value,
                            PRBool *tokenRemoved);
 
 /*
  * check to see if we have the NULL password set.
  * We special case the NULL password so that if you have no password set, you
- * don't do thousands of hash rounds. This allows us to startup and get 
+ * don't do thousands of hash rounds. This allows us to startup and get
  * webpages without slowdown in normal mode.
  */
 SECStatus
 sftkdb_CheckPasswordNull(SFTKDBHandle *keydb, PRBool *tokenRemoved)
 {
     /* just like sftkdb_CheckPassowd, we get the salt and value, and
      * create a dbkey */
     SECStatus rv;
@@ -810,21 +846,24 @@ done:
 /* we need to pass iterationCount in case we are updating a new database
  * and from an old one. */
 SECStatus
 sftkdb_finishPasswordCheck(SFTKDBHandle *keydb, SECItem *key, const char *pw,
                            SECItem *value, PRBool *tokenRemoved)
 {
     SECItem *result = NULL;
     SECStatus rv;
-    int iterationCount = NSS_DEFAULT_ITERATION_COUNT;
+    int iterationCount = getPBEIterationCount();
 
     if (*pw == 0) {
         iterationCount = 1;
+    } else if (keydb->usesLegacyStorage && !sftk_isLegacyIterationCountAllowed()) {
+        iterationCount = 1;
     }
+
     /* decrypt the entry value */
     rv = sftkdb_DecryptAttribute(key, value, &result);
     if (rv != SECSuccess) {
         goto done;
     }
 
     /* if it's what we expect, update our key in the database handle and
      * return Success */
@@ -1193,17 +1232,17 @@ sftkdb_ChangePassword(SFTKDBHandle *keyd
     SECStatus rv = SECSuccess;
     SECItem plainText;
     SECItem newKey;
     SECItem *result = NULL;
     SECItem salt, value;
     SFTKDBHandle *certdb;
     unsigned char saltData[SDB_MAX_META_DATA_LEN];
     unsigned char valueData[SDB_MAX_META_DATA_LEN];
-    int iterationCount = NSS_DEFAULT_ITERATION_COUNT;
+    int iterationCount = getPBEIterationCount();
     CK_RV crv;
     SDB *db;
 
     if (keydb == NULL) {
         return SECFailure;
     }
 
     db = SFTK_GET_SDB(keydb);
@@ -1231,16 +1270,18 @@ sftkdb_ChangePassword(SFTKDBHandle *keyd
         }
     } else {
         salt.len = SHA1_LENGTH;
         RNG_GenerateGlobalRandomBytes(salt.data, salt.len);
     }
 
     if (newPin && *newPin == 0) {
         iterationCount = 1;
+    } else if (keydb->usesLegacyStorage && !sftk_isLegacyIterationCountAllowed()) {
+        iterationCount = 1;
     }
 
     rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey);
     if (rv != SECSuccess) {
         goto loser;
     }
 
     /*