gPass/firefox_addon/lib/main.js
Gregory Soutade a994074db0 Server side :
* Add $ADMIN_MODE to enable create users
	* Add protocol version (1 for now)
	* Give priority to letters in password generator

Client side :
	* Don't still use global variable to get document after loading
	* Add email type in possible values for username (used by gmail)
2013-10-16 18:40:06 +02:00

224 lines
6.1 KiB
JavaScript

/*
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;
var pkdbf2 = require("pkdbf2").pkdbf2;
var aes = require("jsaes").aes;
var parseURI = require("parseuri").parseURI;
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();
domain = parseURI.parseUri(form.ownerDocument.baseURI);
domain = domain["host"];
salt = parseURI.parseUri(prefSet.prefs["account_url"]);
salt = salt["host"] + salt["path"];
debug("salt " + salt);
// Get all <input type="text">
for (i=0; i<fields.length; i++)
{
var field = fields[i];
if (field.getAttribute("type") == "text" || field.getAttribute("type") == "email")
{
if (field.hasAttribute("name"))
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);
mkey = pkdbf2.pkdbf2(mkey, salt, 1000, 256/8);
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);
debug("with " + a2hex(mkey));
enc = aes.encryptLongString(v, aes.init(mkey));
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");
debug("resp " + r);
protocol = r[0].split("=");
if (protocol[1] != "1")
{
ret = false;
notifications.notify({
title: "gPasss",
text: "Protocol version not supported, please upgrade your addon",
data: "Protocol version not supported, please upgrade your addon",
});
}
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",
});
}
}
}, false);
gPassRequest.addEventListener("error", function(evt) {
debug("error");
ret = false;
notifications.notify({
title: "gPasss",
text: "Error",
data: "Error",
});
}, false);
debug("connect to " + prefSet.prefs["account_url"]);
gPassRequest.open("POST", prefSet.prefs["account_url"], false);
gPassRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
gPassRequest.send(keys);
if (!ret) return ret;
}
}
}
function document_loaded(event)
{
doc = event.target;
// If there is a password in the form, add a "submit" listener
for(i=0; i<doc.forms.length; i++)
{
var form = doc.forms[i];
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")
{
subject.addEventListener("DOMContentLoaded", document_loaded, false);
}
}
};
var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver, "content-document-global-created", false);