forked from mirrors/gecko-dev
		
	 17741f0004
			
		
	
	
		17741f0004
		
	
	
	
	
		
			
			This isn't related to this bug report, but while reading this code, I noticed that we are using the wrong principal object here too.
		
			
				
	
	
		
			711 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			711 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | |
|  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "AntiTrackingCommon.h"
 | |
| 
 | |
| #include "mozilla/dom/ContentChild.h"
 | |
| #include "mozilla/ipc/MessageChannel.h"
 | |
| #include "mozilla/AbstractThread.h"
 | |
| #include "mozilla/Logging.h"
 | |
| #include "mozilla/StaticPrefs.h"
 | |
| #include "mozIThirdPartyUtil.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsGlobalWindowInner.h"
 | |
| #include "nsICookiePermission.h"
 | |
| #include "nsICookieService.h"
 | |
| #include "nsIIOService.h"
 | |
| #include "nsIPermissionManager.h"
 | |
| #include "nsIPrincipal.h"
 | |
| #include "nsIURI.h"
 | |
| #include "nsIURL.h"
 | |
| #include "nsPIDOMWindow.h"
 | |
| #include "nsScriptSecurityManager.h"
 | |
| #include "prtime.h"
 | |
| 
 | |
| #define ANTITRACKING_PERM_KEY "3rdPartyStorage"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using mozilla::dom::ContentChild;
 | |
| 
 | |
| static LazyLogModule gAntiTrackingLog("AntiTracking");
 | |
| static const nsCString::size_type sMaxSpecLength = 128;
 | |
| 
 | |
| #define LOG(format) MOZ_LOG(gAntiTrackingLog, mozilla::LogLevel::Debug, format)
 | |
| 
 | |
| #define LOG_SPEC(format, uri)                                                 \
 | |
|   PR_BEGIN_MACRO                                                              \
 | |
|     if (MOZ_LOG_TEST(gAntiTrackingLog, mozilla::LogLevel::Debug)) {           \
 | |
|       nsAutoCString _specStr(NS_LITERAL_CSTRING("(null)"));                   \
 | |
|       _specStr.Truncate(std::min(_specStr.Length(), sMaxSpecLength));         \
 | |
|       if (uri) {                                                              \
 | |
|         _specStr = uri->GetSpecOrDefault();                                   \
 | |
|       }                                                                       \
 | |
|       const char* _spec = _specStr.get();                                     \
 | |
|       LOG(format);                                                            \
 | |
|     }                                                                         \
 | |
|   PR_END_MACRO
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| bool
 | |
| GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner* a3rdPartyTrackingWindow,
 | |
|                                     nsIPrincipal** aTopLevelStoragePrincipal,
 | |
|                                     nsACString& aTrackingOrigin)
 | |
| {
 | |
|   MOZ_ASSERT(nsContentUtils::IsTrackingResourceWindow(a3rdPartyTrackingWindow));
 | |
| 
 | |
|   // Now we need the principal and the origin of the parent window.
 | |
|   nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal =
 | |
|     a3rdPartyTrackingWindow->GetTopLevelStorageAreaPrincipal();
 | |
|   if (NS_WARN_IF(!topLevelStoragePrincipal)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // Let's take the principal and the origin of the tracker.
 | |
|   nsIPrincipal* trackingPrincipal = a3rdPartyTrackingWindow->GetPrincipal();
 | |
|   if (NS_WARN_IF(!trackingPrincipal)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = trackingPrincipal->GetOriginNoSuffix(aTrackingOrigin);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   topLevelStoragePrincipal.forget(aTopLevelStoragePrincipal);
 | |
|   return true;
 | |
| };
 | |
| 
 | |
| void
 | |
| CreatePermissionKey(const nsCString& aTrackingOrigin,
 | |
|                     const nsCString& aGrantedOrigin,
 | |
|                     nsACString& aPermissionKey)
 | |
| {
 | |
|   if (aTrackingOrigin == aGrantedOrigin) {
 | |
|     aPermissionKey = nsPrintfCString(ANTITRACKING_PERM_KEY "^%s",
 | |
|                                      aTrackingOrigin.get());
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   aPermissionKey = nsPrintfCString(ANTITRACKING_PERM_KEY "^%s^%s",
 | |
|                                    aTrackingOrigin.get(),
 | |
|                                    aGrantedOrigin.get());
 | |
| }
 | |
| 
 | |
| // This internal method returns ACCESS_DENY if the access is denied,
 | |
| // ACCESS_DEFAULT if unknown, some other access code if granted.
 | |
| nsCookieAccess
 | |
| CheckCookiePermissionForPrincipal(nsIPrincipal* aPrincipal)
 | |
| {
 | |
|   nsCookieAccess access = nsICookiePermission::ACCESS_DEFAULT;
 | |
|   if (!aPrincipal->GetIsCodebasePrincipal()) {
 | |
|     return access;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsICookiePermission> cps =
 | |
|     do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
 | |
|   if (NS_WARN_IF(!cps)) {
 | |
|     return access;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = cps->CanAccess(aPrincipal, &access);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return access;
 | |
|   }
 | |
| 
 | |
|   // If we have a custom cookie permission, let's use it.
 | |
|   return access;
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| CookiesBehavior(nsIPrincipal* aPrincipal)
 | |
| {
 | |
|   // WebExtensions principals always get BEHAVIOR_ACCEPT as cookieBehavior
 | |
|   // (See Bug 1406675 for rationale).
 | |
|   if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
 | |
|     return nsICookieService::BEHAVIOR_ACCEPT;
 | |
|   }
 | |
| 
 | |
|   return StaticPrefs::network_cookie_cookieBehavior();
 | |
| }
 | |
| 
 | |
| } // anonymous
 | |
| 
 | |
| /* static */ RefPtr<AntiTrackingCommon::StorageAccessGrantPromise>
 | |
| AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin,
 | |
|                                                          nsPIDOMWindowInner* aParentWindow)
 | |
| {
 | |
|   MOZ_ASSERT(aParentWindow);
 | |
| 
 | |
|   LOG(("Adding a first-party storage exception for %s...",
 | |
|        NS_ConvertUTF16toUTF8(aOrigin).get()));
 | |
| 
 | |
|   if (StaticPrefs::network_cookie_cookieBehavior() !=
 | |
|         nsICookieService::BEHAVIOR_REJECT_TRACKER) {
 | |
|     LOG(("Disabled by network.cookie.cookieBehavior pref (%d), bailing out early",
 | |
|          StaticPrefs::network_cookie_cookieBehavior()));
 | |
|     return StorageAccessGrantPromise::CreateAndResolve(true, __func__);
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal;
 | |
|   nsAutoCString trackingOrigin;
 | |
| 
 | |
|   nsGlobalWindowInner* parentWindow = nsGlobalWindowInner::Cast(aParentWindow);
 | |
|   nsGlobalWindowOuter* outerParentWindow =
 | |
|     nsGlobalWindowOuter::Cast(parentWindow->GetOuterWindow());
 | |
|   if (NS_WARN_IF(!outerParentWindow)) {
 | |
|     LOG(("No outer window found for our parent window, bailing out early"));
 | |
|     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
 | |
|   }
 | |
| 
 | |
|   LOG(("The current resource is %s-party",
 | |
|        outerParentWindow->IsTopLevelWindow() ? "first" : "third"));
 | |
| 
 | |
|   // We are a first party resource.
 | |
|   if (outerParentWindow->IsTopLevelWindow()) {
 | |
|     CopyUTF16toUTF8(aOrigin, trackingOrigin);
 | |
|     topLevelStoragePrincipal = parentWindow->GetPrincipal();
 | |
|     if (NS_WARN_IF(!topLevelStoragePrincipal)) {
 | |
|       LOG(("Top-level storage area principal not found, bailing out early"));
 | |
|       return StorageAccessGrantPromise::CreateAndReject(false, __func__);
 | |
|     }
 | |
| 
 | |
|   // We are a 3rd party source.
 | |
|   } else if (!GetParentPrincipalAndTrackingOrigin(parentWindow,
 | |
|                                                   getter_AddRefs(topLevelStoragePrincipal),
 | |
|                                                   trackingOrigin)) {
 | |
|     LOG(("Error while computing the parent principal and tracking origin, bailing out early"));
 | |
|     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
 | |
|   }
 | |
| 
 | |
|   NS_ConvertUTF16toUTF8 grantedOrigin(aOrigin);
 | |
| 
 | |
|   if (XRE_IsParentProcess()) {
 | |
|     LOG(("Saving the permission: trackingOrigin=%s, grantedOrigin=%s",
 | |
|          trackingOrigin.get(), grantedOrigin.get()));
 | |
| 
 | |
|     RefPtr<StorageAccessGrantPromise::Private> p = new StorageAccessGrantPromise::Private(__func__);
 | |
|     SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(topLevelStoragePrincipal,
 | |
|                                                                trackingOrigin,
 | |
|                                                                grantedOrigin,
 | |
|                                                                [p] (bool success) {
 | |
|                                                                  p->Resolve(success, __func__);
 | |
|                                                                });
 | |
|     return p;
 | |
|   }
 | |
| 
 | |
|   ContentChild* cc = ContentChild::GetSingleton();
 | |
|   MOZ_ASSERT(cc);
 | |
| 
 | |
|   LOG(("Asking the parent process to save the permission for us: trackingOrigin=%s, grantedOrigin=%s",
 | |
|        trackingOrigin.get(), grantedOrigin.get()));
 | |
| 
 | |
|   // This is not really secure, because here we have the content process sending
 | |
|   // the request of storing a permission.
 | |
|   RefPtr<StorageAccessGrantPromise::Private> p = new StorageAccessGrantPromise::Private(__func__);
 | |
|   cc->SendFirstPartyStorageAccessGrantedForOrigin(IPC::Principal(topLevelStoragePrincipal),
 | |
|                                                   trackingOrigin,
 | |
|                                                   grantedOrigin)
 | |
|     ->Then(GetCurrentThreadSerialEventTarget(), __func__,
 | |
|            [p] (bool success) {
 | |
|              p->Resolve(success, __func__);
 | |
|            }, [p] (ipc::ResponseRejectReason aReason) {
 | |
|              p->Reject(false, __func__);
 | |
|            });
 | |
|   return p;
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aParentPrincipal,
 | |
|                                                                                const nsCString& aTrackingOrigin,
 | |
|                                                                                const nsCString& aGrantedOrigin,
 | |
|                                                                                FirstPartyStorageAccessGrantedForOriginResolver&& aResolver)
 | |
| {
 | |
|   MOZ_ASSERT(XRE_IsParentProcess());
 | |
| 
 | |
|   nsCOMPtr<nsIURI> parentPrincipalURI;
 | |
|   Unused << aParentPrincipal->GetURI(getter_AddRefs(parentPrincipalURI));
 | |
|   LOG_SPEC(("Saving a first-party storage permission on %s for trackingOrigin=%s grantedOrigin=%s",
 | |
|             _spec, aTrackingOrigin.get(), aGrantedOrigin.get()), parentPrincipalURI);
 | |
| 
 | |
|   if (NS_WARN_IF(!aParentPrincipal)) {
 | |
|     // The child process is sending something wrong. Let's ignore it.
 | |
|     LOG(("aParentPrincipal is null, bailing out early"));
 | |
|     aResolver(false);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
 | |
|   if (NS_WARN_IF(!pm)) {
 | |
|     LOG(("Permission manager is null, bailing out early"));
 | |
|     aResolver(false);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Remember that this pref is stored in seconds!
 | |
|   uint32_t expirationTime =
 | |
|     StaticPrefs::privacy_restrict3rdpartystorage_expiration() * 1000;
 | |
|   int64_t when = (PR_Now() / PR_USEC_PER_MSEC) + expirationTime;
 | |
| 
 | |
|   nsAutoCString type;
 | |
|   CreatePermissionKey(aTrackingOrigin, aGrantedOrigin, type);
 | |
| 
 | |
|   LOG(("Computed permission key: %s, expiry: %d, proceeding to save in the permission manager",
 | |
|        type.get(), expirationTime));
 | |
| 
 | |
|   nsresult rv = pm->AddFromPrincipal(aParentPrincipal, type.get(),
 | |
|                                      nsIPermissionManager::ALLOW_ACTION,
 | |
|                                      nsIPermissionManager::EXPIRE_TIME, when);
 | |
|   Unused << NS_WARN_IF(NS_FAILED(rv));
 | |
|   aResolver(NS_SUCCEEDED(rv));
 | |
| 
 | |
|   LOG(("Result: %s", NS_SUCCEEDED(rv) ? "success" : "failure"));
 | |
| }
 | |
| 
 | |
| bool
 | |
| AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aWindow,
 | |
|                                                         nsIURI* aURI)
 | |
| {
 | |
|   MOZ_ASSERT(aWindow);
 | |
|   MOZ_ASSERT(aURI);
 | |
| 
 | |
|   LOG_SPEC(("Computing whether window %p has access to URI %s", aWindow, _spec), aURI);
 | |
| 
 | |
|   nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
 | |
|   nsIPrincipal* toplevelPrincipal = innerWindow->GetTopLevelPrincipal();
 | |
|   if (!toplevelPrincipal) {
 | |
|     // We are already the top-level principal. Let's use the window's principal.
 | |
|     LOG(("Our inner window lacks a top-level principal, use the window's principal instead"));
 | |
|     toplevelPrincipal = innerWindow->GetPrincipal();
 | |
|   }
 | |
| 
 | |
|   if (!toplevelPrincipal) {
 | |
|     // This should not be possible, right?
 | |
|     LOG(("No top-level principal, this shouldn't be happening! Bail out early"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCookieAccess access = CheckCookiePermissionForPrincipal(toplevelPrincipal);
 | |
|   if (access != nsICookiePermission::ACCESS_DEFAULT) {
 | |
|     LOG(("CheckCookiePermissionForPrincipal() returned a non-default access code (%d), returning %s",
 | |
|          int(access), access != nsICookiePermission::ACCESS_DENY ?
 | |
|                         "success" : "failure"));
 | |
|     return access != nsICookiePermission::ACCESS_DENY;
 | |
|   }
 | |
| 
 | |
|   int32_t behavior = CookiesBehavior(toplevelPrincipal);
 | |
|   if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
 | |
|     LOG(("The cookie behavior pref mandates accepting all cookies!"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (behavior == nsICookieService::BEHAVIOR_REJECT) {
 | |
|     LOG(("The cookie behavior pref mandates rejecting all cookies!"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // Let's check if this is a 3rd party context.
 | |
|   if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, nullptr, aURI)) {
 | |
|     LOG(("Our window isn't a third-party window"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
 | |
|       behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
 | |
|     // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
 | |
|     // simply rejecting the request to use the storage. In the future, if we
 | |
|     // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
 | |
|     // for non-cookie storage types, this may change.
 | |
|     LOG(("Nothing more to do due to the behavior code %d", int(behavior)));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER);
 | |
|   if (!nsContentUtils::IsTrackingResourceWindow(aWindow)) {
 | |
|     LOG(("Our window isn't a tracking window"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> parentPrincipal;
 | |
|   nsAutoCString trackingOrigin;
 | |
|   if (!GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner::Cast(aWindow),
 | |
|                                            getter_AddRefs(parentPrincipal),
 | |
|                                            trackingOrigin)) {
 | |
|     LOG(("Failed to obtain the parent principal and the tracking origin"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsAutoString origin;
 | |
|   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG_SPEC(("Failed to compute the origin from %s", _spec), aURI);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsAutoCString type;
 | |
|   CreatePermissionKey(trackingOrigin, NS_ConvertUTF16toUTF8(origin), type);
 | |
| 
 | |
|   nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
 | |
|   if (NS_WARN_IF(!pm)) {
 | |
|     LOG(("Failed to obtain the permission manager"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   uint32_t result = 0;
 | |
|   rv = pm->TestPermissionFromPrincipal(parentPrincipal, type.get(), &result);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG(("Failed to test the permission"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> parentPrincipalURI;
 | |
|   Unused << parentPrincipal->GetURI(getter_AddRefs(parentPrincipalURI));
 | |
|   LOG_SPEC(("Testing permission type %s for %s resulted in %d (%s)",
 | |
|             type.get(), _spec, int(result),
 | |
|             result == nsIPermissionManager::ALLOW_ACTION ?
 | |
|               "success" : "failure"), parentPrincipalURI);
 | |
| 
 | |
|   return result == nsIPermissionManager::ALLOW_ACTION;
 | |
| }
 | |
| 
 | |
| bool
 | |
| AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* aChannel,
 | |
|                                                         nsIURI* aURI)
 | |
| {
 | |
|   MOZ_ASSERT(aURI);
 | |
|   MOZ_ASSERT(aChannel);
 | |
| 
 | |
|   nsCOMPtr<nsIURI> channelURI;
 | |
|   Unused << aChannel->GetURI(getter_AddRefs(channelURI));
 | |
|   LOG_SPEC(("Computing whether channel %p has access to URI %s", aChannel, _spec),
 | |
|            channelURI);
 | |
| 
 | |
|   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
 | |
|   if (!loadInfo) {
 | |
|     LOG(("No loadInfo, bail out early"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // We need to find the correct principal to check the cookie permission. For
 | |
|   // third-party contexts, we want to check if the top-level window has a custom
 | |
|   // cookie permission.
 | |
|   nsIPrincipal* toplevelPrincipal = loadInfo->TopLevelPrincipal();
 | |
| 
 | |
|   // If this is already the top-level window, we should use the loading
 | |
|   // principal.
 | |
|   if (!toplevelPrincipal) {
 | |
|     LOG(("Our loadInfo lacks a top-level principal, use the loadInfo's loading principal instead"));
 | |
|     toplevelPrincipal = loadInfo->LoadingPrincipal();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> channelPrincipal;
 | |
|   nsIScriptSecurityManager* ssm = nsScriptSecurityManager::GetScriptSecurityManager();
 | |
|   nsresult rv = ssm->GetChannelResultPrincipal(aChannel,
 | |
|                                                getter_AddRefs(channelPrincipal));
 | |
| 
 | |
|   // If we don't have a loading principal and this is a document channel, we are
 | |
|   // a top-level window!
 | |
|   if (!toplevelPrincipal) {
 | |
|     LOG(("We don't have a loading principal, let's see if this is a document channel"
 | |
|          " that belongs to a top-level window"));
 | |
|     bool isDocument = false;
 | |
|     nsresult rv2 = aChannel->GetIsMainDocumentChannel(&isDocument);
 | |
|     if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2) && isDocument) {
 | |
|       toplevelPrincipal = channelPrincipal;
 | |
|       LOG(("Yes, we guessed right!"));
 | |
|     } else {
 | |
|       LOG(("No, we guessed wrong!"));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Let's use the triggering principal then.
 | |
|   if (!toplevelPrincipal) {
 | |
|     LOG(("Our loadInfo lacks a top-level principal, use the loadInfo's triggering principal instead"));
 | |
|     toplevelPrincipal = loadInfo->TriggeringPrincipal();
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!toplevelPrincipal)) {
 | |
|     LOG(("No top-level principal! Bail out early"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCookieAccess access = CheckCookiePermissionForPrincipal(toplevelPrincipal);
 | |
|   if (access != nsICookiePermission::ACCESS_DEFAULT) {
 | |
|     LOG(("CheckCookiePermissionForPrincipal() returned a non-default access code (%d), returning %s",
 | |
|          int(access), access != nsICookiePermission::ACCESS_DENY ?
 | |
|                         "success" : "failure"));
 | |
|     return access != nsICookiePermission::ACCESS_DENY;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(NS_FAILED(rv) || !channelPrincipal)) {
 | |
|     LOG(("No channel principal, bail out early"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   int32_t behavior = CookiesBehavior(toplevelPrincipal);
 | |
|   if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
 | |
|     LOG(("The cookie behavior pref mandates accepting all cookies!"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (behavior == nsICookieService::BEHAVIOR_REJECT) {
 | |
|     LOG(("The cookie behavior pref mandates rejecting all cookies!"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
 | |
|   if (!thirdPartyUtil) {
 | |
|     LOG(("No thirdPartyUtil, bail out early"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool thirdParty = false;
 | |
|   Unused << thirdPartyUtil->IsThirdPartyChannel(aChannel,
 | |
|                                                 nullptr,
 | |
|                                                 &thirdParty);
 | |
|   // Grant if it's not a 3rd party.
 | |
|   if (!thirdParty) {
 | |
|     LOG(("Our channel isn't a third-party channel"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
 | |
|       behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
 | |
|     // XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
 | |
|     // simply rejecting the request to use the storage. In the future, if we
 | |
|     // change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
 | |
|     // for non-cookie storage types, this may change.
 | |
|     LOG(("Nothing more to do due to the behavior code %d", int(behavior)));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER);
 | |
| 
 | |
|   nsIPrincipal* parentPrincipal = loadInfo->TopLevelStorageAreaPrincipal();
 | |
|   if (!parentPrincipal) {
 | |
|     LOG(("No top-level storage area principal at hand"));
 | |
| 
 | |
|     // parentPrincipal can be null if the parent window is not the top-level
 | |
|     // window.
 | |
|     if (loadInfo->TopLevelPrincipal()) {
 | |
|       LOG(("Parent window is the top-level window, bail out early"));
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     parentPrincipal = toplevelPrincipal;
 | |
|     if (NS_WARN_IF(!parentPrincipal)) {
 | |
|       LOG(("No triggering principal, this shouldn't be happening! Bail out early"));
 | |
|       // Why we are here?!?
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Not a tracker.
 | |
|   if (!aChannel->GetIsTrackingResource()) {
 | |
|     LOG(("Our channel isn't a tracking channel"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Let's see if we have to grant the access for this particular channel.
 | |
| 
 | |
|   nsCOMPtr<nsIURI> trackingURI;
 | |
|   rv = aChannel->GetURI(getter_AddRefs(trackingURI));
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG(("Failed to get the channel URI"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   nsAutoString trackingOrigin;
 | |
|   rv = nsContentUtils::GetUTFOrigin(trackingURI, trackingOrigin);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG_SPEC(("Failed to compute the origin from %s", _spec), trackingURI);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsAutoString origin;
 | |
|   rv = nsContentUtils::GetUTFOrigin(aURI, origin);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG_SPEC(("Failed to compute the origin from %s", _spec), aURI);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsAutoCString type;
 | |
|   CreatePermissionKey(NS_ConvertUTF16toUTF8(trackingOrigin),
 | |
|                       NS_ConvertUTF16toUTF8(origin), type);
 | |
| 
 | |
|   nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
 | |
|   if (NS_WARN_IF(!pm)) {
 | |
|     LOG(("Failed to obtain the permission manager"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   uint32_t result = 0;
 | |
|   rv = pm->TestPermissionFromPrincipal(parentPrincipal, type.get(), &result);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG(("Failed to test the permission"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> parentPrincipalURI;
 | |
|   Unused << parentPrincipal->GetURI(getter_AddRefs(parentPrincipalURI));
 | |
|   LOG_SPEC(("Testing permission type %s for %s resulted in %d (%s)",
 | |
|             type.get(), _spec, int(result),
 | |
|             result == nsIPermissionManager::ALLOW_ACTION ?
 | |
|               "success" : "failure"), parentPrincipalURI);
 | |
| 
 | |
|   return result == nsIPermissionManager::ALLOW_ACTION;
 | |
| }
 | |
| 
 | |
| bool
 | |
| AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal)
 | |
| {
 | |
|   MOZ_ASSERT(aPrincipal);
 | |
| 
 | |
|   nsCookieAccess access = CheckCookiePermissionForPrincipal(aPrincipal);
 | |
|   if (access != nsICookiePermission::ACCESS_DEFAULT) {
 | |
|     return access != nsICookiePermission::ACCESS_DENY;
 | |
|   }
 | |
| 
 | |
|   int32_t behavior = CookiesBehavior(aPrincipal);
 | |
|   return behavior != nsICookieService::BEHAVIOR_REJECT;
 | |
| }
 | |
| 
 | |
| /* static */ bool
 | |
| AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(nsPIDOMWindowInner* aFirstPartyWindow,
 | |
|                                                              nsIURI* aURI)
 | |
| {
 | |
|   MOZ_ASSERT(aFirstPartyWindow);
 | |
|   MOZ_ASSERT(!nsContentUtils::IsTrackingResourceWindow(aFirstPartyWindow));
 | |
|   MOZ_ASSERT(aURI);
 | |
| 
 | |
|   LOG_SPEC(("Computing a best guess as to whether window %p has access to URI %s",
 | |
|             aFirstPartyWindow, _spec), aURI);
 | |
| 
 | |
|   if (StaticPrefs::network_cookie_cookieBehavior() !=
 | |
|         nsICookieService::BEHAVIOR_REJECT_TRACKER) {
 | |
|     LOG(("Disabled by the pref (%d), bail out early",
 | |
|          StaticPrefs::network_cookie_cookieBehavior()));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (!nsContentUtils::IsThirdPartyWindowOrChannel(aFirstPartyWindow,
 | |
|                                                    nullptr, aURI)) {
 | |
|     LOG(("Our window isn't a third-party window"));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> parentPrincipal =
 | |
|     nsGlobalWindowInner::Cast(aFirstPartyWindow)->GetPrincipal();
 | |
|   if (NS_WARN_IF(!parentPrincipal)) {
 | |
|     LOG(("Failed to get the first party window's principal"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCookieAccess access = CheckCookiePermissionForPrincipal(parentPrincipal);
 | |
|   if (access != nsICookiePermission::ACCESS_DEFAULT) {
 | |
|     LOG(("CheckCookiePermissionForPrincipal() returned a non-default access code (%d), returning %s",
 | |
|          int(access), access != nsICookiePermission::ACCESS_DENY ?
 | |
|                         "success" : "failure"));
 | |
|     return access != nsICookiePermission::ACCESS_DENY;
 | |
|   }
 | |
| 
 | |
|   nsAutoString origin;
 | |
|   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG_SPEC(("Failed to compute the origin from %s", _spec), aURI);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   NS_ConvertUTF16toUTF8 utf8Origin(origin);
 | |
| 
 | |
|   nsAutoCString type;
 | |
|   CreatePermissionKey(utf8Origin, utf8Origin, type);
 | |
| 
 | |
|   nsCOMPtr<nsIPermissionManager> pm = services::GetPermissionManager();
 | |
|   if (NS_WARN_IF(!pm)) {
 | |
|     LOG(("Failed to obtain the permission manager"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   uint32_t result = 0;
 | |
|   rv = pm->TestPermissionFromPrincipal(parentPrincipal, type.get(), &result);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     LOG(("Failed to test the permission"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> parentPrincipalURI;
 | |
|   Unused << parentPrincipal->GetURI(getter_AddRefs(parentPrincipalURI));
 | |
|   LOG_SPEC(("Testing permission type %s for %s resulted in %d (%s)",
 | |
|             type.get(), _spec, int(result),
 | |
|             result == nsIPermissionManager::ALLOW_ACTION ?
 | |
|               "success" : "failure"), parentPrincipalURI);
 | |
| 
 | |
|   return result == nsIPermissionManager::ALLOW_ACTION;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| AntiTrackingCommon::IsOnContentBlockingAllowList(nsIURI* aTopWinURI,
 | |
|                                                  bool& aIsAllowListed)
 | |
| {
 | |
|   aIsAllowListed = false;
 | |
| 
 | |
|   LOG_SPEC(("Deciding whether the user has overridden content blocking for %s",
 | |
|             _spec), aTopWinURI);
 | |
| 
 | |
|   nsCOMPtr<nsIIOService> ios = services::GetIOService();
 | |
|   NS_ENSURE_TRUE(ios, NS_ERROR_FAILURE);
 | |
| 
 | |
|   // Take the host/port portion so we can allowlist by site. Also ignore the
 | |
|   // scheme, since users who put sites on the allowlist probably don't expect
 | |
|   // allowlisting to depend on scheme.
 | |
|   nsresult rv = NS_ERROR_FAILURE;
 | |
|   nsCOMPtr<nsIURL> url = do_QueryInterface(aTopWinURI, &rv);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv; // normal for some loads, no need to print a warning
 | |
|   }
 | |
| 
 | |
|   nsCString escaped(NS_LITERAL_CSTRING("https://"));
 | |
|   nsAutoCString temp;
 | |
|   rv = url->GetHostPort(temp);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
|   escaped.Append(temp);
 | |
| 
 | |
|   // Stuff the whole thing back into a URI for the permission manager.
 | |
|   nsCOMPtr<nsIURI> topWinURI;
 | |
|   rv = ios->NewURI(escaped, nullptr, nullptr, getter_AddRefs(topWinURI));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   nsCOMPtr<nsIPermissionManager> permMgr =
 | |
|     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   // Check both the normal mode and private browsing mode user override permissions.
 | |
|   const char* types[] = {
 | |
|     "trackingprotection",
 | |
|     "trackingprotection-pb"
 | |
|   };
 | |
| 
 | |
|   for (size_t i = 0; i < ArrayLength(types); ++i) {
 | |
|     uint32_t permissions = nsIPermissionManager::UNKNOWN_ACTION;
 | |
|     rv = permMgr->TestPermission(topWinURI, types[i], &permissions);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     if (permissions == nsIPermissionManager::ALLOW_ACTION) {
 | |
|       aIsAllowListed = true;
 | |
|       LOG_SPEC(("Found user override type %s for %s", types[i], _spec),
 | |
|                topWinURI);
 | |
|       // Stop checking the next permisson type if we decided to override.
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!aIsAllowListed) {
 | |
|     LOG(("No user override found"));
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 |