Bug 1244357: Use a shim around the certificate DB to allow the add-ons manager to think that add-ons are signed when they aren't. r=rhelmer

Because the add-ons manager hasn't startup up yet we can replace the certificate
database in xpcshell tests with one that claims add-ons are signed by valid
certificates even when they aren't. This allows us to run tests even in builds
where signing cannot be disabled during for the normal application.

This adds an override for all tests except those that are explicitely testing
signing.

--HG--
extra : commitid : 24s3ni5gVYe
extra : rebase_source : a95571dc3556bb035511eea424ba57e8c7007eba
This commit is contained in:
Dave Townsend 2016-01-29 16:41:18 -08:00
Родитель c48a870a5f
Коммит 2baeb943ca
17 изменённых файлов: 239 добавлений и 39 удалений

Просмотреть файл

@ -92,6 +92,7 @@ def build_dict(config, env=os.environ):
d['tests_enabled'] = substs.get('ENABLE_TESTS') == "1"
d['bin_suffix'] = substs.get('BIN_SUFFIX', '')
d['addon_signing'] = substs.get('MOZ_ADDON_SIGNING') == '1'
d['require_signing'] = substs.get('MOZ_REQUIRE_SIGNING') == '1'
d['official'] = bool(substs.get('MOZILLA_OFFICIAL'))
def guess_platform():

Просмотреть файл

@ -1015,7 +1015,7 @@ add_task(function* test_addonsAndPlugins() {
hasBinaryComponents: false,
installDay: ADDON_INSTALL_DATE,
updateDay: ADDON_INSTALL_DATE,
signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
};
const EXPECTED_PLUGIN_DATA = {

Просмотреть файл

@ -1641,11 +1641,18 @@ function verifyZipSignedState(aFile, aAddon) {
root = Ci.nsIX509CertDB.AddonsStageRoot;
return new Promise(resolve => {
gCertDB.openSignedAppFileAsync(root, aFile, (aRv, aZipReader, aCert) => {
if (aZipReader)
aZipReader.close();
resolve(getSignedStatus(aRv, aCert, aAddon.id));
});
let callback = {
openSignedAppFileFinished: function(aRv, aZipReader, aCert) {
if (aZipReader)
aZipReader.close();
resolve(getSignedStatus(aRv, aCert, aAddon.id));
}
};
// This allows the certificate DB to get the raw JS callback object so the
// test code can pass through objects that XPConnect would reject.
callback.wrappedJSObject = callback;
gCertDB.openSignedAppFileAsync(root, aFile, callback);
});
}
@ -1668,9 +1675,16 @@ function verifyDirSignedState(aDir, aAddon) {
root = Ci.nsIX509CertDB.AddonsStageRoot;
return new Promise(resolve => {
gCertDB.verifySignedDirectoryAsync(root, aDir, (aRv, aCert) => {
resolve(getSignedStatus(aRv, aCert, aAddon.id));
});
let callback = {
verifySignedDirectoryFinished: function(aRv, aCert) {
resolve(getSignedStatus(aRv, aCert, aAddon.id));
}
};
// This allows the certificate DB to get the raw JS callback object so the
// test code can pass through objects that XPConnect would reject.
callback.wrappedJSObject = callback;
gCertDB.verifySignedDirectoryAsync(root, aDir, callback);
});
}

Просмотреть файл

@ -7,6 +7,8 @@ var AM_Ci = Components.interfaces;
const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
@ -296,6 +298,179 @@ function createAppInfo(id, name, version, platformVersion) {
XULAPPINFO_CONTRACTID, XULAppInfoFactory);
}
function getManifestURIForBundle(file) {
if (file.isDirectory()) {
file.append("install.rdf");
if (file.exists()) {
return NetUtil.newURI(file);
}
file.leafName = "manifest.json";
if (file.exists()) {
return NetUtil.newURI(file);
}
throw new Error("No manifest file present");
}
let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
createInstance(AM_Ci.nsIZipReader);
zip.open(file);
try {
let uri = NetUtil.newURI(file);
if (zip.hasEntry("install.rdf")) {
return NetUtil.newURI("jar:" + uri.spec + "!/" + "install.rdf");
}
if (zip.hasEntry("manifest.json")) {
return NetUtil.newURI("jar:" + uri.spec + "!/" + "manifest.json");
}
throw new Error("No manifest file present");
}
finally {
zip.close();
}
}
let getIDForManifest = Task.async(function*(manifestURI) {
// Load it
let inputStream = yield new Promise((resolve, reject) => {
NetUtil.asyncFetch({
uri: manifestURI,
loadUsingSystemPrincipal: true,
}, (inputStream, status) => {
if (status != Components.results.NS_OK)
reject(status);
resolve(inputStream);
});
});
// Get the data as a string
let data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
if (manifestURI.spec.endsWith(".rdf")) {
let rdfParser = AM_Cc["@mozilla.org/rdf/xml-parser;1"].
createInstance(AM_Ci.nsIRDFXMLParser)
let ds = AM_Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
createInstance(AM_Ci.nsIRDFDataSource);
rdfParser.parseString(ds, manifestURI, data);
let rdfService = AM_Cc["@mozilla.org/rdf/rdf-service;1"].
getService(AM_Ci.nsIRDFService);
let rdfID = ds.GetTarget(rdfService.GetResource("urn:mozilla:install-manifest"),
rdfService.GetResource("http://www.mozilla.org/2004/em-rdf#id"),
true);
return rdfID.QueryInterface(AM_Ci.nsIRDFLiteral).Value;
}
else {
let manifest = JSON.parse(data);
return manifest.applications.gecko.id;
}
});
let gUseRealCertChecks = false;
function overrideCertDB(handler) {
// Unregister the real database. This only works because the add-ons manager
// hasn't started up and grabbed the certificate database yet.
let registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
let factory = registrar.getClassObject(CERTDB_CID, AM_Ci.nsIFactory);
registrar.unregisterFactory(CERTDB_CID, factory);
// Get the real DB
let realCertDB = factory.createInstance(null, AM_Ci.nsIX509CertDB);
let verifyCert = Task.async(function*(caller, file, result, cert, callback) {
// If this isn't a callback we can get directly to through JS then just
// pass on the results
if (!callback.wrappedJSObject) {
caller(callback, result, cert);
return;
}
// Bypassing XPConnect allows us to create a fake x509 certificate from
// JS
callback = callback.wrappedJSObject;
if (gUseRealCertChecks || result != Components.results.NS_ERROR_SIGNED_JAR_NOT_SIGNED) {
// If the real DB found a useful result of some kind then pass it on.
caller(callback, result, cert);
return;
}
try {
let manifestURI = getManifestURIForBundle(file);
let id = yield getIDForManifest(manifestURI);
// Make sure to close the open zip file or it will be locked.
if (file.isFile()) {
Services.obs.notifyObservers(file, "flush-cache-entry", "cert-override");
}
let fakeCert = {
commonName: id
}
caller(callback, Components.results.NS_OK, fakeCert);
}
catch (e) {
// If there is any error then just pass along the original results
caller(callback, result, cert);
}
});
let fakeCertDB = {
openSignedAppFileAsync(root, file, callback) {
// First try calling the real cert DB
realCertDB.openSignedAppFileAsync(root, file, (result, zipReader, cert) => {
function call(callback, result, cert) {
callback.openSignedAppFileFinished(result, zipReader, cert);
}
verifyCert(call, file.clone(), result, cert, callback);
});
},
verifySignedDirectoryAsync(root, dir, callback) {
// First try calling the real cert DB
realCertDB.verifySignedDirectoryAsync(root, dir, (result, cert) => {
function call(callback, result, cert) {
callback.verifySignedDirectoryFinished(result, cert);
}
verifyCert(call, dir.clone(), result, cert, callback);
});
},
QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIX509CertDB])
};
for (let property of Object.keys(realCertDB)) {
if (property in fakeCertDB) {
continue;
}
if (typeof realCertDB[property] == "function") {
fakeCertDB[property] = realCertDB[property].bind(realCertDB);
}
}
let certDBFactory = {
createInstance: function(outer, iid) {
if (outer != null) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return fakeCertDB.QueryInterface(iid);
}
};
registrar.registerFactory(CERTDB_CID, "CertDB",
CERTDB_CONTRACTID, certDBFactory);
}
overrideCertDB();
/**
* Tests that an add-on does appear in the crash report annotations, if
* crash reporting is enabled. The test will fail if the add-on is not in the
@ -1736,8 +1911,8 @@ Services.prefs.setCharPref("extensions.hotfix.id", "");
Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0");
Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, "0");
// Disable signature checks for most tests
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
// Ensure signature checks are enabled by default
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
// Register a temporary directory for the tests.
const gTmpD = gProfD.clone();

Просмотреть файл

@ -11,6 +11,9 @@ var CacheFlushObserver = {
observe: function(aSubject, aTopic, aData) {
if (aTopic != "flush-cache-entry")
return;
// Ignore flushes triggered by the fake cert DB
if (aData == "cert-override")
return;
do_check_true(gExpectedFile != null);
do_check_true(aSubject instanceof AM_Ci.nsIFile);

Просмотреть файл

@ -285,6 +285,7 @@ add_task(function*() {
gAppInfo.browserTabsRemoteAutostart = true;
Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
Services.prefs.setCharPref("extensions.hotfix.id", ID);
Services.prefs.setBoolPref("extensions.hotfix.cert.checkAttributes", false);
yield check_normal();
});

Просмотреть файл

@ -49,7 +49,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_true(proxyFile.exists());

Просмотреть файл

@ -1,5 +1,5 @@
// Enable signature checks for these tests
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
gUseRealCertChecks = true;
// Disable update security
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);

Просмотреть файл

@ -1,5 +1,5 @@
// Enable signature checks for these tests
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
gUseRealCertChecks = true;
// Disable update security
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);

Просмотреть файл

@ -6,6 +6,8 @@ Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
// The test add-ons were signed by the dev root
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_DEV_ROOT, true);
gUseRealCertChecks = true;
const DATA = "data/signing_checks/";
const ID_63 = "123456789012345678901234567890123456789012345@tests.mozilla.org"
@ -35,17 +37,18 @@ add_task(function* test_working() {
}
});
// Installs the cases that should be broken
// Checks the cases that should be broken
add_task(function* test_broken() {
yield promiseInstallAllFiles([do_get_file(DATA + "long_63_hash.xpi"),
do_get_file(DATA + "long_64_hash.xpi")]);
function promiseInstallForFile(file) {
return new Promise(resolve => AddonManager.getInstallForFile(file, resolve));
}
let addons = yield promiseAddonsByIDs([ID_63, ID_64]);
let promises = [promiseInstallForFile(do_get_file(DATA + "long_63_hash.xpi")),
promiseInstallForFile(do_get_file(DATA + "long_64_hash.xpi"))];
let installs = yield Promise.all(promises);
for (let addon of addons) {
do_check_neq(addon, null);
do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
addon.uninstall();
for (let install of installs) {
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
}
});

Просмотреть файл

@ -1,5 +1,5 @@
// Enable signature checks for these tests
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
gUseRealCertChecks = true;
// Disable update security
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
// Allow attempting to show the compatibility UI which should not happen

Просмотреть файл

@ -1,5 +1,5 @@
// Enable signature checks for these tests
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
gUseRealCertChecks = true;
// Disable update security
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);

Просмотреть файл

@ -1,5 +1,6 @@
// Disable update security
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
gUseRealCertChecks = true;
const DATA = "data/signing_checks/";
const ID = "test@tests.mozilla.org";
@ -54,6 +55,7 @@ function run_test() {
// Updating the pref without changing the app version won't disable add-ons
// immediately but will after a signing check
add_task(function*() {
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
startupManager();
// Install the signed add-on

Просмотреть файл

@ -1,5 +1,5 @@
// Enable signature checks for these tests
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
gUseRealCertChecks = true;
// Disable update security
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);

Просмотреть файл

@ -57,7 +57,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
yield promiseRestartManager();
@ -87,7 +87,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
// test that an unpacked add-on works too
let tempdir = gTmpD.clone();
@ -125,7 +125,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
restartManager();
@ -142,7 +142,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
unpacked_addon.remove(true);
addon.uninstall();
@ -226,7 +226,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
addon.uninstall();
@ -243,7 +243,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
unpacked_addon.remove(true);
addon.uninstall();
@ -314,7 +314,7 @@ add_task(function*() {
do_check_false(tempAddon.appDisabled);
do_check_true(tempAddon.isActive);
do_check_eq(tempAddon.type, "extension");
do_check_eq(tempAddon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(tempAddon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
tempAddon.uninstall();
unpacked_addon.remove(true);
@ -333,7 +333,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
addon.uninstall();
@ -368,7 +368,7 @@ add_task(function*(){
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
let tempdir = gTmpD.clone();
writeInstallRDFToDir({
@ -423,7 +423,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
try {
yield AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));

Просмотреть файл

@ -55,7 +55,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
let uri = do_get_addon_root_uri(profileDir, ID);
@ -82,7 +82,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
let file = getFileForAddon(profileDir, ID);
do_check_true(file.exists());
@ -135,7 +135,7 @@ add_task(function*() {
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_MISSING : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
let file = getFileForAddon(profileDir, ID);
do_check_true(file.exists());

Просмотреть файл

@ -239,6 +239,7 @@ fail-if = buildapp == "mulet" || os == "android"
[test_safemode.js]
[test_signed_updatepref.js]
run-if = addon_signing
skip-if = require_signing
[test_signed_verify.js]
run-if = addon_signing
[test_signed_inject.js]