Bug 1260931 - Part 1: add firstPartyDomain. r=smaug
☠☠ backed out by 86e1a437021b ☠ ☠
authorYoshi Huang <allstars.chh@mozilla.com>
Tue, 09 Aug 2016 16:34:53 +0800
changeset 312383 dd200883aa79f18a4536afed86bb311a50d0524b
parent 312382 93657f8dedaafa384bcd49c71b52e195ec968de8
child 312384 2de0e171f15b38ab134d153d776a207d19afbcc4
push id20447
push userkwierso@gmail.com
push dateFri, 02 Sep 2016 20:36:44 +0000
treeherderfx-team@969397f22187 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1260931
milestone51.0a1
Bug 1260931 - Part 1: add firstPartyDomain. r=smaug Add an origin attribute called 'firstPartyDomain'. This value will be extracted from the URL bar. And the purpose of this attribute is used to isolate the data-jars. Please see the tor documentation. https://www.torproject.org/projects/torbrowser/design/#identifier-linkability The idea is like a superset of 'reject third party cookies', but not only apply for cookies, it also applies to all data-jars like localStorage, indexedDB and so on. So basically an iframe will have its own data-jar, and this data-jar is isolated by the URL from URL bar, for instance, an iframe https://facebook.com inside https://cnn.com won't share data-jar with the iframe (https://facebook.com) in https://bbc.com
caps/BasePrincipal.cpp
caps/BasePrincipal.h
caps/tests/unit/test_origin.js
dom/webidl/ChromeUtils.webidl
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -42,29 +42,31 @@ PrincipalOriginAttributes::InheritFromDo
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225349 - PrincipalOriginAttributes should inherit mSignedPkg
   // accordingly by URI
   mSignedPkg = aAttrs.mSignedPkg;
 
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+  mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 void
 PrincipalOriginAttributes::InheritFromNecko(const NeckoOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
   mSignedPkg = aAttrs.mSignedPkg;
 
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+  mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 void
 DocShellOriginAttributes::InheritFromDocToChildDocShell(const PrincipalOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
@@ -72,48 +74,51 @@ DocShellOriginAttributes::InheritFromDoc
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
   // mSignedPkg accordingly by mSignedPkgInBrowser
   mSignedPkg = aAttrs.mSignedPkg;
 
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+  mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 void
 NeckoOriginAttributes::InheritFromDocToNecko(const PrincipalOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
   // mSignedPkg accordingly by mSignedPkgInBrowser
 
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+  mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 void
 NeckoOriginAttributes::InheritFromDocShellToNecko(const DocShellOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
   // mSignedPkg accordingly by mSignedPkgInBrowser
 
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+  mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 void
 OriginAttributes::CreateSuffix(nsACString& aStr) const
 {
   UniquePtr<URLParams> params(new URLParams());
   nsAutoString value;
 
@@ -156,16 +161,21 @@ OriginAttributes::CreateSuffix(nsACStrin
   }
 
   if (mPrivateBrowsingId) {
     value.Truncate();
     value.AppendInt(mPrivateBrowsingId);
     params->Set(NS_LITERAL_STRING("privateBrowsingId"), value);
   }
 
+  if (!mFirstPartyDomain.IsEmpty()) {
+    MOZ_RELEASE_ASSERT(mFirstPartyDomain.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
+    params->Set(NS_LITERAL_STRING("firstPartyDomain"), mFirstPartyDomain);
+  }
+
   aStr.Truncate();
 
   params->Serialize(value);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("^");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
   }
 
@@ -242,16 +252,22 @@ public:
       int64_t val = aValue.ToInteger64(&rv);
       NS_ENSURE_SUCCESS(rv, false);
       NS_ENSURE_TRUE(val >= 0 && val <= UINT32_MAX, false);
       mOriginAttributes->mPrivateBrowsingId = static_cast<uint32_t>(val);
 
       return true;
     }
 
+    if (aName.EqualsLiteral("firstPartyDomain")) {
+      MOZ_RELEASE_ASSERT(mOriginAttributes->mFirstPartyDomain.IsEmpty());
+      mOriginAttributes->mFirstPartyDomain.Assign(aValue);
+      return true;
+    }
+
     // No other attributes are supported.
     return false;
   }
 
 private:
   OriginAttributes* mOriginAttributes;
 };
 
@@ -302,16 +318,17 @@ void
 OriginAttributes::SetFromGenericAttributes(const GenericOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
   mAddonId = aAttrs.mAddonId;
   mUserContextId = aAttrs.mUserContextId;
   mSignedPkg = aAttrs.mSignedPkg;
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+  mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 BasePrincipal::BasePrincipal()
 {}
 
 BasePrincipal::~BasePrincipal()
 {}
 
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -32,17 +32,18 @@ class OriginAttributes : public dom::Ori
 public:
   bool operator==(const OriginAttributes& aOther) const
   {
     return mAppId == aOther.mAppId &&
            mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser &&
            mAddonId == aOther.mAddonId &&
            mUserContextId == aOther.mUserContextId &&
            mSignedPkg == aOther.mSignedPkg &&
-           mPrivateBrowsingId == aOther.mPrivateBrowsingId;
+           mPrivateBrowsingId == aOther.mPrivateBrowsingId &&
+           mFirstPartyDomain == aOther.mFirstPartyDomain;
   }
   bool operator!=(const OriginAttributes& aOther) const
   {
     return !(*this == aOther);
   }
 
   // Serializes/Deserializes non-default values into the suffix format, i.e.
   // |!key1=value1&key2=value2|. If there are no non-default attributes, this
@@ -184,16 +185,20 @@ public:
     if (mSignedPkg.WasPassed() && mSignedPkg.Value() != aAttrs.mSignedPkg) {
       return false;
     }
 
     if (mPrivateBrowsingId.WasPassed() && mPrivateBrowsingId.Value() != aAttrs.mPrivateBrowsingId) {
       return false;
     }
 
+    if (mFirstPartyDomain.WasPassed() && mFirstPartyDomain.Value() != aAttrs.mFirstPartyDomain) {
+      return false;
+    }
+
     return true;
   }
 
   bool Overlaps(const OriginAttributesPattern& aOther) const
   {
     if (mAppId.WasPassed() && aOther.mAppId.WasPassed() &&
         mAppId.Value() != aOther.mAppId.Value()) {
       return false;
@@ -220,16 +225,21 @@ public:
       return false;
     }
 
     if (mPrivateBrowsingId.WasPassed() && aOther.mPrivateBrowsingId.WasPassed() &&
         mPrivateBrowsingId.Value() != aOther.mPrivateBrowsingId.Value()) {
       return false;
     }
 
+    if (mFirstPartyDomain.WasPassed() && aOther.mFirstPartyDomain.WasPassed() &&
+        mFirstPartyDomain.Value() != aOther.mFirstPartyDomain.Value()) {
+      return false;
+    }
+
     return true;
   }
 };
 
 /*
  * Base class from which all nsIPrincipal implementations inherit. Use this for
  * default implementations and other commonalities between principal
  * implementations.
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -37,29 +37,33 @@ function checkOriginAttributes(prin, att
 
 // utility function useful for debugging
 function printAttrs(name, attrs) {
   do_print(name + " {\n" +
            "\tappId: " + attrs.appId + ",\n" +
            "\tuserContextId: " + attrs.userContextId + ",\n" +
            "\tinIsolatedMozBrowser: " + attrs.inIsolatedMozBrowser + ",\n" +
            "\taddonId: '" + attrs.addonId + "',\n" +
-           "\tsignedPkg: '" + attrs.signedPkg + "'\n}");
+           "\tsignedPkg: '" + attrs.signedPkg + "',\n" +
+           "\tprivateBrowsingId: '" + attrs.privateBrowsingId + "',\n" +
+           "\tfirstPartyDomain: '" + attrs.firstPartyDomain + "'\n}");
 }
 
 
 function checkValues(attrs, values) {
   values = values || {};
   //printAttrs("attrs", attrs);
   //printAttrs("values", values);
   do_check_eq(attrs.appId, values.appId || 0);
   do_check_eq(attrs.userContextId, values.userContextId || 0);
   do_check_eq(attrs.inIsolatedMozBrowser, values.inIsolatedMozBrowser || false);
   do_check_eq(attrs.addonId, values.addonId || '');
   do_check_eq(attrs.signedPkg, values.signedPkg || '');
+  do_check_eq(attrs.privateBrowsingId, values.privateBrowsingId || '');
+  do_check_eq(attrs.firstPartyDomain, values.firstPartyDomain || '');
 }
 
 function run_test() {
   // Attributeless origins.
   do_check_eq(ssm.getSystemPrincipal().origin, '[System Principal]');
   checkOriginAttributes(ssm.getSystemPrincipal());
   var exampleOrg = ssm.createCodebasePrincipal(makeURI('http://example.org'), {});
   do_check_eq(exampleOrg.origin, 'http://example.org');
@@ -117,16 +121,21 @@ function run_test() {
   checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inIsolatedMozBrowser: true}, '^appId=42&inBrowser=1');
   do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123^appId=42&inBrowser=1');
 
   // Addon.
   var exampleOrg_addon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy'});
   checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '^addonId=dummy');
   do_check_eq(exampleOrg_addon.origin, 'http://example.org^addonId=dummy');
 
+  // First party Uri
+  var exampleOrg_firstPartyDomain = ssm.createCodebasePrincipal(makeURI('http://example.org'), {firstPartyDomain: 'example.org'});
+  checkOriginAttributes(exampleOrg_firstPartyDomain, { firstPartyDomain: "example.org" }, '^firstPartyDomain=example.org');
+  do_check_eq(exampleOrg_firstPartyDomain.origin, 'http://example.org^firstPartyDomain=example.org');
+
   // Make sure we don't crash when serializing principals with UNKNOWN_APP_ID.
   try {
     let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
                        createInstance(Ci.nsIObjectOutputStream);
     let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
     pipe.init(false, false, 0, 0xffffffff, null);
     binaryStream.setOutputStream(pipe.outputStream);
     binaryStream.writeCompoundObject(simplePrin, Ci.nsISupports, true);
@@ -172,16 +181,17 @@ function run_test() {
   checkCrossOrigin(exampleOrg_app, exampleOrg);
   checkCrossOrigin(exampleOrg_app, nullPrin_app);
   checkCrossOrigin(exampleOrg_browser, exampleOrg_app);
   checkCrossOrigin(exampleOrg_browser, nullPrin_browser);
   checkCrossOrigin(exampleOrg_appBrowser, exampleOrg_app);
   checkCrossOrigin(exampleOrg_appBrowser, nullPrin_appBrowser);
   checkCrossOrigin(exampleOrg_appBrowser, exampleCom_appBrowser);
   checkCrossOrigin(exampleOrg_addon, exampleOrg);
+  checkCrossOrigin(exampleOrg_firstPartyDomain, exampleOrg);
   checkCrossOrigin(exampleOrg_userContext, exampleOrg);
   checkCrossOrigin(exampleOrg_userContextAddon, exampleOrg);
   checkCrossOrigin(exampleOrg_userContext, exampleOrg_userContextAddon);
   checkCrossOrigin(exampleOrg_userContext, exampleOrg_userContextApp);
   checkCrossOrigin(exampleOrg_signedPkg, exampleOrg);
   checkCrossOrigin(exampleOrg_signedPkg, exampleOrg_signedPkg_browser);
   checkCrossOrigin(exampleOrg_signedPkg, exampleOrg_signedPkg_another);
 
@@ -198,39 +208,40 @@ function run_test() {
   checkKind(ssm.getSystemPrincipal(), 'systemPrincipal');
 
   //
   // Test Origin Attribute Manipulation
   //
 
   // check that we can create an empty origin attributes dict with default
   // members and values.
-  emptyAttrs = ChromeUtils.fillNonDefaultOriginAttributes({});
+  var emptyAttrs = ChromeUtils.fillNonDefaultOriginAttributes({});
   checkValues(emptyAttrs);
 
   var uri = "http://example.org";
   var tests = [
     [ "", {} ],
     [ "^appId=5", {appId: 5} ],
     [ "^userContextId=3", {userContextId: 3} ],
     [ "^addonId=fooBar", {addonId: "fooBar"} ],
     [ "^inBrowser=1", {inIsolatedMozBrowser: true} ],
+    [ "^firstPartyDomain=example.org", {firstPartyDomain: "example.org"} ],
     [ "^signedPkg=bazQux", {signedPkg: "bazQux"} ],
     [ "^appId=3&inBrowser=1&userContextId=6",
       {appId: 3, userContextId: 6, inIsolatedMozBrowser: true} ] ];
 
   // check that we can create an origin attributes from an origin properly
-  tests.forEach(function(t) {
+  tests.forEach(t => {
     let attrs = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
     checkValues(attrs, t[1]);
     do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), t[0]);
   });
 
   // check that we can create an origin attributes from a dict properly
-  tests.forEach(function(t) {
+  tests.forEach(t => {
     let attrs = ChromeUtils.fillNonDefaultOriginAttributes(t[1]);
     checkValues(attrs, t[1]);
     do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), t[0]);
   });
 
   // each row in the set_tests array has these values:
   // [0] - the suffix used to create an origin attribute from
   // [1] - the expected result of creating an origin attribute from [0]
@@ -239,17 +250,17 @@ function run_test() {
   // [4] - the expected result of creating a suffix from [3]
   var set_tests = [
     [ "", {}, {appId: 5}, {appId: 5}, "^appId=5" ],
     [ "^appId=5", {appId: 5}, {appId: 3}, {appId: 3}, "^appId=3" ],
     [ "^appId=5", {appId: 5}, {userContextId: 3}, {appId: 5, userContextId: 3}, "^appId=5&userContextId=3" ],
     [ "^appId=5", {appId: 5}, {appId: 3, userContextId: 7}, {appId: 3, userContextId: 7}, "^appId=3&userContextId=7" ] ];
 
   // check that we can set origin attributes values properly
-  set_tests.forEach(function(t) {
+  set_tests.forEach(t => {
     let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
     checkValues(orig, t[1]);
     let mod = orig;
     for (var key in t[2]) {
       mod[key] = t[2][key];
     }
     checkValues(mod, t[3]);
     do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[4]);
@@ -262,17 +273,39 @@ function run_test() {
   // [3] - the expected result of creating a suffix from [2]
   var dflt_tests = [
     [ "", {}, {}, "" ],
     [ "^userContextId=3", {userContextId: 3}, {}, "" ],
     [ "^appId=5", {appId: 5}, {appId: 5}, "^appId=5" ],
     [ "^appId=5&userContextId=3", {appId: 5, userContextId: 3}, {appId: 5}, "^appId=5" ] ];
 
   // check that we can set the userContextId to default properly
-  dflt_tests.forEach(function(t) {
+  dflt_tests.forEach(t => {
     let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
     checkValues(orig, t[1]);
     let mod = orig;
     mod['userContextId'] = 0;
     checkValues(mod, t[2]);
     do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[3]);
   });
+
+  // each row in the dflt2_tests array has these values:
+  // [0] - the suffix used to create an origin attribute from
+  // [1] - the expected result of creating an origin attributes from [0]
+  // [2] - the expected result after setting firstPartyUri to the default
+  // [3] - the expected result of creating a suffix from [2]
+  var dflt2_tests = [
+    [ "", {}, {}, "" ],
+    [ "^firstPartyDomain=foo.com", {firstPartyDomain: "foo.com"}, {}, "" ],
+    [ "^appId=5", {appId: 5}, {appId: 5}, "^appId=5" ],
+    [ "^appId=5&firstPartyDomain=foo.com", {appId: 5, firstPartyDomain: "foo.com"}, {appId: 5}, "^appId=5" ] ];
+
+  // check that we can set the userContextId to default properly
+  dflt2_tests.forEach(t => {
+    let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+    checkValues(orig, t[1]);
+    let mod = orig;
+    mod['firstPartyDomain'] = "";
+    checkValues(mod, t[2]);
+    do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[3]);
+  });
+
 }
--- a/dom/webidl/ChromeUtils.webidl
+++ b/dom/webidl/ChromeUtils.webidl
@@ -76,17 +76,19 @@ interface ChromeUtils : ThreadSafeChrome
  */
 dictionary OriginAttributesDictionary {
   unsigned long appId = 0;
   unsigned long userContextId = 0;
   boolean inIsolatedMozBrowser = false;
   DOMString addonId = "";
   DOMString signedPkg = "";
   unsigned long privateBrowsingId = 0;
+  DOMString firstPartyDomain = "";
 };
 dictionary OriginAttributesPatternDictionary {
   unsigned long appId;
   unsigned long userContextId;
   boolean inIsolatedMozBrowser;
   DOMString addonId;
   DOMString signedPkg;
   unsigned long privateBrowsingId;
+  DOMString firstPartyDomain;
 };