mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 02:35:41 +00:00
1f830c96da
# ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D36042 --HG-- extra : source : d3afcafdce650a6f36cebbc126ee93b17f13cf52
173 lines
5.7 KiB
JavaScript
173 lines
5.7 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
var EXPORTED_SYMBOLS = ["AboutNetErrorHandler"];
|
|
|
|
const { RemotePages } = ChromeUtils.import(
|
|
"resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm"
|
|
);
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"BrowserUtils",
|
|
"resource://gre/modules/BrowserUtils.jsm"
|
|
);
|
|
|
|
var AboutNetErrorHandler = {
|
|
_inited: false,
|
|
_topics: [
|
|
"Browser:OpenCaptivePortalPage",
|
|
"Browser:PrimeMitm",
|
|
"Browser:ResetEnterpriseRootsPref",
|
|
"RecordCertErrorLoad",
|
|
],
|
|
|
|
init() {
|
|
this._boundReceiveMessage = this.receiveMessage.bind(this);
|
|
this.pageListener = new RemotePages(["about:certerror", "about:neterror"]);
|
|
for (let topic of this._topics) {
|
|
this.pageListener.addMessageListener(topic, this._boundReceiveMessage);
|
|
}
|
|
this._inited = true;
|
|
|
|
Services.obs.addObserver(this, "captive-portal-login-abort");
|
|
Services.obs.addObserver(this, "captive-portal-login-success");
|
|
},
|
|
|
|
uninit() {
|
|
if (!this._inited) {
|
|
return;
|
|
}
|
|
|
|
for (let topic of this._topics) {
|
|
this.pageListener.removeMessageListener(topic, this._boundReceiveMessage);
|
|
}
|
|
this.pageListener.destroy();
|
|
|
|
Services.obs.removeObserver(this, "captive-portal-login-abort");
|
|
Services.obs.removeObserver(this, "captive-portal-login-success");
|
|
},
|
|
|
|
observe(aSubject, aTopic, aData) {
|
|
switch (aTopic) {
|
|
case "captive-portal-login-abort":
|
|
case "captive-portal-login-success":
|
|
// Send a message to the content when a captive portal is freed
|
|
// so that error pages can refresh themselves.
|
|
this.pageListener.sendAsyncMessage("AboutNetErrorCaptivePortalFreed");
|
|
break;
|
|
}
|
|
},
|
|
|
|
receiveMessage(msg) {
|
|
switch (msg.name) {
|
|
case "Browser:OpenCaptivePortalPage":
|
|
Services.obs.notifyObservers(null, "ensure-captive-portal-tab");
|
|
break;
|
|
case "Browser:PrimeMitm":
|
|
this.primeMitm(msg.target.browser);
|
|
break;
|
|
case "Browser:ResetEnterpriseRootsPref":
|
|
Services.prefs.clearUserPref("security.enterprise_roots.enabled");
|
|
Services.prefs.clearUserPref("security.enterprise_roots.auto-enabled");
|
|
break;
|
|
case "RecordCertErrorLoad":
|
|
Services.telemetry.recordEvent(
|
|
"security.ui.certerror",
|
|
"load",
|
|
"aboutcerterror",
|
|
msg.data.errorCode,
|
|
{
|
|
has_sts: msg.data.has_sts.toString(),
|
|
is_frame: msg.data.is_frame.toString(),
|
|
}
|
|
);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* This function does a canary request to a reliable, maintained endpoint, in
|
|
* order to help network code detect a system-wide man-in-the-middle.
|
|
*/
|
|
primeMitm(browser) {
|
|
// If we already have a mitm canary issuer stored, then don't bother with the
|
|
// extra request. This will be cleared on every update ping.
|
|
if (Services.prefs.getStringPref("security.pki.mitm_canary_issuer", null)) {
|
|
return;
|
|
}
|
|
|
|
let url = Services.prefs.getStringPref(
|
|
"security.certerrors.mitm.priming.endpoint"
|
|
);
|
|
let request = new XMLHttpRequest({ mozAnon: true });
|
|
request.open("HEAD", url);
|
|
request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
|
request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
|
|
|
request.addEventListener("error", event => {
|
|
// Make sure the user is still on the cert error page.
|
|
if (!browser.documentURI.spec.startsWith("about:certerror")) {
|
|
return;
|
|
}
|
|
|
|
let secInfo = request.channel.securityInfo.QueryInterface(
|
|
Ci.nsITransportSecurityInfo
|
|
);
|
|
if (secInfo.errorCodeString != "SEC_ERROR_UNKNOWN_ISSUER") {
|
|
return;
|
|
}
|
|
|
|
// When we get to this point there's already something deeply wrong, it's very likely
|
|
// that there is indeed a system-wide MitM.
|
|
if (secInfo.serverCert && secInfo.serverCert.issuerName) {
|
|
// Grab the issuer of the certificate used in the exchange and store it so that our
|
|
// network-level MitM detection code has a comparison baseline.
|
|
Services.prefs.setStringPref(
|
|
"security.pki.mitm_canary_issuer",
|
|
secInfo.serverCert.issuerName
|
|
);
|
|
|
|
// MitM issues are sometimes caused by software not registering their root certs in the
|
|
// Firefox root store. We might opt for using third party roots from the system root store.
|
|
if (
|
|
Services.prefs.getBoolPref(
|
|
"security.certerrors.mitm.auto_enable_enterprise_roots"
|
|
)
|
|
) {
|
|
if (
|
|
!Services.prefs.getBoolPref("security.enterprise_roots.enabled")
|
|
) {
|
|
// Loading enterprise roots happens on a background thread, so wait for import to finish.
|
|
BrowserUtils.promiseObserved("psm:enterprise-certs-imported").then(
|
|
() => {
|
|
if (browser.documentURI.spec.startsWith("about:certerror")) {
|
|
browser.reload();
|
|
}
|
|
}
|
|
);
|
|
|
|
Services.prefs.setBoolPref(
|
|
"security.enterprise_roots.enabled",
|
|
true
|
|
);
|
|
// Record that this pref was automatically set.
|
|
Services.prefs.setBoolPref(
|
|
"security.enterprise_roots.auto-enabled",
|
|
true
|
|
);
|
|
}
|
|
} else {
|
|
// Need to reload the page to make sure network code picks up the canary issuer pref.
|
|
browser.reload();
|
|
}
|
|
}
|
|
});
|
|
|
|
request.send(null);
|
|
},
|
|
};
|