/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ // vim: ft=cpp tw=80 sw=2 et ts=8 /* 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/. */ /* * Implementation of the "@mozilla.org/layout/content-policy;1" contract. */ #include "mozilla/Logging.h" #include "nsISupports.h" #include "nsXPCOM.h" #include "nsContentPolicyUtils.h" #include "mozilla/dom/nsCSPService.h" #include "nsContentPolicy.h" #include "nsIURI.h" #include "nsIBrowserChild.h" #include "nsIContent.h" #include "nsIImageLoadingContent.h" #include "nsCOMArray.h" #include "nsContentUtils.h" #include "mozilla/dom/nsMixedContentBlocker.h" #include "nsIContentSecurityPolicy.h" #include "mozilla/TaskCategory.h" class nsIDOMWindow; using mozilla::LogLevel; NS_IMPL_ISUPPORTS(nsContentPolicy, nsIContentPolicy) static mozilla::LazyLogModule gConPolLog("nsContentPolicy"); nsresult NS_NewContentPolicy(nsIContentPolicy** aResult) { *aResult = new nsContentPolicy; NS_ADDREF(*aResult); return NS_OK; } nsContentPolicy::nsContentPolicy() : mPolicies(NS_CONTENTPOLICY_CATEGORY) {} nsContentPolicy::~nsContentPolicy() = default; #ifdef DEBUG # define WARN_IF_URI_UNINITIALIZED(uri, name) \ PR_BEGIN_MACRO \ if ((uri)) { \ nsAutoCString spec; \ (uri)->GetAsciiSpec(spec); \ if (spec.IsEmpty()) { \ NS_WARNING(name " is uninitialized, fix caller"); \ } \ } \ PR_END_MACRO #else // ! defined(DEBUG) # define WARN_IF_URI_UNINITIALIZED(uri, name) #endif // defined(DEBUG) inline nsresult nsContentPolicy::CheckPolicy(CPMethod policyMethod, nsIURI* contentLocation, nsILoadInfo* loadInfo, const nsACString& mimeType, int16_t* decision) { nsContentPolicyType contentType = loadInfo->InternalContentPolicyType(); nsCOMPtr requestingContext = loadInfo->GetLoadingContext(); // sanity-check passed-through parameters MOZ_ASSERT(decision, "Null out pointer"); WARN_IF_URI_UNINITIALIZED(contentLocation, "Request URI"); #ifdef DEBUG { nsCOMPtr node(do_QueryInterface(requestingContext)); nsCOMPtr window(do_QueryInterface(requestingContext)); nsCOMPtr browserChild( do_QueryInterface(requestingContext)); NS_ASSERTION(!requestingContext || node || window || browserChild, "Context should be a DOM node, DOM window or a browserChild!"); } #endif nsCOMPtr doc; nsCOMPtr node = do_QueryInterface(requestingContext); if (node) { doc = node->OwnerDoc(); } if (!doc) { doc = do_QueryInterface(requestingContext); } nsContentPolicyType externalType = nsContentUtils::InternalContentPolicyTypeToExternal(contentType); /* * Enumerate mPolicies and ask each of them, taking the logical AND of * their permissions. */ nsresult rv; const nsCOMArray& entries = mPolicies.GetCachedEntries(); nsCOMPtr window; if (nsCOMPtr node = do_QueryInterface(requestingContext)) { window = node->OwnerDoc()->GetWindow(); } else { window = do_QueryInterface(requestingContext); } if (doc) { nsCOMPtr csp = doc->GetCsp(); if (csp && window) { csp->EnsureEventTarget( window->EventTargetFor(mozilla::TaskCategory::Other)); } } int32_t count = entries.Count(); for (int32_t i = 0; i < count; i++) { /* check the appropriate policy */ rv = (entries[i]->*policyMethod)(contentLocation, loadInfo, mimeType, decision); if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) { // If we are blocking an image, we have to let the // ImageLoadingContent know that we blocked the load. if (externalType == nsIContentPolicy::TYPE_IMAGE || externalType == nsIContentPolicy::TYPE_IMAGESET) { nsCOMPtr img = do_QueryInterface(requestingContext); if (img) { img->SetBlockedRequest(*decision); } } /* policy says no, no point continuing to check */ return NS_OK; } } // everyone returned failure, or no policies: sanitize result *decision = nsIContentPolicy::ACCEPT; return NS_OK; } // uses the parameters from ShouldXYZ to produce and log a message // logType must be a literal string constant #define LOG_CHECK(logType) \ PR_BEGIN_MACRO \ /* skip all this nonsense if the call failed or logging is disabled */ \ if (NS_SUCCEEDED(rv) && MOZ_LOG_TEST(gConPolLog, LogLevel::Debug)) { \ const char* resultName; \ if (decision) { \ resultName = NS_CP_ResponseName(*decision); \ } else { \ resultName = "(null ptr)"; \ } \ MOZ_LOG( \ gConPolLog, LogLevel::Debug, \ ("Content Policy: " logType ": <%s> result=%s", \ contentLocation ? contentLocation->GetSpecOrDefault().get() : "None", \ resultName)); \ } \ PR_END_MACRO NS_IMETHODIMP nsContentPolicy::ShouldLoad(nsIURI* contentLocation, nsILoadInfo* loadInfo, const nsACString& mimeType, int16_t* decision) { // ShouldProcess does not need a content location, but we do MOZ_ASSERT(contentLocation, "Must provide request location"); nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldLoad, contentLocation, loadInfo, mimeType, decision); LOG_CHECK("ShouldLoad"); return rv; } NS_IMETHODIMP nsContentPolicy::ShouldProcess(nsIURI* contentLocation, nsILoadInfo* loadInfo, const nsACString& mimeType, int16_t* decision) { nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldProcess, contentLocation, loadInfo, mimeType, decision); LOG_CHECK("ShouldProcess"); return rv; }