summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs')
-rw-r--r--toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs107
1 files changed, 92 insertions, 15 deletions
diff --git a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs
index 0402c2f2ca..4a26785da8 100644
--- a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs
@@ -21,6 +21,7 @@ import { XPIExports } from "resource://gre/modules/addons/XPIExports.sys.mjs";
import {
computeSha256HashAsString,
getHashStringForCrypto,
+ hasStrongSignature,
} from "resource://gre/modules/addons/crypto-utils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import {
@@ -94,6 +95,9 @@ const PREF_XPI_ENABLED = "xpinstall.enabled";
const PREF_XPI_DIRECT_WHITELISTED = "xpinstall.whitelist.directRequest";
const PREF_XPI_FILE_WHITELISTED = "xpinstall.whitelist.fileRequest";
const PREF_XPI_WHITELIST_REQUIRED = "xpinstall.whitelist.required";
+const PREF_XPI_WEAK_SIGNATURES_ALLOWED =
+ "xpinstall.signatures.weakSignaturesTemporarilyAllowed";
+const PREF_XPI_WEAK_SIGNATURES_ALLOWED_DEFAULT = true;
const PREF_SELECTED_THEME = "extensions.activeThemeID";
@@ -230,7 +234,15 @@ class Package {
let root = Ci.nsIX509CertDB.AddonsPublicRoot;
if (
- !AppConstants.MOZ_REQUIRE_SIGNING &&
+ (!AppConstants.MOZ_REQUIRE_SIGNING ||
+ // Allow mochitests to switch to dev-root on all channels.
+ Cu.isInAutomation ||
+ // Allow xpcshell tests to switch to dev-root on all channels,
+ // included tests where "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer"
+ // pref is set to false and Cu.isInAutomation is going to be false (e.g. test_signed_langpack.js).
+ // TODO(Bug 1598804): we should be able to remove the following checks once Cu.isAutomation is fixed.
+ (Services.env.exists("XPCSHELL_TEST_PROFILE_DIR") &&
+ Services.appinfo.name === "XPCShell")) &&
Services.prefs.getBoolPref(PREF_XPI_SIGNATURES_DEV_ROOT, false)
) {
root = Ci.nsIX509CertDB.AddonsStageRoot;
@@ -287,7 +299,7 @@ DirPackage = class DirPackage extends Package {
return IOUtils.read(PathUtils.join(this.filePath, ...path));
}
- async verifySignedStateForRoot(addonId, root) {
+ async verifySignedStateForRoot() {
return { signedState: AddonManager.SIGNEDSTATE_UNKNOWN, cert: null };
}
};
@@ -341,8 +353,11 @@ XPIPackage = class XPIPackage extends Package {
aZipReader.close();
}
resolve({
- signedState: getSignedStatus(aRv, cert, addonId),
cert,
+ signedState: getSignedStatus(aRv, cert, addonId),
+ signedTypes: aSignatureInfos?.map(
+ signatureInfo => signatureInfo.signatureAlgorithm
+ ),
});
},
};
@@ -511,17 +526,18 @@ async function loadManifestFromWebManifest(aPackage, aLocation) {
addon.siteOrigin = manifest.install_origins[0];
}
- if (manifest.options_ui) {
+ const { optionsPageProperties } = extension;
+ if (optionsPageProperties) {
// Store just the relative path here, the AddonWrapper getURL
// wrapper maps this to a full URL.
- addon.optionsURL = manifest.options_ui.page;
- if (manifest.options_ui.open_in_tab) {
+ addon.optionsURL = optionsPageProperties.page;
+ if (optionsPageProperties.open_in_tab) {
addon.optionsType = AddonManager.OPTIONS_TYPE_TAB;
} else {
addon.optionsType = AddonManager.OPTIONS_TYPE_INLINE_BROWSER;
}
- addon.optionsBrowserStyle = manifest.options_ui.browser_style;
+ addon.optionsBrowserStyle = optionsPageProperties.browser_style;
}
// WebExtensions don't use iconURLs
@@ -693,9 +709,13 @@ var loadManifest = async function (aPackage, aLocation, aOldAddon) {
addon.rootURI = aPackage.rootURI.spec;
addon.location = aLocation;
- let { signedState, cert } = verifiedSignedState;
+ let { cert, signedState, signedTypes } = verifiedSignedState;
addon.signedState = signedState;
addon.signedDate = cert?.validity?.notBefore / 1000 || null;
+ // An array of the algorithms used by the signatures found in the signed XPI files,
+ // as an array of integers (see nsIAppSignatureInfo_SignatureAlgorithm enum defined
+ // in nsIX509CertDB.idl).
+ addon.signedTypes = signedTypes;
if (!addon.id) {
if (cert) {
@@ -909,18 +929,21 @@ function shouldVerifySignedState(aAddonType, aLocation) {
* The nsIFile for the bundle to check, either a directory or zip file.
* @param {AddonInternal} aAddon
* The add-on object to verify.
- * @returns {Promise<number>}
- * A Promise that resolves to an AddonManager.SIGNEDSTATE_* constant.
+ * @returns {Promise<{ signedState: number, signedTypes: Array<number>}>?}
+ * A Promise that resolves to object including a signedState property set to
+ * an AddonManager.SIGNEDSTATE_* constant and a signedTypes property set to
+ * either an array of Ci.nsIAppSignatureInfo SignatureAlgorithm enum values
+ * or undefined if the file wasn't signed.
*/
export var verifyBundleSignedState = async function (aBundle, aAddon) {
let pkg = Package.get(aBundle);
try {
- let { signedState } = await pkg.verifySignedState(
+ let { signedState, signedTypes } = await pkg.verifySignedState(
aAddon.id,
aAddon.type,
aAddon.location
);
- return signedState;
+ return { signedState, signedTypes };
} finally {
pkg.close();
}
@@ -1633,6 +1656,49 @@ class AddonInstall {
"signature verification failed",
]);
}
+
+ // Restrict install for signed extension only signed with weak signature algorithms, unless the
+ // restriction is explicitly disabled through prefs or enterprise policies.
+ if (
+ !XPIInstall.isWeakSignatureInstallAllowed() &&
+ this.addon.signedDate &&
+ !hasStrongSignature(this.addon)
+ ) {
+ const addonAllowedByPolicies = Services.policies.getExtensionSettings(
+ this.addon.id
+ )?.temporarily_allow_weak_signatures;
+
+ const globallyAllowedByPolicies =
+ Services.policies.getExtensionSettings(
+ "*"
+ )?.temporarily_allow_weak_signatures;
+
+ const allowedByPolicies =
+ (globallyAllowedByPolicies &&
+ (addonAllowedByPolicies || addonAllowedByPolicies == null)) ||
+ addonAllowedByPolicies;
+
+ if (
+ !allowedByPolicies &&
+ (!this.existingAddon || hasStrongSignature(this.existingAddon))
+ ) {
+ // Reject if it is a new install or installing over an existing addon including
+ // strong cryptographic signatures.
+ return Promise.reject([
+ AddonManager.ERROR_CORRUPT_FILE,
+ "install rejected due to the package not including a strong cryptographic signature",
+ ]);
+ }
+
+ // Still allow installs using weak signatures to install if either:
+ // - it is explicitly allowed through Enterprise Policies Settings
+ // - or there is an existing addon with a weak signature.
+ logger.warn(
+ allowedByPolicies
+ ? `Allow weak signature install for ${this.addon.id} XPI due to Enterprise Policies`
+ : `Allow weak signature install over existing "${this.existingAddon.id}" XPI`
+ );
+ }
}
} finally {
pkg.close();
@@ -2321,7 +2387,7 @@ var DownloadAddonInstall = class extends AddonInstall {
}
}
- observe(aSubject, aTopic, aData) {
+ observe() {
// Network is going offline
this.cancel();
}
@@ -2592,7 +2658,7 @@ var DownloadAddonInstall = class extends AddonInstall {
new UpdateChecker(
this.addon,
{
- onUpdateFinished: aAddon => this.downloadCompleted(),
+ onUpdateFinished: () => this.downloadCompleted(),
},
AddonManager.UPDATE_WHEN_ADDON_INSTALLED
);
@@ -3880,7 +3946,7 @@ class SystemAddonInstaller extends DirectoryInstaller {
}
// old system add-on upgrade dirs get automatically removed
- uninstallAddon(aAddon) {}
+ uninstallAddon() {}
}
var AppUpdate = {
@@ -4344,6 +4410,17 @@ export var XPIInstall = {
return Services.prefs.getBoolPref(PREF_XPI_FILE_WHITELISTED, true);
},
+ isWeakSignatureInstallAllowed() {
+ return Services.prefs.getBoolPref(
+ PREF_XPI_WEAK_SIGNATURES_ALLOWED,
+ PREF_XPI_WEAK_SIGNATURES_ALLOWED_DEFAULT
+ );
+ },
+
+ getWeakSignatureInstallPrefName() {
+ return PREF_XPI_WEAK_SIGNATURES_ALLOWED;
+ },
+
/**
* Called to test whether installing XPI add-ons from a URI is allowed.
*