зеркало из https://github.com/mozilla/gecko-dev.git
Bug 829934 - Implement a hash verifier for webapp/mini manifest r=ferjm
This commit is contained in:
Родитель
5b76466f52
Коммит
2ca010d058
|
@ -58,6 +58,8 @@ this.AppsUtils = {
|
||||||
updateTime: aApp.updateTime,
|
updateTime: aApp.updateTime,
|
||||||
etag: aApp.etag,
|
etag: aApp.etag,
|
||||||
packageEtag: aApp.packageEtag,
|
packageEtag: aApp.packageEtag,
|
||||||
|
manifestHash: aApp.manifestHash,
|
||||||
|
packageHash: aApp.packageHash,
|
||||||
installerAppId: aApp.installerAppId || Ci.nsIScriptSecurityManager.NO_APP_ID,
|
installerAppId: aApp.installerAppId || Ci.nsIScriptSecurityManager.NO_APP_ID,
|
||||||
installerIsBrowser: !!aApp.installerIsBrowser
|
installerIsBrowser: !!aApp.installerIsBrowser
|
||||||
};
|
};
|
||||||
|
|
|
@ -1124,6 +1124,77 @@ this.DOMApplicationRegistry = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Returns the MD5 hash of a file, doing async IO off the main thread.
|
||||||
|
computeFileHash: function computeFileHash(aFile, aCallback) {
|
||||||
|
Cu.import("resource://gre/modules/osfile.jsm");
|
||||||
|
const CHUNK_SIZE = 16384;
|
||||||
|
|
||||||
|
// Return the two-digit hexadecimal code for a byte.
|
||||||
|
function toHexString(charCode) {
|
||||||
|
return ("0" + charCode.toString(16)).slice(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasher = Cc["@mozilla.org/security/hash;1"]
|
||||||
|
.createInstance(Ci.nsICryptoHash);
|
||||||
|
// We want to use the MD5 algorithm.
|
||||||
|
hasher.init(hasher.MD5);
|
||||||
|
|
||||||
|
OS.File.open(aFile.path, { read: true }).then(
|
||||||
|
function opened(file) {
|
||||||
|
let readChunk = function readChunk() {
|
||||||
|
file.read(CHUNK_SIZE).then(
|
||||||
|
function readSuccess(array) {
|
||||||
|
hasher.update(array, array.length);
|
||||||
|
if (array.length == CHUNK_SIZE) {
|
||||||
|
readChunk();
|
||||||
|
} else {
|
||||||
|
// We're passing false to get the binary hash and not base64.
|
||||||
|
let hash = hasher.finish(false);
|
||||||
|
// convert the binary hash data to a hex string.
|
||||||
|
aCallback([toHexString(hash.charCodeAt(i)) for (i in hash)]
|
||||||
|
.join(""));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function readError() {
|
||||||
|
debug("Error reading " + aFile.path);
|
||||||
|
aCallback(null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
readChunk();
|
||||||
|
},
|
||||||
|
function openError() {
|
||||||
|
debug("Error opening " + aFile.path);
|
||||||
|
aCallback(null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Returns the MD5 hash of the manifest.
|
||||||
|
computeManifestHash: function(aManifest) {
|
||||||
|
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||||
|
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||||
|
converter.charset = "UTF-8";
|
||||||
|
let result = {};
|
||||||
|
// Data is an array of bytes.
|
||||||
|
let data = converter.convertToByteArray(JSON.stringify(aManifest), result);
|
||||||
|
|
||||||
|
let hasher = Cc["@mozilla.org/security/hash;1"]
|
||||||
|
.createInstance(Ci.nsICryptoHash);
|
||||||
|
hasher.init(hasher.MD5);
|
||||||
|
hasher.update(data, data.length);
|
||||||
|
// We're passing false to get the binary hash and not base64.
|
||||||
|
let hash = hasher.finish(false);
|
||||||
|
|
||||||
|
function toHexString(charCode) {
|
||||||
|
return ("0" + charCode.toString(16)).slice(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the binary hash data to a hex string.
|
||||||
|
return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
|
||||||
|
},
|
||||||
|
|
||||||
checkForUpdate: function(aData, aMm) {
|
checkForUpdate: function(aData, aMm) {
|
||||||
debug("checkForUpdate for " + aData.manifestURL);
|
debug("checkForUpdate for " + aData.manifestURL);
|
||||||
let id = this._appIdForManifestURL(aData.manifestURL);
|
let id = this._appIdForManifestURL(aData.manifestURL);
|
||||||
|
@ -1336,12 +1407,15 @@ this.DOMApplicationRegistry = {
|
||||||
|
|
||||||
xhr.addEventListener("load", (function() {
|
xhr.addEventListener("load", (function() {
|
||||||
debug("Got http status=" + xhr.status + " for " + aData.manifestURL);
|
debug("Got http status=" + xhr.status + " for " + aData.manifestURL);
|
||||||
|
let oldHash = app.manifestHash;
|
||||||
|
|
||||||
if (xhr.status == 200) {
|
if (xhr.status == 200) {
|
||||||
let manifest = xhr.response;
|
let manifest = xhr.response;
|
||||||
if (manifest == null) {
|
if (manifest == null) {
|
||||||
sendError("MANIFEST_PARSE_ERROR");
|
sendError("MANIFEST_PARSE_ERROR");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AppsUtils.checkManifest(manifest, app)) {
|
if (!AppsUtils.checkManifest(manifest, app)) {
|
||||||
sendError("INVALID_MANIFEST");
|
sendError("INVALID_MANIFEST");
|
||||||
return;
|
return;
|
||||||
|
@ -1349,14 +1423,33 @@ this.DOMApplicationRegistry = {
|
||||||
sendError("INSTALL_FROM_DENIED");
|
sendError("INSTALL_FROM_DENIED");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
let hash = this.computeManifestHash(manifest);
|
||||||
|
debug("Manifest hash = " + hash);
|
||||||
|
app.manifestHash = hash;
|
||||||
|
|
||||||
app.etag = xhr.getResponseHeader("Etag");
|
app.etag = xhr.getResponseHeader("Etag");
|
||||||
debug("at update got app etag=" + app.etag);
|
debug("at update got app etag=" + app.etag);
|
||||||
app.lastCheckedUpdate = Date.now();
|
app.lastCheckedUpdate = Date.now();
|
||||||
if (app.origin.startsWith("app://")) {
|
if (app.origin.startsWith("app://")) {
|
||||||
updatePackagedApp.call(this, manifest);
|
if (oldHash != hash) {
|
||||||
|
updatePackagedApp.call(this, manifest);
|
||||||
|
} else {
|
||||||
|
// Like if we got a 304, just send a 'downloadapplied'
|
||||||
|
// or downloadavailable event.
|
||||||
|
aData.event = app.downloadAvailable ? "downloadavailable"
|
||||||
|
: "downloadapplied";
|
||||||
|
aData.app = {
|
||||||
|
lastCheckedUpdate: app.lastCheckedUpdate
|
||||||
|
}
|
||||||
|
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
|
||||||
|
this._saveApps();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this._readManifests([{ id: id }], (function(aResult) {
|
this._readManifests([{ id: id }], (function(aResult) {
|
||||||
updateHostedApp.call(this, aResult[0].manifest, manifest);
|
// Update only the appcache if the manifest has not changed
|
||||||
|
// based on the hash value.
|
||||||
|
updateHostedApp.call(this, aResult[0].manifest,
|
||||||
|
oldHash == hash ? null : manifest);
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1364,7 +1457,7 @@ this.DOMApplicationRegistry = {
|
||||||
// The manifest has not changed.
|
// The manifest has not changed.
|
||||||
if (app.origin.startsWith("app://")) {
|
if (app.origin.startsWith("app://")) {
|
||||||
// If the app is a packaged app, we just send a 'downloadapplied'
|
// If the app is a packaged app, we just send a 'downloadapplied'
|
||||||
// event.
|
// or downloadavailable event.
|
||||||
app.lastCheckedUpdate = Date.now();
|
app.lastCheckedUpdate = Date.now();
|
||||||
aData.event = app.downloadAvailable ? "downloadavailable"
|
aData.event = app.downloadAvailable ? "downloadavailable"
|
||||||
: "downloadapplied";
|
: "downloadapplied";
|
||||||
|
@ -1467,6 +1560,7 @@ this.DOMApplicationRegistry = {
|
||||||
sendError("INVALID_SECURITY_LEVEL");
|
sendError("INVALID_SECURITY_LEVEL");
|
||||||
} else {
|
} else {
|
||||||
app.etag = xhr.getResponseHeader("Etag");
|
app.etag = xhr.getResponseHeader("Etag");
|
||||||
|
app.manifestHash = this.computeManifestHash(app.manifest);
|
||||||
// We allow bypassing the install confirmation process to facilitate
|
// We allow bypassing the install confirmation process to facilitate
|
||||||
// automation.
|
// automation.
|
||||||
let prefName = "dom.mozApps.auto_confirm_install";
|
let prefName = "dom.mozApps.auto_confirm_install";
|
||||||
|
@ -1521,6 +1615,7 @@ this.DOMApplicationRegistry = {
|
||||||
sendError("MANIFEST_PARSE_ERROR");
|
sendError("MANIFEST_PARSE_ERROR");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(AppsUtils.checkManifest(manifest, app) &&
|
if (!(AppsUtils.checkManifest(manifest, app) &&
|
||||||
manifest.package_path)) {
|
manifest.package_path)) {
|
||||||
sendError("INVALID_MANIFEST");
|
sendError("INVALID_MANIFEST");
|
||||||
|
@ -1528,6 +1623,7 @@ this.DOMApplicationRegistry = {
|
||||||
sendError("INSTALL_FROM_DENIED");
|
sendError("INSTALL_FROM_DENIED");
|
||||||
} else {
|
} else {
|
||||||
app.etag = xhr.getResponseHeader("Etag");
|
app.etag = xhr.getResponseHeader("Etag");
|
||||||
|
app.manifestHash = this.computeManifestHash(manifest);
|
||||||
debug("at install package got app etag=" + app.etag);
|
debug("at install package got app etag=" + app.etag);
|
||||||
Services.obs.notifyObservers(aMm, "webapps-ask-install",
|
Services.obs.notifyObservers(aMm, "webapps-ask-install",
|
||||||
JSON.stringify(aData));
|
JSON.stringify(aData));
|
||||||
|
@ -1952,151 +2048,158 @@ this.DOMApplicationRegistry = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestChannel.responseStatus == 304) {
|
self.computeFileHash(zipFile, function onHashComputed(aHash) {
|
||||||
// The package's Etag has not changed.
|
debug("packageHash=" + aHash);
|
||||||
// We send a "applied" event right away.
|
let newPackage = (requestChannel.responseStatus != 304) &&
|
||||||
app.downloading = false;
|
(aHash != app.packageHash);
|
||||||
app.downloadAvailable = false;
|
|
||||||
app.downloadSize = 0;
|
if (!newPackage) {
|
||||||
app.installState = "installed";
|
// The package's Etag or hash has not changed.
|
||||||
app.readyToApplyDownload = false;
|
// We send a "applied" event right away.
|
||||||
self.broadcastMessage("Webapps:PackageEvent", {
|
app.downloading = false;
|
||||||
type: "applied",
|
app.downloadAvailable = false;
|
||||||
manifestURL: aApp.manifestURL,
|
app.downloadSize = 0;
|
||||||
app: app });
|
app.installState = "installed";
|
||||||
// Save the updated registry, and cleanup the tmp directory.
|
app.readyToApplyDownload = false;
|
||||||
self._saveApps();
|
self.broadcastMessage("Webapps:PackageEvent", {
|
||||||
let file = FileUtils.getFile("TmpD", ["webapps", id], false);
|
type: "applied",
|
||||||
if (file && file.exists()) {
|
manifestURL: aApp.manifestURL,
|
||||||
file.remove(true);
|
app: app });
|
||||||
|
// Save the updated registry, and cleanup the tmp directory.
|
||||||
|
self._saveApps();
|
||||||
|
let file = FileUtils.getFile("TmpD", ["webapps", id], false);
|
||||||
|
if (file && file.exists()) {
|
||||||
|
file.remove(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let certdb;
|
let certdb;
|
||||||
try {
|
|
||||||
certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
|
||||||
.getService(Ci.nsIX509CertDB);
|
|
||||||
} catch (e) {
|
|
||||||
cleanup("CERTDB_ERROR");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
certdb.openSignedJARFileAsync(zipFile, function(aRv, aZipReader) {
|
|
||||||
let zipReader;
|
|
||||||
try {
|
try {
|
||||||
let isSigned;
|
certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
||||||
if (Components.isSuccessCode(aRv)) {
|
.getService(Ci.nsIX509CertDB);
|
||||||
isSigned = true;
|
|
||||||
zipReader = aZipReader;
|
|
||||||
} else if (aRv != Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED) {
|
|
||||||
throw "INVALID_SIGNATURE";
|
|
||||||
} else {
|
|
||||||
isSigned = false;
|
|
||||||
zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
|
|
||||||
.createInstance(Ci.nsIZipReader);
|
|
||||||
zipReader.open(zipFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX Security: You CANNOT safely add a new app store for
|
|
||||||
// installing privileged apps just by modifying this pref and
|
|
||||||
// adding the signing cert for that store to the cert trust
|
|
||||||
// database. *Any* origin listed can install apps signed with
|
|
||||||
// *any* certificate trusted; we don't try to maintain a strong
|
|
||||||
// association between certificate with installOrign. The
|
|
||||||
// expectation here is that in production builds the pref will
|
|
||||||
// contain exactly one origin. However, in custom development
|
|
||||||
// builds it may contain more than one origin so we can test
|
|
||||||
// different stages (dev, staging, prod) of the same app store.
|
|
||||||
//
|
|
||||||
// Only allow signed apps to be installed from a whitelist of
|
|
||||||
// domains, and require all packages installed from any of the
|
|
||||||
// domains on the whitelist to be signed. This is a stopgap until
|
|
||||||
// we have a real story for handling multiple app stores signing
|
|
||||||
// apps.
|
|
||||||
let signedAppOriginsStr =
|
|
||||||
Services.prefs.getCharPref(
|
|
||||||
"dom.mozApps.signed_apps_installable_from");
|
|
||||||
let isSignedAppOrigin
|
|
||||||
= signedAppOriginsStr.split(",").indexOf(aApp.installOrigin) > -1;
|
|
||||||
if (!isSigned && isSignedAppOrigin) {
|
|
||||||
// Packaged apps installed from these origins must be signed;
|
|
||||||
// if not, assume somebody stripped the signature.
|
|
||||||
throw "INVALID_SIGNATURE";
|
|
||||||
} else if (isSigned && !isSignedAppOrigin) {
|
|
||||||
// Other origins are *prohibited* from installing signed apps.
|
|
||||||
// One reason is that our app revociation mechanism requires
|
|
||||||
// strong cooperation from the host of the mini-manifest, which
|
|
||||||
// we assume to be under the control of the install origin,
|
|
||||||
// even if it has a different origin.
|
|
||||||
throw "INSTALL_FROM_DENIED";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!zipReader.hasEntry("manifest.webapp")) {
|
|
||||||
throw "MISSING_MANIFEST";
|
|
||||||
}
|
|
||||||
|
|
||||||
let istream = zipReader.getInputStream("manifest.webapp");
|
|
||||||
|
|
||||||
// Obtain a converter to read from a UTF-8 encoded input stream.
|
|
||||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
|
||||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
|
||||||
converter.charset = "UTF-8";
|
|
||||||
|
|
||||||
let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
|
|
||||||
istream.available()) || ""));
|
|
||||||
|
|
||||||
// Call checkManifest before compareManifests, as checkManifest
|
|
||||||
// will normalize some attributes that has already been normalized
|
|
||||||
// for aManifest during checkForUpdate.
|
|
||||||
if (!AppsUtils.checkManifest(manifest, app)) {
|
|
||||||
throw "INVALID_MANIFEST";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AppsUtils.compareManifests(manifest,
|
|
||||||
aManifest._manifest)) {
|
|
||||||
throw "MANIFEST_MISMATCH";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AppsUtils.checkInstallAllowed(manifest, aApp.installOrigin)) {
|
|
||||||
throw "INSTALL_FROM_DENIED";
|
|
||||||
}
|
|
||||||
|
|
||||||
let maxStatus = isSigned ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
|
|
||||||
: Ci.nsIPrincipal.APP_STATUS_INSTALLED;
|
|
||||||
|
|
||||||
if (AppsUtils.getAppManifestStatus(manifest) > maxStatus) {
|
|
||||||
throw "INVALID_SECURITY_LEVEL";
|
|
||||||
}
|
|
||||||
aApp.appStatus = AppsUtils.getAppManifestStatus(manifest);
|
|
||||||
// Save the new Etag for the package.
|
|
||||||
try {
|
|
||||||
app.packageEtag = requestChannel.getResponseHeader("Etag");
|
|
||||||
debug("Package etag=" + app.packageEtag);
|
|
||||||
} catch (e) {
|
|
||||||
// in https://bugzilla.mozilla.org/show_bug.cgi?id=825218
|
|
||||||
// we'll fail gracefully in this case
|
|
||||||
// for now, just going on
|
|
||||||
app.packageEtag = null;
|
|
||||||
debug("Can't find an etag, this should not happen");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aOnSuccess) {
|
|
||||||
aOnSuccess(id, manifest);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Something bad happened when reading the package.
|
cleanup("CERTDB_ERROR");
|
||||||
if (typeof e == 'object') {
|
return;
|
||||||
Cu.reportError("Error while reading package:" + e);
|
|
||||||
cleanup("INVALID_PACKAGE");
|
|
||||||
} else {
|
|
||||||
cleanup(e);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
AppDownloadManager.remove(aApp.manifestURL);
|
|
||||||
if (zipReader)
|
|
||||||
zipReader.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
certdb.openSignedJARFileAsync(zipFile, function(aRv, aZipReader) {
|
||||||
|
let zipReader;
|
||||||
|
try {
|
||||||
|
let isSigned;
|
||||||
|
if (Components.isSuccessCode(aRv)) {
|
||||||
|
isSigned = true;
|
||||||
|
zipReader = aZipReader;
|
||||||
|
} else if (aRv != Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED) {
|
||||||
|
throw "INVALID_SIGNATURE";
|
||||||
|
} else {
|
||||||
|
isSigned = false;
|
||||||
|
zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
|
||||||
|
.createInstance(Ci.nsIZipReader);
|
||||||
|
zipReader.open(zipFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX Security: You CANNOT safely add a new app store for
|
||||||
|
// installing privileged apps just by modifying this pref and
|
||||||
|
// adding the signing cert for that store to the cert trust
|
||||||
|
// database. *Any* origin listed can install apps signed with
|
||||||
|
// *any* certificate trusted; we don't try to maintain a strong
|
||||||
|
// association between certificate with installOrign. The
|
||||||
|
// expectation here is that in production builds the pref will
|
||||||
|
// contain exactly one origin. However, in custom development
|
||||||
|
// builds it may contain more than one origin so we can test
|
||||||
|
// different stages (dev, staging, prod) of the same app store.
|
||||||
|
//
|
||||||
|
// Only allow signed apps to be installed from a whitelist of
|
||||||
|
// domains, and require all packages installed from any of the
|
||||||
|
// domains on the whitelist to be signed. This is a stopgap until
|
||||||
|
// we have a real story for handling multiple app stores signing
|
||||||
|
// apps.
|
||||||
|
let signedAppOriginsStr =
|
||||||
|
Services.prefs.getCharPref(
|
||||||
|
"dom.mozApps.signed_apps_installable_from");
|
||||||
|
let isSignedAppOrigin
|
||||||
|
= signedAppOriginsStr.split(",").indexOf(aApp.installOrigin) > -1;
|
||||||
|
if (!isSigned && isSignedAppOrigin) {
|
||||||
|
// Packaged apps installed from these origins must be signed;
|
||||||
|
// if not, assume somebody stripped the signature.
|
||||||
|
throw "INVALID_SIGNATURE";
|
||||||
|
} else if (isSigned && !isSignedAppOrigin) {
|
||||||
|
// Other origins are *prohibited* from installing signed apps.
|
||||||
|
// One reason is that our app revociation mechanism requires
|
||||||
|
// strong cooperation from the host of the mini-manifest, which
|
||||||
|
// we assume to be under the control of the install origin,
|
||||||
|
// even if it has a different origin.
|
||||||
|
throw "INSTALL_FROM_DENIED";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zipReader.hasEntry("manifest.webapp")) {
|
||||||
|
throw "MISSING_MANIFEST";
|
||||||
|
}
|
||||||
|
|
||||||
|
let istream = zipReader.getInputStream("manifest.webapp");
|
||||||
|
|
||||||
|
// Obtain a converter to read from a UTF-8 encoded input stream.
|
||||||
|
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||||
|
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||||
|
converter.charset = "UTF-8";
|
||||||
|
|
||||||
|
let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
|
||||||
|
istream.available()) || ""));
|
||||||
|
|
||||||
|
// Call checkManifest before compareManifests, as checkManifest
|
||||||
|
// will normalize some attributes that has already been normalized
|
||||||
|
// for aManifest during checkForUpdate.
|
||||||
|
if (!AppsUtils.checkManifest(manifest, app)) {
|
||||||
|
throw "INVALID_MANIFEST";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AppsUtils.compareManifests(manifest,
|
||||||
|
aManifest._manifest)) {
|
||||||
|
throw "MANIFEST_MISMATCH";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AppsUtils.checkInstallAllowed(manifest, aApp.installOrigin)) {
|
||||||
|
throw "INSTALL_FROM_DENIED";
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxStatus = isSigned ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
|
||||||
|
: Ci.nsIPrincipal.APP_STATUS_INSTALLED;
|
||||||
|
|
||||||
|
if (AppsUtils.getAppManifestStatus(manifest) > maxStatus) {
|
||||||
|
throw "INVALID_SECURITY_LEVEL";
|
||||||
|
}
|
||||||
|
app.appStatus = AppsUtils.getAppManifestStatus(manifest);
|
||||||
|
app.packageHash = aHash;
|
||||||
|
// Save the new Etag for the package.
|
||||||
|
try {
|
||||||
|
app.packageEtag = requestChannel.getResponseHeader("Etag");
|
||||||
|
debug("Package etag=" + app.packageEtag);
|
||||||
|
} catch (e) {
|
||||||
|
// in https://bugzilla.mozilla.org/show_bug.cgi?id=825218
|
||||||
|
// we'll fail gracefully in this case
|
||||||
|
// for now, just going on
|
||||||
|
app.packageEtag = null;
|
||||||
|
debug("Can't find an etag, this should not happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aOnSuccess) {
|
||||||
|
aOnSuccess(id, manifest);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Something bad happened when reading the package.
|
||||||
|
if (typeof e == 'object') {
|
||||||
|
Cu.reportError("Error while reading package:" + e);
|
||||||
|
cleanup("INVALID_PACKAGE");
|
||||||
|
} else {
|
||||||
|
cleanup(e);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
AppDownloadManager.remove(aApp.manifestURL);
|
||||||
|
if (zipReader)
|
||||||
|
zipReader.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Загрузка…
Ссылка в новой задаче