/* -*- 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 "ThirdPartyUtil.h" #include "nsDocShell.h" #include "nsGlobalWindowOuter.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsIChannel.h" #include "nsIClassifiedChannel.h" #include "nsIHttpChannelInternal.h" #include "nsILoadContext.h" #include "nsIPrincipal.h" #include "nsIScriptObjectPrincipal.h" #include "nsIURI.h" #include "nsReadableUtils.h" #include "nsThreadUtils.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/ContentBlocking.h" #include "mozilla/ContentBlockingAllowList.h" #include "mozilla/dom/Document.h" #include "mozilla/Logging.h" #include "mozilla/StaticPtr.h" #include "mozilla/TextUtils.h" #include "mozilla/Unused.h" #include "nsGlobalWindowOuter.h" using namespace mozilla; using namespace mozilla::dom; NS_IMPL_ISUPPORTS(ThirdPartyUtil, mozIThirdPartyUtil) // // MOZ_LOG=thirdPartyUtil:5 // static mozilla::LazyLogModule gThirdPartyLog("thirdPartyUtil"); #undef LOG #define LOG(args) MOZ_LOG(gThirdPartyLog, mozilla::LogLevel::Debug, args) static mozilla::StaticRefPtr gService; // static void ThirdPartyUtil::Startup() { nsCOMPtr tpu; if (NS_WARN_IF(!(tpu = do_GetService(THIRDPARTYUTIL_CONTRACTID)))) { NS_WARNING("Failed to get third party util!"); } } nsresult ThirdPartyUtil::Init() { NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE); MOZ_ASSERT(!gService); gService = this; mozilla::ClearOnShutdown(&gService); mTLDService = nsEffectiveTLDService::GetInstance(); return mTLDService ? NS_OK : NS_ERROR_FAILURE; } ThirdPartyUtil::~ThirdPartyUtil() { gService = nullptr; } // static ThirdPartyUtil* ThirdPartyUtil::GetInstance() { if (gService) { return gService; } nsCOMPtr tpuService = mozilla::services::GetThirdPartyUtil(); if (!tpuService) { return nullptr; } MOZ_ASSERT( gService, "gService must have been initialized in nsEffectiveTLDService::Init"); return gService; } // Determine if aFirstDomain is a different base domain to aSecondURI; or, if // the concept of base domain does not apply, determine if the two hosts are not // string-identical. nsresult ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain, nsIURI* aSecondURI, bool* aResult) { if (!aSecondURI) { return NS_ERROR_INVALID_ARG; } // BlobURLs are always first-party. if (aSecondURI->SchemeIs("blob")) { *aResult = false; return NS_OK; } // Get the base domain for aSecondURI. nsAutoCString secondDomain; nsresult rv = GetBaseDomain(aSecondURI, secondDomain); LOG(("ThirdPartyUtil::IsThirdPartyInternal %s =? %s", aFirstDomain.get(), secondDomain.get())); if (NS_FAILED(rv)) return rv; *aResult = IsThirdPartyInternal(aFirstDomain, secondDomain); return NS_OK; } nsCString ThirdPartyUtil::GetBaseDomainFromWindow(nsPIDOMWindowOuter* aWindow) { mozilla::dom::Document* doc = aWindow ? aWindow->GetExtantDoc() : nullptr; if (!doc) { return EmptyCString(); } return doc->GetBaseDomain(); } NS_IMETHODIMP ThirdPartyUtil::GetPrincipalFromWindow(mozIDOMWindowProxy* aWin, nsIPrincipal** result) { nsCOMPtr scriptObjPrin = do_QueryInterface(aWin); if (!scriptObjPrin) { return NS_ERROR_INVALID_ARG; } nsCOMPtr prin = scriptObjPrin->GetPrincipal(); if (!prin) { return NS_ERROR_INVALID_ARG; } prin.forget(result); return NS_OK; } // Get the URI associated with a window. NS_IMETHODIMP ThirdPartyUtil::GetURIFromWindow(mozIDOMWindowProxy* aWin, nsIURI** result) { nsCOMPtr prin; nsresult rv = GetPrincipalFromWindow(aWin, getter_AddRefs(prin)); if (NS_FAILED(rv)) { return rv; } if (prin->GetIsNullPrincipal()) { LOG(("ThirdPartyUtil::GetURIFromWindow can't use null principal\n")); return NS_ERROR_INVALID_ARG; } auto* basePrin = BasePrincipal::Cast(prin); return basePrin->GetURI(result); } NS_IMETHODIMP ThirdPartyUtil::GetContentBlockingAllowListPrincipalFromWindow( mozIDOMWindowProxy* aWin, nsIURI* aURIBeingLoaded, nsIPrincipal** result) { nsPIDOMWindowOuter* outerWindow = nsPIDOMWindowOuter::From(aWin); nsPIDOMWindowInner* innerWindow = outerWindow->GetCurrentInnerWindow(); Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr; if (!doc) { return GetPrincipalFromWindow(aWin, result); } nsCOMPtr principal = doc->GetContentBlockingAllowListPrincipal(); if (aURIBeingLoaded && principal && principal->GetIsNullPrincipal()) { // If we have an initial principal during navigation, recompute it to get // the real content blocking allow list principal. nsIDocShell* docShell = doc->GetDocShell(); OriginAttributes attrs = docShell ? nsDocShell::Cast(docShell)->GetOriginAttributes() : OriginAttributes(); ContentBlockingAllowList::RecomputePrincipal(aURIBeingLoaded, attrs, getter_AddRefs(principal)); } if (!principal || !principal->GetIsContentPrincipal()) { // This is for compatibility with GetURIFromWindow. Null principals are // explicitly special cased there. GetURI returns nullptr for // SystemPrincipal and ExpandedPrincipal. LOG( ("ThirdPartyUtil::GetContentBlockingAllowListPrincipalFromWindow can't " "use null principal\n")); return NS_ERROR_INVALID_ARG; } principal.forget(result); return NS_OK; } // Determine if aFirstURI is third party with respect to aSecondURI. See docs // for mozIThirdPartyUtil. NS_IMETHODIMP ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI, nsIURI* aSecondURI, bool* aResult) { NS_ENSURE_ARG(aFirstURI); NS_ENSURE_ARG(aSecondURI); NS_ASSERTION(aResult, "null outparam pointer"); nsAutoCString firstHost; nsresult rv = GetBaseDomain(aFirstURI, firstHost); if (NS_FAILED(rv)) return rv; return IsThirdPartyInternal(firstHost, aSecondURI, aResult); } // If the optional aURI is provided, determine whether aWindow is foreign with // respect to aURI. If the optional aURI is not provided, determine whether the // given "window hierarchy" is third party. See docs for mozIThirdPartyUtil. NS_IMETHODIMP ThirdPartyUtil::IsThirdPartyWindow(mozIDOMWindowProxy* aWindow, nsIURI* aURI, bool* aResult) { NS_ENSURE_ARG(aWindow); NS_ASSERTION(aResult, "null outparam pointer"); bool result; // Ignore about:blank URIs here since they have no domain and attempting to // compare against them will fail. if (aURI && !NS_IsAboutBlank(aURI)) { nsCOMPtr prin; nsresult rv = GetPrincipalFromWindow(aWindow, getter_AddRefs(prin)); NS_ENSURE_SUCCESS(rv, rv); // Determine whether aURI is foreign with respect to the current principal. rv = prin->IsThirdPartyURI(aURI, &result); if (NS_FAILED(rv)) { return rv; } if (result) { *aResult = true; return NS_OK; } } nsPIDOMWindowOuter* current = nsPIDOMWindowOuter::From(aWindow); do { // We use GetInProcessScriptableParent rather than GetParent because we // consider