Bug 1214079 - Doom the package cache if the signature verification failed. r=valentin
authorHenry <hchang@mozilla.com>
Thu, 05 Nov 2015 23:44:00 +0100
changeset 271592 a93a69859594569b51d6c8a792983d95d22115f0
parent 271591 50441cd0d6a14568d31a156934c9df106825e840
child 271593 516b30eec70daeec8259e3f84b13fe8f308d6a85
push id16135
push usercbook@mozilla.com
push dateMon, 09 Nov 2015 13:59:56 +0000
treeherderfx-team@5898d8162f44 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin
bugs1214079
milestone45.0a1
Bug 1214079 - Doom the package cache if the signature verification failed. r=valentin
netwerk/protocol/http/PackagedAppVerifier.cpp
netwerk/test/unit/test_packaged_app_bug1214079.js
netwerk/test/unit/xpcshell.ini
--- a/netwerk/protocol/http/PackagedAppVerifier.cpp
+++ b/netwerk/protocol/http/PackagedAppVerifier.cpp
@@ -379,16 +379,22 @@ PackagedAppVerifier::OnManifestVerified(
                     : STATE_MANIFEST_VERIFIED_FAILED;
 
   // Obtain the package identifier from manifest if the package is signed.
   if (mIsPackageSigned) {
     mPackagedAppUtils->GetPackageIdentifier(mPackageIdentifer);
     LOG(("PackageIdentifer is: %s", mPackageIdentifer.get()));
   }
 
+  // If the signature verification failed, doom the package cache to
+  // make its subresources unavailable in the subsequent requests.
+  if (!aSuccess && mPackageCacheEntry) {
+    mPackageCacheEntry->AsyncDoom(nullptr);
+  }
+
   // If the package is signed, add related info to the package cache.
   if (mIsPackageSigned && mPackageCacheEntry) {
     LOG(("This package is signed. Add this info to the cache channel."));
     if (mPackageCacheEntry) {
       mPackageCacheEntry->SetMetaDataElement(kSignedPakIdMetadataKey,
                                              mPackageIdentifer.get());
       mPackageCacheEntry = nullptr; // the cache entry is no longer needed.
     }
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_packaged_app_bug1214079.js
@@ -0,0 +1,181 @@
+// This test is to verify Bug 1214079: when we failed to load a signed packaged
+// content due to the bad signature, we are able to load in the next time since
+// the cache is not cleaned up after signature verification.
+//
+// In order to verify it, we do two identical requests in a row, where the
+// second request will be loaded from cache. The request is made for a signed
+// package with bad signature. If the package cache isn't doomed after the
+// first load, the second request will get valid data from the cache.
+
+Cu.import('resource://gre/modules/LoadContextInfo.jsm');
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+var gPrefs = Cc["@mozilla.org/preferences-service;1"]
+               .getService(Components.interfaces.nsIPrefBranch);
+
+var badSignature = [
+  "manifest-signature: AAAAAAAAAAAAAAAAAAAAA\r",
+  "--7B0MKBI3UH\r",
+  "Content-Location: manifest.webapp\r",
+  "Content-Type: application/x-web-app-manifest+json\r",
+  "\r",
+  "{",
+  "  \"name\": \"My App\",",
+  "  \"moz-resources\": [",
+  "    {",
+  "      \"src\": \"page2.html\",",
+  "      \"integrity\": \"JREF3JbXGvZ+I1KHtoz3f46ZkeIPrvXtG4VyFQrJ7II=\"",
+  "    },",
+  "    {",
+  "      \"src\": \"index.html\",",
+  "      \"integrity\": \"zEubR310nePwd30NThIuoCxKJdnz7Mf5z+dZHUbH1SE=\"",
+  "    },",
+  "    {",
+  "      \"src\": \"scripts/script.js\",",
+  "      \"integrity\": \"6TqtNArQKrrsXEQWu3D9ZD8xvDRIkhyV6zVdTcmsT5Q=\"",
+  "    },",
+  "    {",
+  "      \"src\": \"scripts/library.js\",",
+  "      \"integrity\": \"TN2ByXZiaBiBCvS4MeZ02UyNi44vED+KjdjLInUl4o8=\"",
+  "    }",
+  "  ],",
+  "  \"moz-permissions\": [",
+  "    {",
+  "      \"systemXHR\": {",
+  "        \"description\": \"Needed to download stuff\"",
+  "      },",
+  "      \"devicestorage:pictures\": {",
+  "        \"description\": \"Need to load pictures\"",
+  "      }",
+  "    }",
+  "  ],",
+  "  \"package-identifier\": \"611FC2FE-491D-4A47-B3B3-43FBDF6F404F\",",
+  "  \"moz-package-location\": \"https://example.com/myapp/app.pak\",",
+  "  \"description\": \"A great app!\"",
+  "}\r",
+  "--7B0MKBI3UH\r",
+  "Content-Location: page2.html\r",
+  "Content-Type: text/html\r",
+  "\r",
+  "<html>",
+  "  page2.html",
+  "</html>",
+  "\r",
+  "--7B0MKBI3UH\r",
+  "Content-Location: index.html\r",
+  "Content-Type: text/html\r",
+  "\r",
+  "<html>",
+  "  Last updated: 2015/10/01 14:10 PST",
+  "</html>",
+  "\r",
+  "--7B0MKBI3UH\r",
+  "Content-Location: scripts/script.js\r",
+  "Content-Type: text/javascript\r",
+  "\r",
+  "// script.js",
+  "\r",
+  "--7B0MKBI3UH\r",
+  "Content-Location: scripts/library.js\r",
+  "Content-Type: text/javascript\r",
+  "\r",
+  "// library.js",
+  "\r",
+  "--7B0MKBI3UH--"
+].join("\n");
+
+XPCOMUtils.defineLazyGetter(this, "uri", function() {
+  return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+// The active http server initialized in run_test
+var httpserver = null;
+// The packaged app service initialized in run_test
+var paservice = null;
+
+function run_test()
+{
+  // setup test
+  httpserver = new HttpServer();
+  httpserver.registerPathHandler("/badSignature", badSignatureHandler);
+  httpserver.start(-1);
+
+  do_register_cleanup(function() {
+    gPrefs.clearUserPref("network.http.signed-packages.enabled");
+  });
+
+  paservice = Cc["@mozilla.org/network/packaged-app-service;1"]
+                     .getService(Ci.nsIPackagedAppService);
+  ok(!!paservice, "test service exists");
+
+  gPrefs.setBoolPref("network.http.signed-packages.enabled", true);
+
+  add_test(test_badSignature_package);
+  add_test(test_badSignature_package);
+
+  // run tests
+  run_next_test();
+}
+
+//-----------------------------------------------------------------------------
+
+// The number of times this package has been requested
+// This number might be reset by tests that use it
+var packagedAppRequestsMade = 0;
+
+function badSignatureHandler(metadata, response)
+{
+  packagedAppRequestsMade++;
+  if (packagedAppRequestsMade == 2) {
+    // The second request returns a 304 not modified response
+    response.setStatusLine(metadata.httpVersion, 304, "Not Modified");
+    response.bodyOutputStream.write("", 0);
+    return;
+  }
+
+  response.setHeader("Content-Type", 'application/package');
+  var body = badSignature;
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function getChannelForURL(url) {
+  let uri = createURI(url);
+  let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+              .getService(Ci.nsIScriptSecurityManager);
+  let principal = ssm.createCodebasePrincipal(uri, {});
+  let tmpChannel =
+    NetUtil.newChannel({
+      uri: url,
+      loadingPrincipal: principal,
+      contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
+    });
+
+  tmpChannel.notificationCallbacks =
+    new LoadContextCallback(principal.appId,
+                            principal.isInBrowserElement,
+                            false,
+                            false);
+
+  return tmpChannel;
+}
+
+function test_badSignature_package()
+{
+  let packagePath = "/badSignature";
+  let url = uri + packagePath + "!//index.html";
+  let channel = getChannelForURL(url);
+
+  paservice.getResource(channel, {
+    onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
+    onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+      // XXX: it returns NS_ERROR_FAILURE for a missing package
+      // and NS_ERROR_FILE_NOT_FOUND for a missing file from the package.
+      // Maybe make them both return NS_ERROR_FILE_NOT_FOUND?
+      notEqual(status, Cr.NS_OK, "NOT FOUND");
+      ok(!entry, "There should be no entry");
+      run_next_test();
+    }
+  });
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -333,8 +333,9 @@ firefox-appdir = browser
 [test_packaged_app_utils.js]
 [test_suspend_channel_before_connect.js]
 [test_inhibit_caching.js]
 [test_dns_disable_ipv4.js]
 [test_dns_disable_ipv6.js]
 [test_packaged_app_service_paths.js]
 [test_bug1195415.js]
 [test_cookie_blacklist.js]
+[test_packaged_app_bug1214079.js]