forked from mirrors/gecko-dev
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:
parent
2461270e57
commit
790c008f43
8 changed files with 392 additions and 314 deletions
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -454,6 +454,12 @@ struct CookieStruct
|
|||
uint8_t schemeMap;
|
||||
};
|
||||
|
||||
struct CookieStructTable
|
||||
{
|
||||
OriginAttributes attrs;
|
||||
CookieStruct[] cookies;
|
||||
};
|
||||
|
||||
struct DocumentCreationArgs {
|
||||
bool uriModified;
|
||||
bool isEmbeddingBlockedError;
|
||||
|
|
|
|||
Loading…
Reference in a new issue