nsContentPolicy.cpp (6143B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 // vim: ft=cpp tw=80 sw=2 et ts=8 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Implementation of the "@mozilla.org/layout/content-policy;1" contract. 9 */ 10 11 #include "nsContentPolicy.h" 12 13 #include "mozilla/Logging.h" 14 #include "mozilla/dom/PolicyContainer.h" 15 #include "mozilla/dom/nsCSPService.h" 16 #include "mozilla/dom/nsMixedContentBlocker.h" 17 #include "nsCOMArray.h" 18 #include "nsContentPolicyUtils.h" 19 #include "nsContentUtils.h" 20 #include "nsIBrowserChild.h" 21 #include "nsIContent.h" 22 #include "nsIContentSecurityPolicy.h" 23 #include "nsIImageLoadingContent.h" 24 #include "nsISupports.h" 25 #include "nsIURI.h" 26 #include "nsXPCOM.h" 27 28 class nsIDOMWindow; 29 30 using mozilla::LogLevel; 31 32 NS_IMPL_ISUPPORTS(nsContentPolicy, nsIContentPolicy) 33 34 static mozilla::LazyLogModule gConPolLog("nsContentPolicy"); 35 36 nsresult NS_NewContentPolicy(nsIContentPolicy** aResult) { 37 *aResult = new nsContentPolicy; 38 NS_ADDREF(*aResult); 39 return NS_OK; 40 } 41 42 nsContentPolicy::nsContentPolicy() : mPolicies(NS_CONTENTPOLICY_CATEGORY) {} 43 44 nsContentPolicy::~nsContentPolicy() = default; 45 46 #ifdef DEBUG 47 # define WARN_IF_URI_UNINITIALIZED(uri, name) \ 48 PR_BEGIN_MACRO \ 49 if ((uri)) { \ 50 nsAutoCString spec; \ 51 (uri)->GetAsciiSpec(spec); \ 52 if (spec.IsEmpty()) { \ 53 NS_WARNING(name " is uninitialized, fix caller"); \ 54 } \ 55 } \ 56 PR_END_MACRO 57 58 #else // ! defined(DEBUG) 59 60 # define WARN_IF_URI_UNINITIALIZED(uri, name) 61 62 #endif // defined(DEBUG) 63 64 inline nsresult nsContentPolicy::CheckPolicy(CPMethod policyMethod, 65 nsIURI* contentLocation, 66 nsILoadInfo* loadInfo, 67 int16_t* decision) { 68 nsCOMPtr<nsISupports> requestingContext = loadInfo->GetLoadingContext(); 69 // sanity-check passed-through parameters 70 MOZ_ASSERT(decision, "Null out pointer"); 71 WARN_IF_URI_UNINITIALIZED(contentLocation, "Request URI"); 72 73 #ifdef DEBUG 74 { 75 nsCOMPtr<nsINode> node(do_QueryInterface(requestingContext)); 76 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(requestingContext)); 77 nsCOMPtr<nsIBrowserChild> browserChild( 78 do_QueryInterface(requestingContext)); 79 NS_ASSERTION(!requestingContext || node || window || browserChild, 80 "Context should be a DOM node, DOM window or a browserChild!"); 81 } 82 #endif 83 84 nsCOMPtr<mozilla::dom::Document> doc; 85 nsCOMPtr<nsIContent> node = do_QueryInterface(requestingContext); 86 if (node) { 87 doc = node->OwnerDoc(); 88 } 89 if (!doc) { 90 doc = do_QueryInterface(requestingContext); 91 } 92 93 /* 94 * Enumerate mPolicies and ask each of them, taking the logical AND of 95 * their permissions. 96 */ 97 nsresult rv; 98 const nsCOMArray<nsIContentPolicy>& entries = mPolicies.GetCachedEntries(); 99 if (doc) { 100 if (nsCOMPtr<nsIContentSecurityPolicy> csp = 101 PolicyContainer::GetCSP(doc->GetPolicyContainer())) { 102 csp->EnsureEventTarget(mozilla::GetMainThreadSerialEventTarget()); 103 } 104 } 105 106 int32_t count = entries.Count(); 107 for (int32_t i = 0; i < count; i++) { 108 /* check the appropriate policy */ 109 rv = (entries[i]->*policyMethod)(contentLocation, loadInfo, decision); 110 111 if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) { 112 /* policy says no, no point continuing to check */ 113 return NS_OK; 114 } 115 } 116 117 // everyone returned failure, or no policies: sanitize result 118 *decision = nsIContentPolicy::ACCEPT; 119 return NS_OK; 120 } 121 122 // uses the parameters from ShouldXYZ to produce and log a message 123 // logType must be a literal string constant 124 #define LOG_CHECK(logType) \ 125 PR_BEGIN_MACRO \ 126 /* skip all this nonsense if the call failed or logging is disabled */ \ 127 if (NS_SUCCEEDED(rv) && MOZ_LOG_TEST(gConPolLog, LogLevel::Debug)) { \ 128 const char* resultName; \ 129 if (decision) { \ 130 resultName = NS_CP_ResponseName(*decision); \ 131 } else { \ 132 resultName = "(null ptr)"; \ 133 } \ 134 MOZ_LOG( \ 135 gConPolLog, LogLevel::Debug, \ 136 ("Content Policy: " logType ": <%s> result=%s", \ 137 contentLocation ? contentLocation->GetSpecOrDefault().get() : "None", \ 138 resultName)); \ 139 } \ 140 PR_END_MACRO 141 142 NS_IMETHODIMP 143 nsContentPolicy::ShouldLoad(nsIURI* contentLocation, nsILoadInfo* loadInfo, 144 int16_t* decision) { 145 // ShouldProcess does not need a content location, but we do 146 MOZ_ASSERT(contentLocation, "Must provide request location"); 147 nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldLoad, contentLocation, 148 loadInfo, decision); 149 LOG_CHECK("ShouldLoad"); 150 151 return rv; 152 } 153 154 NS_IMETHODIMP 155 nsContentPolicy::ShouldProcess(nsIURI* contentLocation, nsILoadInfo* loadInfo, 156 int16_t* decision) { 157 nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldProcess, contentLocation, 158 loadInfo, decision); 159 LOG_CHECK("ShouldProcess"); 160 161 return rv; 162 }