Bug 1880026 - Support accessing multiple cookie jars for cookie service. 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
This commit is contained in:
Tim Huang 2024-02-29 09:58:57 +00:00
parent 2461270e57
commit 790c008f43
8 changed files with 392 additions and 314 deletions

View file

@ -372,44 +372,14 @@ CookieService::GetCookieStringFromDocument(Document* aDocument,
return NS_OK;
}
nsCOMPtr<nsIPrincipal> principal = aDocument->EffectiveCookiePrincipal();
nsCOMPtr<nsIPrincipal> cookiePrincipal =
aDocument->EffectiveCookiePrincipal();
if (!CookieCommons::IsSchemeSupported(principal)) {
return NS_OK;
}
CookieStorage* storage = PickStorage(principal->OriginAttributesRef());
nsAutoCString baseDomain;
rv = CookieCommons::GetBaseDomain(principal, baseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
nsAutoCString hostFromURI;
rv = nsContentUtils::GetHostOrIPv6WithBrackets(principal, hostFromURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
nsAutoCString pathFromURI;
rv = principal->GetFilePath(pathFromURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
int64_t currentTimeInUsec = PR_Now();
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
const nsTArray<RefPtr<Cookie>>* cookies =
storage->GetCookiesFromHost(baseDomain, principal->OriginAttributesRef());
if (!cookies) {
return NS_OK;
}
// check if the nsIPrincipal is using an https secure protocol.
// if it isn't, then we can't send a secure cookie over the connection.
bool potentiallyTrustworthy = principal->GetIsOriginPotentiallyTrustworthy();
// 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();
@ -424,47 +394,98 @@ CookieService::GetCookieStringFromDocument(Document* aDocument,
}
}
bool stale = false;
nsTArray<Cookie*> cookieList;
// iterate the cookies!
for (Cookie* cookie : *cookies) {
// check the host, since the base domain lookup is conservative.
if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
for (auto& principal : principals) {
if (!CookieCommons::IsSchemeSupported(principal)) {
return NS_OK;
}
CookieStorage* storage = PickStorage(principal->OriginAttributesRef());
nsAutoCString baseDomain;
rv = CookieCommons::GetBaseDomain(principal, baseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
nsAutoCString hostFromURI;
rv = nsContentUtils::GetHostOrIPv6WithBrackets(principal, hostFromURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
nsAutoCString pathFromURI;
rv = principal->GetFilePath(pathFromURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
int64_t currentTimeInUsec = PR_Now();
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
const nsTArray<RefPtr<Cookie>>* cookies = storage->GetCookiesFromHost(
baseDomain, principal->OriginAttributesRef());
if (!cookies) {
continue;
}
// if the cookie is httpOnly and it's not going directly to the HTTP
// connection, don't send it
if (cookie->IsHttpOnly()) {
// check if the nsIPrincipal is using an https secure protocol.
// if it isn't, then we can't send a secure cookie over the connection.
bool potentiallyTrustworthy =
principal->GetIsOriginPotentiallyTrustworthy();
bool stale = false;
// iterate the cookies!
for (Cookie* cookie : *cookies) {
// check the host, since the base domain lookup is conservative.
if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
continue;
}
// if the cookie is httpOnly and it's not going directly to the HTTP
// connection, don't send it
if (cookie->IsHttpOnly()) {
continue;
}
if (thirdParty && !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
cookie, aDocument)) {
continue;
}
// if the cookie is secure and the host scheme isn't, we can't send it
if (cookie->IsSecure() && !potentiallyTrustworthy) {
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;
}
// all checks passed - add to list and check if lastAccessed stamp needs
// updating
cookieList.AppendElement(cookie);
if (cookie->IsStale()) {
stale = true;
}
}
if (cookieList.IsEmpty()) {
continue;
}
if (thirdParty && !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
cookie, aDocument)) {
continue;
}
// if the cookie is secure and the host scheme isn't, we can't send it
if (cookie->IsSecure() && !potentiallyTrustworthy) {
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;
}
// all checks passed - add to list and check if lastAccessed stamp needs
// updating
cookieList.AppendElement(cookie);
if (cookie->IsStale()) {
stale = true;
// update lastAccessed timestamps. we only do this if the timestamp is stale
// by a certain amount, to avoid thrashing the db during pageload.
if (stale) {
storage->StaleCookies(cookieList, currentTimeInUsec);
}
}
@ -472,12 +493,6 @@ CookieService::GetCookieStringFromDocument(Document* aDocument,
return NS_OK;
}
// update lastAccessed timestamps. we only do this if the timestamp is stale
// by a certain amount, to avoid thrashing the db during pageload.
if (stale) {
storage->StaleCookies(cookieList, currentTimeInUsec);
}
// return cookies in order of path length; longest to shortest.
// this is required per RFC2109. if cookies match in length,
// then sort by creation time (see bug 236772).
@ -512,6 +527,12 @@ CookieService::GetCookieStringFromHttp(nsIURI* aHostURI, nsIChannel* aChannel,
bool isSameSiteForeign = CookieCommons::IsSameSiteForeign(
aChannel, aHostURI, &hadCrossSiteRedirects);
// TODO (Bug 1874174): A channel could load both unpartitioned and partitioned
// cookie jars together. We will need to get cookies from both unpartitioned
// and partitioned cookie jars according to storage access.
nsTArray<OriginAttributes> originAttributesList;
originAttributesList.AppendElement(attrs);
AutoTArray<Cookie*, 8> foundCookieList;
GetCookiesForURI(
aHostURI, aChannel, result.contains(ThirdPartyAnalysis::IsForeign),
@ -519,7 +540,8 @@ CookieService::GetCookieStringFromHttp(nsIURI* aHostURI, nsIChannel* aChannel,
result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource),
result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
rejectedReason, isSafeTopLevelNav, isSameSiteForeign,
hadCrossSiteRedirects, true, false, attrs, foundCookieList);
hadCrossSiteRedirects, true, false, originAttributesList,
foundCookieList);
ComposeCookieString(foundCookieList, aCookieString);
@ -936,7 +958,8 @@ void CookieService::GetCookiesForURI(
bool aIsSafeTopLevelNav, bool aIsSameSiteForeign,
bool aHadCrossSiteRedirects, bool aHttpBound,
bool aAllowSecureCookiesToInsecureOrigin,
const OriginAttributes& aOriginAttrs, nsTArray<Cookie*>& aCookieList) {
const nsTArray<OriginAttributes>& aOriginAttrsList,
nsTArray<Cookie*>& aCookieList) {
NS_ASSERTION(aHostURI, "null host!");
if (!CookieCommons::IsSchemeSupported(aHostURI)) {
@ -947,152 +970,165 @@ void CookieService::GetCookiesForURI(
return;
}
CookieStorage* storage = PickStorage(aOriginAttrs);
// get the base domain, host, and path from the URI.
// e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
// file:// URI's (i.e. with an empty host) are allowed, but any other
// scheme must have a non-empty host. A trailing dot in the host
// is acceptable.
bool requireHostMatch;
nsAutoCString baseDomain;
nsAutoCString hostFromURI;
nsAutoCString pathFromURI;
nsresult rv = CookieCommons::GetBaseDomain(mTLDService, aHostURI, baseDomain,
requireHostMatch);
if (NS_SUCCEEDED(rv)) {
rv = nsContentUtils::GetHostOrIPv6WithBrackets(aHostURI, hostFromURI);
}
if (NS_SUCCEEDED(rv)) {
rv = aHostURI->GetFilePath(pathFromURI);
}
if (NS_FAILED(rv)) {
COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, VoidCString(),
"invalid host/path from URI");
return;
}
nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
CookieCommons::GetCookieJarSettings(aChannel);
nsAutoCString normalizedHostFromURI(hostFromURI);
rv = NormalizeHost(normalizedHostFromURI);
NS_ENSURE_SUCCESS_VOID(rv);
nsAutoCString baseDomainFromURI;
rv = CookieCommons::GetBaseDomainFromHost(mTLDService, normalizedHostFromURI,
baseDomainFromURI);
NS_ENSURE_SUCCESS_VOID(rv);
// check default prefs
uint32_t rejectedReason = aRejectedReason;
uint32_t priorCookieCount = storage->CountCookiesFromHost(
baseDomainFromURI, aOriginAttrs.mPrivateBrowsingId);
nsCOMPtr<nsIConsoleReportCollector> crc = do_QueryInterface(aChannel);
CookieStatus cookieStatus = CheckPrefs(
crc, cookieJarSettings, aHostURI, aIsForeign,
aIsThirdPartyTrackingResource, aIsThirdPartySocialTrackingResource,
aStorageAccessPermissionGranted, VoidCString(), priorCookieCount,
aOriginAttrs, &rejectedReason);
MOZ_ASSERT_IF(rejectedReason, cookieStatus == STATUS_REJECTED);
for (const auto& attrs : aOriginAttrsList) {
CookieStorage* storage = PickStorage(attrs);
// for GetCookie(), we only fire acceptance/rejection notifications
// (but not if there was an error)
switch (cookieStatus) {
case STATUS_REJECTED:
// If we don't have any cookies from this host, fail silently.
if (priorCookieCount) {
CookieCommons::NotifyRejected(aHostURI, aChannel, rejectedReason,
OPERATION_READ);
}
// get the base domain, host, and path from the URI.
// e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
// file:// URI's (i.e. with an empty host) are allowed, but any other
// scheme must have a non-empty host. A trailing dot in the host
// is acceptable.
bool requireHostMatch;
nsAutoCString baseDomain;
nsAutoCString hostFromURI;
nsAutoCString pathFromURI;
nsresult rv = CookieCommons::GetBaseDomain(mTLDService, aHostURI,
baseDomain, requireHostMatch);
if (NS_SUCCEEDED(rv)) {
rv = nsContentUtils::GetHostOrIPv6WithBrackets(aHostURI, hostFromURI);
}
if (NS_SUCCEEDED(rv)) {
rv = aHostURI->GetFilePath(pathFromURI);
}
if (NS_FAILED(rv)) {
COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, VoidCString(),
"invalid host/path from URI");
return;
default:
break;
}
// Note: The following permissions logic is mirrored in
// extensions::MatchPattern::MatchesCookie.
// If it changes, please update that function, or file a bug for someone
// else to do so.
// check if aHostURI is using an https secure protocol.
// if it isn't, then we can't send a secure cookie over the connection.
// if SchemeIs fails, assume an insecure connection, to be on the safe side
bool potentiallyTrustworthy =
nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI);
int64_t currentTimeInUsec = PR_Now();
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
bool stale = false;
const nsTArray<RefPtr<Cookie>>* cookies =
storage->GetCookiesFromHost(baseDomain, aOriginAttrs);
if (!cookies) {
return;
}
bool laxByDefault =
StaticPrefs::network_cookie_sameSite_laxByDefault() &&
!nsContentUtils::IsURIInPrefList(
aHostURI, "network.cookie.sameSite.laxByDefault.disabledHosts");
// iterate the cookies!
for (Cookie* cookie : *cookies) {
// check the host, since the base domain lookup is conservative.
if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
continue;
}
// if the cookie is secure and the host scheme isn't, we avoid sending
// cookie if possible. But for process synchronization purposes, we may want
// the content process to know about the cookie (without it's value). In
// which case we will wipe the value before sending
if (cookie->IsSecure() && !potentiallyTrustworthy &&
!aAllowSecureCookiesToInsecureOrigin) {
continue;
}
nsAutoCString normalizedHostFromURI(hostFromURI);
rv = NormalizeHost(normalizedHostFromURI);
NS_ENSURE_SUCCESS_VOID(rv);
// if the cookie is httpOnly and it's not going directly to the HTTP
// connection, don't send it
if (cookie->IsHttpOnly() && !aHttpBound) {
continue;
}
nsAutoCString baseDomainFromURI;
rv = CookieCommons::GetBaseDomainFromHost(
mTLDService, normalizedHostFromURI, baseDomainFromURI);
NS_ENSURE_SUCCESS_VOID(rv);
// if the nsIURI path doesn't match the cookie path, don't send it back
if (!CookieCommons::PathMatches(cookie, pathFromURI)) {
continue;
}
// check default prefs
uint32_t rejectedReason = aRejectedReason;
uint32_t priorCookieCount = storage->CountCookiesFromHost(
baseDomainFromURI, attrs.mPrivateBrowsingId);
// check if the cookie has expired
if (cookie->Expiry() <= currentTime) {
continue;
}
CookieStatus cookieStatus = CheckPrefs(
crc, cookieJarSettings, aHostURI, aIsForeign,
aIsThirdPartyTrackingResource, aIsThirdPartySocialTrackingResource,
aStorageAccessPermissionGranted, VoidCString(), priorCookieCount, attrs,
&rejectedReason);
if (aHttpBound && aIsSameSiteForeign) {
bool blockCookie = !ProcessSameSiteCookieForForeignRequest(
aChannel, cookie, aIsSafeTopLevelNav, aHadCrossSiteRedirects,
laxByDefault);
MOZ_ASSERT_IF(rejectedReason, cookieStatus == STATUS_REJECTED);
if (blockCookie) {
if (aHadCrossSiteRedirects) {
CookieLogging::LogMessageToConsole(
crc, aHostURI, nsIScriptError::warningFlag,
CONSOLE_REJECTION_CATEGORY, "CookieBlockedCrossSiteRedirect"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(cookie->Name()),
});
// for GetCookie(), we only fire acceptance/rejection notifications
// (but not if there was an error)
switch (cookieStatus) {
case STATUS_REJECTED:
// If we don't have any cookies from this host, fail silently.
if (priorCookieCount) {
CookieCommons::NotifyRejected(aHostURI, aChannel, rejectedReason,
OPERATION_READ);
}
return;
default:
break;
}
// Note: The following permissions logic is mirrored in
// extensions::MatchPattern::MatchesCookie.
// If it changes, please update that function, or file a bug for someone
// else to do so.
// check if aHostURI is using an https secure protocol.
// if it isn't, then we can't send a secure cookie over the connection.
// if SchemeIs fails, assume an insecure connection, to be on the safe side
bool potentiallyTrustworthy =
nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI);
int64_t currentTimeInUsec = PR_Now();
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
bool stale = false;
const nsTArray<RefPtr<Cookie>>* cookies =
storage->GetCookiesFromHost(baseDomain, attrs);
if (!cookies) {
continue;
}
bool laxByDefault =
StaticPrefs::network_cookie_sameSite_laxByDefault() &&
!nsContentUtils::IsURIInPrefList(
aHostURI, "network.cookie.sameSite.laxByDefault.disabledHosts");
// iterate the cookies!
for (Cookie* cookie : *cookies) {
// check the host, since the base domain lookup is conservative.
if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
continue;
}
// if the cookie is secure and the host scheme isn't, we avoid sending
// cookie if possible. But for process synchronization purposes, we may
// want the content process to know about the cookie (without it's value).
// In which case we will wipe the value before sending
if (cookie->IsSecure() && !potentiallyTrustworthy &&
!aAllowSecureCookiesToInsecureOrigin) {
continue;
}
// if the cookie is httpOnly and it's not going directly to the HTTP
// connection, don't send it
if (cookie->IsHttpOnly() && !aHttpBound) {
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 (aHttpBound && aIsSameSiteForeign) {
bool blockCookie = !ProcessSameSiteCookieForForeignRequest(
aChannel, cookie, aIsSafeTopLevelNav, aHadCrossSiteRedirects,
laxByDefault);
if (blockCookie) {
if (aHadCrossSiteRedirects) {
CookieLogging::LogMessageToConsole(
crc, aHostURI, nsIScriptError::warningFlag,
CONSOLE_REJECTION_CATEGORY, "CookieBlockedCrossSiteRedirect"_ns,
AutoTArray<nsString, 1>{
NS_ConvertUTF8toUTF16(cookie->Name()),
});
}
continue;
}
}
// all checks passed - add to list and check if lastAccessed stamp needs
// updating
aCookieList.AppendElement(cookie);
if (cookie->IsStale()) {
stale = true;
}
}
// all checks passed - add to list and check if lastAccessed stamp needs
// updating
aCookieList.AppendElement(cookie);
if (cookie->IsStale()) {
stale = true;
if (aCookieList.IsEmpty()) {
continue;
}
// update lastAccessed timestamps. we only do this if the timestamp is stale
// by a certain amount, to avoid thrashing the db during pageload.
if (stale) {
storage->StaleCookies(aCookieList, currentTimeInUsec);
}
}
@ -1104,12 +1140,6 @@ void CookieService::GetCookiesForURI(
// some.
NotifyAccepted(aChannel);
// update lastAccessed timestamps. we only do this if the timestamp is stale
// by a certain amount, to avoid thrashing the db during pageload.
if (stale) {
storage->StaleCookies(aCookieList, currentTimeInUsec);
}
// return cookies in order of path length; longest to shortest.
// this is required per RFC2109. if cookies match in length,
// then sort by creation time (see bug 236772).

View file

@ -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);
/**

View file

@ -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),
@ -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__);
},
@ -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();
@ -313,34 +325,14 @@ CookieServiceChild::GetCookieStringFromDocument(dom::Document* aDocument,
aCookieString.Truncate();
nsCOMPtr<nsIPrincipal> principal = aDocument->EffectiveCookiePrincipal();
nsCOMPtr<nsIPrincipal> cookiePrincipal =
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;
}
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();
@ -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;
for (auto& principal : principals) {
if (!CookieCommons::IsSchemeSupported(principal)) {
return NS_OK;
}
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)) {
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) {
continue;
}
// We don't show HttpOnly cookies in content processes.
if (cookie->IsHttpOnly()) {
continue;
nsAutoCString hostFromURI;
rv = nsContentUtils::GetHostOrIPv6WithBrackets(principal, hostFromURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
if (thirdParty && !CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
cookie, aDocument)) {
continue;
}
nsAutoCString pathFromURI;
principal->GetFilePath(pathFromURI);
// do not display the cookie if it is secure and the host scheme isn't
if (cookie->IsSecure() && !isPotentiallyTrustworthy) {
continue;
}
bool isPotentiallyTrustworthy =
principal->GetIsOriginPotentiallyTrustworthy();
int64_t currentTimeInUsec = PR_Now();
int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
// 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("; ");
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());
}
}
}
}

View file

@ -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();

View file

@ -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"
@ -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);
@ -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;
@ -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
@ -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
@ -200,7 +219,7 @@ 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");
@ -208,7 +227,9 @@ IPCResult CookieServiceParent::RecvGetCookieList(
// 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
@ -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();
}

View file

@ -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;

View file

@ -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);

View file

@ -454,6 +454,12 @@ struct CookieStruct
uint8_t schemeMap;
};
struct CookieStructTable
{
OriginAttributes attrs;
CookieStruct[] cookies;
};
struct DocumentCreationArgs {
bool uriModified;
bool isEmbeddingBlockedError;