summaryrefslogtreecommitdiff
path: root/data/extensions/https-everywhere@eff.org/components/ssl-observatory.js
diff options
context:
space:
mode:
authorRuben Rodriguez <ruben@gnu.org>2014-10-20 02:25:28 +0200
committerRuben Rodriguez <ruben@gnu.org>2014-10-20 02:25:28 +0200
commit971b7690860c677374cbe14ee64558b1bd174718 (patch)
tree943eb7bb9ed6f0466b6d688c10ab067ac5208a95 /data/extensions/https-everywhere@eff.org/components/ssl-observatory.js
parent6e7918b6ccb69876d339a320091fdee811445395 (diff)
https-everywhere updated to 4.0.2
Diffstat (limited to 'data/extensions/https-everywhere@eff.org/components/ssl-observatory.js')
-rw-r--r--data/extensions/https-everywhere@eff.org/components/ssl-observatory.js280
1 files changed, 77 insertions, 203 deletions
diff --git a/data/extensions/https-everywhere@eff.org/components/ssl-observatory.js b/data/extensions/https-everywhere@eff.org/components/ssl-observatory.js
index 7b301d1..15df1db 100644
--- a/data/extensions/https-everywhere@eff.org/components/ssl-observatory.js
+++ b/data/extensions/https-everywhere@eff.org/components/ssl-observatory.js
@@ -5,7 +5,6 @@ const Cr = Components.results;
const CI = Components.interfaces;
const CC = Components.classes;
const CR = Components.results;
-const CU = Components.utils;
// Log levels
VERB=1;
@@ -15,12 +14,12 @@ NOTE=4;
WARN=5;
BASE_REQ_SIZE=4096;
+TIMEOUT = 60000;
MAX_OUTSTANDING = 20; // Max # submission XHRs in progress
MAX_DELAYED = 32; // Max # XHRs are waiting around to be sent or retried
-TIMEOUT = 60000;
ASN_PRIVATE = -1; // Do not record the ASN this cert was seen on
-ASN_IMPLICIT = -2; // ASN can be learned from connecting IP
+ASN_IMPLICIT = -2 // ASN can be learned from connecting IP
ASN_UNKNOWABLE = -3; // Cert was seen in the absence of [trustworthy] Internet access
// XXX: We should make the _observatory tree relative.
@@ -52,7 +51,7 @@ const INCLUDE = function(name) {
dump("INCLUDE " + name + ": " + e + "\n");
}
}
-};
+}
INCLUDE('Root-CAs');
INCLUDE('sha256');
@@ -72,10 +71,6 @@ function SSLObservatory() {
this.torbutton_installed = false;
}
- this.HTTPSEverywhere = CC["@eff.org/https-everywhere;1"]
- .getService(Components.interfaces.nsISupports)
- .wrappedJSObject;
-
/* The proxy test result starts out null until the test is attempted.
* This is for UI notification purposes */
this.proxy_test_successful = null;
@@ -89,6 +84,7 @@ function SSLObservatory() {
// Clear these on cookies-cleared observer event
this.already_submitted = {};
this.delayed_submissions = {};
+ OS.addObserver(this, "cookie-changed", false);
// Figure out the url to submit to
this.submit_host = null;
@@ -97,32 +93,14 @@ function SSLObservatory() {
// Used to track current number of pending requests to the server
this.current_outstanding_requests = 0;
- // We can't always know private browsing state per request, sometimes
- // we have to guess based on what we've seen in the past
- this.everSeenPrivateBrowsing = false;
-
// Generate nonce to append to url, to catch in nsIProtocolProxyFilter
// and to protect against CSRF
this.csrf_nonce = "#"+Math.random().toString()+Math.random().toString();
this.compatJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
- var pref_service = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefBranchInternal);
- var branch = pref_service.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
-
- branch.addObserver("extensions.https_everywhere._observatory.enabled",
- this, false);
-
- if (this.myGetBoolPref("enabled")) {
- OS.addObserver(this, "cookie-changed", false);
- OS.addObserver(this, "http-on-examine-response", false);
-
- var dls = CC['@mozilla.org/docloaderservice;1']
- .getService(CI.nsIWebProgress);
- dls.addProgressListener(this,
- Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
- }
+ // Register observer
+ OS.addObserver(this, "http-on-examine-response", false);
// Register protocolproxyfilter
this.pps = CC["@mozilla.org/network/protocol-proxy-service;1"]
@@ -152,9 +130,7 @@ SSLObservatory.prototype = {
[ CI.nsIObserver,
CI.nsIProtocolProxyFilter,
//CI.nsIWifiListener,
- CI.nsIWebProgressListener,
- CI.nsISupportsWeakReference,
- CI.nsIInterfaceRequestor]),
+ CI.nsIBadCertListener2]),
wrappedJSObject: null, // Initialized by constructor
@@ -164,7 +140,7 @@ SSLObservatory.prototype = {
contractID: SERVICE_CTRID,
// https://developer.mozilla.org/En/How_to_check_the_security_state_of_an_XMLHTTPRequest_over_SSL
- getSSLCertChain: function(channel) {
+ getSSLCert: function(channel) {
try {
// Do we have a valid channel argument?
if (!channel instanceof Ci.nsIChannel) {
@@ -314,29 +290,6 @@ SSLObservatory.prototype = {
return (cert.md5Fingerprint+cert.sha1Fingerprint).replace(":", "", "g");
},
- // onSecurity is used to listen for bad cert warnings
- // There is also onSecurityStateChange, but it does not handle subdocuments. See git
- // history for an implementation stub.
- onStateChange: function(aProgress, aRequest, aState, aStatus) {
- if (!aRequest) return;
- var chan = null;
- try {
- chan = aRequest.QueryInterface(Ci.nsIHttpChannel);
- } catch(e) {
- return;
- }
- if (chan) {
- if (!this.observatoryActive(chan)) return;
- var certchain = this.getSSLCertChain(chan);
- if (certchain) {
- this.log(INFO, "Got state cert chain for "
- + chan.originalURI.spec + "->" + chan.URI.spec + ", state: " + aState);
- var warning = true;
- this.submitCertChainForChannel(certchain, chan, warning);
- }
- }
- },
-
observe: function(subject, topic, data) {
if (topic == "cookie-changed" && data == "cleared") {
this.already_submitted = {};
@@ -345,13 +298,16 @@ SSLObservatory.prototype = {
return;
}
- if ("http-on-examine-response" == topic) {
- var channel = subject;
- if (!this.observatoryActive(channel)) return;
-
- var certchain = this.getSSLCertChain(subject);
- var warning = false;
- this.submitCertChainForChannel(certchain, channel, warning);
+ if (topic == "nsPref:changed") {
+ // XXX: We somehow need to only call this once. Right now, we'll make
+ // like 3 calls to getClientASN().. The only thing I can think
+ // of is a timer...
+ if (data == "network.proxy.ssl" || data == "network.proxy.ssl_port" ||
+ data == "network.proxy.socks" || data == "network.proxy.socks_port") {
+ this.log(INFO, "Proxy settings have changed. Getting new ASN");
+ this.getClientASN();
+ }
+ return;
}
if (topic == "network:offline-status-changed" && data == "online") {
@@ -360,98 +316,55 @@ SSLObservatory.prototype = {
return;
}
- if (topic == "nsPref:changed") {
- // If the user toggles the SSL Observatory settings, we need to add or remove
- // our observers
- switch (data) {
- case "network.proxy.ssl":
- case "network.proxy.ssl_port":
- case "network.proxy.socks":
- case "network.proxy.socks_port":
- // XXX: We somehow need to only call this once. Right now, we'll make
- // like 3 calls to getClientASN().. The only thing I can think
- // of is a timer...
- this.log(INFO, "Proxy settings have changed. Getting new ASN");
- this.getClientASN();
- break;
- case "extensions.https_everywhere._observatory.enabled":
- if (this.myGetBoolPref("enabled")) {
- this.pps.registerFilter(this, 0);
- OS.addObserver(this, "cookie-changed", false);
- OS.addObserver(this, "http-on-examine-response", false);
-
- var dls = CC['@mozilla.org/docloaderservice;1']
- .getService(CI.nsIWebProgress);
- dls.addProgressListener(this,
- Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
- this.log(INFO,"SSL Observatory is now enabled via pref change!");
- } else {
- try {
- this.pps.unregisterFilter(this);
- OS.removeObserver(this, "cookie-changed");
- OS.removeObserver(this, "http-on-examine-response");
-
- var dls = CC['@mozilla.org/docloaderservice;1']
- .getService(CI.nsIWebProgress);
- dls.removeProgressListener(this);
- this.log(INFO,"SSL Observatory is now disabled via pref change!");
- } catch(e) {
- this.log(WARN, "Removing SSL Observatory observers failed: "+e);
- }
- }
- break;
- }
- return;
- }
-
- },
+ if ("http-on-examine-response" == topic) {
- submitCertChainForChannel: function(certchain, channel, warning) {
- if (!certchain) {
- return;
- }
- var host_ip = "-1";
- var httpchannelinternal = channel.QueryInterface(Ci.nsIHttpChannelInternal);
- try {
- host_ip = httpchannelinternal.remoteAddress;
- } catch(e) {
- this.log(INFO, "Could not get server IP address.");
- }
+ if (!this.observatoryActive()) return;
- channel.QueryInterface(Ci.nsIHttpChannel);
- var chainEnum = certchain.getChain();
- var chainArray = [];
- var chainArrayFpStr = '';
- var fps = [];
- for(var i = 0; i < chainEnum.length; i++) {
- var cert = chainEnum.queryElementAt(i, Ci.nsIX509Cert);
- chainArray.push(cert);
- var fp = this.ourFingerprint(cert);
- fps.push(fp);
- chainArrayFpStr = chainArrayFpStr + fp;
- }
- var chain_hash = sha256_digest(chainArrayFpStr).toUpperCase();
- this.log(INFO, "SHA-256 hash of cert chain for "+new String(channel.URI.host)+" is "+ chain_hash);
+ var host_ip = "-1";
+ var httpchannelinternal = subject.QueryInterface(Ci.nsIHttpChannelInternal);
+ try {
+ host_ip = httpchannelinternal.remoteAddress;
+ } catch(e) {
+ this.log(INFO, "Could not get server IP address.");
+ }
+ subject.QueryInterface(Ci.nsIHttpChannel);
+ var certchain = this.getSSLCert(subject);
+ if (certchain) {
+ var chainEnum = certchain.getChain();
+ var chainArray = [];
+ var chainArrayFpStr = '';
+ var fps = [];
+ for(var i = 0; i < chainEnum.length; i++) {
+ var cert = chainEnum.queryElementAt(i, Ci.nsIX509Cert);
+ chainArray.push(cert);
+ var fp = this.ourFingerprint(cert);
+ fps.push(fp);
+ chainArrayFpStr = chainArrayFpStr + fp;
+ }
+ var chain_hash = sha256_digest(chainArrayFpStr).toUpperCase();
+ this.log(INFO, "SHA-256 hash of cert chain for "+new String(subject.URI.host)+" is "+ chain_hash);
- if(!this.myGetBoolPref("use_whitelist")) {
- this.log(WARN, "Not using whitelist to filter cert chains.");
- }
- else if (this.isChainWhitelisted(chain_hash)) {
- this.log(INFO, "This cert chain is whitelisted. Not submitting.");
- return;
- }
- else {
- this.log(INFO, "Cert chain is NOT whitelisted. Proceeding with submission.");
- }
+ if(!this.myGetBoolPref("use_whitelist")) {
+ this.log(WARN, "Not using whitelist to filter cert chains.");
+ }
+ else if (this.isChainWhitelisted(chain_hash)) {
+ this.log(INFO, "This cert chain is whitelisted. Not submitting.");
+ return;
+ }
+ else {
+ this.log(INFO, "Cert chain is NOT whitelisted. Proceeding with submission.");
+ }
- if (channel.URI.port == -1) {
- this.submitChainArray(chainArray, fps, new String(channel.URI.host), channel, host_ip, warning, false);
- } else {
- this.submitChainArray(chainArray, fps, channel.URI.host+":"+channel.URI.port, channel, host_ip, warning, false);
+ if (subject.URI.port == -1) {
+ this.submitChain(chainArray, fps, new String(subject.URI.host), subject, host_ip, false);
+ } else {
+ this.submitChain(chainArray, fps, subject.URI.host+":"+subject.URI.port, subject, host_ip, false);
+ }
+ }
}
},
- observatoryActive: function(channel) {
+ observatoryActive: function() {
if (!this.myGetBoolPref("enabled"))
return false;
@@ -477,43 +390,14 @@ SSLObservatory.prototype = {
// submit certs without strong anonymisation. Because the
// anonymisation is weak, we avoid submitting during private browsing
// mode.
- var pbm = this.inPrivateBrowsingMode(channel);
- this.log(DBUG, "Private browsing mode: " + pbm);
- return !pbm;
- }
- },
-
- inPrivateBrowsingMode: function(channel) {
- // In classic firefox fashion, there are multiple versions of this API
- // https://developer.mozilla.org/EN/docs/Supporting_per-window_private_browsing
- try {
- // Firefox 20+, this state is per-window;
- // should raise an exception on FF < 20
- CU.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
- if (!(channel instanceof CI.nsIHttpChannel)) {
- this.log(NOTE, "observatoryActive() without a channel");
- // This is a windowless request. We cannot tell if private browsing
- // applies. Conservatively, if we have ever seen PBM, it might be
- // active now
- return this.everSeenPrivateBrowsing;
- }
- var win = this.HTTPSEverywhere.getWindowForChannel(channel);
- if (!win) return this.everSeenPrivateBrowsing; // windowless request
-
- if (PrivateBrowsingUtils.isWindowPrivate(win)) {
- this.everSeenPrivateBrowsing = true;
- return true;
- }
- } catch (e) {
- // Firefox < 20, this state is global
try {
var pbs = CC["@mozilla.org/privatebrowsing;1"].getService(CI.nsIPrivateBrowsingService);
- if (pbs.privateBrowsingEnabled) {
- this.everSeenPrivateBrowsing = true;
- return true;
- }
- } catch (e) { /* seamonkey or very old firefox */ }
+ if (pbs.privateBrowsingEnabled) return false;
+ } catch (e) { /* seamonkey or old firefox */ }
+
+ return true;
}
+
return false;
},
@@ -621,7 +505,7 @@ SSLObservatory.prototype = {
shouldSubmit: function(chain, domain) {
// Return true if we should submit this chain to the SSL Observatory
var rootidx = this.findRootInChain(chain.certArray);
- var ss = false; // ss: self-signed
+ var ss= false;
if (chain.leaf.issuerName == chain.leaf.subjectName)
ss = true;
@@ -652,7 +536,7 @@ SSLObservatory.prototype = {
return true;
},
- submitChainArray: function(certArray, fps, domain, channel, host_ip, warning, resubmitting) {
+ submitChain: function(certArray, fps, domain, channel, host_ip, resubmitting) {
var base64Certs = [];
// Put all this chain data in one object so that it can be modified by
// subroutines if required
@@ -669,7 +553,7 @@ SSLObservatory.prototype = {
if (Object.keys(this.delayed_submissions).length < MAX_DELAYED)
if (!(c.fps[0] in this.delayed_submissions)) {
this.log(WARN, "Planning to retry submission...");
- let retry = function() { this.submitChainArray(certArray, fps, domain, channel, host_ip, warning, true); };
+ let retry = function() { this.submitChain(certArray, fps, domain, channel, host_ip, true); };
this.delayed_submissions[c.fps[0]] = retry;
}
return;
@@ -696,20 +580,16 @@ SSLObservatory.prototype = {
if (resubmitting) {
reqParams.push("client_asn="+ASN_UNKNOWABLE);
- } else {
+ }
+ else {
reqParams.push("client_asn="+this.client_asn);
}
if (this.myGetBoolPref("priv_dns")) {
reqParams.push("private_opt_in=1");
- } else {
- reqParams.push("private_opt_in=0");
}
-
- if (warning) {
- reqParams.push("browser_warning=1");
- } else {
- reqParams.push("browser_warning=0");
+ else {
+ reqParams.push("private_opt_in=0");
}
var params = reqParams.join("&") + "&padding=0";
@@ -728,7 +608,10 @@ SSLObservatory.prototype = {
var that = this; // We have neither SSLObservatory nor this in scope in the lambda
- var win = channel ? this.HTTPSEverywhere.getWindowForChannel(channel) : null;
+ var HTTPSEverywhere = CC["@eff.org/https-everywhere;1"]
+ .getService(Components.interfaces.nsISupports)
+ .wrappedJSObject;
+ var win = channel ? HTTPSEverywhere.getWindowForChannel(channel) : null;
var req = this.buildRequest(params);
req.timeout = TIMEOUT;
@@ -775,7 +658,7 @@ SSLObservatory.prototype = {
if (Object.keys(that.delayed_submissions).length < MAX_DELAYED)
if (!(c.fps[0] in that.delayed_submissions)) {
that.log(WARN, "Planning to retry submission...");
- let retry = function() { that.submitChainArray(certArray, fps, domain, channel, host_ip, warning, true); };
+ let retry = function() { that.submitChain(certArray, fps, domain, channel, host_ip, true); };
that.delayed_submissions[c.fps[0]] = retry;
}
@@ -857,7 +740,6 @@ SSLObservatory.prototype = {
if(req.status == 200) {
if(!req.responseXML) {
that.log(INFO, "Tor check failed: No XML returned by check service.");
- that.proxyTestFinished();
return;
}
@@ -882,7 +764,6 @@ SSLObservatory.prototype = {
that.proxy_test_callback(that.proxy_test_successful);
that.proxy_test_callback = null;
}
- that.proxyTestFinished();
}
};
req.send(null);
@@ -896,13 +777,6 @@ SSLObservatory.prototype = {
this.proxy_test_callback(this.proxy_test_successful);
this.proxy_test_callback = null;
}
- that.proxyTestFinished();
- }
- },
-
- proxyTestFinished: function() {
- if (!this.myGetBoolPref("enabled")) {
- this.pps.unregisterFilter(this);
}
},