From 0830861f7e1410448d9c9582106459a92ce8ed23 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 11 Apr 2019 16:28:51 +0000 Subject: [PATCH] Bug 1536411 - StoragePrincipal - part 6 - Cookies, r=Ehsan Differential Revision: https://phabricator.services.mozilla.com/D24864 --HG-- extra : moz-landing-system : lando --- dom/html/nsHTMLDocument.cpp | 18 +++- netwerk/base/nsNetUtil.cpp | 10 ++- netwerk/base/nsNetUtil.h | 5 +- netwerk/cookie/CookieServiceChild.cpp | 6 +- netwerk/cookie/CookieServiceParent.cpp | 15 ++-- netwerk/cookie/CookieServiceParent.h | 4 +- netwerk/cookie/PCookieService.ipdl | 1 + netwerk/cookie/nsCookieService.cpp | 12 ++- .../antitracking/StoragePrincipalHelper.cpp | 86 +++++++++++++------ .../antitracking/StoragePrincipalHelper.h | 5 ++ .../antitracking/test/browser/browser.ini | 2 + .../browser/browser_partitionedCookies.js | 49 +++++++++++ .../antitracking/test/browser/cookies.sjs | 12 +++ 13 files changed, 179 insertions(+), 46 deletions(-) create mode 100644 toolkit/components/antitracking/test/browser/browser_partitionedCookies.js create mode 100644 toolkit/components/antitracking/test/browser/cookies.sjs diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index 3b8b87129d34..ba48cd947729 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -1030,7 +1030,14 @@ void nsHTMLDocument::GetCookie(nsAString& aCookie, ErrorResult& rv) { return; } - if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) { + nsContentUtils::StorageAccess storageAccess = + nsContentUtils::StorageAllowedForDocument(this); + if (storageAccess == nsContentUtils::StorageAccess::eDeny) { + return; + } + + if (storageAccess == nsContentUtils::StorageAccess::ePartitionedOrDeny && + !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) { return; } @@ -1083,7 +1090,14 @@ void nsHTMLDocument::SetCookie(const nsAString& aCookie, ErrorResult& rv) { return; } - if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) { + nsContentUtils::StorageAccess storageAccess = + nsContentUtils::StorageAllowedForDocument(this); + if (storageAccess == nsContentUtils::StorageAccess::eDeny) { + return; + } + + if (storageAccess == nsContentUtils::StorageAccess::ePartitionedOrDeny && + !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) { return; } diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index c0d53fed3319..7d3671c078bc 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -16,6 +16,7 @@ #include "mozilla/LoadInfo.h" #include "mozilla/BasePrincipal.h" #include "mozilla/Monitor.h" +#include "mozilla/StoragePrincipalHelper.h" #include "mozilla/TaskQueue.h" #include "mozilla/Telemetry.h" #include "nsCategoryCache.h" @@ -1851,13 +1852,14 @@ nsresult NS_LoadPersistentPropertiesFromURISpec( bool NS_UsePrivateBrowsing(nsIChannel *channel) { OriginAttributes attrs; - bool result = NS_GetOriginAttributes(channel, attrs); + bool result = NS_GetOriginAttributes(channel, attrs, false); NS_ENSURE_TRUE(result, result); return attrs.mPrivateBrowsingId > 0; } bool NS_GetOriginAttributes(nsIChannel *aChannel, - mozilla::OriginAttributes &aAttributes) { + mozilla::OriginAttributes &aAttributes, + bool aUsingStoragePrincipal) { nsCOMPtr loadInfo = aChannel->LoadInfo(); loadInfo->GetOriginAttributes(&aAttributes); @@ -1873,6 +1875,10 @@ bool NS_GetOriginAttributes(nsIChannel *aChannel, isPrivate = loadContext && loadContext->UsePrivateBrowsing(); } aAttributes.SyncAttributesWithPrivateBrowsing(isPrivate); + + if (aUsingStoragePrincipal) { + StoragePrincipalHelper::PrepareOriginAttributes(aChannel, aAttributes); + } return true; } diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index a88499906429..88581e7fa9e5 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -610,9 +610,12 @@ bool NS_UsePrivateBrowsing(nsIChannel *channel); /** * Extract the OriginAttributes from the channel's triggering principal. + * If aUsingStoragePrincipal is set to true, the originAttributes could have + * first-party isolation domain set to the top-level URI. */ bool NS_GetOriginAttributes(nsIChannel *aChannel, - mozilla::OriginAttributes &aAttributes); + mozilla::OriginAttributes &aAttributes, + bool aUsingStoragePrincipal = false); /** * Returns true if the channel has visited any cross-origin URLs on any diff --git a/netwerk/cookie/CookieServiceChild.cpp b/netwerk/cookie/CookieServiceChild.cpp index 20c5a798959a..39d48f630829 100644 --- a/netwerk/cookie/CookieServiceChild.cpp +++ b/netwerk/cookie/CookieServiceChild.cpp @@ -13,6 +13,7 @@ #include "mozilla/ipc/URIUtils.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/SystemGroup.h" +#include "mozilla/StoragePrincipalHelper.h" #include "nsCookie.h" #include "nsCookieService.h" #include "nsContentUtils.h" @@ -175,6 +176,7 @@ void CookieServiceChild::TrackCookieLoad(nsIChannel *aChannel) { } } mozilla::OriginAttributes attrs = loadInfo->GetOriginAttributes(); + StoragePrincipalHelper::PrepareOriginAttributes(aChannel, attrs); URIParams uriParams; SerializeURI(uri, uriParams); bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel); @@ -294,6 +296,7 @@ void CookieServiceChild::GetCookieStringFromCookieHashTable( if (aChannel) { loadInfo = aChannel->LoadInfo(); attrs = loadInfo->GetOriginAttributes(); + StoragePrincipalHelper::PrepareOriginAttributes(aChannel, attrs); } nsCookieService::GetBaseDomain(TLDService, aHostURI, baseDomain, @@ -562,6 +565,7 @@ nsresult CookieServiceChild::SetCookieStringInternal(nsIURI *aHostURI, MOZ_ASSERT(loadInfo); attrs = loadInfo->GetOriginAttributes(); + StoragePrincipalHelper::PrepareOriginAttributes(aChannel, attrs); } else { SerializeURI(nullptr, channelURIParams); } @@ -573,7 +577,7 @@ nsresult CookieServiceChild::SetCookieStringInternal(nsIURI *aHostURI, if (mIPCOpen) { SendSetCookieString(hostURIParams, channelURIParams, optionalLoadInfoArgs, isForeign, isTrackingResource, - firstPartyStorageAccessGranted, cookieString, + firstPartyStorageAccessGranted, attrs, cookieString, stringServerTime, aFromHttp); } diff --git a/netwerk/cookie/CookieServiceParent.cpp b/netwerk/cookie/CookieServiceParent.cpp index 384622dd2cf5..e78e148e7310 100644 --- a/netwerk/cookie/CookieServiceParent.cpp +++ b/netwerk/cookie/CookieServiceParent.cpp @@ -9,6 +9,7 @@ #include "mozilla/BasePrincipal.h" #include "mozilla/ipc/URIUtils.h" +#include "mozilla/StoragePrincipalHelper.h" #include "nsArrayUtils.h" #include "nsCookieService.h" #include "nsIChannel.h" @@ -126,6 +127,8 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel *aChannel) { bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel); bool aIsSameSiteForeign = NS_IsSameSiteForeign(aChannel, uri); + StoragePrincipalHelper::PrepareOriginAttributes(aChannel, attrs); + // Send matching cookies to Child. nsCOMPtr thirdPartyUtil; thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID); @@ -207,8 +210,9 @@ mozilla::ipc::IPCResult CookieServiceParent::RecvSetCookieString( const URIParams &aHost, const Maybe &aChannelURI, const Maybe &aLoadInfoArgs, const bool &aIsForeign, const bool &aIsTrackingResource, - const bool &aFirstPartyStorageAccessGranted, const nsCString &aCookieString, - const nsCString &aServerTime, const bool &aFromHttp) { + const bool &aFirstPartyStorageAccessGranted, const OriginAttributes &aAttrs, + const nsCString &aCookieString, const nsCString &aServerTime, + const bool &aFromHttp) { if (!mCookieService) return IPC_OK(); // Deserialize URI. Having a host URI is mandatory and should always be @@ -240,17 +244,12 @@ mozilla::ipc::IPCResult CookieServiceParent::RecvSetCookieString( // NB: dummyChannel could be null if something failed in CreateDummyChannel. nsDependentCString cookieString(aCookieString, 0); - OriginAttributes attrs; - if (loadInfo) { - attrs = loadInfo->GetOriginAttributes(); - } - // We set this to true while processing this cookie update, to make sure // we don't send it back to the same content process. mProcessingCookie = true; mCookieService->SetCookieStringInternal( hostURI, aIsForeign, aIsTrackingResource, aFirstPartyStorageAccessGranted, - cookieString, aServerTime, aFromHttp, attrs, dummyChannel); + cookieString, aServerTime, aFromHttp, aAttrs, dummyChannel); mProcessingCookie = false; return IPC_OK(); } diff --git a/netwerk/cookie/CookieServiceParent.h b/netwerk/cookie/CookieServiceParent.h index 3129bf96c5e6..8d67a2958ea3 100644 --- a/netwerk/cookie/CookieServiceParent.h +++ b/netwerk/cookie/CookieServiceParent.h @@ -49,8 +49,8 @@ class CookieServiceParent : public PCookieServiceParent { const Maybe &aLoadInfoArgs, const bool &aIsForeign, const bool &aIsTrackingResource, const bool &aFirstPartyStorageAccessGranted, - const nsCString &aCookieString, const nsCString &aServerTime, - const bool &aFromHttp); + const OriginAttributes &aAttrs, const nsCString &aCookieString, + const nsCString &aServerTime, const bool &aFromHttp); mozilla::ipc::IPCResult RecvPrepareCookieList( const URIParams &aHost, const bool &aIsForeign, diff --git a/netwerk/cookie/PCookieService.ipdl b/netwerk/cookie/PCookieService.ipdl index 4f8f3985f45a..0d02a097c839 100644 --- a/netwerk/cookie/PCookieService.ipdl +++ b/netwerk/cookie/PCookieService.ipdl @@ -75,6 +75,7 @@ parent: bool isForeign, bool isTrackingResource, bool firstPartyStorageAccessGranted, + OriginAttributes aStoragePrincipalAttrs, nsCString cookieString, nsCString serverTime, bool aFromHttp); diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp index be266997c579..0fb1e4d41511 100644 --- a/netwerk/cookie/nsCookieService.cpp +++ b/netwerk/cookie/nsCookieService.cpp @@ -1990,7 +1990,8 @@ nsresult nsCookieService::GetCookieStringCommon(nsIURI *aHostURI, OriginAttributes attrs; if (aChannel) { - NS_GetOriginAttributes(aChannel, attrs); + NS_GetOriginAttributes(aChannel, attrs, + true /* considering storage principal */); } bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel); @@ -2109,7 +2110,8 @@ nsresult nsCookieService::SetCookieStringCommon(nsIURI *aHostURI, OriginAttributes attrs; if (aChannel) { - NS_GetOriginAttributes(aChannel, attrs); + NS_GetOriginAttributes(aChannel, attrs, + true /* considering storage principal */); } nsDependentCString cookieString(aCookieHeader); @@ -4040,6 +4042,12 @@ CookieStatus nsCookieService::CheckPrefs( if (aIsForeign && aIsTrackingResource && !aFirstPartyStorageAccessGranted && aCookieSettings->GetCookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER) { + if (StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) { + MOZ_ASSERT(!aOriginAttrs.mFirstPartyDomain.IsEmpty(), + "We must have a StoragePrincipal here!"); + return STATUS_ACCEPTED; + } + COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are disabled in trackers"); *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER; diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.cpp b/toolkit/components/antitracking/StoragePrincipalHelper.cpp index 4178642c8193..3faad336484f 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp +++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp @@ -13,6 +13,47 @@ namespace mozilla { +namespace { + +already_AddRefed MaybeGetFirstPartyURI(nsIChannel* aChannel) { + MOZ_ASSERT(aChannel); + + if (!StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) { + return nullptr; + } + + // Let's use the storage principal only if we need to partition the cookie + // jar. + nsContentUtils::StorageAccess access = + nsContentUtils::StorageAllowedForChannel(aChannel); + if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny) { + return nullptr; + } + + nsCOMPtr httpChannel = do_QueryInterface(aChannel); + if (!httpChannel) { + return nullptr; + } + + MOZ_ASSERT(httpChannel->IsThirdPartyTrackingResource()); + + nsCOMPtr loadInfo = aChannel->LoadInfo(); + nsCOMPtr toplevelPrincipal = loadInfo->GetTopLevelPrincipal(); + if (!toplevelPrincipal) { + return nullptr; + } + + nsCOMPtr principalURI; + nsresult rv = toplevelPrincipal->GetURI(getter_AddRefs(principalURI)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + return principalURI.forget(); +} + +} // namespace + // static nsresult StoragePrincipalHelper::Create(nsIChannel* aChannel, nsIPrincipal* aPrincipal, @@ -26,37 +67,11 @@ nsresult StoragePrincipalHelper::Create(nsIChannel* aChannel, storagePrincipal.forget(aStoragePrincipal); }); - if (!StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) { + nsCOMPtr principalURI = MaybeGetFirstPartyURI(aChannel); + if (!principalURI) { return NS_OK; } - // Let's use the storage principal only if we need to partition the cookie - // jar. - nsContentUtils::StorageAccess access = - nsContentUtils::StorageAllowedForChannel(aChannel); - if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny) { - return NS_OK; - } - - nsCOMPtr httpChannel = do_QueryInterface(aChannel); - if (!httpChannel) { - return NS_OK; - } - - MOZ_ASSERT(httpChannel->IsThirdPartyTrackingResource()); - - nsCOMPtr loadInfo = aChannel->LoadInfo(); - nsCOMPtr toplevelPrincipal = loadInfo->GetTopLevelPrincipal(); - if (!toplevelPrincipal) { - return NS_OK; - } - - nsCOMPtr principalURI; - nsresult rv = toplevelPrincipal->GetURI(getter_AddRefs(principalURI)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - scopeExit.release(); nsCOMPtr storagePrincipal = @@ -67,4 +82,19 @@ nsresult StoragePrincipalHelper::Create(nsIChannel* aChannel, return NS_OK; } +// static +nsresult StoragePrincipalHelper::PrepareOriginAttributes( + nsIChannel* aChannel, OriginAttributes& aOriginAttributes) { + MOZ_ASSERT(aChannel); + + nsCOMPtr principalURI = MaybeGetFirstPartyURI(aChannel); + if (!principalURI) { + return NS_OK; + } + + aOriginAttributes.SetFirstPartyDomain(false, principalURI, + true /* aForced */); + return NS_OK; +} + } // namespace mozilla diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.h b/toolkit/components/antitracking/StoragePrincipalHelper.h index b47da0eddd3d..38699a0f758b 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.h +++ b/toolkit/components/antitracking/StoragePrincipalHelper.h @@ -12,10 +12,15 @@ class nsIPrincipal; namespace mozilla { +class OriginAttributes; + class StoragePrincipalHelper final { public: static nsresult Create(nsIChannel* aChannel, nsIPrincipal* aPrincipal, nsIPrincipal** aStoragePrincipal); + + static nsresult PrepareOriginAttributes(nsIChannel* aChannel, + OriginAttributes& aOriginAttributes); }; } // namespace mozilla diff --git a/toolkit/components/antitracking/test/browser/browser.ini b/toolkit/components/antitracking/test/browser/browser.ini index d366383a516e..f2ee9bb92c15 100644 --- a/toolkit/components/antitracking/test/browser/browser.ini +++ b/toolkit/components/antitracking/test/browser/browser.ini @@ -93,3 +93,5 @@ support-files = workerIframe.html [browser_cookieBetweenTabs.js] [browser_partitionedMessaging.js] [browser_partitionedIndexedDB.js] +[browser_partitionedCookies.js] +support-files = cookies.sjs diff --git a/toolkit/components/antitracking/test/browser/browser_partitionedCookies.js b/toolkit/components/antitracking/test/browser/browser_partitionedCookies.js new file mode 100644 index 000000000000..0eaf3e1a85ae --- /dev/null +++ b/toolkit/components/antitracking/test/browser/browser_partitionedCookies.js @@ -0,0 +1,49 @@ +/* import-globals-from storageprincipal_head.js */ + +StoragePrincipalHelper.runTest("HTTP Cookies", + async (win3rdParty, win1stParty, allowed) => { + await win3rdParty.fetch("cookies.sjs?3rd").then(r => r.text()); + await win3rdParty.fetch("cookies.sjs").then(r => r.text()).then(text => { + is(text, "cookie:foopy=3rd", "3rd party cookie set"); + }); + + await win1stParty.fetch("cookies.sjs?first").then(r => r.text()); + await win1stParty.fetch("cookies.sjs").then(r => r.text()).then(text => { + is(text, "cookie:foopy=first", "First party cookie set"); + }); + + await win3rdParty.fetch("cookies.sjs").then(r => r.text()).then(text => { + if (allowed) { + is(text, "cookie:foopy=first", "3rd party has the first party cookie set"); + } else { + is(text, "cookie:foopy=3rd", "3rd party has not the first party cookie set"); + } + }); + }, + + async _ => { + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve()); + }); + }); + +StoragePrincipalHelper.runTest("DOM Cookies", + async (win3rdParty, win1stParty, allowed) => { + win3rdParty.document.cookie = "foo=3rd"; + is(win3rdParty.document.cookie, "foo=3rd", "3rd party cookie set"); + + win1stParty.document.cookie = "foo=first"; + is(win1stParty.document.cookie, "foo=first", "First party cookie set"); + + if (allowed) { + is(win3rdParty.document.cookie, "foo=first", "3rd party has the first party cookie set"); + } else { + is(win3rdParty.document.cookie, "foo=3rd", "3rd party has not the first party cookie set"); + } + }, + + async _ => { + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve()); + }); + }); diff --git a/toolkit/components/antitracking/test/browser/cookies.sjs b/toolkit/components/antitracking/test/browser/cookies.sjs new file mode 100644 index 000000000000..1267a69d8c4e --- /dev/null +++ b/toolkit/components/antitracking/test/browser/cookies.sjs @@ -0,0 +1,12 @@ +function handleRequest(aRequest, aResponse) { + aResponse.setStatusLine(aRequest.httpVersion, 200); + let cookie = ""; + if (aRequest.hasHeader("Cookie")) { + cookie = aRequest.getHeader("Cookie"); + } + aResponse.write("cookie:" + cookie); + + if (aRequest.queryString) { + aResponse.setHeader("Set-Cookie", "foopy=" + aRequest.queryString); + } +}