2013-10-09 20:47:43 +02:00
|
|
|
/*
|
|
|
|
Copyright (C) 2013 Grégory Soutadé
|
|
|
|
|
|
|
|
This file is part of gPass.
|
|
|
|
|
|
|
|
gPass is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
gPass is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with gPass. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var {Cc, Ci} = require("chrome");
|
|
|
|
var notifications = require("sdk/notifications");
|
|
|
|
|
|
|
|
// http://www.timdown.co.uk/jshashtable/
|
|
|
|
var Hashtable = require("jshashtable-3.0").Hashtable;
|
2013-10-15 19:29:34 +02:00
|
|
|
var pkdbf2 = require("pkdbf2").pkdbf2;
|
2013-10-09 20:47:43 +02:00
|
|
|
var aes = require("jsaes").aes;
|
2013-10-12 11:16:07 +02:00
|
|
|
var parseURI = require("parseuri").parseURI;
|
2013-10-09 20:47:43 +02:00
|
|
|
var prefSet = require("simple-prefs");
|
|
|
|
var DEBUG = false;
|
|
|
|
|
|
|
|
|
|
|
|
// http://stackoverflow.com/questions/3745666/how-to-convert-from-hex-to-ascii-in-javascript
|
|
|
|
function hex2a(hex) {
|
|
|
|
var str = '';
|
|
|
|
for (var i = 0; i < hex.length; i += 2)
|
|
|
|
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
function a2hex(str) {
|
|
|
|
var hex = '';
|
|
|
|
for (var i = 0; i < str.length; i++)
|
|
|
|
{
|
|
|
|
c = str.charCodeAt(i).toString(16);
|
|
|
|
if (c.length == 1) c = "0" + c;
|
|
|
|
hex += c;
|
|
|
|
}
|
|
|
|
return hex;
|
|
|
|
}
|
|
|
|
|
|
|
|
function debug(s)
|
|
|
|
{
|
|
|
|
if (DEBUG)
|
|
|
|
console.log(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
function on_sumbit()
|
|
|
|
{
|
|
|
|
var form = this;
|
|
|
|
var fields = form.getElementsByTagName("input");
|
|
|
|
var my_map = new Hashtable();
|
|
|
|
|
2013-10-12 11:16:07 +02:00
|
|
|
domain = parseURI.parseUri(form.ownerDocument.baseURI);
|
|
|
|
domain = domain["host"];
|
2013-10-09 20:47:43 +02:00
|
|
|
|
2013-10-16 08:57:06 +02:00
|
|
|
salt = parseURI.parseUri(prefSet.prefs["account_url"]);
|
|
|
|
salt = salt["host"] + salt["path"];
|
|
|
|
|
2013-10-16 18:40:06 +02:00
|
|
|
debug("salt " + salt);
|
2013-10-16 08:57:06 +02:00
|
|
|
|
2013-10-17 18:26:54 +02:00
|
|
|
// Get all <input type="text"> && <input type="email">
|
2013-10-09 20:47:43 +02:00
|
|
|
for (i=0; i<fields.length; i++)
|
|
|
|
{
|
|
|
|
var field = fields[i];
|
2013-10-16 18:40:06 +02:00
|
|
|
if (field.getAttribute("type") == "text" || field.getAttribute("type") == "email")
|
2013-10-09 20:47:43 +02:00
|
|
|
{
|
2013-10-17 18:26:54 +02:00
|
|
|
if (field.hasAttribute("name") && field.value != "")
|
2013-10-09 20:47:43 +02:00
|
|
|
my_map.put(field.getAttribute("name"), field.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for <input type="password" value="@@...">
|
|
|
|
for (i=0; i<fields.length; i++)
|
|
|
|
{
|
|
|
|
var field = fields[i];
|
|
|
|
|
|
|
|
if (field.getAttribute("type") == "password")
|
|
|
|
{
|
|
|
|
debug(field.value);
|
|
|
|
password = field.value;
|
|
|
|
if (password.indexOf("@@") != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mkey = password.substring(2);
|
2013-10-16 08:57:06 +02:00
|
|
|
mkey = pkdbf2.pkdbf2(mkey, salt, 1000, 256/8);
|
2013-10-09 20:47:43 +02:00
|
|
|
|
|
|
|
user = null;
|
|
|
|
// Subset of common user field
|
|
|
|
if (my_map.containsKey("user")) user = my_map.get("user");
|
|
|
|
else if (my_map.containsKey("usr")) user = my_map.get("usr");
|
|
|
|
else if (my_map.containsKey("username")) user = my_map.get("username");
|
|
|
|
else if (my_map.containsKey("login")) user = my_map.get("login");
|
|
|
|
|
|
|
|
// If no one found, use all
|
|
|
|
logins = (user != null) ? new Array(user) : my_map.values();
|
|
|
|
|
|
|
|
keys = "";
|
|
|
|
for(a=0; a<logins.length; a++)
|
|
|
|
{
|
|
|
|
v = "@@" + domain + ";" + logins[a];
|
|
|
|
debug("will encrypt " + v);
|
2013-10-15 19:29:34 +02:00
|
|
|
debug("with " + a2hex(mkey));
|
|
|
|
enc = aes.encryptLongString(v, aes.init(mkey));
|
2013-10-09 20:47:43 +02:00
|
|
|
aes.finish();
|
|
|
|
debug("res " + enc);
|
|
|
|
|
|
|
|
keys += (keys.length != 0) ? "&" : "";
|
|
|
|
keys += "k" + a + "=" + a2hex(enc);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug(keys);
|
|
|
|
|
|
|
|
// Need to do a synchronous request
|
|
|
|
var gPassRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
|
|
|
createInstance(Ci.nsIXMLHttpRequest);
|
|
|
|
|
|
|
|
var ret = true;
|
|
|
|
// gPassRequest.addEventListener("progress", function(evt) { ; }, false);
|
|
|
|
gPassRequest.addEventListener("load", function(evt) {
|
|
|
|
r = this.responseText.split("\n");
|
2013-10-10 18:29:08 +02:00
|
|
|
debug("resp " + r);
|
2013-10-16 18:40:06 +02:00
|
|
|
protocol = r[0].split("=");
|
|
|
|
if (protocol[1] != "1")
|
2013-10-09 20:47:43 +02:00
|
|
|
{
|
|
|
|
ret = false;
|
|
|
|
notifications.notify({
|
|
|
|
title: "gPasss",
|
2013-10-16 18:40:06 +02:00
|
|
|
text: "Protocol version not supported, please upgrade your addon",
|
|
|
|
data: "Protocol version not supported, please upgrade your addon",
|
2013-10-09 20:47:43 +02:00
|
|
|
});
|
|
|
|
}
|
2013-10-16 18:40:06 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (r[1] != "<end>" && r[1].startsWith("pass="))
|
|
|
|
{
|
|
|
|
ciphered_password = r[1].split("=");
|
|
|
|
ciphered_password = ciphered_password[1];
|
|
|
|
debug("Ciphered password : " + ciphered_password);
|
|
|
|
clear_password = aes.decryptLongString(hex2a(ciphered_password), aes.init(mkey));
|
|
|
|
aes.finish();
|
|
|
|
// Remove salt
|
|
|
|
clear_password = clear_password.replace(/\0*$/, "");
|
|
|
|
clear_password = clear_password.substr(0, clear_password.length-3);
|
|
|
|
debug("Clear password " + clear_password);
|
|
|
|
field.value = clear_password;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug("No password found");
|
|
|
|
|
|
|
|
ret = false;
|
|
|
|
|
|
|
|
notifications.notify({
|
|
|
|
title: "gPasss",
|
|
|
|
text: "No password found in database",
|
|
|
|
data: "No password found in database",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2013-10-09 20:47:43 +02:00
|
|
|
}, false);
|
|
|
|
gPassRequest.addEventListener("error", function(evt) {
|
|
|
|
debug("error");
|
|
|
|
ret = false;
|
|
|
|
notifications.notify({
|
|
|
|
title: "gPasss",
|
|
|
|
text: "Error",
|
|
|
|
data: "Error",
|
|
|
|
});
|
|
|
|
|
|
|
|
}, false);
|
2013-10-10 18:29:08 +02:00
|
|
|
debug("connect to " + prefSet.prefs["account_url"]);
|
|
|
|
gPassRequest.open("POST", prefSet.prefs["account_url"], false);
|
2013-10-09 20:47:43 +02:00
|
|
|
gPassRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
|
|
|
gPassRequest.send(keys);
|
|
|
|
|
|
|
|
if (!ret) return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function document_loaded(event)
|
|
|
|
{
|
2013-10-16 18:40:06 +02:00
|
|
|
doc = event.target;
|
2013-10-09 20:47:43 +02:00
|
|
|
// If there is a password in the form, add a "submit" listener
|
2013-10-16 18:40:06 +02:00
|
|
|
for(i=0; i<doc.forms.length; i++)
|
2013-10-09 20:47:43 +02:00
|
|
|
{
|
2013-10-16 18:40:06 +02:00
|
|
|
var form = doc.forms[i];
|
2013-10-09 20:47:43 +02:00
|
|
|
var fields = form.getElementsByTagName("input");
|
|
|
|
for (a=0; a<fields.length; a++)
|
|
|
|
{
|
|
|
|
var field = fields[a];
|
|
|
|
if (field.getAttribute("type") == "password")
|
|
|
|
{
|
|
|
|
form.addEventListener("submit", on_sumbit);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var httpRequestObserver =
|
|
|
|
{
|
|
|
|
observe: function(subject, topic, data)
|
|
|
|
{
|
|
|
|
if (topic == "content-document-global-created")
|
|
|
|
{
|
2013-10-16 18:40:06 +02:00
|
|
|
subject.addEventListener("DOMContentLoaded", document_loaded, false);
|
2013-10-09 20:47:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
|
|
|
observerService.addObserver(httpRequestObserver, "content-document-global-created", false);
|