From 35abf466883b0452097febcd8679bdf21a47a37b Mon Sep 17 00:00:00 2001 From: Manuel Bucher Date: Wed, 8 Jun 2022 14:33:10 +0000 Subject: [PATCH] Bug 1761242 - Expose computing security flags for early hint preloader r=ckerschb,smaug Differential Revision: https://phabricator.services.mozilla.com/D144798 --- dom/script/ModuleLoader.cpp | 27 +++++++--------- dom/script/ScriptLoader.cpp | 12 +++---- dom/security/nsContentSecurityManager.cpp | 38 +++++++++++++++++++++++ dom/security/nsContentSecurityManager.h | 29 +++++++++++++++++ image/imgLoader.cpp | 13 +++----- layout/style/FontPreloader.cpp | 15 +++++---- layout/style/Loader.cpp | 24 +++++++------- uriloader/preload/FetchPreloader.cpp | 13 +++----- 8 files changed, 115 insertions(+), 56 deletions(-) diff --git a/dom/script/ModuleLoader.cpp b/dom/script/ModuleLoader.cpp index 5aa83ea94e62..964a1cc8c531 100644 --- a/dom/script/ModuleLoader.cpp +++ b/dom/script/ModuleLoader.cpp @@ -23,6 +23,7 @@ #include "js/loader/ModuleLoadRequest.h" #include "xpcpublic.h" #include "GeckoProfiler.h" +#include "nsContentSecurityManager.h" #include "nsIContent.h" #include "nsJSUtils.h" #include "mozilla/dom/AutoEntryScript.h" @@ -88,24 +89,20 @@ bool ModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest, nsresult* aRvOut) { } nsresult ModuleLoader::StartFetch(ModuleLoadRequest* aRequest) { - nsSecurityFlags securityFlags; - // According to the spec, module scripts have different behaviour to classic // scripts and always use CORS. Only exception: Non linkable about: pages // which load local module scripts. - if (GetScriptLoader()->IsAboutPageLoadingChromeURI( - aRequest, GetScriptLoader()->GetDocument())) { - securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; - } else { - securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; - if (aRequest->CORSMode() == CORS_NONE || - aRequest->CORSMode() == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else { - MOZ_ASSERT(aRequest->CORSMode() == CORS_USE_CREDENTIALS); - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; - } - } + bool isAboutPageLoadingChromeURI = ScriptLoader::IsAboutPageLoadingChromeURI( + aRequest, GetScriptLoader()->GetDocument()); + + nsContentSecurityManager::CORSSecurityMapping corsMapping = + isAboutPageLoadingChromeURI + ? nsContentSecurityManager::CORSSecurityMapping::DISABLE_CORS_CHECKS + : nsContentSecurityManager::CORSSecurityMapping::REQUIRE_CORS_CHECKS; + + nsSecurityFlags securityFlags = + nsContentSecurityManager::ComputeSecurityFlags(aRequest->CORSMode(), + corsMapping); securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index a554d9d8e89f..f4c1b2971b70 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -32,6 +32,7 @@ #include "js/Utility.h" #include "xpcpublic.h" #include "GeckoProfiler.h" +#include "nsContentSecurityManager.h" #include "nsCycleCollectionParticipant.h" #include "nsIContent.h" #include "nsJSUtils.h" @@ -556,14 +557,9 @@ nsresult ScriptLoader::StartClassicLoad(ScriptLoadRequest* aRequest) { } nsSecurityFlags securityFlags = - aRequest->CORSMode() == CORS_NONE - ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL - : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; - if (aRequest->CORSMode() == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else if (aRequest->CORSMode() == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; - } + nsContentSecurityManager::ComputeSecurityFlags( + aRequest->CORSMode(), nsContentSecurityManager::CORSSecurityMapping:: + CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS); securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index ae1bcbc3c113..f29992482de6 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -992,6 +992,44 @@ void nsContentSecurityManager::MeasureUnexpectedPrivilegedLoads( extra); } +/* static */ +nsSecurityFlags nsContentSecurityManager::ComputeSecurityFlags( + mozilla::CORSMode aCORSMode, CORSSecurityMapping aCORSSecurityMapping) { + if (aCORSSecurityMapping == CORSSecurityMapping::DISABLE_CORS_CHECKS) { + return nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; + } + + switch (aCORSMode) { + case CORS_NONE: + if (aCORSSecurityMapping == CORSSecurityMapping::REQUIRE_CORS_CHECKS) { + // CORS_NONE gets treated like CORS_ANONYMOUS in this mode + return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | + nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; + } else if (aCORSSecurityMapping == + CORSSecurityMapping::CORS_NONE_MAPS_TO_INHERITED_CONTEXT) { + // CORS_NONE inherits + return nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT; + } else { + // CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS, the only remaining enum + // variant. CORSSecurityMapping::DISABLE_CORS_CHECKS returned early. + MOZ_ASSERT(aCORSSecurityMapping == + CORSSecurityMapping::CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS); + return nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; + } + case CORS_ANONYMOUS: + return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | + nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; + case CORS_USE_CREDENTIALS: + return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | + nsILoadInfo::SEC_COOKIES_INCLUDE; + break; + default: + MOZ_ASSERT_UNREACHABLE("Invalid aCORSMode enum value"); + return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | + nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; + } +} + /* static */ nsresult nsContentSecurityManager::CheckAllowLoadInSystemPrivilegedContext( nsIChannel* aChannel) { diff --git a/dom/security/nsContentSecurityManager.h b/dom/security/nsContentSecurityManager.h index 3aa5e6f1a92d..4226101ca0fb 100644 --- a/dom/security/nsContentSecurityManager.h +++ b/dom/security/nsContentSecurityManager.h @@ -7,9 +7,11 @@ #ifndef nsContentSecurityManager_h___ #define nsContentSecurityManager_h___ +#include "mozilla/CORSMode.h" #include "nsIContentSecurityManager.h" #include "nsIChannel.h" #include "nsIChannelEventSink.h" +#include "nsILoadInfo.h" class nsILoadInfo; class nsIStreamListener; @@ -42,6 +44,33 @@ class nsContentSecurityManager : public nsIContentSecurityManager, nsIURI* aFinalURI, const nsACString& aRemoteType); + enum CORSSecurityMapping { + // Disables all CORS checking overriding the value of aCORSMode. All checks + // are disabled even when CORSMode::CORS_ANONYMOUS or + // CORSMode::CORS_USE_CREDENTIALS is passed. This is mostly used for chrome + // code, where we don't need security checks. See + // SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL for the detailed explanation + // of the security mode. + DISABLE_CORS_CHECKS, + // Disables all CORS checking on CORSMode::CORS_NONE. The other two CORS + // modes CORSMode::CORS_ANONYMOUS and CORSMode::CORS_USE_CREDENTIALS are + // respected. + CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS, + // Allow load from any origin, but cross-origin requests require CORS. See + // SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT. Like above the other two + // CORS modes are unaffected and get parsed. + CORS_NONE_MAPS_TO_INHERITED_CONTEXT, + // Always require the server to acknowledge the request via CORS. + // CORSMode::CORS_NONE is parsed as if CORSMode::CORS_ANONYMOUS is passed. + REQUIRE_CORS_CHECKS, + }; + + // computes the security flags for the requested CORS mode + // @param aCORSSecurityMapping: See CORSSecurityMapping for variant + // descriptions + static nsSecurityFlags ComputeSecurityFlags( + mozilla::CORSMode aCORSMode, CORSSecurityMapping aCORSSecurityMapping); + private: static nsresult CheckChannel(nsIChannel* aChannel); static nsresult CheckFTPSubresourceLoad(nsIChannel* aChannel); diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp index 6f8090713442..6b42b4caa683 100644 --- a/image/imgLoader.cpp +++ b/image/imgLoader.cpp @@ -37,6 +37,7 @@ #include "nsCRT.h" #include "nsComponentManagerUtils.h" #include "nsContentPolicyUtils.h" +#include "nsContentSecurityManager.h" #include "nsContentUtils.h" #include "nsHttpChannel.h" #include "nsIAsyncVerifyRedirectCallback.h" @@ -860,14 +861,10 @@ static nsresult NewImageChannel( // nsSecurityFlags securityFlags = - aCORSMode == CORS_NONE - ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT - : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; - if (aCORSMode == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else if (aCORSMode == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; - } + nsContentSecurityManager::ComputeSecurityFlags( + aCORSMode, nsContentSecurityManager::CORSSecurityMapping:: + CORS_NONE_MAPS_TO_INHERITED_CONTEXT); + securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; // Note we are calling NS_NewChannelWithTriggeringPrincipal() here with a diff --git a/layout/style/FontPreloader.cpp b/layout/style/FontPreloader.cpp index a36fd3b4921d..05e35e393f24 100644 --- a/layout/style/FontPreloader.cpp +++ b/layout/style/FontPreloader.cpp @@ -8,6 +8,7 @@ #include "gfxUserFontSet.h" #include "mozilla/dom/Document.h" +#include "nsContentSecurityManager.h" #include "nsIClassOfService.h" #include "nsIHttpChannel.h" #include "nsISupportsPriority.h" @@ -53,13 +54,15 @@ nsresult FontPreloader::BuildChannel( // aCORSMode is ignored. We always load as crossorigin=anonymous, but a // preload started with anything other then "anonymous" will never be found. + nsContentSecurityManager::CORSSecurityMapping corsMapping = + aURI->SchemeIs("file") + ? nsContentSecurityManager::CORSSecurityMapping:: + CORS_NONE_MAPS_TO_INHERITED_CONTEXT + : nsContentSecurityManager::CORSSecurityMapping::REQUIRE_CORS_CHECKS; - uint32_t securityFlags = 0; - if (aURI->SchemeIs("file")) { - securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT; - } else { - securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; - } + nsSecurityFlags securityFlags = + nsContentSecurityManager::ComputeSecurityFlags(CORSMode::CORS_NONE, + corsMapping); nsContentPolicyType contentPolicyType = aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index f7a8296b6a90..01d2d36a73e4 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -25,6 +25,7 @@ #include "nsITimedChannel.h" #include "nsICachingChannel.h" #include "nsSyncLoadService.h" +#include "nsContentSecurityManager.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsIContent.h" @@ -1230,9 +1231,14 @@ nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState, mDocument); } + // Synchronous loads should only be used internally. Therefore no CORS + // policy is needed. nsSecurityFlags securityFlags = - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT | - nsILoadInfo::SEC_ALLOW_CHROME; + nsContentSecurityManager::ComputeSecurityFlags( + CORSMode::CORS_NONE, nsContentSecurityManager::CORSSecurityMapping:: + CORS_NONE_MAPS_TO_INHERITED_CONTEXT); + + securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; nsContentPolicyType contentPolicyType = aLoadData.mPreloadKind == StylePreloadKind::None @@ -1376,16 +1382,12 @@ nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState, mSyncCallback = true; #endif - CORSMode ourCORSMode = aLoadData.mSheet->GetCORSMode(); nsSecurityFlags securityFlags = - ourCORSMode == CORS_NONE - ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT - : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; - if (ourCORSMode == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else if (ourCORSMode == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; - } + nsContentSecurityManager::ComputeSecurityFlags( + aLoadData.mSheet->GetCORSMode(), + nsContentSecurityManager::CORSSecurityMapping:: + CORS_NONE_MAPS_TO_INHERITED_CONTEXT); + securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; nsContentPolicyType contentPolicyType = diff --git a/uriloader/preload/FetchPreloader.cpp b/uriloader/preload/FetchPreloader.cpp index 411ca266fc90..5ec37cdcb07e 100644 --- a/uriloader/preload/FetchPreloader.cpp +++ b/uriloader/preload/FetchPreloader.cpp @@ -7,12 +7,14 @@ #include "FetchPreloader.h" +#include "mozilla/CORSMode.h" #include "mozilla/DebugOnly.h" #include "mozilla/dom/Document.h" #include "mozilla/LoadInfo.h" #include "mozilla/ScopeExit.h" #include "mozilla/Unused.h" #include "nsContentPolicyUtils.h" +#include "nsContentSecurityManager.h" #include "nsContentUtils.h" #include "nsIChannel.h" #include "nsIClassOfService.h" @@ -86,14 +88,9 @@ nsresult FetchPreloader::CreateChannel( nsresult rv; nsSecurityFlags securityFlags = - aCORSMode == CORS_NONE - ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL - : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; - if (aCORSMode == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else if (aCORSMode == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; - } + nsContentSecurityManager::ComputeSecurityFlags( + aCORSMode, nsContentSecurityManager::CORSSecurityMapping:: + CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS); nsCOMPtr channel; rv = NS_NewChannelWithTriggeringPrincipal(