Bug 772365, Part 4: Check JAR signature during packaged app installation, r=anygregor
authorBrian Smith <bsmith@mozilla.com>
Thu, 13 Dec 2012 20:35:58 -0800
changeset 125013 b11065872128ed68e5023b1861004d4af6324166
parent 125012 6a0ed6484811bc7233c39eec1687bb8ce09cb98e
child 125030 8a30e07815ff4aa3d1ff2fd2d21daf93e251fa7d
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersanygregor
bugs772365
milestone20.0a1
first release with
nightly linux32
b11065872128 / 20.0a1 / 20121214030827 / files
nightly linux64
b11065872128 / 20.0a1 / 20121214030827 / files
nightly mac
b11065872128 / 20.0a1 / 20121214030827 / files
nightly win32
b11065872128 / 20.0a1 / 20121214030827 / files
nightly win64
b11065872128 / 20.0a1 / 20121214030827 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 772365, Part 4: Check JAR signature during packaged app installation, r=anygregor
dom/apps/src/Webapps.jsm
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -1617,16 +1617,17 @@ this.DOMApplicationRegistry = {
         this._readManifests(aData, aFinalCallback, index + 1);
     }).bind(this));
   },
 
   downloadPackage: function(aManifest, aApp, aIsUpdate, aOnSuccess) {
     // Here are the steps when installing a package:
     // - create a temp directory where to store the app.
     // - download the zip in this directory.
+    // - check the signature on the zip.
     // - extract the manifest from the zip and check it.
     // - ask confirmation to the user.
     // - add the new app to the registry.
     // If we fail at any step, we backout the previous ones and return an error.
 
     debug("downloadPackage " + JSON.stringify(aApp));
 
     let id = this._appIdForManifestURL(aApp.manifestURL);
@@ -1656,38 +1657,16 @@ this.DOMApplicationRegistry = {
       app.installState = download ? download.previousState : "pending";
       self.broadcastMessage("Webapps:PackageEvent",
                             { type: "error",
                               manifestURL:  aApp.manifestURL,
                               error: aError,
                               app: app });
     }
 
-    function getInferedStatus() {
-      // XXX Update once we have digital signatures (bug 772365)
-      return Ci.nsIPrincipal.APP_STATUS_INSTALLED;
-    }
-
-    function getAppStatus(aManifest) {
-      let manifestStatus = AppsUtils.getAppManifestStatus(aManifest);
-      let inferedStatus = getInferedStatus();
-
-      return (Services.prefs.getBoolPref("dom.mozApps.dev_mode") ? manifestStatus
-                                                                : inferedStatus);
-    }
-    // Returns true if the privilege level from the manifest
-    // is lower or equal to the one we infered for the app.
-    function checkAppStatus(aManifest) {
-      if (Services.prefs.getBoolPref("dom.mozApps.dev_mode")) {
-        return true;
-      }
-
-      return (AppsUtils.getAppManifestStatus(aManifest) <= getInferedStatus());
-    }
-
     function download() {
       debug("About to download " + aManifest.fullPackagePath());
 
       let requestChannel = NetUtil.newChannel(aManifest.fullPackagePath())
                                   .QueryInterface(Ci.nsIHttpChannel);
       self.downloads[aApp.manifestURL] =
         { channel:requestChannel,
           appId: id,
@@ -1737,71 +1716,97 @@ this.DOMApplicationRegistry = {
       app.installState = aIsUpdate ? "updating" : "pending";
 
       NetUtil.asyncFetch(requestChannel, function(aInput, aResult, aRequest) {
         if (!Components.isSuccessCode(aResult)) {
           // We failed to fetch the zip.
           cleanup("NETWORK_ERROR");
           return;
         }
-        // Copy the zip on disk.
+        // Copy the zip on disk. XXX: this can consume all disk space.
         let zipFile = FileUtils.getFile("TmpD",
                                         ["webapps", id, "application.zip"], true);
         let ostream = FileUtils.openSafeFileOutputStream(zipFile);
         NetUtil.asyncCopy(aInput, ostream, function (aResult) {
           if (!Components.isSuccessCode(aResult)) {
             // We failed to save the zip.
             cleanup("DOWNLOAD_ERROR");
             return;
           }
 
-          let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
-                          .createInstance(Ci.nsIZipReader);
-          try {
-            zipReader.open(zipFile);
-            if (!zipReader.hasEntry("manifest.webapp")) {
-              throw "MISSING_MANIFEST";
-            }
-
-            let istream = zipReader.getInputStream("manifest.webapp");
+		  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) {
+            try {
+              let zipReader;
+              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);
+              }
 
-            // 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";
+              if (!zipReader.hasEntry("manifest.webapp")) {
+                throw "MISSING_MANIFEST";
+              }
 
-            let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
-                                                                 istream.available()) || ""));
+              let istream = zipReader.getInputStream("manifest.webapp");
 
-            if (!AppsUtils.checkManifest(manifest)) {
-              throw "INVALID_MANIFEST";
-            }
+              // 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";
 
-            if (!AppsUtils.checkInstallAllowed(manifest, aApp.installOrigin)) {
-              throw "INSTALL_FROM_DENIED";
-            }
+              let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
+                                                                   istream.available()) || ""));
+
+              if (!AppsUtils.checkManifest(manifest)) {
+                throw "INVALID_MANIFEST";
+              }
+
+              if (!AppsUtils.checkInstallAllowed(manifest, aApp.installOrigin)) {
+                throw "INSTALL_FROM_DENIED";
+              }
+
+              let isDevMode = Services.prefs.getBoolPref("dom.mozApps.dev_mode");
+              let maxStatus = isDevMode ? Ci.nsIPrincipal.APP_STATUS_CERTIFIED
+                            : isSigned  ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
+                                        : Ci.nsIPrincipal.APP_STATUS_INSTALLED;
 
-            if (!checkAppStatus(manifest)) {
-              throw "INVALID_SECURITY_LEVEL";
-            }
+              if (AppsUtils.getAppManifestStatus(aManifest) > maxStatus) {
+                throw "INVALID_SECURITY_LEVEL";
+              }
 
-            if (aOnSuccess) {
-              aOnSuccess(id, manifest);
+              if (aOnSuccess) {
+                aOnSuccess(id, manifest);
+              }
+              delete self.downloads[aApp.manifestURL];
+            } catch (e) {
+              // Something bad happened when reading the package.
+              if (typeof e == 'object') {
+                cleanup("INVALID_PACKAGE");
+              } else {
+                cleanup(e);
+              }
+            } finally {
+              zipReader.close();
             }
-            delete self.downloads[aApp.manifestURL];
-          } catch (e) {
-            // Something bad happened when reading the package.
-            if (typeof e == 'object') {
-              cleanup("INVALID_PACKAGE");
-            } else {
-              cleanup(e);
-            }
-          } finally {
-            zipReader.close();
-          }
+          });
         });
       });
     };
 
     let deviceStorage = Services.wm.getMostRecentWindow("navigator:browser")
                                 .navigator.getDeviceStorage("apps");
     let req = deviceStorage.stat();
     req.onsuccess = req.onerror = function statResult(e) {