From 971b7690860c677374cbe14ee64558b1bd174718 Mon Sep 17 00:00:00 2001 From: Ruben Rodriguez Date: Mon, 20 Oct 2014 02:25:28 +0200 Subject: https-everywhere updated to 4.0.2 --- .../components/ssl-observatory.js | 280 ++++++--------------- 1 file changed, 77 insertions(+), 203 deletions(-) (limited to 'data/extensions/https-everywhere@eff.org/components/ssl-observatory.js') 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); } }, -- cgit v1.2.3