1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 var EXPORTED_SYMBOLS = ["NetErrorParent"];
8 const { PrivateBrowsingUtils } = ChromeUtils.import(
9 "resource://gre/modules/PrivateBrowsingUtils.jsm"
11 const { HomePage } = ChromeUtils.import("resource:///modules/HomePage.jsm");
13 const { TelemetryController } = ChromeUtils.import(
14 "resource://gre/modules/TelemetryController.jsm"
17 const PREF_SSL_IMPACT_ROOTS = [
18 "security.tls.version.",
25 ChromeUtils.defineModuleGetter(
28 "resource://gre/modules/BrowserUtils.jsm"
31 class CaptivePortalObserver {
34 Services.obs.addObserver(this, "captive-portal-login-abort");
35 Services.obs.addObserver(this, "captive-portal-login-success");
39 Services.obs.removeObserver(this, "captive-portal-login-abort");
40 Services.obs.removeObserver(this, "captive-portal-login-success");
43 observe(aSubject, aTopic, aData) {
45 case "captive-portal-login-abort":
46 case "captive-portal-login-success":
47 // Send a message to the content when a captive portal is freed
48 // so that error pages can refresh themselves.
49 this.actor.sendAsyncMessage("AboutNetErrorCaptivePortalFreed");
55 class NetErrorParent extends JSWindowActorParent {
58 this.captivePortalObserver = new CaptivePortalObserver(this);
62 if (this.captivePortalObserver) {
63 this.captivePortalObserver.stop();
68 return this.browsingContext.top.embedderElement;
71 hasChangedCertPrefs() {
72 let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
73 return prefs.concat(Services.prefs.getChildList(root));
75 for (let prefName of prefSSLImpact) {
76 if (Services.prefs.prefHasUserValue(prefName)) {
84 async ReportBlockingError(bcID, scheme, host, port, path, xfoAndCspInfo) {
85 // For reporting X-Frame-Options error and CSP: frame-ancestors errors, We
86 // are collecting 4 pieces of information.
87 // 1. The X-Frame-Options in the response header.
88 // 2. The CSP: frame-ancestors in the response header.
89 // 3. The URI of the frame who triggers this error.
90 // 4. The top-level URI which loads the frame.
92 // We will exclude the query strings from the reporting URIs.
94 // More details about the data we send can be found in
95 // https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/data/xfocsp-error-report-ping.html
98 let topBC = BrowsingContext.get(bcID).top;
99 let topURI = topBC.currentWindowGlobal.documentURI;
101 // Get the URLs without query strings.
102 let frame_uri = `${scheme}://${host}${port == -1 ? "" : ":" + port}${path}`;
103 let top_uri = `${topURI.scheme}://${topURI.hostPort}${topURI.filePath}`;
105 TelemetryController.submitExternalPing(
106 "xfocsp-error-report",
109 frame_hostname: host,
110 top_hostname: topURI.host,
114 { addClientId: false, addEnvironment: false }
119 * Return the default start page for the cases when the user's own homepage is
120 * infected, so we can get them somewhere safe.
122 getDefaultHomePage(win) {
123 if (PrivateBrowsingUtils.isWindowPrivate(win)) {
124 return win.BROWSER_NEW_TAB_URL;
126 let url = HomePage.getDefault();
127 // If url is a pipe-delimited set of pages, just take the first one.
128 if (url.includes("|")) {
129 url = url.split("|")[0];
135 * Re-direct the browser to the previous page or a known-safe page if no
136 * previous page is found in history. This function is used when the user
137 * browses to a secure page with certificate issues and is presented with
138 * about:certerror. The "Go Back" button should take the user to the previous
139 * or a default start page so that even when their own homepage is on a server
140 * that has certificate errors, we can get them somewhere safe.
142 goBackFromErrorPage(browser) {
143 if (!browser.canGoBack) {
144 // If the unsafe page is the first or the only one in history, go to the
146 browser.loadURI(this.getDefaultHomePage(browser.ownerGlobal), {
147 triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
155 * This function does a canary request to a reliable, maintained endpoint, in
156 * order to help network code detect a system-wide man-in-the-middle.
159 // If we already have a mitm canary issuer stored, then don't bother with the
160 // extra request. This will be cleared on every update ping.
161 if (Services.prefs.getStringPref("security.pki.mitm_canary_issuer", null)) {
165 let url = Services.prefs.getStringPref(
166 "security.certerrors.mitm.priming.endpoint"
168 let request = new XMLHttpRequest({ mozAnon: true });
169 request.open("HEAD", url);
170 request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
171 request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
173 request.addEventListener("error", event => {
174 // Make sure the user is still on the cert error page.
175 if (!browser.documentURI.spec.startsWith("about:certerror")) {
179 let secInfo = request.channel.securityInfo;
180 if (secInfo.errorCodeString != "SEC_ERROR_UNKNOWN_ISSUER") {
184 // When we get to this point there's already something deeply wrong, it's very likely
185 // that there is indeed a system-wide MitM.
186 if (secInfo.serverCert && secInfo.serverCert.issuerName) {
187 // Grab the issuer of the certificate used in the exchange and store it so that our
188 // network-level MitM detection code has a comparison baseline.
189 Services.prefs.setStringPref(
190 "security.pki.mitm_canary_issuer",
191 secInfo.serverCert.issuerName
194 // MitM issues are sometimes caused by software not registering their root certs in the
195 // Firefox root store. We might opt for using third party roots from the system root store.
197 Services.prefs.getBoolPref(
198 "security.certerrors.mitm.auto_enable_enterprise_roots"
202 !Services.prefs.getBoolPref("security.enterprise_roots.enabled")
204 // Loading enterprise roots happens on a background thread, so wait for import to finish.
205 lazy.BrowserUtils.promiseObserved(
206 "psm:enterprise-certs-imported"
208 if (browser.documentURI.spec.startsWith("about:certerror")) {
213 Services.prefs.setBoolPref(
214 "security.enterprise_roots.enabled",
217 // Record that this pref was automatically set.
218 Services.prefs.setBoolPref(
219 "security.enterprise_roots.auto-enabled",
224 // Need to reload the page to make sure network code picks up the canary issuer pref.
233 displayOfflineSupportPage(supportPageSlug) {
234 const AVAILABLE_PAGES = ["connection-not-secure", "time-errors"];
235 if (!AVAILABLE_PAGES.includes(supportPageSlug)) {
237 `[Not supported] Offline support is not yet available for ${supportPageSlug} errors.`
242 let offlinePagePath = `chrome://browser/content/certerror/supportpages/${supportPageSlug}.html`;
243 let triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
244 this.browser.loadURI(offlinePagePath, { triggeringPrincipal });
247 receiveMessage(message) {
248 switch (message.name) {
249 case "Browser:EnableOnlineMode":
250 // Reset network state and refresh the page.
251 Services.io.offline = false;
252 this.browser.reload();
254 case "Browser:OpenCaptivePortalPage":
255 this.browser.ownerGlobal.CaptivePortalWatcher.ensureCaptivePortalTab();
257 case "Browser:PrimeMitm":
258 this.primeMitm(this.browser);
260 case "Browser:ResetEnterpriseRootsPref":
261 Services.prefs.clearUserPref("security.enterprise_roots.enabled");
262 Services.prefs.clearUserPref("security.enterprise_roots.auto-enabled");
264 case "Browser:ResetSSLPreferences":
265 let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
266 return prefs.concat(Services.prefs.getChildList(root));
268 for (let prefName of prefSSLImpact) {
269 Services.prefs.clearUserPref(prefName);
271 this.browser.reload();
273 case "Browser:SSLErrorGoBack":
274 this.goBackFromErrorPage(this.browser);
276 case "Browser:SSLErrorReportTelemetry":
277 let reportStatus = message.data.reportStatus;
279 .getHistogramById("TLS_ERROR_REPORT_UI")
282 case "GetChangedCertPrefs":
283 let hasChangedCertPrefs = this.hasChangedCertPrefs();
284 this.sendAsyncMessage("HasChangedCertPrefs", {
288 case "ReportBlockingError":
289 this.ReportBlockingError(
290 this.browsingContext.id,
295 message.data.xfoAndCspInfo
298 case "DisplayOfflineSupportPage":
299 this.displayOfflineSupportPage(message.data.supportPageSlug);
301 case "Browser:CertExceptionError":
302 switch (message.data.elementId) {
303 case "viewCertificate": {
304 let window = this.browser.ownerGlobal;
305 let certs = message.data.failedCertChain.map(certBase64 =>
306 encodeURIComponent(certBase64)
308 let certsStringURL = certs.map(elem => `cert=${elem}`);
309 certsStringURL = certsStringURL.join("&");
310 let url = `about:certificate?${certsStringURL}`;
311 window.switchToTabHavingURI(url, true, {});