Bug 316084 - Migrated base64 suite passwords not encrypted when master PW added in Firefox; r=(dolske + gavin.sharp)

This commit is contained in:
Paul O'Shannessy 2009-03-28 03:00:17 +01:00
Родитель c6ae4ba1ac
Коммит 5a54b08217
6 изменённых файлов: 305 добавлений и 13 удалений

Просмотреть файл

@ -41,7 +41,10 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const DB_VERSION = 2; // The database schema version
const DB_VERSION = 3; // The database schema version
const ENCTYPE_BASE64 = 0;
const ENCTYPE_SDR = 1;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
@ -118,7 +121,7 @@ LoginManagerStorage_mozStorage.prototype = {
},
// The current database schema
// The current database schema.
_dbSchema: {
tables: {
moz_logins: "id INTEGER PRIMARY KEY," +
@ -129,8 +132,9 @@ LoginManagerStorage_mozStorage.prototype = {
"passwordField TEXT NOT NULL," +
"encryptedUsername TEXT NOT NULL," +
"encryptedPassword TEXT NOT NULL," +
"guid TEXT",
"guid TEXT," +
"encType INTEGER",
// Changes must be reflected in this._dbAreExpectedColumnsPresent
moz_disabledHosts: "id INTEGER PRIMARY KEY," +
"hostname TEXT UNIQUE ON CONFLICT REPLACE",
},
@ -150,6 +154,10 @@ LoginManagerStorage_mozStorage.prototype = {
moz_logins_guid_index: {
table: "moz_logins",
columns: ["guid"]
},
moz_logins_encType_index: {
table: "moz_logins",
columns: ["encType"]
}
}
},
@ -160,6 +168,7 @@ LoginManagerStorage_mozStorage.prototype = {
_signonsFile : null, // nsIFile for "signons.sqlite"
_importFile : null, // nsIFile for import from legacy
_debug : false, // mirrors signon.debug
_base64checked : false,
/*
@ -292,14 +301,20 @@ LoginManagerStorage_mozStorage.prototype = {
loginClone.guid = this._uuidService.generateUUID().toString();
}
// Determine encryption type
let encType = ENCTYPE_SDR;
if (isEncrypted &&
(encUsername.charAt(0) == '~' || encPassword.charAt(0) == '~'))
encType = ENCTYPE_BASE64;
let query =
"INSERT INTO moz_logins " +
"(hostname, httpRealm, formSubmitURL, usernameField, " +
"passwordField, encryptedUsername, encryptedPassword, " +
"guid) " +
"guid, encType) " +
"VALUES (:hostname, :httpRealm, :formSubmitURL, :usernameField, " +
":passwordField, :encryptedUsername, :encryptedPassword, " +
":guid)";
":guid, :encType)";
let params = {
hostname: loginClone.hostname,
@ -309,7 +324,8 @@ LoginManagerStorage_mozStorage.prototype = {
passwordField: loginClone.passwordField,
encryptedUsername: encUsername,
encryptedPassword: encPassword,
guid: loginClone.guid
guid: loginClone.guid,
encType: encType
};
let stmt;
@ -429,7 +445,8 @@ LoginManagerStorage_mozStorage.prototype = {
"passwordField = :passwordField, " +
"encryptedUsername = :encryptedUsername, " +
"encryptedPassword = :encryptedPassword, " +
"guid = :guid " +
"guid = :guid, " +
"encType = :encType " +
"WHERE id = :id";
let params = {
@ -441,7 +458,8 @@ LoginManagerStorage_mozStorage.prototype = {
passwordField: newLogin.passwordField,
encryptedUsername: encUsername,
encryptedPassword: encPassword,
guid: newLogin.guid
guid: newLogin.guid,
encType: ENCTYPE_SDR
};
let stmt;
@ -697,13 +715,18 @@ LoginManagerStorage_mozStorage.prototype = {
* is an array of encrypted nsLoginInfo and ids is an array of associated
* ids in the database.
*/
_queryLogins : function (hostname, formSubmitURL, httpRealm) {
_queryLogins : function (hostname, formSubmitURL, httpRealm, encType) {
let logins = [], ids = [];
let query = "SELECT * FROM moz_logins";
let [conditions, params] =
this._buildConditionsAndParams(hostname, formSubmitURL, httpRealm);
if (typeof encType != "undefined") {
conditions.push("encType = :encType");
params.encType = encType;
}
if (conditions.length) {
conditions = conditions.map(function(c) "(" + c + ")");
query += " WHERE " + conditions.join(" AND ");
@ -975,6 +998,9 @@ LoginManagerStorage_mozStorage.prototype = {
if (userCanceled)
return [null, null, true];
if (!this._base64checked)
this._reencryptBase64Logins();
return [encUsername, encPassword, false];
},
@ -1020,10 +1046,46 @@ LoginManagerStorage_mozStorage.prototype = {
result.push(login);
}
if (!this._base64checked && !userCanceled)
this._reencryptBase64Logins();
return [result, userCanceled];
},
/*
* _reencryptBase64Logins
*
* Checks the signons DB for any logins using the old wallet-style base64
* obscuring of the username/password, instead of proper encryption. We're
* called once per session, after the user has successfully encrypted or
* decrypted some login (this helps ensure the user doesn't get mysterious
* prompts for a master password, when set).
*/
_reencryptBase64Logins : function () {
this._base64checked = true;
// Ignore failures, will try again next session...
try {
let [logins, ids] =
this._queryLogins("", "", "", 0);
if (!logins.length)
return;
let userCancelled;
[logins, userCanceled] = this._decryptLogins(logins);
if (userCanceled)
return;
for each (let login in logins)
this.modifyLogin(login, login);
} catch (e) {
this.log("_reencryptBase64Logins caught error: " + e);
}
},
/*
* _encrypt
*
@ -1303,6 +1365,73 @@ LoginManagerStorage_mozStorage.prototype = {
},
/*
* _dbMigrateToVersion3
*
* Version 3 adds a encType column.
*/
_dbMigrateToVersion3 : function () {
// Check to see if encType column already exists.
let exists = true;
let query = "SELECT encType FROM moz_logins";
let stmt;
try {
stmt = this._dbConnection.createStatement(query);
// (no need to execute statement, if it compiled we're good)
stmt.finalize();
} catch (e) {
exists = false;
}
// Add the new column and index only if needed.
if (!exists) {
query = "ALTER TABLE moz_logins ADD COLUMN encType INTEGER";
this._dbConnection.executeSimpleSQL(query);
query = "CREATE INDEX IF NOT EXISTS " +
"moz_logins_encType_index ON moz_logins (encType)";
this._dbConnection.executeSimpleSQL(query);
}
// Get a list of existing logins
let logins = [];
query = "SELECT id, encryptedUsername, encryptedPassword " +
"FROM moz_logins WHERE encType isnull";
try {
stmt = this._dbCreateStatement(query);
while (stmt.step()) {
let params = { id: stmt.row.id };
if (stmt.row.encryptedUsername.charAt(0) == '~' ||
stmt.row.encryptedPassword.charAt(0) == '~')
params.encType = ENCTYPE_BASE64;
else
params.encType = ENCTYPE_SDR;
logins.push(params);
}
} catch (e) {
this.log("Failed getting logins: " + e);
throw e;
} finally {
stmt.reset();
}
// Determine encryption type for each login and update the DB.
query = "UPDATE moz_logins SET encType = :encType WHERE id = :id";
for each (params in logins) {
try {
stmt = this._dbCreateStatement(query, params);
stmt.execute();
} catch (e) {
this.log("Failed setting encType: " + e);
throw e;
} finally {
stmt.reset();
}
}
},
/*
* _dbAreExpectedColumnsPresent
*
@ -1319,7 +1448,8 @@ LoginManagerStorage_mozStorage.prototype = {
"passwordField, " +
"encryptedUsername, " +
"encryptedPassword, " +
"guid " +
"guid, " +
"encType " +
"FROM moz_logins";
try {
let stmt = this._dbConnection.createStatement(query);

Двоичные данные
toolkit/components/passwordmgr/test/unit/data/signons-v2.sqlite Normal file

Двоичный файл не отображается.

Двоичные данные
toolkit/components/passwordmgr/test/unit/data/signons-v2v3.sqlite Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -8,6 +8,8 @@
const STORAGE_TYPE = "mozStorage";
const ENCTYPE_BASE64 = 0;
const ENCTYPE_SDR = 1;
function run_test() {
@ -15,6 +17,15 @@ try {
var storage, testnum = 0;
function countBase64Logins(conn) {
let stmt = conn.createStatement("SELECT COUNT(1) as numBase64 FROM moz_logins " +
"WHERE encType = " + ENCTYPE_BASE64);
do_check_true(stmt.step());
let numBase64 = stmt.row.numBase64;
stmt.finalize();
return numBase64;
}
/* ========== 1 ========== */
testnum++;
@ -26,6 +37,8 @@ var dummyuser2 = Cc["@mozilla.org/login-manager/loginInfo;1"].
createInstance(Ci.nsILoginInfo);
var dummyuser3 = Cc["@mozilla.org/login-manager/loginInfo;1"].
createInstance(Ci.nsILoginInfo);
var dummyuser4 = Cc["@mozilla.org/login-manager/loginInfo;1"].
createInstance(Ci.nsILoginInfo);
dummyuser1.init("http://dummyhost.mozilla.org", "", null,
"testuser1", "testpass1", "put_user_here", "put_pw_here");
@ -36,6 +49,10 @@ dummyuser2.init("http://dummyhost2.mozilla.org", "", null,
dummyuser3.init("http://dummyhost2.mozilla.org", "", null,
"testuser3", "testpass3", "put_user3_here", "put_pw3_here");
dummyuser4.init("http://dummyhost4.mozilla.org", "", null,
"testuser4", "testpass4", "put_user4_here", "put_pw4_here");
LoginTest.deleteFile(OUTDIR, "signons.sqlite");
@ -560,6 +577,77 @@ testdesc = "[flush and reload for verification]"
storage = LoginTest.reloadStorage(OUTDIR, "output-451155.sqlite");
LoginTest.checkStorageData(storage, [utfHost], [utfUser1, utfUser2]);
LoginTest.deleteFile(OUTDIR, "output-451155.sqlite");
/*
* ---------------------- Bug 316984 ----------------------
* Ensure that base64 logins are reencrypted upon call to
* getAllLogins
*/
/* ========== 15 ========== */
testnum++;
testdesc = "ensure base64 logins are reencrypted on first call to getAllLogins"
// signons-380961-3.txt contains 2 base64 logins & 1 normal
storage = LoginTest.initStorage(INDIR, "signons-380961-3.txt",
OUTDIR, "output-316984-1.sqlite");
// Check that we do have 2 base64 logins here.
let dbConnection = LoginTest.openDB("output-316984-1.sqlite");
do_check_eq(countBase64Logins(dbConnection), 2);
// This makes a call to getAllLogins which should reencrypt
LoginTest.checkStorageData(storage, [], [dummyuser1, dummyuser2, dummyuser3]);
// Check that there are 0 base64 logins remaining
do_check_eq(countBase64Logins(dbConnection), 0);
LoginTest.deleteFile(OUTDIR, "output-316984-1.sqlite");
/* ========== 16 ========== */
testnum++;
testdesc = "ensure base64 logins are reencrypted when first new login is added"
// signons-380961-3.txt contains 2 base64 logins & 1 normal
storage = LoginTest.initStorage(INDIR, "signons-380961-3.txt",
OUTDIR, "output-316984-2.sqlite");
// Check that we do have 2 base64 logins here.
dbConnection = LoginTest.openDB("output-316984-2.sqlite");
do_check_eq(countBase64Logins(dbConnection), 2);
// Adding a new user should reencrypt the first time
storage.addLogin(dummyuser4)
// Check that there are 0 base64 logins remaining
do_check_eq(countBase64Logins(dbConnection), 0);
LoginTest.deleteFile(OUTDIR, "output-316984-2.sqlite");
/* ========== 17 ========== */
testnum++;
testdesc = "ensure base64 logins are NOT reencrypted on call to countLogins"
// signons-380961-3.txt contains 2 base64 logins & 1 normal
storage = LoginTest.initStorage(INDIR, "signons-380961-3.txt",
OUTDIR, "output-316984-3.sqlite");
// Check that we do have 2 base64 logins here.
dbConnection = LoginTest.openDB("output-316984-3.sqlite");
do_check_eq(countBase64Logins(dbConnection), 2);
// countLogins should NOT reencrypt logins
storage.countLogins("", "", "")
// Check that there are still 2 base64 logins here
do_check_eq(countBase64Logins(dbConnection), 2);
LoginTest.deleteFile(OUTDIR, "output-316984-3.sqlite");
/* ========== end ========== */

Просмотреть файл

@ -9,10 +9,12 @@
const STORAGE_TYPE = "mozStorage";
const ENCTYPE_BASE64 = 0;
const ENCTYPE_SDR = 1;
// Current schema version used by storage-mozStorage.js. This will need to be
// kept in sync with the version there (or else the tests fail).
const CURRENT_SCHEMA = 2;
const CURRENT_SCHEMA = 3;
function run_test() {
@ -27,6 +29,14 @@ function getGUIDforID(conn, id) {
return guid;
}
function getEncTypeForID(conn, id) {
var stmt = conn.createStatement("SELECT encType from moz_logins WHERE id = " + id);
stmt.executeStep();
var encType = stmt.row.encType;
stmt.finalize();
return encType;
}
var storage;
var dbConnection;
var testnum = 0;
@ -48,6 +58,13 @@ testuser2.init("http://test.org", "http://test.org", null,
var testuser3 = new nsLoginInfo;
testuser3.init("http://test.gov", "http://test.gov", null,
"testuser3", "testpass3", "u3", "p3");
var testuser4 = new nsLoginInfo;
testuser4.init("http://test.gov", "http://test.gov", null,
"testuser1", "testpass2", "u4", "p4");
var testuser5 = new nsLoginInfo;
testuser5.init("http://test.gov", "http://test.gov", null,
"testuser2", "testpass1", "u5", "p5");
/* ========== 1 ========== */
testnum++;
@ -99,7 +116,7 @@ LoginTest.deleteFile(OUTDIR, "signons-v999-2.sqlite.corrupt");
/* ========== 3 ========== */
testnum++;
testdesc = "Test upgrade from v1 storage"
testdesc = "Test upgrade from v1->v2 storage"
LoginTest.copyFile("signons-v1.sqlite");
// Sanity check the test file.
@ -156,6 +173,63 @@ dbConnection.close();
LoginTest.deleteFile(OUTDIR, "signons-v1v2.sqlite");
/* ========== 5 ========== */
testnum++;
testdesc = "Test upgrade from v2->v3 storage"
LoginTest.copyFile("signons-v2.sqlite");
// Sanity check the test file.
dbConnection = LoginTest.openDB("signons-v2.sqlite");
do_check_eq(2, dbConnection.schemaVersion);
storage = LoginTest.reloadStorage(OUTDIR, "signons-v2.sqlite");
// Check to see that we added the correct encType to the logins.
do_check_eq(CURRENT_SCHEMA, dbConnection.schemaVersion);
let encTypes = [ENCTYPE_BASE64, ENCTYPE_SDR, ENCTYPE_BASE64, ENCTYPE_BASE64];
for (let i = 0; i < encTypes.length; i++)
do_check_eq(encTypes[i], getEncTypeForID(dbConnection, i + 1));
dbConnection.close();
// Ensure that call to getAllLogins will reencrypt
LoginTest.checkStorageData(storage, ["https://disabled.net"],
[testuser1, testuser2, testuser4, testuser5]);
LoginTest.deleteFile(OUTDIR, "signons-v2.sqlite");
/* ========== 6 ========== */
testnum++;
testdesc = "Test upgrade v3->v2 storage";
// This is the case where a v3 DB has been accessed with v2 code, and now we
// are upgrading it again. Any logins added by the v2 code must be properly
// upgraded.
LoginTest.copyFile("signons-v2v3.sqlite");
// Sanity check the test file.
dbConnection = LoginTest.openDB("signons-v2v3.sqlite");
do_check_eq(2, dbConnection.schemaVersion);
encTypes = [ENCTYPE_BASE64, ENCTYPE_SDR, ENCTYPE_BASE64, ENCTYPE_BASE64, null];
for (let i = 0; i < encTypes.length; i++)
do_check_eq(encTypes[i], getEncTypeForID(dbConnection, i + 1));
// Reload storage, check that the new login now has encType=1, others untouched
storage = LoginTest.reloadStorage(OUTDIR, "signons-v2v3.sqlite");
do_check_eq(CURRENT_SCHEMA, dbConnection.schemaVersion);
encTypes = [ENCTYPE_BASE64, ENCTYPE_SDR, ENCTYPE_BASE64, ENCTYPE_BASE64, ENCTYPE_SDR];
for (let i = 0; i < encTypes.length; i++)
do_check_eq(encTypes[i], getEncTypeForID(dbConnection, i + 1));
// Sanity check that the data gets migrated
LoginTest.checkStorageData(storage, ["https://disabled.net"],
[testuser1, testuser2, testuser4, testuser5, testuser3]);
encTypes = [ENCTYPE_SDR, ENCTYPE_SDR, ENCTYPE_SDR, ENCTYPE_SDR, ENCTYPE_SDR];
for (let i = 0; i < encTypes.length; i++)
do_check_eq(encTypes[i], getEncTypeForID(dbConnection, i + 1));
dbConnection.close();
LoginTest.deleteFile(OUTDIR, "signons-v2v3.sqlite");
} catch (e) {
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;