diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index f36e0f00a29f..eddcfcd1e72b 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -63,7 +63,7 @@ #include "mozilla/ipc/URIUtils.h" #include "mozilla/Telemetry.h" #include "mozilla/Unused.h" -#include "mozilla/AntiTrackingCommon.h" +#include "mozilla/AntiTrackingRedirectHeuristic.h" #include "mozilla/BasePrincipal.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/SyncRunnable.h" @@ -838,7 +838,7 @@ nsresult nsHttpHandler::AsyncOnChannelRedirect( newChan->GetURI(getter_AddRefs(newURI)); MOZ_ASSERT(newURI); - AntiTrackingCommon::RedirectHeuristic(oldChan, oldURI, newChan, newURI); + AntiTrackingRedirectHeuristic(oldChan, oldURI, newChan, newURI); // TODO E10S This helper has to be initialized on the other process RefPtr redirectCallbackHelper = diff --git a/toolkit/components/antitracking/AntiTrackingCommon.cpp b/toolkit/components/antitracking/AntiTrackingCommon.cpp index f5cf0ba801a2..80361ae7967c 100644 --- a/toolkit/components/antitracking/AntiTrackingCommon.cpp +++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp @@ -45,7 +45,6 @@ #include "nsPIDOMWindow.h" #include "nsPrintfCString.h" #include "nsScriptSecurityManager.h" -#include "nsSandboxFlags.h" #include "prtime.h" #define ANTITRACKING_PERM_KEY "3rdPartyStorage" @@ -1422,200 +1421,3 @@ bool AntiTrackingCommon::HasUserInteraction(nsIPrincipal* aPrincipal) { return result == nsIPermissionManager::ALLOW_ACTION; } - -/* static */ -void AntiTrackingCommon::RedirectHeuristic(nsIChannel* aOldChannel, - nsIURI* aOldURI, - nsIChannel* aNewChannel, - nsIURI* aNewURI) { - MOZ_ASSERT(aOldChannel); - MOZ_ASSERT(aOldURI); - MOZ_ASSERT(aNewChannel); - MOZ_ASSERT(aNewURI); - - nsresult rv; - - if (!StaticPrefs::privacy_restrict3rdpartystorage_heuristic_redirect()) { - return; - } - - nsCOMPtr newChannel = do_QueryInterface(aNewChannel); - if (!newChannel) { - return; - } - - LOG_SPEC(("Checking redirect-heuristic for %s", _spec), aOldURI); - - nsCOMPtr oldLoadInfo = aOldChannel->LoadInfo(); - MOZ_ASSERT(oldLoadInfo); - - nsCOMPtr newLoadInfo = aNewChannel->LoadInfo(); - MOZ_ASSERT(newLoadInfo); - - nsContentPolicyType contentType = oldLoadInfo->GetExternalContentPolicyType(); - if (contentType != nsIContentPolicy::TYPE_DOCUMENT || - !aOldChannel->IsDocument()) { - LOG_SPEC(("Ignoring redirect for %s because it's not a document", _spec), - aOldURI); - // We care about document redirects only. - return; - } - - nsCOMPtr classifiedOldChannel = - do_QueryInterface(aOldChannel); - nsCOMPtr classifiedNewChannel = - do_QueryInterface(aNewChannel); - if (!classifiedOldChannel || !classifiedNewChannel) { - LOG_SPEC2(("Ignoring redirect for %s to %s because there is not " - "nsIClassifiedChannel interface", - _spec1, _spec2), - aOldURI, aNewURI); - return; - } - - bool allowedByPreviousRedirect = - oldLoadInfo->GetAllowListFutureDocumentsCreatedFromThisRedirectChain(); - - // We're looking at the first-party classification flags because we're - // interested in first-party redirects. - uint32_t newClassificationFlags = - classifiedNewChannel->GetFirstPartyClassificationFlags(); - - if (net::UrlClassifierCommon::IsTrackingClassificationFlag( - newClassificationFlags)) { - // This is not a tracking -> non-tracking redirect. - LOG_SPEC2(("Redirect for %s to %s because it's not tracking to " - "non-tracking. Part of a chain of granted redirects: %d", - _spec1, _spec2, allowedByPreviousRedirect), - aOldURI, aNewURI); - newLoadInfo->SetAllowListFutureDocumentsCreatedFromThisRedirectChain( - allowedByPreviousRedirect); - return; - } - - uint32_t oldClassificationFlags = - classifiedOldChannel->GetFirstPartyClassificationFlags(); - - if (!net::UrlClassifierCommon::IsTrackingClassificationFlag( - oldClassificationFlags) && - !allowedByPreviousRedirect) { - // This is not a tracking -> non-tracking redirect. - LOG_SPEC2( - ("Redirect for %s to %s because it's not tracking to non-tracking.", - _spec1, _spec2), - aOldURI, aNewURI); - return; - } - - nsIScriptSecurityManager* ssm = - nsScriptSecurityManager::GetScriptSecurityManager(); - MOZ_ASSERT(ssm); - - nsCOMPtr trackingPrincipal; - - const nsTArray>& chain = - oldLoadInfo->RedirectChain(); - - if (allowedByPreviousRedirect && !chain.IsEmpty()) { - rv = chain[0]->GetPrincipal(getter_AddRefs(trackingPrincipal)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG(("Can't obtain the principal from the redirect chain")); - return; - } - } else { - rv = ssm->GetChannelResultPrincipal(aOldChannel, - getter_AddRefs(trackingPrincipal)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG(("Can't obtain the principal from the tracking")); - return; - } - } - - nsCOMPtr redirectedPrincipal; - rv = ssm->GetChannelResultPrincipal(aNewChannel, - getter_AddRefs(redirectedPrincipal)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG(("Can't obtain the principal from the redirected")); - return; - } - - if (!AntiTrackingCommon::HasUserInteraction(trackingPrincipal)) { - LOG_SPEC2(("Ignoring redirect for %s to %s because no user-interaction on " - "tracker", - _spec1, _spec2), - aOldURI, aNewURI); - return; - } - - nsAutoCString trackingOrigin; - rv = trackingPrincipal->GetOrigin(trackingOrigin); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG(("Can't get the origin from the Principal")); - return; - } - - nsAutoCString redirectedOrigin; - rv = nsContentUtils::GetASCIIOrigin(aNewURI, redirectedOrigin); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG(("Can't get the origin from the URI")); - return; - } - - LOG(("Adding a first-party storage exception for %s...", - PromiseFlatCString(redirectedOrigin).get())); - - nsCOMPtr cookieJarSettings; - rv = oldLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG(("Can't get the cookieJarSettings")); - return; - } - - int32_t behavior = cookieJarSettings->GetCookieBehavior(); - - if (!cookieJarSettings->GetRejectThirdPartyTrackers()) { - LOG( - ("Disabled by network.cookie.cookieBehavior pref (%d), bailing out " - "early", - behavior)); - return; - } - - MOZ_ASSERT( - behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER || - behavior == - nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN); - - if (ContentBlockingAllowList::Check(newChannel)) { - return; - } - - LOG(("Saving the permission: trackingOrigin=%s, grantedOrigin=%s", - trackingOrigin.get(), redirectedOrigin.get())); - - // Any new redirect from this loadInfo must be considered as granted. - newLoadInfo->SetAllowListFutureDocumentsCreatedFromThisRedirectChain(true); - - uint64_t innerWindowID; - Unused << newChannel->GetTopLevelContentWindowId(&innerWindowID); - - nsAutoString errorText; - AutoTArray params = {NS_ConvertUTF8toUTF16(redirectedOrigin), - NS_ConvertUTF8toUTF16(trackingOrigin)}; - rv = nsContentUtils::FormatLocalizedString( - nsContentUtils::eNECKO_PROPERTIES, "CookieAllowedForTrackerByHeuristic", - params, errorText); - if (NS_SUCCEEDED(rv)) { - nsContentUtils::ReportToConsoleByWindowID( - errorText, nsIScriptError::warningFlag, ANTITRACKING_CONSOLE_CATEGORY, - innerWindowID); - } - - // We don't care about this promise because the operation is actually sync. - RefPtr promise = - SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess( - redirectedPrincipal, trackingPrincipal, trackingOrigin, - StorageAccessPromptChoices::eAllow, - StaticPrefs::privacy_restrict3rdpartystorage_expiration_redirect()); - Unused << promise; -} diff --git a/toolkit/components/antitracking/AntiTrackingCommon.h b/toolkit/components/antitracking/AntiTrackingCommon.h index 5fb2424c5c16..cec0ad3af735 100644 --- a/toolkit/components/antitracking/AntiTrackingCommon.h +++ b/toolkit/components/antitracking/AntiTrackingCommon.h @@ -123,9 +123,6 @@ class AntiTrackingCommon final { const nsCString& aTrackingOrigin, int aAllowMode, uint64_t aExpirationTime = StaticPrefs::privacy_restrict3rdpartystorage_expiration()); - - static void RedirectHeuristic(nsIChannel* aOldChannel, nsIURI* aOldURI, - nsIChannel* aNewChannel, nsIURI* aNewURI); }; } // namespace mozilla diff --git a/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.cpp b/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.cpp new file mode 100644 index 000000000000..c1ea54eb3f24 --- /dev/null +++ b/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.cpp @@ -0,0 +1,222 @@ +/* -*- 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 "AntiTrackingLog.h" +#include "AntiTrackingRedirectHeuristic.h" +#include "AntiTrackingCommon.h" +#include "ContentBlockingAllowList.h" + +#include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/Document.h" +#include "mozilla/net/UrlClassifierCommon.h" +#include "nsContentUtils.h" +#include "nsIChannel.h" +#include "nsIClassifiedChannel.h" +#include "nsIRedirectHistoryEntry.h" +#include "nsIScriptError.h" +#include "nsIURI.h" +#include "nsPIDOMWindow.h" +#include "nsScriptSecurityManager.h" + +namespace mozilla { + +void AntiTrackingRedirectHeuristic(nsIChannel* aOldChannel, nsIURI* aOldURI, + nsIChannel* aNewChannel, nsIURI* aNewURI) { + MOZ_ASSERT(aOldChannel); + MOZ_ASSERT(aOldURI); + MOZ_ASSERT(aNewChannel); + MOZ_ASSERT(aNewURI); + + nsresult rv; + + if (!StaticPrefs::privacy_restrict3rdpartystorage_heuristic_redirect()) { + return; + } + + nsCOMPtr newChannel = do_QueryInterface(aNewChannel); + if (!newChannel) { + return; + } + + LOG_SPEC(("Checking redirect-heuristic for %s", _spec), aOldURI); + + nsCOMPtr oldLoadInfo = aOldChannel->LoadInfo(); + MOZ_ASSERT(oldLoadInfo); + + nsCOMPtr newLoadInfo = aNewChannel->LoadInfo(); + MOZ_ASSERT(newLoadInfo); + + nsContentPolicyType contentType = oldLoadInfo->GetExternalContentPolicyType(); + if (contentType != nsIContentPolicy::TYPE_DOCUMENT || + !aOldChannel->IsDocument()) { + LOG_SPEC(("Ignoring redirect for %s because it's not a document", _spec), + aOldURI); + // We care about document redirects only. + return; + } + + nsCOMPtr classifiedOldChannel = + do_QueryInterface(aOldChannel); + nsCOMPtr classifiedNewChannel = + do_QueryInterface(aNewChannel); + if (!classifiedOldChannel || !classifiedNewChannel) { + LOG_SPEC2(("Ignoring redirect for %s to %s because there is not " + "nsIClassifiedChannel interface", + _spec1, _spec2), + aOldURI, aNewURI); + return; + } + + bool allowedByPreviousRedirect = + oldLoadInfo->GetAllowListFutureDocumentsCreatedFromThisRedirectChain(); + + // We're looking at the first-party classification flags because we're + // interested in first-party redirects. + uint32_t newClassificationFlags = + classifiedNewChannel->GetFirstPartyClassificationFlags(); + + if (net::UrlClassifierCommon::IsTrackingClassificationFlag( + newClassificationFlags)) { + // This is not a tracking -> non-tracking redirect. + LOG_SPEC2(("Redirect for %s to %s because it's not tracking to " + "non-tracking. Part of a chain of granted redirects: %d", + _spec1, _spec2, allowedByPreviousRedirect), + aOldURI, aNewURI); + newLoadInfo->SetAllowListFutureDocumentsCreatedFromThisRedirectChain( + allowedByPreviousRedirect); + return; + } + + uint32_t oldClassificationFlags = + classifiedOldChannel->GetFirstPartyClassificationFlags(); + + if (!net::UrlClassifierCommon::IsTrackingClassificationFlag( + oldClassificationFlags) && + !allowedByPreviousRedirect) { + // This is not a tracking -> non-tracking redirect. + LOG_SPEC2( + ("Redirect for %s to %s because it's not tracking to non-tracking.", + _spec1, _spec2), + aOldURI, aNewURI); + return; + } + + nsIScriptSecurityManager* ssm = + nsScriptSecurityManager::GetScriptSecurityManager(); + MOZ_ASSERT(ssm); + + nsCOMPtr trackingPrincipal; + + const nsTArray>& chain = + oldLoadInfo->RedirectChain(); + + if (allowedByPreviousRedirect && !chain.IsEmpty()) { + rv = chain[0]->GetPrincipal(getter_AddRefs(trackingPrincipal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Can't obtain the principal from the redirect chain")); + return; + } + } else { + rv = ssm->GetChannelResultPrincipal(aOldChannel, + getter_AddRefs(trackingPrincipal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Can't obtain the principal from the tracking")); + return; + } + } + + nsCOMPtr redirectedPrincipal; + rv = ssm->GetChannelResultPrincipal(aNewChannel, + getter_AddRefs(redirectedPrincipal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Can't obtain the principal from the redirected")); + return; + } + + if (!AntiTrackingCommon::HasUserInteraction(trackingPrincipal)) { + LOG_SPEC2(("Ignoring redirect for %s to %s because no user-interaction on " + "tracker", + _spec1, _spec2), + aOldURI, aNewURI); + return; + } + + nsAutoCString trackingOrigin; + rv = trackingPrincipal->GetOrigin(trackingOrigin); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Can't get the origin from the Principal")); + return; + } + + nsAutoCString redirectedOrigin; + rv = nsContentUtils::GetASCIIOrigin(aNewURI, redirectedOrigin); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Can't get the origin from the URI")); + return; + } + + LOG(("Adding a first-party storage exception for %s...", + PromiseFlatCString(redirectedOrigin).get())); + + nsCOMPtr cookieJarSettings; + rv = oldLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + LOG(("Can't get the cookieJarSettings")); + return; + } + + int32_t behavior = cookieJarSettings->GetCookieBehavior(); + + if (!cookieJarSettings->GetRejectThirdPartyTrackers()) { + LOG( + ("Disabled by network.cookie.cookieBehavior pref (%d), bailing out " + "early", + behavior)); + return; + } + + MOZ_ASSERT( + behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER || + behavior == + nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN); + + if (ContentBlockingAllowList::Check(newChannel)) { + return; + } + + LOG(("Saving the permission: trackingOrigin=%s, grantedOrigin=%s", + trackingOrigin.get(), redirectedOrigin.get())); + + // Any new redirect from this loadInfo must be considered as granted. + newLoadInfo->SetAllowListFutureDocumentsCreatedFromThisRedirectChain(true); + + uint64_t innerWindowID; + Unused << newChannel->GetTopLevelContentWindowId(&innerWindowID); + + nsAutoString errorText; + AutoTArray params = {NS_ConvertUTF8toUTF16(redirectedOrigin), + NS_ConvertUTF8toUTF16(trackingOrigin)}; + rv = nsContentUtils::FormatLocalizedString( + nsContentUtils::eNECKO_PROPERTIES, "CookieAllowedForTrackerByHeuristic", + params, errorText); + if (NS_SUCCEEDED(rv)) { + nsContentUtils::ReportToConsoleByWindowID( + errorText, nsIScriptError::warningFlag, ANTITRACKING_CONSOLE_CATEGORY, + innerWindowID); + } + + // We don't care about this promise because the operation is actually sync. + RefPtr promise = + AntiTrackingCommon:: + SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess( + redirectedPrincipal, trackingPrincipal, trackingOrigin, + AntiTrackingCommon::StorageAccessPromptChoices::eAllow, + StaticPrefs:: + privacy_restrict3rdpartystorage_expiration_redirect()); + Unused << promise; +} + +} // namespace mozilla diff --git a/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.h b/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.h new file mode 100644 index 000000000000..4b8d9a602a1d --- /dev/null +++ b/toolkit/components/antitracking/AntiTrackingRedirectHeuristic.h @@ -0,0 +1,20 @@ +/* -*- 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/. */ + +#ifndef mozilla_antitrackingredirectheuristic_h +#define mozilla_antitrackingredirectheuristic_h + +class nsIChannel; +class nsIURI; + +namespace mozilla { + +void AntiTrackingRedirectHeuristic(nsIChannel* aOldChannel, nsIURI* aOldURI, + nsIChannel* aNewChannel, nsIURI* aNewURI); + +} // namespace mozilla + +#endif // mozilla_antitrackingredirectheuristic_h diff --git a/toolkit/components/antitracking/AntiTrackingUtils.h b/toolkit/components/antitracking/AntiTrackingUtils.h index 9fa85fc60cfa..01c07f9b12ba 100644 --- a/toolkit/components/antitracking/AntiTrackingUtils.h +++ b/toolkit/components/antitracking/AntiTrackingUtils.h @@ -11,6 +11,8 @@ class nsPIDOMWindowInner; class nsPIDOMWindowOuter; +class nsIChannel; +class nsIURI; namespace mozilla { diff --git a/toolkit/components/antitracking/ContentBlockingAllowList.h b/toolkit/components/antitracking/ContentBlockingAllowList.h index 050e9cd7e0c3..f3183c840865 100644 --- a/toolkit/components/antitracking/ContentBlockingAllowList.h +++ b/toolkit/components/antitracking/ContentBlockingAllowList.h @@ -26,6 +26,8 @@ class ContentBlockingAllowList final { static nsresult Check(nsIPrincipal* aContentBlockingAllowListPrincipal, bool aIsPrivateBrowsing, bool& aIsAllowListed); + static bool Check(nsIHttpChannel* aChannel); + // Computes the principal used to check the content blocking allow list for a // top-level document based on the document principal. This function is used // right after setting up the document principal. @@ -44,7 +46,6 @@ class ContentBlockingAllowList final { // Utility APIs for AntiTrackingCommon. static bool Check(nsIPrincipal* aTopWinPrincipal, bool aIsPrivateBrowsing); static bool Check(nsPIDOMWindowInner* aWindow); - static bool Check(nsIHttpChannel* aChannel); friend class AntiTrackingCommon; }; diff --git a/toolkit/components/antitracking/StorageAccess.cpp b/toolkit/components/antitracking/StorageAccess.cpp index 14053e09f4be..8e125fdaadf9 100644 --- a/toolkit/components/antitracking/StorageAccess.cpp +++ b/toolkit/components/antitracking/StorageAccess.cpp @@ -11,9 +11,16 @@ #include "mozilla/StaticPrefs_network.h" #include "mozilla/StaticPrefs_privacy.h" #include "mozilla/StorageAccess.h" +#include "nsContentUtils.h" +#include "nsICookiePermission.h" #include "nsICookieService.h" #include "nsICookieJarSettings.h" +#include "nsIPermission.h" #include "nsIWebProgressListener.h" +#include "nsSandboxFlags.h" + +using namespace mozilla; +using namespace mozilla::dom; /** * Gets the cookie lifetime policy for a given cookieJarSettings and a given diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.h b/toolkit/components/antitracking/StoragePrincipalHelper.h index 314bdc05d8c3..ba88165420df 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.h +++ b/toolkit/components/antitracking/StoragePrincipalHelper.h @@ -7,6 +7,8 @@ #ifndef mozilla_StoragePrincipalHelper_h #define mozilla_StoragePrincipalHelper_h +#include "nsError.h" + /** * StoragePrincipal * ~~~~~~~~~~~~~~~~ diff --git a/toolkit/components/antitracking/URLDecorationStripper.cpp b/toolkit/components/antitracking/URLDecorationStripper.cpp index 5122e89620a1..642f16a321ea 100644 --- a/toolkit/components/antitracking/URLDecorationStripper.cpp +++ b/toolkit/components/antitracking/URLDecorationStripper.cpp @@ -10,6 +10,7 @@ #include "mozilla/Preferences.h" #include "nsCharSeparatedTokenizer.h" #include "nsEffectiveTLDService.h" +#include "nsIURI.h" #include "nsIURIMutator.h" namespace { diff --git a/toolkit/components/antitracking/moz.build b/toolkit/components/antitracking/moz.build index 41bf807b7876..7078a77d7db1 100644 --- a/toolkit/components/antitracking/moz.build +++ b/toolkit/components/antitracking/moz.build @@ -33,6 +33,7 @@ XPCOM_MANIFESTS += [ EXPORTS.mozilla = [ 'AntiTrackingCommon.h', 'AntiTrackingIPCUtils.h', + 'AntiTrackingRedirectHeuristic.h', 'AntiTrackingUtils.h', 'ContentBlockingAllowList.h', 'ContentBlockingNotifier.h', @@ -43,6 +44,7 @@ EXPORTS.mozilla = [ UNIFIED_SOURCES += [ 'AntiTrackingCommon.cpp', + 'AntiTrackingRedirectHeuristic.cpp', 'AntiTrackingUtils.cpp', 'ContentBlockingAllowList.cpp', 'ContentBlockingNotifier.cpp',