Bug 1172028: Sideloaded add-ons without full signing shouldn't ever be loaded. r=dveditz

This commit is contained in:
Dave Townsend 2015-06-05 11:46:11 -07:00
parent c1ce312bb6
commit 909bcb3a36
4 changed files with 68 additions and 7 deletions

View File

@ -221,6 +221,9 @@ class RefTest(object):
# And for about:newtab content fetch and pings.
prefs['browser.newtabpage.directory.source'] = 'data:application/json,{"reftest":1}'
prefs['browser.newtabpage.directory.ping'] = ''
# Only allow add-ons from the profile and app and allow foreign injection
prefs["extensions.enabledScopes"] = 5;
prefs["extensions.autoDisableScopes"] = 0;
# Allow unsigned add-ons
prefs['xpinstall.signatures.required'] = False

View File

@ -69,6 +69,7 @@ user_pref("experiments.manifest.uri", "http://%(server)s/experiments-dummy/manif
// Only load extensions from the application and user profile
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
user_pref("extensions.enabledScopes", 5);
user_pref("extensions.autoDisableScopes", 0);
// Disable metadata caching for installed add-ons by default
user_pref("extensions.getAddons.cache.enabled", false);
// Disable intalling any distribution add-ons

View File

@ -657,8 +657,12 @@ function isUsableAddon(aAddon) {
if (aAddon.type == "theme" && aAddon.internalName == XPIProvider.defaultSkin)
return true;
if (mustSign(aAddon.type) && aAddon.signedState <= AddonManager.SIGNEDSTATE_MISSING)
return false;
if (mustSign(aAddon.type)) {
if (aAddon.signedState <= AddonManager.SIGNEDSTATE_MISSING)
return false;
if (aAddon.foreignInstall && aAddon.signedState < AddonManager.SIGNEDSTATE_SIGNED)
return false;
}
if (aAddon.blocklistState == Blocklist.STATE_BLOCKED)
return false;
@ -2751,9 +2755,12 @@ this.XPIProvider = {
let jsonfile = stagingDir.clone();
jsonfile.append(id + ".json");
// Assume this was a foreign install if there is no cached metadata file
let foreignInstall = !jsonfile.exists();
let addon;
try {
aManifests[aLocation.name][id] = syncLoadManifestFromFile(stageDirEntry);
addon = syncLoadManifestFromFile(stageDirEntry);
}
catch (e) {
logger.error("Unable to read add-on manifest from " + stageDirEntry.path, e);
@ -2763,9 +2770,9 @@ this.XPIProvider = {
continue;
}
let addon = aManifests[aLocation.name][id];
if ((addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) && mustSign(addon.type)) {
if (mustSign(addon.type) &&
(addon.signedState <= AddonManager.SIGNEDSTATE_MISSING ||
(foreignInstall && addon.signedState < AddonManager.SIGNEDSTATE_SIGNED))) {
logger.warn("Refusing to install staged add-on " + id + " with signed state " + addon.signedState);
seenFiles.push(stageDirEntry.leafName);
seenFiles.push(jsonfile.leafName);
@ -2774,7 +2781,7 @@ this.XPIProvider = {
// Check for a cached metadata for this add-on, it may contain updated
// compatibility information
if (jsonfile.exists()) {
if (!foreignInstall) {
logger.debug("Found updated metadata for " + id + " in " + aLocation.name);
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
@ -2785,6 +2792,10 @@ this.XPIProvider = {
fis.init(jsonfile, -1, 0, 0);
let metadata = json.decodeFromStream(fis, jsonfile.fileSize);
addon.importMetadata(metadata);
// Pass this through to addMetadata so it knows this add-on was
// likely installed through the UI
aManifests[aLocation.name][id] = addon;
}
catch (e) {
// If some data can't be recovered from the cached metadata then it
@ -3380,6 +3391,9 @@ this.XPIProvider = {
newAddon.updateDate = aAddonState.mtime;
newAddon.foreignInstall = isDetectedInstall;
// appDisabled depends on whether the add-on is a foreignInstall so update
newAddon.appDisabled = !isUsableAddon(newAddon);
if (aMigrateData) {
// If there is migration data then apply it.
logger.debug("Migrating data from old database");

View File

@ -9,6 +9,7 @@ const ADDONS = {
unsigned: "unsigned_bootstrap_2.xpi",
badid: "signed_bootstrap_badid_2.xpi",
signed: "signed_bootstrap_2.xpi",
preliminary: "preliminary_bootstrap_2.xpi",
},
nonbootstrap: {
unsigned: "unsigned_nonbootstrap_2.xpi",
@ -334,3 +335,45 @@ add_task(function*() {
yield promiseShutdownManager();
resetPrefs();
});
// Only fully-signed sideloaded add-ons should work
add_task(function*() {
let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.preliminary), profileDir, ID);
startupManager();
// Currently we leave the sideloaded add-on there but just don't run it
let addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
do_check_true(addon.appDisabled);
do_check_false(addon.isActive);
do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_PRELIMINARY);
do_check_eq(getActiveVersion(), -1);
addon.uninstall();
yield promiseShutdownManager();
resetPrefs();
do_check_false(file.exists());
clearCache(file);
});
add_task(function*() {
let stage = profileDir.clone();
stage.append("staged");
let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.preliminary), stage, ID);
startupManager();
// Should have refused to install preliminarily signed version
let addon = yield promiseAddonByID(ID);
do_check_eq(addon, null);
do_check_eq(getActiveVersion(), -1);
do_check_false(file.exists());
clearCache(file);
yield promiseShutdownManager();
resetPrefs();
});