Skip to content

Commit e32384d

Browse files
author
Mike Perry
committed
Use nsIWebProgress to watch for bad cert status warnings.
These aren't visible through the http-on-modify-response path, so we need a different observer. We also don't need onSecurityChange at all. onStateChange for REQUEST is sufficient.
1 parent 3b4b65d commit e32384d

File tree

1 file changed

+96
-37
lines changed

1 file changed

+96
-37
lines changed

src/components/ssl-observatory.js

Lines changed: 96 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,18 @@ function SSLObservatory() {
108108

109109
this.compatJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
110110

111+
// XXX: We shouldn't register any observers or listeners unless the enabled
112+
// pref is set. This goes for the cookie-changed observer above, too..
113+
// (But for this, we need a pref-changed observer)
114+
//
111115
// Register observer
112116
OS.addObserver(this, "http-on-examine-response", false);
113117

118+
var dls = CC['@mozilla.org/docloaderservice;1']
119+
.getService(CI.nsIWebProgress);
120+
dls.addProgressListener(this,
121+
Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
122+
114123
// Register protocolproxyfilter
115124
this.pps = CC["@mozilla.org/network/protocol-proxy-service;1"]
116125
.getService(CI.nsIProtocolProxyService);
@@ -139,7 +148,9 @@ SSLObservatory.prototype = {
139148
[ CI.nsIObserver,
140149
CI.nsIProtocolProxyFilter,
141150
//CI.nsIWifiListener,
142-
CI.nsIBadCertListener2]),
151+
CI.nsIWebProgressListener,
152+
CI.nsISupportsWeakReference,
153+
CI.nsIInterfaceRequestor]),
143154

144155
wrappedJSObject: null, // Initialized by constructor
145156

@@ -149,7 +160,7 @@ SSLObservatory.prototype = {
149160
contractID: SERVICE_CTRID,
150161

151162
// https://developer.mozilla.org/En/How_to_check_the_security_state_of_an_XMLHTTPRequest_over_SSL
152-
getSSLCert: function(channel) {
163+
getSSLCertChain: function(channel) {
153164
try {
154165
// Do we have a valid channel argument?
155166
if (!channel instanceof Ci.nsIChannel) {
@@ -299,6 +310,49 @@ SSLObservatory.prototype = {
299310
return (cert.md5Fingerprint+cert.sha1Fingerprint).replace(":", "", "g");
300311
},
301312

313+
// onSecurity is used to listen for bad cert warnings
314+
onStateChange: function(aProgress, aRequest, aState, aStatus) {
315+
if (!aRequest) return;
316+
var chan = null;
317+
try {
318+
chan = aRequest.QueryInterface(Ci.nsIHttpChannel);
319+
} catch(e) {
320+
return;
321+
}
322+
if (chan) {
323+
if (!this.observatoryActive(chan)) return;
324+
var certchain = this.getSSLCertChain(chan);
325+
if (certchain) {
326+
this.log(INFO, "Got state cert chain for "
327+
+ chan.originalURI.spec + "->" + chan.URI.spec + ", state: " + aState);
328+
this.submitCertChainForChannel(certchain, chan);
329+
}
330+
}
331+
},
332+
333+
// onSecurityStateChange is used to listen for bad cert warnings
334+
// XXX: This is disabled. It does not handle subdocuments, but onStateChange does.
335+
onSecurityChange: function(aProgress, aRequest, aState) {
336+
if (!aRequest) return;
337+
var chan = null;
338+
try {
339+
chan = aRequest.QueryInterface(Ci.nsIHttpChannel);
340+
} catch(e) {
341+
return;
342+
}
343+
if (chan) {
344+
if (!this.observatoryActive(chan)) return;
345+
this.log(INFO, "Got security state change for "
346+
+ chan.originalURI.spec + "->" + chan.URI.spec + ", state: " + aState);
347+
var certchain = this.getSSLCertChain(chan);
348+
if (certchain) {
349+
this.log(INFO, "Got cert chain for "
350+
+ chan.originalURI.spec + "->" + chan.URI.spec + ", state: " + aState);
351+
this.submitCertChainForChannel(certchain, chan);
352+
}
353+
}
354+
},
355+
302356
observe: function(subject, topic, data) {
303357
if (topic == "cookie-changed" && data == "cleared") {
304358
this.already_submitted = {};
@@ -330,46 +384,51 @@ SSLObservatory.prototype = {
330384
var channel = subject;
331385
if (!this.observatoryActive(channel)) return;
332386

387+
var certchain = this.getSSLCertChain(subject);
388+
this.submitCertChainForChannel(certchain, channel);
389+
}
390+
},
391+
392+
submitCertChainForChannel: function(certchain, channel) {
393+
if (certchain) {
333394
var host_ip = "-1";
334-
var httpchannelinternal = subject.QueryInterface(Ci.nsIHttpChannelInternal);
395+
var httpchannelinternal = channel.QueryInterface(Ci.nsIHttpChannelInternal);
335396
try {
336397
host_ip = httpchannelinternal.remoteAddress;
337398
} catch(e) {
338399
this.log(INFO, "Could not get server IP address.");
339400
}
340-
subject.QueryInterface(Ci.nsIHttpChannel);
341-
var certchain = this.getSSLCert(subject);
342-
if (certchain) {
343-
var chainEnum = certchain.getChain();
344-
var chainArray = [];
345-
var chainArrayFpStr = '';
346-
var fps = [];
347-
for(var i = 0; i < chainEnum.length; i++) {
348-
var cert = chainEnum.queryElementAt(i, Ci.nsIX509Cert);
349-
chainArray.push(cert);
350-
var fp = this.ourFingerprint(cert);
351-
fps.push(fp);
352-
chainArrayFpStr = chainArrayFpStr + fp;
353-
}
354-
var chain_hash = sha256_digest(chainArrayFpStr).toUpperCase();
355-
this.log(INFO, "SHA-256 hash of cert chain for "+new String(subject.URI.host)+" is "+ chain_hash);
356401

357-
if(!this.myGetBoolPref("use_whitelist")) {
358-
this.log(WARN, "Not using whitelist to filter cert chains.");
359-
}
360-
else if (this.isChainWhitelisted(chain_hash)) {
361-
this.log(INFO, "This cert chain is whitelisted. Not submitting.");
362-
return;
363-
}
364-
else {
365-
this.log(INFO, "Cert chain is NOT whitelisted. Proceeding with submission.");
366-
}
402+
channel.QueryInterface(Ci.nsIHttpChannel);
403+
var chainEnum = certchain.getChain();
404+
var chainArray = [];
405+
var chainArrayFpStr = '';
406+
var fps = [];
407+
for(var i = 0; i < chainEnum.length; i++) {
408+
var cert = chainEnum.queryElementAt(i, Ci.nsIX509Cert);
409+
chainArray.push(cert);
410+
var fp = this.ourFingerprint(cert);
411+
fps.push(fp);
412+
chainArrayFpStr = chainArrayFpStr + fp;
413+
}
414+
var chain_hash = sha256_digest(chainArrayFpStr).toUpperCase();
415+
this.log(INFO, "SHA-256 hash of cert chain for "+new String(channel.URI.host)+" is "+ chain_hash);
367416

368-
if (subject.URI.port == -1) {
369-
this.submitChain(chainArray, fps, new String(subject.URI.host), subject, host_ip, false);
370-
} else {
371-
this.submitChain(chainArray, fps, subject.URI.host+":"+subject.URI.port, subject, host_ip, false);
372-
}
417+
if(!this.myGetBoolPref("use_whitelist")) {
418+
this.log(WARN, "Not using whitelist to filter cert chains.");
419+
}
420+
else if (this.isChainWhitelisted(chain_hash)) {
421+
this.log(INFO, "This cert chain is whitelisted. Not submitting.");
422+
return;
423+
}
424+
else {
425+
this.log(INFO, "Cert chain is NOT whitelisted. Proceeding with submission.");
426+
}
427+
428+
if (channel.URI.port == -1) {
429+
this.submitChainArray(chainArray, fps, new String(channel.URI.host), channel, host_ip, false);
430+
} else {
431+
this.submitChainArray(chainArray, fps, channel.URI.host+":"+channel.URI.port, channel, host_ip, false);
373432
}
374433
}
375434
},
@@ -575,7 +634,7 @@ SSLObservatory.prototype = {
575634
return true;
576635
},
577636

578-
submitChain: function(certArray, fps, domain, channel, host_ip, resubmitting) {
637+
submitChainArray: function(certArray, fps, domain, channel, host_ip, resubmitting) {
579638
var base64Certs = [];
580639
// Put all this chain data in one object so that it can be modified by
581640
// subroutines if required
@@ -592,7 +651,7 @@ SSLObservatory.prototype = {
592651
if (Object.keys(this.delayed_submissions).length < MAX_DELAYED)
593652
if (!(c.fps[0] in this.delayed_submissions)) {
594653
this.log(WARN, "Planning to retry submission...");
595-
let retry = function() { this.submitChain(certArray, fps, domain, channel, host_ip, true); };
654+
let retry = function() { this.submitChainArray(certArray, fps, domain, channel, host_ip, true); };
596655
this.delayed_submissions[c.fps[0]] = retry;
597656
}
598657
return;
@@ -694,7 +753,7 @@ SSLObservatory.prototype = {
694753
if (Object.keys(that.delayed_submissions).length < MAX_DELAYED)
695754
if (!(c.fps[0] in that.delayed_submissions)) {
696755
that.log(WARN, "Planning to retry submission...");
697-
let retry = function() { that.submitChain(certArray, fps, domain, channel, host_ip, true); };
756+
let retry = function() { that.submitChainArray(certArray, fps, domain, channel, host_ip, true); };
698757
that.delayed_submissions[c.fps[0]] = retry;
699758
}
700759

0 commit comments

Comments
 (0)