Bug 1290529 - Clear HSTS and HPKP for subdomains in ForgetAboutSite. r=MattN,keeler draft
authorJonathan Hao <jhao@mozilla.com>
Tue, 24 Jan 2017 11:14:36 +0800
changeset 465941 b1a1822cd2af1c43af704730063a8ce4a4cc03ec
parent 463935 bff86e08986830ea55b455dae61b149508dfdad4
child 465943 bb4279cad77b1f11e94b77ed1b7f917a4377797e
push id42760
push userbmo:jhao@mozilla.com
push dateWed, 25 Jan 2017 03:11:25 +0000
reviewersMattN, keeler
bugs1290529
milestone53.0a1
Bug 1290529 - Clear HSTS and HPKP for subdomains in ForgetAboutSite. r=MattN,keeler
security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
toolkit/forgetaboutsite/ForgetAboutSite.jsm
--- a/security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
+++ b/security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
@@ -66,35 +66,47 @@ add_task(function* () {
   Assert.ok(!sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS,
                               "a.pinning2.example.com", 0),
             "a.pinning2.example.com should not be HSTS now");
   Assert.ok(!sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
                               "a.pinning2.example.com", 0),
             "a.pinning2.example.com should not be HPKP now");
 });
 
-// TODO (bug 1290529): the platform does not support this yet.
 // Test the case of processing HSTS and HPKP headers for a.pinning2.example.com,
 // using "Forget About Site" on example.com, and then checking that the platform
-// doesn't consider the subdomain to be HSTS or HPKP any longer.
+// doesn't consider the subdomain to be HSTS or HPKP any longer. Also test that
+// unrelated sites don't also get removed.
 add_task(function* () {
   sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, GOOD_MAX_AGE,
                     sslStatus, 0);
   sss.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri,
                     GOOD_MAX_AGE + VALID_PIN + BACKUP_PIN, sslStatus, 0);
 
   Assert.ok(sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS,
                              "a.pinning2.example.com", 0),
             "a.pinning2.example.com should be HSTS (subdomain case)");
   Assert.ok(sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
                              "a.pinning2.example.com", 0),
             "a.pinning2.example.com should be HPKP (subdomain case)");
 
+  // Add an unrelated site to HSTS.  Not HPKP because we have no valid keys for
+  // example.org.
+  let unrelatedURI = Services.io.newURI("https://example.org");
+  sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, unrelatedURI,
+                    GOOD_MAX_AGE, sslStatus, 0);
+  Assert.ok(sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS,
+                             "example.org", 0),
+            "example.org should be HSTS");
+
   yield ForgetAboutSite.removeDataFromDomain("example.com");
 
-  // TODO (bug 1290529):
-  // Assert.ok(!sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS,
-  //                             "a.pinning2.example.com", 0),
-  //           "a.pinning2.example.com should not be HSTS now (subdomain case)");
-  // Assert.ok(!sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
-  //                             "a.pinning2.example.com", 0),
-  //           "a.pinning2.example.com should not be HPKP now (subdomain case)");
+  Assert.ok(!sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS,
+                              "a.pinning2.example.com", 0),
+            "a.pinning2.example.com should not be HSTS now (subdomain case)");
+  Assert.ok(!sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP,
+                              "a.pinning2.example.com", 0),
+            "a.pinning2.example.com should not be HPKP now (subdomain case)");
+
+  Assert.ok(sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS,
+                             "example.org", 0),
+            "example.org should still be HSTS");
 });
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -192,24 +192,37 @@ this.ForgetAboutSite = {
         (Components.isSuccessCode(status) ? resolve : reject)(status);
       });
     }).catch(e => {
       Cu.reportError("Exception thrown while clearing Push notifications: " +
                      e.toString());
     }));
 
     // HSTS and HPKP
-    // TODO (bug 1290529): also remove HSTS/HPKP information for subdomains.
-    // Since we can't enumerate the information in the site security service
-    // (bug 1115712), we can't implement this right now.
     try {
       let sss = Cc["@mozilla.org/ssservice;1"].
                 getService(Ci.nsISiteSecurityService);
-      sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, httpsURI, 0);
-      sss.removeState(Ci.nsISiteSecurityService.HEADER_HPKP, httpsURI, 0);
+      for (let type of [Ci.nsISiteSecurityService.HEADER_HSTS,
+                        Ci.nsISiteSecurityService.HEADER_HPKP]) {
+        sss.removeState(type, httpsURI, 0);
+
+        // Also remove HSTS/HPKP information for subdomains by enumerating the
+        // information in the site security service.
+        let enumerator = sss.enumerate(type);
+        while (enumerator.hasMoreElements()) {
+          let entry = enumerator.getNext();
+          let hostname = entry.QueryInterface(Ci.nsISiteSecurityState).hostname;
+          // If the hostname is aDomain's subdomain, we remove its state.
+          if (hostname.endsWith("." + aDomain)) {
+            // This uri is used as a key to remove the state.
+            let uri = caUtils.makeURI("https://" + hostname);
+            sss.removeState(type, uri, 0);
+          }
+        }
+      }
     } catch (e) {
       Cu.reportError("Exception thrown while clearing HSTS/HPKP: " +
                      e.toString());
     }
 
     return Promise.all(promises);
   }
 };