1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CookieCommons.h"
8 #include "CookieLogging.h"
9 #include "CookieService.h"
10 #include "mozilla/ConsoleReportCollector.h"
11 #include "mozilla/ContentBlockingNotifier.h"
12 #include "mozilla/ScopeExit.h"
13 #include "mozilla/StaticPrefs_network.h"
14 #include "mozilla/StorageAccess.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/nsMixedContentBlocker.h"
17 #include "mozilla/net/CookieJarSettings.h"
18 #include "mozIThirdPartyUtil.h"
19 #include "nsContentUtils.h"
20 #include "nsICookiePermission.h"
21 #include "nsICookieService.h"
22 #include "nsIEffectiveTLDService.h"
23 #include "nsIRedirectHistoryEntry.h"
24 #include "nsIWebProgressListener.h"
25 #include "nsNetUtil.h"
26 #include "nsScriptSecurityManager.h"
27 #include "ThirdPartyUtil.h"
36 bool CookieCommons::DomainMatches(Cookie
* aCookie
, const nsACString
& aHost
) {
37 // first, check for an exact host or domain cookie match, e.g. "google.com"
38 // or ".google.com"; second a subdomain match, e.g.
39 // host = "mail.google.com", cookie domain = ".google.com".
40 return aCookie
->RawHost() == aHost
||
41 (aCookie
->IsDomain() && StringEndsWith(aHost
, aCookie
->Host()));
45 bool CookieCommons::PathMatches(Cookie
* aCookie
, const nsACString
& aPath
) {
46 nsCString
cookiePath(aCookie
->GetFilePath());
48 // if our cookie path is empty we can't really perform our prefix check, and
49 // also we can't check the last character of the cookie path, so we would
50 // never return a successful match.
51 if (cookiePath
.IsEmpty()) {
55 // if the cookie path and the request path are identical, they match.
56 if (cookiePath
.Equals(aPath
)) {
60 // if the cookie path is a prefix of the request path, and the last character
61 // of the cookie path is %x2F ("/"), they match.
62 bool isPrefix
= StringBeginsWith(aPath
, cookiePath
);
63 if (isPrefix
&& cookiePath
.Last() == '/') {
67 // if the cookie path is a prefix of the request path, and the first character
68 // of the request path that is not included in the cookie path is a %x2F ("/")
69 // character, they match.
70 uint32_t cookiePathLen
= cookiePath
.Length();
71 return isPrefix
&& aPath
[cookiePathLen
] == '/';
74 // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
75 // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
76 // dot may be present. If aHostURI is an IP address, an alias such as
77 // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
78 // be the exact host, and aRequireHostMatch will be true to indicate that
79 // substring matches should not be performed.
80 nsresult
CookieCommons::GetBaseDomain(nsIEffectiveTLDService
* aTLDService
,
81 nsIURI
* aHostURI
, nsACString
& aBaseDomain
,
82 bool& aRequireHostMatch
) {
83 // get the base domain. this will fail if the host contains a leading dot,
84 // more than one trailing dot, or is otherwise malformed.
85 nsresult rv
= aTLDService
->GetBaseDomain(aHostURI
, 0, aBaseDomain
);
86 aRequireHostMatch
= rv
== NS_ERROR_HOST_IS_IP_ADDRESS
||
87 rv
== NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
;
88 if (aRequireHostMatch
) {
89 // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
90 // such as 'co.uk', or the empty string. use the host as a key in such
92 rv
= nsContentUtils::GetHostOrIPv6WithBrackets(aHostURI
, aBaseDomain
);
94 NS_ENSURE_SUCCESS(rv
, rv
);
96 // aHost (and thus aBaseDomain) may be the string '.'. If so, fail.
97 if (aBaseDomain
.Length() == 1 && aBaseDomain
.Last() == '.') {
98 return NS_ERROR_INVALID_ARG
;
101 // block any URIs without a host that aren't file:// URIs.
102 if (aBaseDomain
.IsEmpty() && !aHostURI
->SchemeIs("file")) {
103 return NS_ERROR_INVALID_ARG
;
109 nsresult
CookieCommons::GetBaseDomain(nsIPrincipal
* aPrincipal
,
110 nsACString
& aBaseDomain
) {
111 MOZ_ASSERT(aPrincipal
);
113 // for historical reasons we use ascii host for file:// URLs.
114 if (aPrincipal
->SchemeIs("file")) {
115 return nsContentUtils::GetHostOrIPv6WithBrackets(aPrincipal
, aBaseDomain
);
118 nsresult rv
= aPrincipal
->GetBaseDomain(aBaseDomain
);
123 nsContentUtils::MaybeFixIPv6Host(aBaseDomain
);
127 // Get the base domain for aHost; e.g. for "www.bbc.co.uk", this would be
128 // "bbc.co.uk". This is done differently than GetBaseDomain(mTLDService, ): it
129 // is assumed that aHost is already normalized, and it may contain a leading dot
130 // (indicating that it represents a domain). A trailing dot may be present.
131 // If aHost is an IP address, an alias such as 'localhost', an eTLD such as
132 // 'co.uk', or the empty string, aBaseDomain will be the exact host, and a
133 // leading dot will be treated as an error.
134 nsresult
CookieCommons::GetBaseDomainFromHost(
135 nsIEffectiveTLDService
* aTLDService
, const nsACString
& aHost
,
136 nsCString
& aBaseDomain
) {
137 // aHost must not be the string '.'.
138 if (aHost
.Length() == 1 && aHost
.Last() == '.') {
139 return NS_ERROR_INVALID_ARG
;
142 // aHost may contain a leading dot; if so, strip it now.
143 bool domain
= !aHost
.IsEmpty() && aHost
.First() == '.';
145 // get the base domain. this will fail if the host contains a leading dot,
146 // more than one trailing dot, or is otherwise malformed.
147 nsresult rv
= aTLDService
->GetBaseDomainFromHost(Substring(aHost
, domain
), 0,
149 if (rv
== NS_ERROR_HOST_IS_IP_ADDRESS
||
150 rv
== NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
) {
151 // aHost is either an IP address, an alias such as 'localhost', an eTLD
152 // such as 'co.uk', or the empty string. use the host as a key in such
153 // cases; however, we reject any such hosts with a leading dot, since it
154 // doesn't make sense for them to be domain cookies.
156 return NS_ERROR_INVALID_ARG
;
167 void NotifyRejectionToObservers(nsIURI
* aHostURI
, CookieOperation aOperation
) {
168 if (aOperation
== OPERATION_WRITE
) {
169 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
171 os
->NotifyObservers(aHostURI
, "cookie-rejected", nullptr);
174 MOZ_ASSERT(aOperation
== OPERATION_READ
);
180 // Notify observers that a cookie was rejected due to the users' prefs.
181 void CookieCommons::NotifyRejected(nsIURI
* aHostURI
, nsIChannel
* aChannel
,
182 uint32_t aRejectedReason
,
183 CookieOperation aOperation
) {
184 NotifyRejectionToObservers(aHostURI
, aOperation
);
186 ContentBlockingNotifier::OnDecision(
187 aChannel
, ContentBlockingNotifier::BlockingDecision::eBlock
,
191 bool CookieCommons::CheckPathSize(const CookieStruct
& aCookieData
) {
192 return aCookieData
.path().Length() <= kMaxBytesPerPath
;
195 bool CookieCommons::CheckNameAndValueSize(const CookieStruct
& aCookieData
) {
196 // reject cookie if it's over the size limit, per RFC2109
197 return (aCookieData
.name().Length() + aCookieData
.value().Length()) <=
201 bool CookieCommons::CheckName(const CookieStruct
& aCookieData
) {
202 const char illegalNameCharacters
[] = {
203 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
204 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
205 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x3B, 0x3D, 0x7F, 0x00};
207 const auto* start
= aCookieData
.name().BeginReading();
208 const auto* end
= aCookieData
.name().EndReading();
210 auto charFilter
= [&](unsigned char c
) {
211 if (StaticPrefs::network_cookie_blockUnicode() && c
>= 0x80) {
214 return std::find(std::begin(illegalNameCharacters
),
215 std::end(illegalNameCharacters
),
216 c
) != std::end(illegalNameCharacters
);
219 return std::find_if(start
, end
, charFilter
) == end
;
222 bool CookieCommons::CheckValue(const CookieStruct
& aCookieData
) {
223 // reject cookie if value contains an RFC 6265 disallowed character - see
224 // https://bugzilla.mozilla.org/show_bug.cgi?id=1191423
225 // NOTE: this is not the full set of characters disallowed by 6265 - notably
226 // 0x09, 0x20, 0x22, 0x2C, and 0x5C are missing from this list.
227 const char illegalCharacters
[] = {
228 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C,
229 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
230 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x3B, 0x7F, 0x00};
232 const auto* start
= aCookieData
.value().BeginReading();
233 const auto* end
= aCookieData
.value().EndReading();
235 auto charFilter
= [&](unsigned char c
) {
236 if (StaticPrefs::network_cookie_blockUnicode() && c
>= 0x80) {
239 return std::find(std::begin(illegalCharacters
), std::end(illegalCharacters
),
240 c
) != std::end(illegalCharacters
);
243 return std::find_if(start
, end
, charFilter
) == end
;
247 bool CookieCommons::CheckCookiePermission(nsIChannel
* aChannel
,
248 CookieStruct
& aCookieData
) {
250 // No channel, let's assume this is a system-principal request.
254 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
255 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
257 loadInfo
->GetCookieJarSettings(getter_AddRefs(cookieJarSettings
));
258 if (NS_WARN_IF(NS_FAILED(rv
))) {
262 nsIScriptSecurityManager
* ssm
=
263 nsScriptSecurityManager::GetScriptSecurityManager();
266 nsCOMPtr
<nsIPrincipal
> channelPrincipal
;
267 rv
= ssm
->GetChannelURIPrincipal(aChannel
, getter_AddRefs(channelPrincipal
));
268 if (NS_WARN_IF(NS_FAILED(rv
))) {
272 return CheckCookiePermission(channelPrincipal
, cookieJarSettings
,
277 bool CookieCommons::CheckCookiePermission(
278 nsIPrincipal
* aPrincipal
, nsICookieJarSettings
* aCookieJarSettings
,
279 CookieStruct
& aCookieData
) {
280 MOZ_ASSERT(aPrincipal
);
281 MOZ_ASSERT(aCookieJarSettings
);
283 if (!aPrincipal
->GetIsContentPrincipal()) {
287 uint32_t cookiePermission
= nsICookiePermission::ACCESS_DEFAULT
;
289 aCookieJarSettings
->CookiePermission(aPrincipal
, &cookiePermission
);
290 if (NS_WARN_IF(NS_FAILED(rv
))) {
294 if (cookiePermission
== nsICookiePermission::ACCESS_ALLOW
) {
298 if (cookiePermission
== nsICookiePermission::ACCESS_SESSION
) {
299 aCookieData
.isSession() = true;
303 if (cookiePermission
== nsICookiePermission::ACCESS_DENY
) {
312 CookieStatus
CookieStatusForWindow(nsPIDOMWindowInner
* aWindow
,
313 nsIURI
* aDocumentURI
) {
315 MOZ_ASSERT(aDocumentURI
);
317 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
318 if (thirdPartyUtil
) {
319 bool isThirdParty
= true;
321 nsresult rv
= thirdPartyUtil
->IsThirdPartyWindow(
322 aWindow
->GetOuterWindow(), aDocumentURI
, &isThirdParty
);
323 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Third-party window check failed.");
325 if (NS_SUCCEEDED(rv
) && !isThirdParty
) {
326 return STATUS_ACCEPTED
;
330 if (StaticPrefs::network_cookie_thirdparty_sessionOnly()) {
331 return STATUS_ACCEPT_SESSION
;
334 if (StaticPrefs::network_cookie_thirdparty_nonsecureSessionOnly() &&
335 !nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aDocumentURI
)) {
336 return STATUS_ACCEPT_SESSION
;
339 return STATUS_ACCEPTED
;
345 already_AddRefed
<Cookie
> CookieCommons::CreateCookieFromDocument(
346 Document
* aDocument
, const nsACString
& aCookieString
,
347 int64_t currentTimeInUsec
, nsIEffectiveTLDService
* aTLDService
,
348 mozIThirdPartyUtil
* aThirdPartyUtil
,
349 std::function
<bool(const nsACString
&, const OriginAttributes
&)>&&
350 aHasExistingCookiesLambda
,
351 nsIURI
** aDocumentURI
, nsACString
& aBaseDomain
, OriginAttributes
& aAttrs
) {
352 nsCOMPtr
<nsIPrincipal
> storagePrincipal
=
353 aDocument
->EffectiveCookiePrincipal();
354 MOZ_ASSERT(storagePrincipal
);
356 nsCOMPtr
<nsIURI
> principalURI
;
357 auto* basePrincipal
= BasePrincipal::Cast(aDocument
->NodePrincipal());
358 basePrincipal
->GetURI(getter_AddRefs(principalURI
));
359 if (NS_WARN_IF(!principalURI
)) {
360 // Document's principal is not a content or null (may be system), so
365 if (!CookieCommons::IsSchemeSupported(principalURI
)) {
369 nsAutoCString baseDomain
;
370 bool requireHostMatch
= false;
371 nsresult rv
= CookieCommons::GetBaseDomain(aTLDService
, principalURI
,
372 baseDomain
, requireHostMatch
);
373 if (NS_WARN_IF(NS_FAILED(rv
))) {
377 nsPIDOMWindowInner
* innerWindow
= aDocument
->GetInnerWindow();
378 if (NS_WARN_IF(!innerWindow
)) {
382 // Check if limit-foreign is required.
383 uint32_t dummyRejectedReason
= 0;
384 if (aDocument
->CookieJarSettings()->GetLimitForeignContexts() &&
385 !aHasExistingCookiesLambda(baseDomain
,
386 storagePrincipal
->OriginAttributesRef()) &&
387 !ShouldAllowAccessFor(innerWindow
, principalURI
, &dummyRejectedReason
)) {
391 bool isForeignAndNotAddon
= false;
392 if (!BasePrincipal::Cast(aDocument
->NodePrincipal())->AddonPolicy()) {
393 rv
= aThirdPartyUtil
->IsThirdPartyWindow(
394 innerWindow
->GetOuterWindow(), principalURI
, &isForeignAndNotAddon
);
395 if (NS_WARN_IF(NS_FAILED(rv
))) {
396 isForeignAndNotAddon
= true;
400 bool mustBePartitioned
=
401 isForeignAndNotAddon
&&
402 aDocument
->CookieJarSettings()->GetCookieBehavior() ==
403 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
&&
404 !aDocument
->UsingStorageAccess();
406 // If we are here, we have been already accepted by the anti-tracking.
407 // We just need to check if we have to be in session-only mode.
408 CookieStatus cookieStatus
= CookieStatusForWindow(innerWindow
, principalURI
);
409 MOZ_ASSERT(cookieStatus
== STATUS_ACCEPTED
||
410 cookieStatus
== STATUS_ACCEPT_SESSION
);
412 // Console report takes care of the correct reporting at the exit of this
414 RefPtr
<ConsoleReportCollector
> crc
= new ConsoleReportCollector();
415 auto scopeExit
= MakeScopeExit([&] { crc
->FlushConsoleReports(aDocument
); });
417 nsCString
cookieString(aCookieString
);
419 CookieStruct cookieData
;
420 MOZ_ASSERT(cookieData
.creationTime() == 0, "Must be initialized to 0");
421 bool canSetCookie
= false;
422 CookieService::CanSetCookie(principalURI
, baseDomain
, cookieData
,
423 requireHostMatch
, cookieStatus
, cookieString
,
424 false, isForeignAndNotAddon
, mustBePartitioned
,
431 // check permissions from site permission list.
432 if (!CookieCommons::CheckCookiePermission(aDocument
->NodePrincipal(),
433 aDocument
->CookieJarSettings(),
435 NotifyRejectionToObservers(principalURI
, OPERATION_WRITE
);
436 ContentBlockingNotifier::OnDecision(
437 innerWindow
, ContentBlockingNotifier::BlockingDecision::eBlock
,
438 nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION
);
442 RefPtr
<Cookie
> cookie
=
443 Cookie::Create(cookieData
, storagePrincipal
->OriginAttributesRef());
446 cookie
->SetLastAccessed(currentTimeInUsec
);
447 cookie
->SetCreationTime(
448 Cookie::GenerateUniqueCreationTime(currentTimeInUsec
));
450 aBaseDomain
= baseDomain
;
451 aAttrs
= storagePrincipal
->OriginAttributesRef();
452 principalURI
.forget(aDocumentURI
);
454 return cookie
.forget();
458 already_AddRefed
<nsICookieJarSettings
> CookieCommons::GetCookieJarSettings(
459 nsIChannel
* aChannel
) {
460 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
461 bool shouldResistFingerprinting
= nsContentUtils::ShouldResistFingerprinting(
462 aChannel
, RFPTarget::IsAlwaysEnabledForPrecompute
);
464 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
466 loadInfo
->GetCookieJarSettings(getter_AddRefs(cookieJarSettings
));
467 if (NS_WARN_IF(NS_FAILED(rv
))) {
469 CookieJarSettings::GetBlockingAll(shouldResistFingerprinting
);
472 cookieJarSettings
= CookieJarSettings::Create(CookieJarSettings::eRegular
,
473 shouldResistFingerprinting
);
476 MOZ_ASSERT(cookieJarSettings
);
477 return cookieJarSettings
.forget();
481 bool CookieCommons::ShouldIncludeCrossSiteCookieForDocument(
482 Cookie
* aCookie
, dom::Document
* aDocument
) {
484 MOZ_ASSERT(aDocument
);
486 int32_t sameSiteAttr
= 0;
487 aCookie
->GetSameSite(&sameSiteAttr
);
489 if (aDocument
->CookieJarSettings()->GetPartitionForeign() &&
490 StaticPrefs::network_cookie_cookieBehavior_optInPartitioning() &&
491 !aCookie
->IsPartitioned()) {
495 return sameSiteAttr
== nsICookie::SAMESITE_NONE
;
498 bool CookieCommons::IsSafeTopLevelNav(nsIChannel
* aChannel
) {
502 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
503 nsCOMPtr
<nsIInterceptionInfo
> interceptionInfo
= loadInfo
->InterceptionInfo();
504 if ((loadInfo
->GetExternalContentPolicyType() !=
505 ExtContentPolicy::TYPE_DOCUMENT
&&
506 loadInfo
->GetExternalContentPolicyType() !=
507 ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD
) &&
512 if (interceptionInfo
&&
513 interceptionInfo
->GetExtContentPolicyType() !=
514 ExtContentPolicy::TYPE_DOCUMENT
&&
515 interceptionInfo
->GetExtContentPolicyType() !=
516 ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD
&&
517 interceptionInfo
->GetExtContentPolicyType() !=
518 ExtContentPolicy::TYPE_INVALID
) {
522 return NS_IsSafeMethodNav(aChannel
);
525 // This function determines if two schemes are equal in the context of
526 // "Schemeful SameSite cookies".
528 // Two schemes are considered equal:
529 // - if the "network.cookie.sameSite.schemeful" pref is set to false.
531 // - if one of the schemes is not http or https.
533 // - if both schemes are equal AND both are either http or https.
534 bool IsSameSiteSchemeEqual(const nsACString
& aFirstScheme
,
535 const nsACString
& aSecondScheme
) {
536 if (!StaticPrefs::network_cookie_sameSite_schemeful()) {
540 auto isSchemeHttpOrHttps
= [](const nsACString
& scheme
) -> bool {
541 return scheme
.EqualsLiteral("http") || scheme
.EqualsLiteral("https");
544 if (!isSchemeHttpOrHttps(aFirstScheme
) ||
545 !isSchemeHttpOrHttps(aSecondScheme
)) {
549 return aFirstScheme
.Equals(aSecondScheme
);
552 bool CookieCommons::IsSameSiteForeign(nsIChannel
* aChannel
, nsIURI
* aHostURI
,
553 bool* aHadCrossSiteRedirects
) {
554 *aHadCrossSiteRedirects
= false;
559 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
560 // Do not treat loads triggered by web extensions as foreign
561 nsCOMPtr
<nsIURI
> channelURI
;
562 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(channelURI
));
564 nsCOMPtr
<nsIInterceptionInfo
> interceptionInfo
= loadInfo
->InterceptionInfo();
566 RefPtr
<BasePrincipal
> triggeringPrincipal
;
567 ExtContentPolicy contentPolicyType
;
568 if (interceptionInfo
&& interceptionInfo
->TriggeringPrincipal()) {
569 triggeringPrincipal
=
570 BasePrincipal::Cast(interceptionInfo
->TriggeringPrincipal());
571 contentPolicyType
= interceptionInfo
->GetExtContentPolicyType();
573 triggeringPrincipal
= BasePrincipal::Cast(loadInfo
->TriggeringPrincipal());
574 contentPolicyType
= loadInfo
->GetExternalContentPolicyType();
576 if (triggeringPrincipal
->AddonPolicy() &&
577 triggeringPrincipal
->AddonAllowsLoad(channelURI
)) {
581 const nsTArray
<nsCOMPtr
<nsIRedirectHistoryEntry
>>& redirectChain(
582 interceptionInfo
&& interceptionInfo
->TriggeringPrincipal()
583 ? interceptionInfo
->RedirectChain()
584 : loadInfo
->RedirectChain());
586 nsAutoCString hostScheme
, otherScheme
;
587 aHostURI
->GetScheme(hostScheme
);
589 bool isForeign
= true;
591 if (contentPolicyType
== ExtContentPolicy::TYPE_DOCUMENT
||
592 contentPolicyType
== ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD
) {
593 // for loads of TYPE_DOCUMENT we query the hostURI from the
594 // triggeringPrincipal which returns the URI of the document that caused the
596 rv
= triggeringPrincipal
->IsThirdPartyChannel(aChannel
, &isForeign
);
598 triggeringPrincipal
->GetScheme(otherScheme
);
600 // If the load is caused by FetchEvent.request or NavigationPreload request,
601 // check the original InterceptedHttpChannel is a third-party channel or
603 if (interceptionInfo
&& interceptionInfo
->TriggeringPrincipal()) {
604 isForeign
= interceptionInfo
->FromThirdParty();
609 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
610 do_GetService(THIRDPARTYUTIL_CONTRACTID
);
611 if (!thirdPartyUtil
) {
614 rv
= thirdPartyUtil
->IsThirdPartyChannel(aChannel
, aHostURI
, &isForeign
);
616 channelURI
->GetScheme(otherScheme
);
618 // if we are dealing with a cross origin request, we can return here
619 // because we already know the request is 'foreign'.
620 if (NS_FAILED(rv
) || isForeign
) {
624 if (!IsSameSiteSchemeEqual(otherScheme
, hostScheme
)) {
625 // If the two schemes are not of the same http(s) scheme then we
626 // consider the request as foreign.
630 // for loads of TYPE_SUBDOCUMENT we have to perform an additional test,
631 // because a cross-origin iframe might perform a navigation to a same-origin
632 // iframe which would send same-site cookies. Hence, if the iframe navigation
633 // was triggered by a cross-origin triggeringPrincipal, we treat the load as
635 if (contentPolicyType
== ExtContentPolicy::TYPE_SUBDOCUMENT
) {
636 rv
= triggeringPrincipal
->IsThirdPartyChannel(aChannel
, &isForeign
);
637 if (NS_FAILED(rv
) || isForeign
) {
642 // for the purpose of same-site cookies we have to treat any cross-origin
643 // redirects as foreign. E.g. cross-site to same-site redirect is a problem
644 // with regards to CSRF.
646 nsCOMPtr
<nsIPrincipal
> redirectPrincipal
;
647 for (nsIRedirectHistoryEntry
* entry
: redirectChain
) {
648 entry
->GetPrincipal(getter_AddRefs(redirectPrincipal
));
649 if (redirectPrincipal
) {
650 rv
= redirectPrincipal
->IsThirdPartyChannel(aChannel
, &isForeign
);
651 // if at any point we encounter a cross-origin redirect we can return.
652 if (NS_FAILED(rv
) || isForeign
) {
653 *aHadCrossSiteRedirects
= isForeign
;
657 nsAutoCString redirectScheme
;
658 redirectPrincipal
->GetScheme(redirectScheme
);
659 if (!IsSameSiteSchemeEqual(redirectScheme
, hostScheme
)) {
660 // If the two schemes are not of the same http(s) scheme then we
661 // consider the request as foreign.
662 *aHadCrossSiteRedirects
= true;
671 nsICookie::schemeType
CookieCommons::URIToSchemeType(nsIURI
* aURI
) {
674 nsAutoCString scheme
;
675 nsresult rv
= aURI
->GetScheme(scheme
);
676 if (NS_WARN_IF(NS_FAILED(rv
))) {
677 return nsICookie::SCHEME_UNSET
;
680 return SchemeToSchemeType(scheme
);
684 nsICookie::schemeType
CookieCommons::PrincipalToSchemeType(
685 nsIPrincipal
* aPrincipal
) {
686 MOZ_ASSERT(aPrincipal
);
688 nsAutoCString scheme
;
689 nsresult rv
= aPrincipal
->GetScheme(scheme
);
690 if (NS_WARN_IF(NS_FAILED(rv
))) {
691 return nsICookie::SCHEME_UNSET
;
694 return SchemeToSchemeType(scheme
);
698 nsICookie::schemeType
CookieCommons::SchemeToSchemeType(
699 const nsACString
& aScheme
) {
700 MOZ_ASSERT(IsSchemeSupported(aScheme
));
702 if (aScheme
.Equals("https")) {
703 return nsICookie::SCHEME_HTTPS
;
706 if (aScheme
.Equals("http")) {
707 return nsICookie::SCHEME_HTTP
;
710 if (aScheme
.Equals("file")) {
711 return nsICookie::SCHEME_FILE
;
714 MOZ_CRASH("Unsupported scheme type");
718 bool CookieCommons::IsSchemeSupported(nsIPrincipal
* aPrincipal
) {
719 MOZ_ASSERT(aPrincipal
);
721 nsAutoCString scheme
;
722 nsresult rv
= aPrincipal
->GetScheme(scheme
);
723 if (NS_WARN_IF(NS_FAILED(rv
))) {
727 return IsSchemeSupported(scheme
);
731 bool CookieCommons::IsSchemeSupported(nsIURI
* aURI
) {
734 nsAutoCString scheme
;
735 nsresult rv
= aURI
->GetScheme(scheme
);
736 if (NS_WARN_IF(NS_FAILED(rv
))) {
740 return IsSchemeSupported(scheme
);
744 bool CookieCommons::IsSchemeSupported(const nsACString
& aScheme
) {
745 return aScheme
.Equals("https") || aScheme
.Equals("http") ||
746 aScheme
.Equals("file");
750 } // namespace mozilla