Backed out 6 changesets (bug 1835907) for causing multiple failures. CLOSED TREE
[gecko.git] / toolkit / components / antitracking / StorageAccess.cpp
blobe0078c9bea4def44f4be26485dd6b6ea96d5d5f5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "StorageAccess.h"
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/Components.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/net/CookieJarSettings.h"
13 #include "mozilla/PermissionManager.h"
14 #include "mozilla/StaticPrefs_browser.h"
15 #include "mozilla/StaticPrefs_network.h"
16 #include "mozilla/StaticPrefs_privacy.h"
17 #include "mozilla/StorageAccess.h"
18 #include "nsAboutProtocolUtils.h"
19 #include "nsContentUtils.h"
20 #include "nsGlobalWindowInner.h"
21 #include "nsICookiePermission.h"
22 #include "nsICookieService.h"
23 #include "nsICookieJarSettings.h"
24 #include "nsIHttpChannel.h"
25 #include "nsIPermission.h"
26 #include "nsIWebProgressListener.h"
27 #include "nsIClassifiedChannel.h"
28 #include "nsNetUtil.h"
29 #include "nsScriptSecurityManager.h"
30 #include "nsSandboxFlags.h"
31 #include "AntiTrackingUtils.h"
32 #include "AntiTrackingLog.h"
33 #include "ContentBlockingAllowList.h"
34 #include "mozIThirdPartyUtil.h"
35 #include "RejectForeignAllowList.h"
37 using namespace mozilla;
38 using namespace mozilla::dom;
39 using mozilla::net::CookieJarSettings;
41 // This internal method returns ACCESS_DENY if the access is denied,
42 // ACCESS_DEFAULT if unknown, some other access code if granted.
43 uint32_t mozilla::detail::CheckCookiePermissionForPrincipal(
44 nsICookieJarSettings* aCookieJarSettings, nsIPrincipal* aPrincipal) {
45 MOZ_ASSERT(aCookieJarSettings);
46 MOZ_ASSERT(aPrincipal);
48 uint32_t cookiePermission = nsICookiePermission::ACCESS_DEFAULT;
49 if (!aPrincipal->GetIsContentPrincipal()) {
50 return cookiePermission;
53 nsresult rv =
54 aCookieJarSettings->CookiePermission(aPrincipal, &cookiePermission);
55 if (NS_WARN_IF(NS_FAILED(rv))) {
56 return nsICookiePermission::ACCESS_DEFAULT;
59 // If we have a custom cookie permission, let's use it.
60 return cookiePermission;
64 * Checks if storage for a given principal is permitted by the user's
65 * preferences. If aWindow is non-null, its principal must be passed as
66 * aPrincipal, and the third-party iframe and sandboxing status of the window
67 * are also checked. If aURI is non-null, then it is used as the comparison
68 * against aWindow to determine if this is a third-party load. We also
69 * allow a channel instead of the window reference when determining 3rd party
70 * status.
72 * Used in the implementation of StorageAllowedForWindow,
73 * StorageAllowedForDocument, StorageAllowedForChannel and
74 * StorageAllowedForServiceWorker.
76 static StorageAccess InternalStorageAllowedCheck(
77 nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow, nsIURI* aURI,
78 nsIChannel* aChannel, nsICookieJarSettings* aCookieJarSettings,
79 uint32_t& aRejectedReason) {
80 MOZ_ASSERT(aPrincipal);
82 aRejectedReason = 0;
84 StorageAccess access = StorageAccess::eAllow;
86 // We don't allow storage on the null principal, in general. Even if the
87 // calling context is chrome.
88 if (aPrincipal->GetIsNullPrincipal()) {
89 return StorageAccess::eDeny;
92 nsCOMPtr<nsIURI> documentURI;
93 if (aWindow) {
94 // If the document is sandboxed, then it is not permitted to use storage
95 Document* document = aWindow->GetExtantDoc();
96 if (document && document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
97 return StorageAccess::eDeny;
100 // Check if we are in private browsing, and record that fact
101 if (nsContentUtils::IsInPrivateBrowsing(document)) {
102 access = StorageAccess::ePrivateBrowsing;
105 // Get the document URI for the below about: URI check.
106 documentURI = document ? document->GetDocumentURI() : nullptr;
109 // About URIs are allowed to access storage, even if they don't have chrome
110 // privileges. If this is not desired, than the consumer will have to
111 // implement their own restriction functionality.
113 // This is due to backwards-compatibility and the state of storage access
114 // before the introducton of InternalStorageAllowedCheck:
116 // BEFORE:
117 // localStorage, caches: allowed in 3rd-party iframes always
118 // IndexedDB: allowed in 3rd-party iframes only if 3rd party URI is an about:
119 // URI within a specific allowlist
121 // AFTER:
122 // localStorage, caches: allowed in 3rd-party iframes by default. Preference
123 // can be set to disable in 3rd-party, which will not disallow in about:
124 // URIs.
125 // IndexedDB: allowed in 3rd-party iframes by default. Preference can be set
126 // to disable in 3rd-party, which will disallow in about: URIs, unless they
127 // are within a specific allowlist.
129 // This means that behavior for storage with internal about: URIs should not
130 // be affected, which is desireable due to the lack of automated testing for
131 // about: URIs with these preferences set, and the importance of the correct
132 // functioning of these URIs even with custom preferences.
134 // We need to check the aURI or the document URI here instead of only checking
135 // the URI from the principal. Because the principal might not have a URI if
136 // it is a system principal.
137 if ((aURI && aURI->SchemeIs("about") &&
138 !NS_IsContentAccessibleAboutURI(aURI)) ||
139 (documentURI && documentURI->SchemeIs("about") &&
140 !NS_IsContentAccessibleAboutURI(documentURI)) ||
141 aPrincipal->SchemeIs("about")) {
142 return access;
145 if (!StorageDisabledByAntiTracking(aWindow, aChannel, aPrincipal, aURI,
146 aRejectedReason)) {
147 return access;
150 // We want to have a partitioned storage only for trackers.
151 if (aRejectedReason ==
152 static_cast<uint32_t>(
153 nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) ||
154 aRejectedReason ==
155 static_cast<uint32_t>(
156 nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER)) {
157 return StorageAccess::ePartitionTrackersOrDeny;
160 // We want to have a partitioned storage for all third parties.
161 if (aRejectedReason ==
162 static_cast<uint32_t>(
163 nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN)) {
164 return StorageAccess::ePartitionForeignOrDeny;
167 return StorageAccess::eDeny;
171 * Wrapper around InternalStorageAllowedCheck which caches the check result on
172 * the inner window to improve performance. nsGlobalWindowInner is responsible
173 * for invalidating the cache state if storage access changes during window
174 * lifetime.
176 static StorageAccess InternalStorageAllowedCheckCached(
177 nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow, nsIURI* aURI,
178 nsIChannel* aChannel, nsICookieJarSettings* aCookieJarSettings,
179 uint32_t& aRejectedReason) {
180 // If enabled, check if we have already computed the storage access field
181 // for this window. This avoids repeated calls to
182 // InternalStorageAllowedCheck.
183 nsGlobalWindowInner* win = nullptr;
184 if (aWindow) {
185 win = nsGlobalWindowInner::Cast(aWindow);
187 Maybe<StorageAccess> storageAccess =
188 win->GetStorageAllowedCache(aRejectedReason);
189 if (storageAccess.isSome()) {
190 return storageAccess.value();
194 StorageAccess result = InternalStorageAllowedCheck(
195 aPrincipal, aWindow, aURI, aChannel, aCookieJarSettings, aRejectedReason);
196 if (win) {
197 // Remember check result for the lifetime of the window. It's the windows
198 // responsibility to invalidate this field if storage access changes
199 // because a storage access permission is granted.
200 win->SetStorageAllowedCache(result, aRejectedReason);
203 return result;
206 static bool StorageDisabledByAntiTrackingInternal(
207 nsPIDOMWindowInner* aWindow, nsIChannel* aChannel, nsIPrincipal* aPrincipal,
208 nsIURI* aURI, nsICookieJarSettings* aCookieJarSettings,
209 uint32_t& aRejectedReason) {
210 MOZ_ASSERT(aWindow || aChannel || aPrincipal);
212 if (aWindow) {
213 nsIURI* documentURI = aURI ? aURI : aWindow->GetDocumentURI();
214 return !documentURI ||
215 !ShouldAllowAccessFor(aWindow, documentURI, &aRejectedReason);
218 if (aChannel) {
219 nsCOMPtr<nsIURI> uri;
220 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
221 if (NS_WARN_IF(NS_FAILED(rv))) {
222 return false;
225 return !ShouldAllowAccessFor(aChannel, uri, &aRejectedReason);
228 MOZ_ASSERT(aPrincipal);
229 return !ShouldAllowAccessFor(aPrincipal, aCookieJarSettings);
232 namespace mozilla {
234 StorageAccess StorageAllowedForWindow(nsPIDOMWindowInner* aWindow,
235 uint32_t* aRejectedReason) {
236 uint32_t rejectedReason;
237 if (!aRejectedReason) {
238 aRejectedReason = &rejectedReason;
241 *aRejectedReason = 0;
243 if (Document* document = aWindow->GetExtantDoc()) {
244 nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
245 // Note that GetChannel() below may return null, but that's OK, since the
246 // callee is able to deal with a null channel argument, and if passed null,
247 // will only fail to notify the UI in case storage gets blocked.
248 nsIChannel* channel = document->GetChannel();
249 return InternalStorageAllowedCheckCached(
250 principal, aWindow, nullptr, channel, document->CookieJarSettings(),
251 *aRejectedReason);
254 // No document? Try checking Private Browsing Mode without document
255 if (const nsCOMPtr<nsIGlobalObject> global = aWindow->AsGlobal()) {
256 if (const nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull()) {
257 if (principal->GetPrivateBrowsingId() > 0) {
258 return StorageAccess::ePrivateBrowsing;
263 // Everything failed? Let's return a generic rejected reason.
264 return StorageAccess::eDeny;
267 StorageAccess StorageAllowedForDocument(const Document* aDoc) {
268 StorageAccess cookieAllowed = CookieAllowedForDocument(aDoc);
269 if (StaticPrefs::
270 privacy_partition_always_partition_third_party_non_cookie_storage() &&
271 cookieAllowed > StorageAccess::eDeny) {
272 return StorageAccess::ePartitionForeignOrDeny;
274 return cookieAllowed;
277 StorageAccess CookieAllowedForDocument(const Document* aDoc) {
278 MOZ_ASSERT(aDoc);
280 if (nsPIDOMWindowInner* inner = aDoc->GetInnerWindow()) {
281 nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
282 // Note that GetChannel() below may return null, but that's OK, since the
283 // callee is able to deal with a null channel argument, and if passed null,
284 // will only fail to notify the UI in case storage gets blocked.
285 nsIChannel* channel = aDoc->GetChannel();
287 uint32_t rejectedReason = 0;
288 return InternalStorageAllowedCheckCached(
289 principal, inner, nullptr, channel,
290 const_cast<Document*>(aDoc)->CookieJarSettings(), rejectedReason);
293 return StorageAccess::eDeny;
296 StorageAccess StorageAllowedForNewWindow(nsIPrincipal* aPrincipal, nsIURI* aURI,
297 nsPIDOMWindowInner* aParent) {
298 MOZ_ASSERT(aPrincipal);
299 MOZ_ASSERT(aURI);
300 // parent may be nullptr
302 uint32_t rejectedReason = 0;
303 nsCOMPtr<nsICookieJarSettings> cjs;
304 if (aParent && aParent->GetExtantDoc()) {
305 cjs = aParent->GetExtantDoc()->CookieJarSettings();
306 } else {
307 cjs = net::CookieJarSettings::Create(aPrincipal);
309 return InternalStorageAllowedCheck(aPrincipal, aParent, aURI, nullptr, cjs,
310 rejectedReason);
313 StorageAccess StorageAllowedForChannel(nsIChannel* aChannel) {
314 MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::GetSecurityManager());
315 MOZ_DIAGNOSTIC_ASSERT(aChannel);
317 nsCOMPtr<nsIPrincipal> principal;
318 Unused << nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
319 aChannel, getter_AddRefs(principal));
320 NS_ENSURE_TRUE(principal, StorageAccess::eDeny);
322 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
323 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
324 nsresult rv =
325 loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
326 NS_ENSURE_SUCCESS(rv, StorageAccess::eDeny);
328 uint32_t rejectedReason = 0;
329 StorageAccess result = InternalStorageAllowedCheck(
330 principal, nullptr, nullptr, aChannel, cookieJarSettings, rejectedReason);
332 return result;
335 StorageAccess StorageAllowedForServiceWorker(
336 nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings) {
337 uint32_t rejectedReason = 0;
338 return InternalStorageAllowedCheck(aPrincipal, nullptr, nullptr, nullptr,
339 aCookieJarSettings, rejectedReason);
342 bool StorageDisabledByAntiTracking(nsPIDOMWindowInner* aWindow,
343 nsIChannel* aChannel,
344 nsIPrincipal* aPrincipal, nsIURI* aURI,
345 uint32_t& aRejectedReason) {
346 MOZ_ASSERT(aWindow || aChannel || aPrincipal);
347 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
348 if (aWindow) {
349 if (aWindow->GetExtantDoc()) {
350 cookieJarSettings = aWindow->GetExtantDoc()->CookieJarSettings();
352 } else if (aChannel) {
353 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
354 Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
356 if (!cookieJarSettings) {
357 cookieJarSettings = net::CookieJarSettings::Create(aPrincipal);
359 bool disabled = StorageDisabledByAntiTrackingInternal(
360 aWindow, aChannel, aPrincipal, aURI, cookieJarSettings, aRejectedReason);
362 if (aWindow) {
363 ContentBlockingNotifier::OnDecision(
364 aWindow,
365 disabled ? ContentBlockingNotifier::BlockingDecision::eBlock
366 : ContentBlockingNotifier::BlockingDecision::eAllow,
367 aRejectedReason);
368 } else if (aChannel) {
369 ContentBlockingNotifier::OnDecision(
370 aChannel,
371 disabled ? ContentBlockingNotifier::BlockingDecision::eBlock
372 : ContentBlockingNotifier::BlockingDecision::eAllow,
373 aRejectedReason);
375 return disabled;
378 bool StorageDisabledByAntiTracking(dom::Document* aDocument, nsIURI* aURI,
379 uint32_t& aRejectedReason) {
380 aRejectedReason = 0;
381 // Note that GetChannel() below may return null, but that's OK, since the
382 // callee is able to deal with a null channel argument, and if passed null,
383 // will only fail to notify the UI in case storage gets blocked.
384 return StorageDisabledByAntiTracking(
385 aDocument->GetInnerWindow(), aDocument->GetChannel(),
386 aDocument->NodePrincipal(), aURI, aRejectedReason);
389 bool ShouldPartitionStorage(StorageAccess aAccess) {
390 return aAccess == StorageAccess::ePartitionTrackersOrDeny ||
391 aAccess == StorageAccess::ePartitionForeignOrDeny;
394 bool ShouldPartitionStorage(uint32_t aRejectedReason) {
395 return aRejectedReason ==
396 static_cast<uint32_t>(
397 nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) ||
398 aRejectedReason ==
399 static_cast<uint32_t>(
400 nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER) ||
401 aRejectedReason ==
402 static_cast<uint32_t>(
403 nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN);
406 bool StoragePartitioningEnabled(StorageAccess aAccess,
407 nsICookieJarSettings* aCookieJarSettings) {
408 return aAccess == StorageAccess::ePartitionForeignOrDeny &&
409 aCookieJarSettings->GetCookieBehavior() ==
410 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
413 bool StoragePartitioningEnabled(uint32_t aRejectedReason,
414 nsICookieJarSettings* aCookieJarSettings) {
415 return aRejectedReason ==
416 static_cast<uint32_t>(
417 nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN) &&
418 aCookieJarSettings->GetCookieBehavior() ==
419 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
422 int32_t CookiesBehavior(Document* a3rdPartyDocument) {
423 MOZ_ASSERT(a3rdPartyDocument);
425 // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior
426 // (See Bug 1406675 and Bug 1525917 for rationale).
427 if (BasePrincipal::Cast(a3rdPartyDocument->NodePrincipal())->AddonPolicy()) {
428 return nsICookieService::BEHAVIOR_ACCEPT;
431 return a3rdPartyDocument->CookieJarSettings()->GetCookieBehavior();
434 int32_t CookiesBehavior(nsILoadInfo* aLoadInfo, nsIURI* a3rdPartyURI) {
435 MOZ_ASSERT(aLoadInfo);
436 MOZ_ASSERT(a3rdPartyURI);
438 // WebExtensions 3rd party URI always get BEHAVIOR_ACCEPT as cookieBehavior,
439 // this is semantically equivalent to the principal having a AddonPolicy().
440 if (a3rdPartyURI->SchemeIs("moz-extension")) {
441 return nsICookieService::BEHAVIOR_ACCEPT;
444 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
445 nsresult rv =
446 aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
447 if (NS_WARN_IF(NS_FAILED(rv))) {
448 return nsICookieService::BEHAVIOR_REJECT;
451 return cookieJarSettings->GetCookieBehavior();
454 int32_t CookiesBehavior(nsIPrincipal* aPrincipal,
455 nsICookieJarSettings* aCookieJarSettings) {
456 MOZ_ASSERT(aPrincipal);
457 MOZ_ASSERT(aCookieJarSettings);
459 // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior
460 // (See Bug 1406675 for rationale).
461 if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
462 return nsICookieService::BEHAVIOR_ACCEPT;
465 return aCookieJarSettings->GetCookieBehavior();
468 bool ShouldAllowAccessFor(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
469 uint32_t* aRejectedReason) {
470 MOZ_ASSERT(aWindow);
471 MOZ_ASSERT(aURI);
473 // Let's avoid a null check on aRejectedReason everywhere else.
474 uint32_t rejectedReason = 0;
475 if (!aRejectedReason) {
476 aRejectedReason = &rejectedReason;
479 LOG_SPEC(("Computing whether window %p has access to URI %s", aWindow, _spec),
480 aURI);
482 nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
483 Document* document = innerWindow->GetExtantDoc();
484 if (!document) {
485 LOG(("Our window has no document"));
486 return false;
489 uint32_t cookiePermission = detail::CheckCookiePermissionForPrincipal(
490 document->CookieJarSettings(), document->NodePrincipal());
491 if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) {
492 LOG(
493 ("CheckCookiePermissionForPrincipal() returned a non-default access "
494 "code (%d) for window's principal, returning %s",
495 int(cookiePermission),
496 cookiePermission != nsICookiePermission::ACCESS_DENY ? "success"
497 : "failure"));
498 if (cookiePermission != nsICookiePermission::ACCESS_DENY) {
499 return true;
502 *aRejectedReason =
503 nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION;
504 return false;
507 int32_t behavior = CookiesBehavior(document);
508 if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
509 LOG(("The cookie behavior pref mandates accepting all cookies!"));
510 return true;
513 if (ContentBlockingAllowList::Check(aWindow)) {
514 return true;
517 if (behavior == nsICookieService::BEHAVIOR_REJECT) {
518 LOG(("The cookie behavior pref mandates rejecting all cookies!"));
519 *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL;
520 return false;
523 // As a performance optimization, we only perform this check for
524 // BEHAVIOR_REJECT_FOREIGN and BEHAVIOR_LIMIT_FOREIGN. For
525 // BEHAVIOR_REJECT_TRACKER and BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
526 // third-partiness is implicily checked later below.
527 if (behavior != nsICookieService::BEHAVIOR_REJECT_TRACKER &&
528 behavior !=
529 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
530 // Let's check if this is a 3rd party context.
531 if (!AntiTrackingUtils::IsThirdPartyWindow(aWindow, aURI)) {
532 LOG(("Our window isn't a third-party window"));
533 return true;
537 if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN &&
538 !CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) ||
539 behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
540 // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
541 // simply rejecting the request to use the storage. In the future, if we
542 // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
543 // for non-cookie storage types, this may change.
544 LOG(("Nothing more to do due to the behavior code %d", int(behavior)));
545 *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN;
546 return false;
549 // The document has been allowlisted. We can return from here directly.
550 if (document->HasStorageAccessPermissionGrantedByAllowList()) {
551 return true;
554 MOZ_ASSERT(
555 CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior) ||
556 behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
557 behavior ==
558 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
560 uint32_t blockedReason =
561 nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
563 if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) {
564 if (!nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow)) {
565 LOG(("Our window isn't a third-party tracking window"));
566 return true;
569 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
570 do_QueryInterface(document->GetChannel());
571 if (classifiedChannel) {
572 uint32_t classificationFlags =
573 classifiedChannel->GetThirdPartyClassificationFlags();
574 if (classificationFlags & nsIClassifiedChannel::ClassificationFlags::
575 CLASSIFIED_SOCIALTRACKING) {
576 blockedReason =
577 nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
580 } else if (behavior ==
581 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
582 if (nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow)) {
583 // fall through
584 } else if (AntiTrackingUtils::IsThirdPartyWindow(aWindow, aURI)) {
585 LOG(("We're in the third-party context, storage should be partitioned"));
586 // fall through, but remember that we're partitioning.
587 blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN;
588 } else {
589 LOG(("Our window isn't a third-party window, storage is allowed"));
590 return true;
592 } else {
593 MOZ_ASSERT(CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior));
594 if (RejectForeignAllowList::Check(document)) {
595 LOG(("This window is exceptionlisted for reject foreign"));
596 return true;
599 blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN;
602 Document* doc = aWindow->GetExtantDoc();
603 // Make sure storage access isn't disabled
604 if (doc && (doc->StorageAccessSandboxed())) {
605 LOG(("Our document is sandboxed"));
606 *aRejectedReason = blockedReason;
607 return false;
610 // Document::HasStoragePermission first checks if storage access granted is
611 // cached in the inner window, if no, it then checks the storage permission
612 // flag in the channel's loadinfo
613 bool allowed = document->HasStorageAccessPermissionGranted();
615 if (!allowed) {
616 *aRejectedReason = blockedReason;
617 } else {
618 if (MOZ_LOG_TEST(gAntiTrackingLog, mozilla::LogLevel::Debug) &&
619 aWindow->HasStorageAccessPermissionGranted()) {
620 LOG(("Permission stored in the window. All good."));
624 return allowed;
627 bool ShouldAllowAccessFor(nsIChannel* aChannel, nsIURI* aURI,
628 uint32_t* aRejectedReason) {
629 MOZ_ASSERT(aURI);
630 MOZ_ASSERT(aChannel);
632 // Let's avoid a null check on aRejectedReason everywhere else.
633 uint32_t rejectedReason = 0;
634 if (!aRejectedReason) {
635 aRejectedReason = &rejectedReason;
638 nsIScriptSecurityManager* ssm =
639 nsScriptSecurityManager::GetScriptSecurityManager();
640 MOZ_ASSERT(ssm);
642 nsCOMPtr<nsIURI> channelURI;
643 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
644 if (NS_FAILED(rv)) {
645 LOG(("Failed to get the channel final URI, bail out early"));
646 return true;
648 LOG_SPEC(
649 ("Computing whether channel %p has access to URI %s", aChannel, _spec),
650 channelURI);
652 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
653 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
654 rv = loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
655 if (NS_WARN_IF(NS_FAILED(rv))) {
656 LOG(
657 ("Failed to get the cookie jar settings from the loadinfo, bail out "
658 "early"));
659 return true;
662 nsCOMPtr<nsIPrincipal> channelPrincipal;
663 rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(channelPrincipal));
664 if (NS_WARN_IF(NS_FAILED(rv))) {
665 LOG(("No channel principal, bail out early"));
666 return false;
669 uint32_t cookiePermission = detail::CheckCookiePermissionForPrincipal(
670 cookieJarSettings, channelPrincipal);
671 if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) {
672 LOG(
673 ("CheckCookiePermissionForPrincipal() returned a non-default access "
674 "code (%d) for channel's principal, returning %s",
675 int(cookiePermission),
676 cookiePermission != nsICookiePermission::ACCESS_DENY ? "success"
677 : "failure"));
678 if (cookiePermission != nsICookiePermission::ACCESS_DENY) {
679 return true;
682 *aRejectedReason =
683 nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION;
684 return false;
687 if (!channelURI) {
688 LOG(("No channel uri, bail out early"));
689 return false;
692 int32_t behavior = CookiesBehavior(loadInfo, channelURI);
693 if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
694 LOG(("The cookie behavior pref mandates accepting all cookies!"));
695 return true;
698 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
700 if (httpChannel && ContentBlockingAllowList::Check(httpChannel)) {
701 return true;
704 if (behavior == nsICookieService::BEHAVIOR_REJECT) {
705 LOG(("The cookie behavior pref mandates rejecting all cookies!"));
706 *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL;
707 return false;
710 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
711 components::ThirdPartyUtil::Service();
712 if (!thirdPartyUtil) {
713 LOG(("No thirdPartyUtil, bail out early"));
714 return true;
717 bool thirdParty = false;
718 rv = thirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &thirdParty);
719 // Grant if it's not a 3rd party.
720 // Be careful to check the return value of IsThirdPartyChannel, since
721 // IsThirdPartyChannel() will fail if the channel's loading principal is the
722 // system principal...
723 if (NS_SUCCEEDED(rv) && !thirdParty) {
724 LOG(("Our channel isn't a third-party channel"));
725 return true;
728 if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN &&
729 !CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) ||
730 behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
731 // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
732 // simply rejecting the request to use the storage. In the future, if we
733 // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
734 // for non-cookie storage types, this may change.
735 LOG(("Nothing more to do due to the behavior code %d", int(behavior)));
736 *aRejectedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN;
737 return false;
740 // The channel has been allowlisted. We can return from here.
741 if (loadInfo->GetStoragePermission() ==
742 nsILoadInfo::StoragePermissionAllowListed) {
743 return true;
746 MOZ_ASSERT(
747 CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior) ||
748 behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
749 behavior ==
750 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);
752 uint32_t blockedReason =
753 nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
755 // Not a tracker.
756 nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
757 do_QueryInterface(aChannel);
758 if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) {
759 if (classifiedChannel) {
760 if (!classifiedChannel->IsThirdPartyTrackingResource()) {
761 LOG(("Our channel isn't a third-party tracking channel"));
762 return true;
765 uint32_t classificationFlags =
766 classifiedChannel->GetThirdPartyClassificationFlags();
767 if (classificationFlags & nsIClassifiedChannel::ClassificationFlags::
768 CLASSIFIED_SOCIALTRACKING) {
769 blockedReason =
770 nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
773 } else if (behavior ==
774 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
775 if (classifiedChannel &&
776 classifiedChannel->IsThirdPartyTrackingResource()) {
777 // fall through
778 } else if (AntiTrackingUtils::IsThirdPartyChannel(aChannel)) {
779 LOG(("We're in the third-party context, storage should be partitioned"));
780 // fall through but remember that we're partitioning.
781 blockedReason = nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN;
782 } else {
783 LOG(("Our channel isn't a third-party channel, storage is allowed"));
784 return true;
786 } else {
787 MOZ_ASSERT(CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior));
788 if (httpChannel && RejectForeignAllowList::Check(httpChannel)) {
789 LOG(("This channel is exceptionlisted"));
790 return true;
792 blockedReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN;
795 RefPtr<BrowsingContext> targetBC;
796 rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(targetBC));
797 if (!targetBC || NS_WARN_IF(NS_FAILED(rv))) {
798 LOG(("Failed to get the channel's target browsing context"));
799 return false;
802 if (Document::StorageAccessSandboxed(targetBC->GetSandboxFlags())) {
803 LOG(("Our document is sandboxed"));
804 *aRejectedReason = blockedReason;
805 return false;
808 // Let's see if we have to grant the access for this particular channel.
810 // HasStorageAccessPermissionGranted only applies to channels that load
811 // documents, for sub-resources loads, just returns the result from loadInfo.
812 bool isDocument = false;
813 aChannel->GetIsDocument(&isDocument);
815 if (isDocument) {
816 nsCOMPtr<nsPIDOMWindowInner> inner =
817 AntiTrackingUtils::GetInnerWindow(targetBC);
818 if (inner && inner->HasStorageAccessPermissionGranted()) {
819 LOG(("Permission stored in the window. All good."));
820 return true;
824 bool allowed =
825 loadInfo->GetStoragePermission() != nsILoadInfo::NoStoragePermission;
826 if (!allowed) {
827 *aRejectedReason = blockedReason;
830 return allowed;
833 bool ShouldAllowAccessFor(nsIPrincipal* aPrincipal,
834 nsICookieJarSettings* aCookieJarSettings) {
835 MOZ_ASSERT(aPrincipal);
836 MOZ_ASSERT(aCookieJarSettings);
838 uint32_t access = nsICookiePermission::ACCESS_DEFAULT;
839 if (aPrincipal->GetIsContentPrincipal()) {
840 PermissionManager* permManager = PermissionManager::GetInstance();
841 if (permManager) {
842 Unused << NS_WARN_IF(NS_FAILED(permManager->TestPermissionFromPrincipal(
843 aPrincipal, "cookie"_ns, &access)));
847 if (access != nsICookiePermission::ACCESS_DEFAULT) {
848 return access != nsICookiePermission::ACCESS_DENY;
851 int32_t behavior = CookiesBehavior(aPrincipal, aCookieJarSettings);
852 return behavior != nsICookieService::BEHAVIOR_REJECT;
855 /* static */
856 bool ApproximateAllowAccessForWithoutChannel(
857 nsPIDOMWindowInner* aFirstPartyWindow, nsIURI* aURI) {
858 MOZ_ASSERT(aFirstPartyWindow);
859 MOZ_ASSERT(aURI);
861 LOG_SPEC(
862 ("Computing a best guess as to whether window %p has access to URI %s",
863 aFirstPartyWindow, _spec),
864 aURI);
866 Document* parentDocument =
867 nsGlobalWindowInner::Cast(aFirstPartyWindow)->GetExtantDoc();
868 if (NS_WARN_IF(!parentDocument)) {
869 LOG(("Failed to get the first party window's document"));
870 return false;
873 if (!parentDocument->CookieJarSettings()->GetRejectThirdPartyContexts()) {
874 LOG(("Disabled by the pref (%d), bail out early",
875 parentDocument->CookieJarSettings()->GetCookieBehavior()));
876 return true;
879 if (ContentBlockingAllowList::Check(aFirstPartyWindow)) {
880 return true;
883 if (!AntiTrackingUtils::IsThirdPartyWindow(aFirstPartyWindow, aURI)) {
884 LOG(("Our window isn't a third-party window"));
885 return true;
888 uint32_t cookiePermission = detail::CheckCookiePermissionForPrincipal(
889 parentDocument->CookieJarSettings(), parentDocument->NodePrincipal());
890 if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) {
891 LOG(
892 ("CheckCookiePermissionForPrincipal() returned a non-default access "
893 "code (%d), returning %s",
894 int(cookiePermission),
895 cookiePermission != nsICookiePermission::ACCESS_DENY ? "success"
896 : "failure"));
897 return cookiePermission != nsICookiePermission::ACCESS_DENY;
900 nsIPrincipal* parentPrincipal = parentDocument->NodePrincipal();
902 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
903 aURI, parentPrincipal->OriginAttributesRef());
905 nsAutoCString type;
906 AntiTrackingUtils::CreateStoragePermissionKey(principal, type);
908 return AntiTrackingUtils::CheckStoragePermission(
909 parentPrincipal, type,
910 nsContentUtils::IsInPrivateBrowsing(parentDocument), nullptr, 0);
912 } // namespace mozilla