mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 381164 - when Master Password is used, it is asked at every startup. r=gavin
This commit is contained in:
parent
646e15fe76
commit
5b43f48b37
@ -49,6 +49,12 @@ nsLoginInfo.prototype = {
|
||||
classID : Components.ID("{0f2f347c-1e4f-40cc-8efd-792dea70a85e}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsILoginInfo]),
|
||||
|
||||
// Allow storage-Legacy.js to get at the JS object so it can
|
||||
// slap on a few extra properties for internal use.
|
||||
get wrappedJSObject() {
|
||||
return this;
|
||||
},
|
||||
|
||||
hostname : null,
|
||||
formSubmitURL : null,
|
||||
httpRealm : null,
|
||||
|
@ -169,6 +169,19 @@ LoginManagerStorage_legacy.prototype = {
|
||||
*
|
||||
*/
|
||||
addLogin : function (login) {
|
||||
// We rely on using login.wrappedJSObject. addLogin is the
|
||||
// only entry point where we might get a nsLoginInfo object
|
||||
// that wasn't created by us (and so might not be a JS
|
||||
// implementation being wrapped)
|
||||
if (!login.wrappedJSObject) {
|
||||
var clone = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
clone.init(login.hostname, login.formSubmitURL, login.httpRealm,
|
||||
login.username, login.password,
|
||||
login.usernameField, login.passwordField);
|
||||
login = clone;
|
||||
}
|
||||
|
||||
var key = login.hostname;
|
||||
|
||||
// If first entry for key, create an Array to hold it's logins.
|
||||
@ -192,6 +205,10 @@ LoginManagerStorage_legacy.prototype = {
|
||||
if (!logins)
|
||||
throw "No logins found for hostname (" + key + ")";
|
||||
|
||||
// The specified login isn't encrypted, so we need to ensure
|
||||
// the logins we're comparing with are decrypted.
|
||||
this._decryptLogins(logins);
|
||||
|
||||
for (var i = 0; i < logins.length; i++) {
|
||||
if (logins[i].equals(login)) {
|
||||
logins.splice(i, 1); // delete that login from array.
|
||||
@ -225,13 +242,16 @@ LoginManagerStorage_legacy.prototype = {
|
||||
* Returns an array of nsAccountInfo.
|
||||
*/
|
||||
getAllLogins : function (count) {
|
||||
var result = [];
|
||||
var result = [], userCanceled;
|
||||
|
||||
// Each entry is an array -- append the array entries to |result|.
|
||||
for each (var hostLogins in this._logins) {
|
||||
result = result.concat(hostLogins);
|
||||
}
|
||||
|
||||
// decrypt entries for caller.
|
||||
[result, userCanceled] = this._decryptLogins(result);
|
||||
|
||||
count.value = result.length; // needed for XPCOM
|
||||
return result;
|
||||
},
|
||||
@ -300,7 +320,7 @@ LoginManagerStorage_legacy.prototype = {
|
||||
return [];
|
||||
}
|
||||
|
||||
var result = [];
|
||||
var result = [], userCanceled;
|
||||
|
||||
for each (var login in hostLogins) {
|
||||
|
||||
@ -318,6 +338,15 @@ LoginManagerStorage_legacy.prototype = {
|
||||
result.push(login);
|
||||
}
|
||||
|
||||
// Decrypt entries found for the caller.
|
||||
[result, userCanceled] = this._decryptLogins(result);
|
||||
|
||||
// We want to throw in this case, so that the Login Manager
|
||||
// knows to stop processing forms on the page so the user isn't
|
||||
// prompted multiple times.
|
||||
if (userCanceled)
|
||||
throw "User canceled Master Password entry";
|
||||
|
||||
count.value = result.length; // needed for XPCOM
|
||||
return result;
|
||||
},
|
||||
@ -383,7 +412,6 @@ LoginManagerStorage_legacy.prototype = {
|
||||
*/
|
||||
_readFile : function () {
|
||||
var oldFormat = false;
|
||||
var writeOnFinish = false;
|
||||
|
||||
this.log("Reading passwords from " + this._signonsFile.path);
|
||||
|
||||
@ -484,7 +512,7 @@ LoginManagerStorage_legacy.prototype = {
|
||||
|
||||
// Line is a username
|
||||
case STATE.USERVALUE:
|
||||
entry.username = this._decrypt(line.value);
|
||||
entry.wrappedJSObject.encryptedUsername = line.value;
|
||||
parseState++;
|
||||
break;
|
||||
|
||||
@ -497,7 +525,7 @@ LoginManagerStorage_legacy.prototype = {
|
||||
|
||||
// Line is a password
|
||||
case STATE.PASSVALUE:
|
||||
entry.password = this._decrypt(line.value);
|
||||
entry.wrappedJSObject.encryptedPassword = line.value;
|
||||
if (oldFormat) {
|
||||
entry.formSubmitURL = "";
|
||||
processEntry = true;
|
||||
@ -517,14 +545,11 @@ LoginManagerStorage_legacy.prototype = {
|
||||
}
|
||||
|
||||
if (processEntry) {
|
||||
if (entry.username == "" && entry.password == "") {
|
||||
// Discard bogus entry, and update the file when done.
|
||||
writeOnFinish = true;
|
||||
} else {
|
||||
if (!this._logins[hostname])
|
||||
this._logins[hostname] = [];
|
||||
this._logins[hostname].push(entry);
|
||||
}
|
||||
if (!this._logins[hostname])
|
||||
this._logins[hostname] = [];
|
||||
|
||||
this._logins[hostname].push(entry);
|
||||
|
||||
entry = null;
|
||||
processEntry = false;
|
||||
}
|
||||
@ -532,9 +557,6 @@ LoginManagerStorage_legacy.prototype = {
|
||||
|
||||
lineStream.close();
|
||||
|
||||
if (writeOnFinish)
|
||||
this._writeFile();
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
@ -594,6 +616,7 @@ LoginManagerStorage_legacy.prototype = {
|
||||
// write each login known for the host
|
||||
var lastRealm = null;
|
||||
var firstEntry = true;
|
||||
var userCanceled = false;
|
||||
for each (var login in this._logins[hostname]) {
|
||||
|
||||
// If this login is for a new realm, start a new entry.
|
||||
@ -611,8 +634,28 @@ LoginManagerStorage_legacy.prototype = {
|
||||
|
||||
firstEntry = false;
|
||||
|
||||
var encUsername = this._encrypt(login.username);
|
||||
var encPassword = this._encrypt(login.password);
|
||||
// Get the encrypted value of the username. Newly added
|
||||
// logins will need the plaintext value encrypted.
|
||||
var encUsername = login.wrappedJSObject.encryptedUsername;
|
||||
if (!encUsername) {
|
||||
[encUsername, userCanceled] = this._encrypt(login.username);
|
||||
login.wrappedJSObject.encryptedUsername = encUsername;
|
||||
}
|
||||
|
||||
if (userCanceled)
|
||||
break;
|
||||
|
||||
// Get the encrypted value of the password. Newly added
|
||||
// logins will need the plaintext value encrypted.
|
||||
var encPassword = login.wrappedJSObject.encryptedPassword;
|
||||
if (!encPassword) {
|
||||
[encPassword, userCanceled] = this._encrypt(login.password);
|
||||
login.wrappedJSObject.encryptedPassword = encPassword;
|
||||
}
|
||||
|
||||
if (userCanceled)
|
||||
break;
|
||||
|
||||
|
||||
writeLine((login.usernameField ? login.usernameField : ""));
|
||||
writeLine(encUsername);
|
||||
@ -624,6 +667,13 @@ LoginManagerStorage_legacy.prototype = {
|
||||
lastRealm = login.httpRealm;
|
||||
}
|
||||
|
||||
if (userCanceled) {
|
||||
this.log("User canceled Master Password, aborting write.");
|
||||
// .close will cause an abort w/o modifying original file
|
||||
outputStream.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// write end-of-host marker
|
||||
writeLine(".");
|
||||
}
|
||||
@ -635,25 +685,110 @@ LoginManagerStorage_legacy.prototype = {
|
||||
|
||||
|
||||
/*
|
||||
* _encrypt
|
||||
* _decryptLogins
|
||||
*
|
||||
* Decrypts username and password fields in the provided array of
|
||||
* logins. This is deferred from the _readFile() code, so that
|
||||
* the user is not prompted for a master password (if set) until
|
||||
* the entries are actually used.
|
||||
*
|
||||
* The entries specified by the array will be decrypted, if possible.
|
||||
* An array of successfully decrypted logins will be returned. The return
|
||||
* value should be given to external callers (since still-encrypted
|
||||
* entries are useless), whereas internal callers generally don't want
|
||||
* to lose unencrypted entries (eg, because the user clicked Cancel
|
||||
* instead of entering their master password)
|
||||
*/
|
||||
_encrypt : function (plainText) {
|
||||
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
var plainOctet = converter.ConvertFromUnicode(plainText);
|
||||
plainOctet += converter.Finish();
|
||||
return this._decoderRing.encryptString(plainOctet);
|
||||
_decryptLogins : function (logins) {
|
||||
var result = [], userCanceled = false;
|
||||
|
||||
for each (var login in logins) {
|
||||
if (!login.username)
|
||||
[login.username, userCanceled] =
|
||||
this._decrypt(login.wrappedJSObject.encryptedUsername);
|
||||
|
||||
if (userCanceled)
|
||||
break;
|
||||
|
||||
if (!login.password)
|
||||
[login.password, userCanceled] =
|
||||
this._decrypt(login.wrappedJSObject.encryptedPassword);
|
||||
|
||||
// Probably can't hit this case, but for completeness...
|
||||
if (userCanceled)
|
||||
break;
|
||||
|
||||
// If decryption failed (corrupt entry?) skip it.
|
||||
// XXX remove it from the original list entirely?
|
||||
if (!login.username || !login.password)
|
||||
continue;
|
||||
|
||||
// Force any old mime64-obscured entries to be reencrypted.
|
||||
if (login.wrappedJSObject.encryptedUsername &&
|
||||
login.wrappedJSObject.encryptedUsername.charAt(0) == '~')
|
||||
login.wrappedJSObject.encryptedUsername = null;
|
||||
|
||||
if (login.wrappedJSObject.encryptedPassword &&
|
||||
login.wrappedJSObject.encryptedPassword.charAt(0) == '~')
|
||||
login.wrappedJSObject.encryptedPassword = null;
|
||||
|
||||
result.push(login);
|
||||
}
|
||||
|
||||
return [result, userCanceled];
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* decrypt
|
||||
* _encrypt
|
||||
*
|
||||
* Encrypts the specified string, using the SecretDecoderRing.
|
||||
*
|
||||
* Returns [cipherText, userCanceled] where:
|
||||
* cipherText -- the encrypted string, or null if it failed.
|
||||
* userCanceled -- if the encryption failed, this is true if the
|
||||
* user selected Cancel when prompted to enter their
|
||||
* Master Password. The caller should bail out, and not
|
||||
* not request that more things be encrypted (which
|
||||
* results in prompting the user for a Master Password
|
||||
* over and over.)
|
||||
*/
|
||||
_encrypt : function (plainText) {
|
||||
var cipherText = null, userCanceled = false;
|
||||
|
||||
try {
|
||||
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
var plainOctet = converter.ConvertFromUnicode(plainText);
|
||||
plainOctet += converter.Finish();
|
||||
cipherText = this._decoderRing.encryptString(plainOctet);
|
||||
} catch (e) {
|
||||
this.log("Failed to encrypt string. (" + e.name + ")");
|
||||
if (e.result == Components.results.NS_ERROR_NOT_AVAILABLE)
|
||||
userCanceled = true;
|
||||
}
|
||||
|
||||
return [cipherText, userCanceled];
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _decrypt
|
||||
*
|
||||
* Decrypts the specified string, using the SecretDecoderRing.
|
||||
*
|
||||
* Returns [plainText, userCanceled] where:
|
||||
* plainText -- the decrypted string, or null if it failed.
|
||||
* userCanceled -- if the decryption failed, this is true if the
|
||||
* user selected Cancel when prompted to enter their
|
||||
* Master Password. The caller should bail out, and not
|
||||
* not request that more things be decrypted (which
|
||||
* results in prompting the user for a Master Password
|
||||
* over and over.)
|
||||
*/
|
||||
_decrypt : function (cipherText) {
|
||||
var plainText = null;
|
||||
var plainText = null, userCanceled = false;
|
||||
|
||||
try {
|
||||
var plainOctet;
|
||||
@ -670,10 +805,18 @@ LoginManagerStorage_legacy.prototype = {
|
||||
converter.charset = "UTF-8";
|
||||
plainText = converter.ConvertToUnicode(plainOctet);
|
||||
} catch (e) {
|
||||
this.log("Failed to decrypt string: " + cipherText);
|
||||
this.log("Failed to decrypt string: " + cipherText +
|
||||
" (" + e.name + ")");
|
||||
|
||||
// If the user clicks Cancel, we get NS_ERROR_NOT_AVAILABLE.
|
||||
// If the cipherText is bad / wrong key, we get NS_ERROR_FAILURE
|
||||
// Wrong passwords are handled by the decoderRing reprompting;
|
||||
// we get no notification.
|
||||
if (e.result == Components.results.NS_ERROR_NOT_AVAILABLE)
|
||||
userCanceled = true;
|
||||
}
|
||||
|
||||
return plainText;
|
||||
return [plainText, userCanceled];
|
||||
},
|
||||
|
||||
}; // end of nsLoginManagerStorage_legacy implementation
|
||||
|
Loading…
Reference in New Issue
Block a user