mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
bug 1591927: remote: implement Security.setIgnoreCertificateErrors; r=remote-protocol-reviewers,maja_zf
This implements an all-or-nothing insecure sweeping override that bypasses security exceptions when loading documents with invalid or otherwise bad TLS certificates. Differential Revision: https://phabricator.services.mozilla.com/D50838 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
4b22ebf93f
commit
ea1f27965e
@ -17,5 +17,6 @@ XPCOMUtils.defineLazyModuleGetters(ParentProcessDomains, {
|
||||
Input: "chrome://remote/content/domains/parent/Input.jsm",
|
||||
Network: "chrome://remote/content/domains/parent/Network.jsm",
|
||||
Page: "chrome://remote/content/domains/parent/Page.jsm",
|
||||
Security: "chrome://remote/content/domains/parent/Security.jsm",
|
||||
Target: "chrome://remote/content/domains/parent/Target.jsm",
|
||||
});
|
||||
|
55
remote/domains/parent/Security.jsm
Normal file
55
remote/domains/parent/Security.jsm
Normal file
@ -0,0 +1,55 @@
|
||||
/* 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 = ["Security"];
|
||||
|
||||
const { Domain } = ChromeUtils.import(
|
||||
"chrome://remote/content/domains/Domain.jsm"
|
||||
);
|
||||
|
||||
const { Preferences } = ChromeUtils.import(
|
||||
"resource://gre/modules/Preferences.jsm"
|
||||
);
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetters(this, {
|
||||
sss: ["@mozilla.org/ssservice;1", "nsISiteSecurityService"],
|
||||
certOverrideService: [
|
||||
"@mozilla.org/security/certoverride;1",
|
||||
"nsICertOverrideService",
|
||||
],
|
||||
});
|
||||
|
||||
const CERT_PINNING_ENFORCEMENT_PREF = "security.cert_pinning.enforcement_level";
|
||||
const HSTS_PRELOAD_LIST_PREF = "network.stricttransportsecurity.preloadlist";
|
||||
|
||||
class Security extends Domain {
|
||||
destructor() {
|
||||
this.setIgnoreCertificateErrors({ ignore: false });
|
||||
}
|
||||
|
||||
setIgnoreCertificateErrors({ ignore }) {
|
||||
if (ignore) {
|
||||
// make it possible to register certificate overrides for domains
|
||||
// that use HSTS or HPKP
|
||||
Preferences.set(HSTS_PRELOAD_LIST_PREF, false);
|
||||
Preferences.set(CERT_PINNING_ENFORCEMENT_PREF, 0);
|
||||
} else {
|
||||
Preferences.reset(HSTS_PRELOAD_LIST_PREF);
|
||||
Preferences.reset(CERT_PINNING_ENFORCEMENT_PREF);
|
||||
|
||||
// clear collected HSTS and HPKP state
|
||||
sss.clearAll();
|
||||
sss.clearPreloads();
|
||||
}
|
||||
|
||||
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
|
||||
ignore
|
||||
);
|
||||
}
|
||||
}
|
@ -52,6 +52,7 @@ remote.jar:
|
||||
content/domains/parent/network/NetworkObserver.jsm (domains/parent/network/NetworkObserver.jsm)
|
||||
content/domains/parent/Page.jsm (domains/parent/Page.jsm)
|
||||
content/domains/parent/page/DialogHandler.jsm (domains/parent/page/DialogHandler.jsm)
|
||||
content/domains/parent/Security.jsm (domains/parent/Security.jsm)
|
||||
content/domains/parent/Target.jsm (domains/parent/Target.jsm)
|
||||
content/domains/parent/target/TabManager.jsm (domains/parent/target/TabManager.jsm)
|
||||
|
||||
|
8
remote/test/browser/security/browser.ini
Normal file
8
remote/test/browser/security/browser.ini
Normal file
@ -0,0 +1,8 @@
|
||||
[DEFAULT]
|
||||
tags = remote
|
||||
subsuite = remote
|
||||
prefs = remote.enabled=true
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
[browser_setIgnoreCertificateErrors.js]
|
@ -0,0 +1,159 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
STATE_IS_SECURE,
|
||||
STATE_IS_BROKEN,
|
||||
STATE_IS_INSECURE,
|
||||
} = Ci.nsIWebProgressListener;
|
||||
|
||||
// from ../../../build/pgo/server-locations.txt
|
||||
const NO_CERT = "https://nocert.example.com:443";
|
||||
const SELF_SIGNED = "https://self-signed.example.com:443";
|
||||
const UNTRUSTED = "https://untrusted.example.com:443";
|
||||
const EXPIRED = "https://expired.example.com:443";
|
||||
const MISMATCH_EXPIRED = "https://mismatch.expired.example.com:443";
|
||||
const MISMATCH_UNTRUSTED = "https://mismatch.untrusted.example.com:443";
|
||||
const UNTRUSTED_EXPIRED = "https://untrusted-expired.example.com:443";
|
||||
const MISMATCH_UNTRUSTED_EXPIRED =
|
||||
"https://mismatch.untrusted-expired.example.com:443";
|
||||
|
||||
const BAD_CERTS = [
|
||||
NO_CERT,
|
||||
SELF_SIGNED,
|
||||
UNTRUSTED,
|
||||
EXPIRED,
|
||||
MISMATCH_EXPIRED,
|
||||
MISMATCH_UNTRUSTED,
|
||||
UNTRUSTED_EXPIRED,
|
||||
MISMATCH_UNTRUSTED_EXPIRED,
|
||||
];
|
||||
|
||||
function getConnectionState() {
|
||||
// prevents items that are being lazy loaded causing issues
|
||||
document.getElementById("identity-box").click();
|
||||
gIdentityHandler.refreshIdentityPopup();
|
||||
return document.getElementById("identity-popup").getAttribute("connection");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the security state of the page with what is expected.
|
||||
* Returns one of "secure", "broken", "insecure", or "unknown".
|
||||
*/
|
||||
function isSecurityState(browser, expectedState) {
|
||||
const ui = browser.securityUI;
|
||||
if (!ui) {
|
||||
ok(false, "No security UI to get the security state");
|
||||
return;
|
||||
}
|
||||
|
||||
const isSecure = ui.state & STATE_IS_SECURE;
|
||||
const isBroken = ui.state & STATE_IS_BROKEN;
|
||||
const isInsecure = ui.state & STATE_IS_INSECURE;
|
||||
|
||||
let actualState;
|
||||
if (isSecure && !(isBroken || isInsecure)) {
|
||||
actualState = "secure";
|
||||
} else if (isBroken && !(isSecure || isInsecure)) {
|
||||
actualState = "broken";
|
||||
} else if (isInsecure && !(isSecure || isBroken)) {
|
||||
actualState = "insecure";
|
||||
} else {
|
||||
actualState = "unknown";
|
||||
}
|
||||
|
||||
is(
|
||||
expectedState,
|
||||
actualState,
|
||||
`Expected state is ${expectedState} and actual state is ${actualState}`
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function testDefault({ Security }) {
|
||||
for (const url of BAD_CERTS) {
|
||||
info(`Navigating to ${url}`);
|
||||
const loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
|
||||
await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
|
||||
await loaded;
|
||||
|
||||
is(
|
||||
getConnectionState(),
|
||||
"cert-error-page",
|
||||
"Security error page is present"
|
||||
);
|
||||
isSecurityState(gBrowser, "insecure");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function testIgnore({ Security }) {
|
||||
info("Enable security certificate override");
|
||||
await Security.setIgnoreCertificateErrors({ ignore: true });
|
||||
|
||||
for (const url of BAD_CERTS) {
|
||||
info(`Navigating to ${url}`);
|
||||
await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
is(
|
||||
getConnectionState(),
|
||||
"secure-cert-user-overridden",
|
||||
"Security certificate was overridden by user"
|
||||
);
|
||||
isSecurityState(gBrowser, "secure");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function testUnignore({ Security }) {
|
||||
info("Disable security certificate override");
|
||||
await Security.setIgnoreCertificateErrors({ ignore: false });
|
||||
|
||||
for (const url of BAD_CERTS) {
|
||||
info(`Navigating to ${url}`);
|
||||
const loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
|
||||
await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
|
||||
await loaded;
|
||||
|
||||
is(
|
||||
getConnectionState(),
|
||||
"cert-error-page",
|
||||
"Security error page is present"
|
||||
);
|
||||
isSecurityState(gBrowser, "insecure");
|
||||
}
|
||||
});
|
||||
|
||||
// smoke test for unignored -> ignored -> unignored
|
||||
add_task(async function testToggle({ Security }) {
|
||||
let loaded;
|
||||
|
||||
info("Enable security certificate override");
|
||||
await Security.setIgnoreCertificateErrors({ ignore: true });
|
||||
|
||||
info(`Navigating to ${UNTRUSTED} having set the override`);
|
||||
await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, UNTRUSTED);
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
is(
|
||||
getConnectionState(),
|
||||
"secure-cert-user-overridden",
|
||||
"Security certificate was overridden by user"
|
||||
);
|
||||
isSecurityState(gBrowser, "secure");
|
||||
|
||||
info("Disable security certificate override");
|
||||
await Security.setIgnoreCertificateErrors({ ignore: false });
|
||||
|
||||
info(`Navigating to ${UNTRUSTED} having unset the override`);
|
||||
loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
|
||||
await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, UNTRUSTED);
|
||||
await loaded;
|
||||
|
||||
is(
|
||||
getConnectionState(),
|
||||
"cert-error-page",
|
||||
"Security error page is present by default"
|
||||
);
|
||||
isSecurityState(gBrowser, "insecure");
|
||||
});
|
11
remote/test/browser/security/head.js
Normal file
11
remote/test/browser/security/head.js
Normal file
@ -0,0 +1,11 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from ../head.js */
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/remote/test/browser/head.js",
|
||||
this
|
||||
);
|
@ -9,6 +9,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
"browser/network/browser.ini",
|
||||
"browser/page/browser.ini",
|
||||
"browser/runtime/browser.ini",
|
||||
"browser/security/browser.ini",
|
||||
"browser/target/browser.ini",
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user