Skip to content

Commit e7debd1

Browse files
committed
Fix bad merge of ssl-observatory.js.
I reset ssl-observatory to 3.4.5, as it was on the stable branch (4.0), then manually replayed the small number of tweaks from master: 2295706 92f45cf e6cd64c 5197662 68421bc We still need to restore the changes that were reverted when we went back to the 3.4.5 version of this file, per EFForg#262
1 parent ddc2fc9 commit e7debd1

File tree

1 file changed

+70
-215
lines changed

1 file changed

+70
-215
lines changed

src/components/ssl-observatory.js

Lines changed: 70 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,14 @@ let NOTE=4;
1414
let WARN=5;
1515

1616
let BASE_REQ_SIZE=4096;
17+
let TIMEOUT = 60000;
1718
let MAX_OUTSTANDING = 20; // Max # submission XHRs in progress
1819
let MAX_DELAYED = 32; // Max # XHRs are waiting around to be sent or retried
19-
let TIMEOUT = 60000;
2020

2121
let ASN_PRIVATE = -1; // Do not record the ASN this cert was seen on
22-
let ASN_IMPLICIT = -2; // ASN can be learned from connecting IP
22+
let ASN_IMPLICIT = -2 // ASN can be learned from connecting IP
2323
let ASN_UNKNOWABLE = -3; // Cert was seen in the absence of [trustworthy] Internet access
2424

25-
let HASHLENGTH = 64; // hex(sha1 + md5)
26-
let MIN_WHITELIST=1000; // do not tolerate whitelists outside these bounds
27-
let MAX_WHITELIST=10000;
28-
2925
// XXX: We should make the _observatory tree relative.
3026
let LLVAR="extensions.https_everywhere.LogLevel";
3127

@@ -59,6 +55,7 @@ const INCLUDE = function(name) {
5955

6056
INCLUDE('Root-CAs');
6157
INCLUDE('sha256');
58+
INCLUDE('X509ChainWhitelist');
6259
INCLUDE('NSS');
6360

6461
function SSLObservatory() {
@@ -117,16 +114,13 @@ function SSLObservatory() {
117114
this.setupASNWatcher();
118115

119116
try {
120-
NSS.initialize(ctypes.libraryName("nss3"));
117+
NSS.initialize("");
121118
} catch(e) {
122119
this.log(WARN, "Failed to initialize NSS component:" + e);
123120
}
124121

125122
this.testProxySettings();
126123

127-
this.loadCertWhitelist();
128-
this.maybeUpdateCertWhitelist();
129-
130124
this.log(DBUG, "Loaded observatory component!");
131125
}
132126

@@ -304,122 +298,68 @@ SSLObservatory.prototype = {
304298
return;
305299
}
306300

307-
if ("http-on-examine-response" == topic) {
308-
var channel = subject;
309-
if (!this.observatoryActive(channel)) return;
310-
311-
var certchain = this.getSSLCertChain(subject);
312-
var warning = false;
313-
this.submitCertChainForChannel(certchain, channel, warning);
314-
}
315-
316-
if (topic == "network:offline-status-changed" && data == "online") {
317-
this.log(INFO, "Browser back online. Getting new ASN.");
318-
this.getClientASN();
319-
return;
320-
}
321-
322301
if (topic == "nsPref:changed") {
323-
// If the user toggles the SSL Observatory settings, we need to add or remove
324-
// our observers
325-
switch (data) {
326-
case "network.proxy.ssl":
327-
case "network.proxy.ssl_port":
328-
case "network.proxy.socks":
329-
case "network.proxy.socks_port":
330-
// XXX: We somehow need to only call this once. Right now, we'll make
331-
// like 3 calls to getClientASN().. The only thing I can think
332-
// of is a timer...
333-
this.log(INFO, "Proxy settings have changed. Getting new ASN");
334-
this.getClientASN();
335-
break;
336-
case "extensions.https_everywhere._observatory.enabled":
337-
if (this.myGetBoolPref("enabled")) {
338-
this.pps.registerFilter(this, 0);
339-
OS.addObserver(this, "cookie-changed", false);
340-
OS.addObserver(this, "http-on-examine-response", false);
341-
342-
var dls = CC['@mozilla.org/docloaderservice;1']
343-
.getService(CI.nsIWebProgress);
344-
dls.addProgressListener(this,
345-
Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
346-
this.log(INFO,"SSL Observatory is now enabled via pref change!");
347-
} else {
348-
try {
349-
this.pps.unregisterFilter(this);
350-
OS.removeObserver(this, "cookie-changed");
351-
OS.removeObserver(this, "http-on-examine-response");
352-
353-
var dls = CC['@mozilla.org/docloaderservice;1']
354-
.getService(CI.nsIWebProgress);
355-
dls.removeProgressListener(this);
356-
this.log(INFO,"SSL Observatory is now disabled via pref change!");
357-
} catch(e) {
358-
this.log(WARN, "Removing SSL Observatory observers failed: "+e);
359-
}
360-
}
361-
break;
302+
// XXX: We somehow need to only call this once. Right now, we'll make
303+
// like 3 calls to getClientASN().. The only thing I can think
304+
// of is a timer...
305+
if (data == "network.proxy.ssl" || data == "network.proxy.ssl_port" ||
306+
data == "network.proxy.socks" || data == "network.proxy.socks_port") {
307+
this.log(INFO, "Proxy settings have changed. Getting new ASN");
308+
this.getClientASN();
362309
}
363310
return;
364311
}
365312

366-
},
367-
368-
submitCertChainForChannel: function(certchain, channel, warning) {
369-
if (!certchain) {
313+
if (topic == "network:offline-status-changed" && data == "online") {
314+
this.log(INFO, "Browser back online. Getting new ASN.");
315+
this.getClientASN();
370316
return;
371317
}
372318

373-
this.maybeUpdateCertWhitelist();
374-
375-
var host_ip = "-1";
376-
var httpchannelinternal = channel.QueryInterface(Ci.nsIHttpChannelInternal);
377-
try {
378-
host_ip = httpchannelinternal.remoteAddress;
379-
} catch(e) {
380-
this.log(INFO, "Could not get server IP address.");
381-
}
319+
if ("http-on-examine-response" == topic) {
382320

383-
if (!this.observatoryActive()) return;
321+
if (!this.observatoryActive()) return;
384322

385-
var host_ip = "-1";
386-
var httpchannelinternal = subject.QueryInterface(Ci.nsIHttpChannelInternal);
387-
try {
388-
host_ip = httpchannelinternal.remoteAddress;
389-
} catch(e) {
390-
this.log(INFO, "Could not get server IP address.");
391-
}
392-
subject.QueryInterface(Ci.nsIHttpChannel);
393-
var certchain = this.getSSLCert(subject);
394-
if (certchain) {
395-
var chainEnum = certchain.getChain();
396-
var chainArray = [];
397-
var chainArrayFpStr = '';
398-
var fps = [];
399-
for(var i = 0; i < chainEnum.length; i++) {
400-
var cert = chainEnum.queryElementAt(i, Ci.nsIX509Cert);
401-
chainArray.push(cert);
402-
var fp = this.ourFingerprint(cert);
403-
fps.push(fp);
404-
chainArrayFpStr = chainArrayFpStr + fp;
323+
var host_ip = "-1";
324+
var httpchannelinternal = subject.QueryInterface(Ci.nsIHttpChannelInternal);
325+
try {
326+
host_ip = httpchannelinternal.remoteAddress;
327+
} catch(e) {
328+
this.log(INFO, "Could not get server IP address.");
405329
}
406-
var chain_hash = sha256_digest(chainArrayFpStr).toUpperCase();
407-
this.log(INFO, "SHA-256 hash of cert chain for "+new String(subject.URI.host)+" is "+ chain_hash);
330+
subject.QueryInterface(Ci.nsIHttpChannel);
331+
var certchain = this.getSSLCert(subject);
332+
if (certchain) {
333+
var chainEnum = certchain.getChain();
334+
var chainArray = [];
335+
var chainArrayFpStr = '';
336+
var fps = [];
337+
for(var i = 0; i < chainEnum.length; i++) {
338+
var cert = chainEnum.queryElementAt(i, Ci.nsIX509Cert);
339+
chainArray.push(cert);
340+
var fp = this.ourFingerprint(cert);
341+
fps.push(fp);
342+
chainArrayFpStr = chainArrayFpStr + fp;
343+
}
344+
var chain_hash = sha256_digest(chainArrayFpStr).toUpperCase();
345+
this.log(INFO, "SHA-256 hash of cert chain for "+new String(subject.URI.host)+" is "+ chain_hash);
408346

409-
if(!this.myGetBoolPref("use_whitelist")) {
410-
this.log(WARN, "Not using whitelist to filter cert chains.");
411-
}
412-
else if (this.isChainWhitelisted(chain_hash)) {
413-
this.log(INFO, "This cert chain is whitelisted. Not submitting.");
414-
return;
415-
} else {
416-
this.log(INFO, "Cert chain is NOT whitelisted. Proceeding with submission.");
417-
}
347+
if(!this.myGetBoolPref("use_whitelist")) {
348+
this.log(WARN, "Not using whitelist to filter cert chains.");
349+
}
350+
else if (this.isChainWhitelisted(chain_hash)) {
351+
this.log(INFO, "This cert chain is whitelisted. Not submitting.");
352+
return;
353+
}
354+
else {
355+
this.log(INFO, "Cert chain is NOT whitelisted. Proceeding with submission.");
356+
}
418357

419-
if (channel.URI.port == -1) {
420-
this.submitChainArray(chainArray, fps, new String(channel.URI.host), channel, host_ip, warning, false, chain_hash);
421-
} else {
422-
this.submitChainArray(chainArray, fps, channel.URI.host+":"+channel.URI.port, channel, host_ip, warning, false, chain_hash);
358+
if (subject.URI.port == -1) {
359+
this.submitChain(chainArray, fps, new String(subject.URI.host), subject, host_ip, false);
360+
} else {
361+
this.submitChain(chainArray, fps, subject.URI.host+":"+subject.URI.port, subject, host_ip, false);
362+
}
423363
}
424364
}
425365
},
@@ -466,95 +406,12 @@ SSLObservatory.prototype = {
466406
return this.prefs.getBoolPref ("extensions.https_everywhere._observatory." + prefstring);
467407
},
468408

469-
loadCertWhitelist: function() {
470-
var loc = "chrome://https-everywhere/content/code/X509ChainWhitelist.json";
471-
var file = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
472-
file.initWithPath(this.HTTPSEverywhere.rw.chromeToPath(loc));
473-
var data = this.HTTPSEverywhere.rw.read(file);
474-
this.whitelist = JSON.parse(data);
475-
},
476-
477-
saveCertWhitelist: function() {
478-
var loc = "chrome://https-everywhere/content/code/X509ChainWhitelist.json";
479-
var file = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
480-
var path = this.HTTPSEverywhere.rw.chromeToPath(loc);
481-
this.log(NOTE,"SAVING cert whitelist to " + path);
482-
file.initWithPath(path);
483-
var store = JSON.stringify(this.whitelist, null, " ");
484-
var data = this.HTTPSEverywhere.rw.write(file, store);
485-
},
486-
487-
maybeUpdateCertWhitelist: function() {
488-
// We aim to update the cert whitelist every 1-3 days
489-
var due_pref = "extensions.https_everywhere._observatory.whitelist_update_due";
490-
var update_due = this.prefs.getIntPref(due_pref);
491-
var now = Date.now() / 1000; // Date.now() is milliseconds, but let's be
492-
// safe with int pref storage on 32 bit
493-
// systems
494-
var next = now + (1 + 2 * Math.random()) * 3600 * 24; // 1-3 days from now
495-
if (update_due == 0) {
496-
// first run
497-
this.prefs.setIntPref(due_pref, next);
498-
return;
499-
}
500-
if (now < update_due) return;
501-
502-
// Updating the certlist might yet fail. But that's okay, we can
503-
// always live with a slightly older one.
504-
this.prefs.setIntPref(due_pref,next);
505-
this.log(INFO, "Next whitelist update due at " + next);
506-
507-
this.updateCertWhitelist();
508-
},
509-
510-
updateCertWhitelist: function() {
511-
// Fetch a new certificate whitelist by XHR and save it to disk
512-
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
513-
.createInstance(Ci.nsIXMLHttpRequest);
514-
515-
req.open("GET", "https://s.eff.org/files/X509ChainWhitelist.json", true);
516-
req.responseType = "json";
517-
518-
var that = this;
519-
req.onreadystatechange = function() {
520-
if (req.status == 200) {
521-
if (typeof req.response != "object") {
522-
that.log(WARN, "INSUFFICIENT WHITELIST OBJECTIVITY");
523-
return false;
524-
}
525-
var whitelist = req.response;
526-
var c = 0;
527-
for (var hash in whitelist) {
528-
c++;
529-
if (typeof hash != "string" || hash.length != HASHLENGTH ) {
530-
that.log(WARN, "UNACCEPTABLE WHITELIST HASH " + hash);
531-
return false;
532-
}
533-
}
534-
if (c < MIN_WHITELIST || c > MAX_WHITELIST) {
535-
that.log(WARN, "Invalid chain whitelist of size " + c);
536-
return false;
537-
}
538-
that.log(WARN, "Routine update of SSL Observatory cert whitelist");
539-
that.whitelist = whitelist;
540-
that.log(NOTE, "Got valid whitelist..." + JSON.stringify(whitelist));
541-
that.saveCertWhitelist();
542-
} else {
543-
that.log(NOTE, "Unexpected response status " + req.status + " fetching chain whitelist");
544-
return false;
545-
}
546-
}
547-
req.send();
548-
},
549-
550409
isChainWhitelisted: function(chainhash) {
551-
if (this.whitelist == null) {
410+
if (X509ChainWhitelist == null) {
552411
this.log(WARN, "Could not find whitelist of popular certificate chains, so ignoring whitelist");
553-
return null;
412+
return false;
554413
}
555-
556-
if (this.whitelist[chainhash] != null) {
557-
this.log(INFO, "whitelist entry for " + chainhash);
414+
if (X509ChainWhitelist[chainhash] != null) {
558415
return true;
559416
}
560417
return false;
@@ -679,7 +536,7 @@ SSLObservatory.prototype = {
679536
return true;
680537
},
681538

682-
submitChainArray: function(certArray, fps, domain, channel, host_ip, warning, resubmitting, chain_hash) {
539+
submitChain: function(certArray, fps, domain, channel, host_ip, resubmitting) {
683540
var base64Certs = [];
684541
// Put all this chain data in one object so that it can be modified by
685542
// subroutines if required
@@ -696,7 +553,7 @@ SSLObservatory.prototype = {
696553
if (Object.keys(this.delayed_submissions).length < MAX_DELAYED)
697554
if (!(c.fps[0] in this.delayed_submissions)) {
698555
this.log(WARN, "Planning to retry submission...");
699-
let retry = function() { this.submitChainArray(certArray, fps, domain, channel, host_ip, warning, true, chain_hash); };
556+
let retry = function() { this.submitChain(certArray, fps, domain, channel, host_ip, true); };
700557
this.delayed_submissions[c.fps[0]] = retry;
701558
}
702559
return;
@@ -717,9 +574,9 @@ SSLObservatory.prototype = {
717574
if (this.myGetBoolPref("testing")) {
718575
reqParams.push("testing=1");
719576
// The server can compute these, but they're a nice test suite item!
720-
reqParams.push("fplist="+JSON.stringify(c.fps));
577+
reqParams.push("fplist="+this.compatJSON.encode(c.fps));
721578
}
722-
reqParams.push("certlist="+JSON.stringify(base64Certs));
579+
reqParams.push("certlist="+this.compatJSON.encode(base64Certs));
723580

724581
if (resubmitting) {
725582
reqParams.push("client_asn="+ASN_UNKNOWABLE);
@@ -765,7 +622,7 @@ SSLObservatory.prototype = {
765622
that.log(DBUG, "Popping one off of outstanding requests, current num is: "+that.current_outstanding_requests);
766623

767624
if (req.status == 200) {
768-
that.log(NOTE, "Successful cert submission for " + domain + " " + chain_hash);
625+
that.log(INFO, "Successful cert submission");
769626
if (!that.prefs.getBoolPref("extensions.https_everywhere._observatory.cache_submitted"))
770627
if (c.fps[0] in that.already_submitted)
771628
delete that.already_submitted[c.fps[0]];
@@ -779,22 +636,20 @@ SSLObservatory.prototype = {
779636
if (++n >= 2) break;
780637
}
781638
} else if (req.status == 403) {
782-
that.log(INFO, "The SSL Observatory has issued a warning about this certificate for " + domain);
783-
if(!warning && that.prefs.getBoolPref("extensions.https_everywhere._observatory.show_cert_warning")) {
784-
try {
785-
var warningObj = JSON.parse(req.responseText);
786-
if (win) that.warnUser(warningObj, win, c.certArray[0]);
787-
} catch(e) {
788-
that.log(WARN, "Failed to process SSL Observatory cert warnings :( " + e);
789-
that.log(WARN, req.responseText);
790-
}
639+
that.log(WARN, "The SSL Observatory has issued a warning about this certificate for " + domain);
640+
try {
641+
var warningObj = JSON.parse(req.responseText);
642+
if (win) that.warnUser(warningObj, win, c.certArray[0]);
643+
} catch(e) {
644+
that.log(WARN, "Failed to process SSL Observatory cert warnings :( " + e);
645+
that.log(WARN, req.responseText);
791646
}
792647
} else {
793648
// Submission failed
794649
if (c.fps[0] in that.already_submitted)
795650
delete that.already_submitted[c.fps[0]];
796651
try {
797-
that.log(WARN, "Cert submission failure "+req.status+ " for " + domain + ": "+req.responseText);
652+
that.log(WARN, "Cert submission failure "+req.status+": "+req.responseText);
798653
} catch(e) {
799654
that.log(WARN, "Cert submission failure and exception: "+e);
800655
}
@@ -803,7 +658,7 @@ SSLObservatory.prototype = {
803658
if (Object.keys(that.delayed_submissions).length < MAX_DELAYED)
804659
if (!(c.fps[0] in that.delayed_submissions)) {
805660
that.log(WARN, "Planning to retry submission...");
806-
let retry = function() { that.submitChainArray(certArray, fps, domain, channel, host_ip, warning, true, chain_hash); };
661+
let retry = function() { that.submitChain(certArray, fps, domain, channel, host_ip, true); };
807662
that.delayed_submissions[c.fps[0]] = retry;
808663
}
809664

0 commit comments

Comments
 (0)