Skip to content

Commit

Permalink
Bug 1880026 - Support accessing multiple cookie jars for cookie servi…
Browse files Browse the repository at this point in the history
…ce. r=edgul,cookie-reviewers

This patch changes the cookie service to support accessing multiple
cookie jars under different originAttributes. This is needed in order to
support accesing unpartitioned and partitioned cookie jars for a given
document.

Differential Revision: https://phabricator.services.mozilla.com/D201968
  • Loading branch information
artines1 committed Feb 29, 2024
1 parent 2461270 commit 790c008
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 300 deletions.
430 changes: 230 additions & 200 deletions netwerk/cookie/CookieService.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion netwerk/cookie/CookieService.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class CookieService final : public nsICookieService,
bool aIsSameSiteForeign, bool aHadCrossSiteRedirects,
bool aHttpBound,
bool aAllowSecureCookiesToInsecureOrigin,
const OriginAttributes& aOriginAttrs,
const nsTArray<OriginAttributes>& aOriginAttrsList,
nsTArray<Cookie*>& aCookieList);

/**
Expand Down
169 changes: 95 additions & 74 deletions netwerk/cookie/CookieServiceChild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ RefPtr<GenericPromise> CookieServiceChild::TrackCookieLoad(

RefPtr<CookieServiceChild> self(this);

// TODO (Bug 1874174): A channel could access both unpartitioned and
// partitioned cookie jars. We will need to pass partitioned and unpartitioned
// originAttributes according the storage access.
nsTArray<OriginAttributes> attrsList;
attrsList.AppendElement(attrs);

return SendGetCookieList(
uri, result.contains(ThirdPartyAnalysis::IsForeign),
result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource),
Expand All @@ -115,14 +121,18 @@ RefPtr<GenericPromise> CookieServiceChild::TrackCookieLoad(
result.contains(
ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
rejectedReason, isSafeTopLevelNav, isSameSiteForeign,
hadCrossSiteRedirects, attrs)
hadCrossSiteRedirects, attrsList)
->Then(
GetCurrentSerialEventTarget(), __func__,
[self, uri, attrs](const nsTArray<CookieStruct>& aCookiesList) {
for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
RefPtr<Cookie> cookie = Cookie::Create(aCookiesList[i], attrs);
cookie->SetIsHttpOnly(false);
self->RecordDocumentCookie(cookie, attrs);
[self, uri](const nsTArray<CookieStructTable>& aCookiesListTable) {
for (auto& entry : aCookiesListTable) {
auto& cookies = entry.cookies();
for (auto& cookieEntry : cookies) {
RefPtr<Cookie> cookie =
Cookie::Create(cookieEntry, entry.attrs());
cookie->SetIsHttpOnly(false);
self->RecordDocumentCookie(cookie, entry.attrs());
}
}
return GenericPromise::CreateAndResolve(true, __func__);
},
Expand Down Expand Up @@ -215,11 +225,13 @@ IPCResult CookieServiceChild::RecvRemoveBatchDeletedCookies(
}

IPCResult CookieServiceChild::RecvTrackCookiesLoad(
nsTArray<CookieStruct>&& aCookiesList, const OriginAttributes& aAttrs) {
for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
RefPtr<Cookie> cookie = Cookie::Create(aCookiesList[i], aAttrs);
cookie->SetIsHttpOnly(false);
RecordDocumentCookie(cookie, aAttrs);
nsTArray<CookieStructTable>&& aCookiesListTable) {
for (auto& entry : aCookiesListTable) {
for (auto& cookieEntry : entry.cookies()) {
RefPtr<Cookie> cookie = Cookie::Create(cookieEntry, entry.attrs());
cookie->SetIsHttpOnly(false);
RecordDocumentCookie(cookie, entry.attrs());
}
}

nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
Expand Down Expand Up @@ -313,34 +325,14 @@ CookieServiceChild::GetCookieStringFromDocument(dom::Document* aDocument,

aCookieString.Truncate();

nsCOMPtr<nsIPrincipal> principal = aDocument->EffectiveCookiePrincipal();

if (!CookieCommons::IsSchemeSupported(principal)) {
return NS_OK;
}

nsAutoCString baseDomain;
nsresult rv = CookieCommons::GetBaseDomain(principal, baseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}

CookieKey key(baseDomain, principal->OriginAttributesRef());
CookiesList* cookiesList = nullptr;
mCookiesMap.Get(key, &cookiesList);

if (!cookiesList) {
return NS_OK;
}
nsCOMPtr<nsIPrincipal> cookiePrincipal =
aDocument->EffectiveCookiePrincipal();

nsAutoCString hostFromURI;
rv = nsContentUtils::GetHostOrIPv6WithBrackets(principal, hostFromURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}

nsAutoCString pathFromURI;
principal->GetFilePath(pathFromURI);
// TODO (Bug 1874174): A document could access both unpartitioned and
// partitioned cookie jars. We will need to prepare partitioned and
// unpartitioned principals for access both cookie jars.
nsTArray<nsCOMPtr<nsIPrincipal>> principals;
principals.AppendElement(cookiePrincipal);

bool thirdParty = true;
nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow();
Expand All @@ -355,54 +347,83 @@ CookieServiceChild::GetCookieStringFromDocument(dom::Document* aDocument,
}
}

bool isPotentiallyTrustworthy =
principal->GetIsOriginPotentiallyTrustworthy();
int64_t currentTimeInUsec = PR_Now();
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;

cookiesList->Sort(CompareCookiesForSending());
for (uint32_t i = 0; i < cookiesList->Length(); i++) {
Cookie* cookie = cookiesList->ElementAt(i);
// check the host, since the base domain lookup is conservative.
if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
continue;
for (auto& principal : principals) {
if (!CookieCommons::IsSchemeSupported(principal)) {
return NS_OK;
}

// We don't show HttpOnly cookies in content processes.
if (cookie->IsHttpOnly()) {
continue;
nsAutoCString baseDomain;
nsresult rv = CookieCommons::GetBaseDomain(principal, baseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}

if (thirdParty && !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
cookie, aDocument)) {
continue;
}
CookieKey key(baseDomain, principal->OriginAttributesRef());
CookiesList* cookiesList = nullptr;
mCookiesMap.Get(key, &cookiesList);

// do not display the cookie if it is secure and the host scheme isn't
if (cookie->IsSecure() && !isPotentiallyTrustworthy) {
if (!cookiesList) {
continue;
}

// if the nsIURI path doesn't match the cookie path, don't send it back
if (!CookieCommons::PathMatches(cookie, pathFromURI)) {
continue;
nsAutoCString hostFromURI;
rv = nsContentUtils::GetHostOrIPv6WithBrackets(principal, hostFromURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}

// check if the cookie has expired
if (cookie->Expiry() <= currentTime) {
continue;
}
nsAutoCString pathFromURI;
principal->GetFilePath(pathFromURI);

if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
if (!aCookieString.IsEmpty()) {
aCookieString.AppendLiteral("; ");
bool isPotentiallyTrustworthy =
principal->GetIsOriginPotentiallyTrustworthy();
int64_t currentTimeInUsec = PR_Now();
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;

cookiesList->Sort(CompareCookiesForSending());
for (uint32_t i = 0; i < cookiesList->Length(); i++) {
Cookie* cookie = cookiesList->ElementAt(i);
// check the host, since the base domain lookup is conservative.
if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
continue;
}
if (!cookie->Name().IsEmpty()) {
aCookieString.Append(cookie->Name().get());
aCookieString.AppendLiteral("=");
aCookieString.Append(cookie->Value().get());
} else {
aCookieString.Append(cookie->Value().get());

// We don't show HttpOnly cookies in content processes.
if (cookie->IsHttpOnly()) {
continue;
}

if (thirdParty && !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
cookie, aDocument)) {
continue;
}

// do not display the cookie if it is secure and the host scheme isn't
if (cookie->IsSecure() && !isPotentiallyTrustworthy) {
continue;
}

// if the nsIURI path doesn't match the cookie path, don't send it back
if (!CookieCommons::PathMatches(cookie, pathFromURI)) {
continue;
}

// check if the cookie has expired
if (cookie->Expiry() <= currentTime) {
continue;
}

if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
if (!aCookieString.IsEmpty()) {
aCookieString.AppendLiteral("; ");
}
if (!cookie->Name().IsEmpty()) {
aCookieString.Append(cookie->Name().get());
aCookieString.AppendLiteral("=");
aCookieString.Append(cookie->Value().get());
} else {
aCookieString.Append(cookie->Value().get());
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion netwerk/cookie/CookieServiceChild.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class CookieServiceChild final : public PCookieServiceChild,
static bool RequireThirdPartyCheck(nsILoadInfo* aLoadInfo);

mozilla::ipc::IPCResult RecvTrackCookiesLoad(
nsTArray<CookieStruct>&& aCookiesList, const OriginAttributes& aAttrs);
nsTArray<CookieStructTable>&& aCookiesListTable);

mozilla::ipc::IPCResult RecvRemoveAll();

Expand Down
53 changes: 37 additions & 16 deletions netwerk/cookie/CookieServiceParent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "CookieCommons.h"
#include "CookieLogging.h"
#include "CookieServiceParent.h"
#include "mozilla/net/CookieService.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/NeckoParent.h"
Expand Down Expand Up @@ -124,6 +125,8 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) {
bool isSameSiteForeign =
CookieCommons::IsSameSiteForeign(aChannel, uri, &hadCrossSiteRedirects);

// TODO (Bug 1874174): A channel could load both unpartitioned and partitioned
// cookie jars together. We will need to track both originAttributes for them.
StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
aChannel, attrs);

Expand All @@ -134,7 +137,12 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) {
ThirdPartyAnalysisResult result = thirdPartyUtil->AnalyzeChannel(
aChannel, false, nullptr, nullptr, &rejectedReason);

UpdateCookieInContentList(uri, attrs);
nsTArray<OriginAttributes> originAttributesList;
originAttributesList.AppendElement(attrs);

for (auto& originAttributes : originAttributesList) {
UpdateCookieInContentList(uri, originAttributes);
}

// Send matching cookies to Child.
nsTArray<Cookie*> foundCookieList;
Expand All @@ -144,10 +152,11 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) {
result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource),
result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
rejectedReason, isSafeTopLevelNav, isSameSiteForeign,
hadCrossSiteRedirects, false, true, attrs, foundCookieList);
nsTArray<CookieStruct> matchingCookiesList;
SerializeCookieList(foundCookieList, matchingCookiesList, uri);
Unused << SendTrackCookiesLoad(matchingCookiesList, attrs);
hadCrossSiteRedirects, false, true, originAttributesList,
foundCookieList);
nsTArray<CookieStructTable> matchingCookiesListTable;
SerializeCookieListTable(foundCookieList, matchingCookiesListTable, uri);
Unused << SendTrackCookiesLoad(matchingCookiesListTable);
}

// we append outgoing cookie info into a list here so the ContentParent can
Expand All @@ -170,12 +179,22 @@ void CookieServiceParent::UpdateCookieInContentList(
}

// static
void CookieServiceParent::SerializeCookieList(
void CookieServiceParent::SerializeCookieListTable(
const nsTArray<Cookie*>& aFoundCookieList,
nsTArray<CookieStruct>& aCookiesList, nsIURI* aHostURI) {
for (uint32_t i = 0; i < aFoundCookieList.Length(); i++) {
Cookie* cookie = aFoundCookieList.ElementAt(i);
CookieStruct* cookieStruct = aCookiesList.AppendElement();
nsTArray<CookieStructTable>& aCookiesListTable, nsIURI* aHostURI) {
nsTHashMap<nsCStringHashKey, CookieStructTable*> cookieListTable;

for (Cookie* cookie : aFoundCookieList) {
nsAutoCString attrsSuffix;
cookie->OriginAttributesRef().CreateSuffix(attrsSuffix);
CookieStructTable* table =
cookieListTable.LookupOrInsertWith(attrsSuffix, [&] {
CookieStructTable* newTable = aCookiesListTable.AppendElement();
newTable->attrs() = cookie->OriginAttributesRef();
return newTable;
});

CookieStruct* cookieStruct = table->cookies().AppendElement();
*cookieStruct = cookie->ToIPC();

// clear http-only cookie values
Expand All @@ -200,15 +219,17 @@ IPCResult CookieServiceParent::RecvGetCookieList(
const bool& aStorageAccessPermissionGranted,
const uint32_t& aRejectedReason, const bool& aIsSafeTopLevelNav,
const bool& aIsSameSiteForeign, const bool& aHadCrossSiteRedirects,
const OriginAttributes& aAttrs, GetCookieListResolver&& aResolve) {
nsTArray<OriginAttributes>&& aAttrsList, GetCookieListResolver&& aResolve) {
// Send matching cookies to Child.
if (!aHost) {
return IPC_FAIL(this, "aHost must not be null");
}

// we append outgoing cookie info into a list here so the ContentParent can
// filter cookies that do not need to go to certain ContentProcesses
UpdateCookieInContentList(aHost, aAttrs);
for (const auto& attrs : aAttrsList) {
UpdateCookieInContentList(aHost, attrs);
}

nsTArray<Cookie*> foundCookieList;
// Note: passing nullptr as aChannel to GetCookiesForURI() here is fine since
Expand All @@ -218,12 +239,12 @@ IPCResult CookieServiceParent::RecvGetCookieList(
aHost, nullptr, aIsForeign, aIsThirdPartyTrackingResource,
aIsThirdPartySocialTrackingResource, aStorageAccessPermissionGranted,
aRejectedReason, aIsSafeTopLevelNav, aIsSameSiteForeign,
aHadCrossSiteRedirects, false, true, aAttrs, foundCookieList);
aHadCrossSiteRedirects, false, true, aAttrsList, foundCookieList);

nsTArray<CookieStruct> matchingCookiesList;
SerializeCookieList(foundCookieList, matchingCookiesList, aHost);
nsTArray<CookieStructTable> matchingCookiesListTable;
SerializeCookieListTable(foundCookieList, matchingCookiesListTable, aHost);

aResolve(matchingCookiesList);
aResolve(matchingCookiesListTable);

return IPC_OK();
}
Expand Down
9 changes: 5 additions & 4 deletions netwerk/cookie/CookieServiceParent.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ class CookieServiceParent : public PCookieServiceParent {
const bool& aStorageAccessPermissionGranted,
const uint32_t& aRejectedReason, const bool& aIsSafeTopLevelNav,
const bool& aIsSameSiteForeign, const bool& aHadCrossSiteRedirects,
const OriginAttributes& aAttrs, GetCookieListResolver&& aResolve);
nsTArray<OriginAttributes>&& aAttrsList,
GetCookieListResolver&& aResolve);

static void SerializeCookieList(const nsTArray<Cookie*>& aFoundCookieList,
nsTArray<CookieStruct>& aCookiesList,
nsIURI* aHostURI);
static void SerializeCookieListTable(
const nsTArray<Cookie*>& aFoundCookieList,
nsTArray<CookieStructTable>& aCookiesListTable, nsIURI* aHostURI);

nsCOMPtr<nsIEffectiveTLDService> mTLDService;
RefPtr<CookieService> mCookieService;
Expand Down
7 changes: 3 additions & 4 deletions netwerk/cookie/PCookieService.ipdl
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,13 @@ parent:
bool isSafeTopLevelNav,
bool isSameSiteForeign,
bool hadCrossSiteRedirects,
OriginAttributes attrs)
returns (CookieStruct[] cookies);
OriginAttributes[] attrsList)
returns (CookieStructTable[] cookies);

async __delete__();

child:
async TrackCookiesLoad(CookieStruct[] cookiesList,
OriginAttributes attrs);
async TrackCookiesLoad(CookieStructTable[] cookiesListTable);

async RemoveCookie(CookieStruct cookie,
OriginAttributes attrs);
Expand Down
6 changes: 6 additions & 0 deletions netwerk/ipc/NeckoChannelParams.ipdlh
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ struct CookieStruct
uint8_t schemeMap;
};

struct CookieStructTable
{
OriginAttributes attrs;
CookieStruct[] cookies;
};

struct DocumentCreationArgs {
bool uriModified;
bool isEmbeddingBlockedError;
Expand Down

0 comments on commit 790c008

Please sign in to comment.