nsContentPolicyUtils.h (13095B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 * Utility routines for checking content load/process policy settings, 8 * and routines helpful for content policy implementors. 9 * 10 * XXXbz it would be nice if some of this stuff could be out-of-lined in 11 * nsContentUtils. That would work for almost all the callers... 12 */ 13 14 #ifndef __nsContentPolicyUtils_h__ 15 #define __nsContentPolicyUtils_h__ 16 17 #include "mozilla/BasePrincipal.h" 18 #include "mozilla/dom/nsCSPService.h" 19 #include "nsContentUtils.h" 20 #include "nsIContent.h" 21 #include "nsIContentPolicy.h" 22 #include "nsIURI.h" 23 #include "nsServiceManagerUtils.h" 24 #include "nsStringFwd.h" 25 26 // XXXtw sadly, this makes consumers of nsContentPolicyUtils depend on widget 27 #include "mozilla/dom/Document.h" 28 #include "nsPIDOMWindow.h" 29 30 #define NS_CONTENTPOLICY_CONTRACTID "@mozilla.org/layout/content-policy;1" 31 #define NS_CONTENTPOLICY_CATEGORY "content-policy" 32 #define NS_CONTENTPOLICY_CID \ 33 {0x0e3afd3d, 0xeb60, 0x4c2b, {0x96, 0x3b, 0x56, 0xd7, 0xc4, 0x39, 0xf1, 0x24}} 34 35 /** 36 * Evaluates to true if val is ACCEPT. 37 * 38 * @param val the status returned from shouldProcess/shouldLoad 39 */ 40 #define NS_CP_ACCEPTED(val) ((val) == nsIContentPolicy::ACCEPT) 41 42 /** 43 * Evaluates to true if val is a REJECT_* status 44 * 45 * @param val the status returned from shouldProcess/shouldLoad 46 */ 47 #define NS_CP_REJECTED(val) ((val) != nsIContentPolicy::ACCEPT) 48 49 // Offer convenient translations of constants -> const char* 50 51 // convenience macro to reduce some repetative typing... 52 // name is the name of a constant from this interface 53 #define CASE_RETURN(name) \ 54 case nsIContentPolicy::name: \ 55 return #name 56 57 /** 58 * Returns a string corresponding to the name of the response constant, or 59 * "<Unknown Response>" if an unknown response value is given. 60 * 61 * The return value is static and must not be freed. 62 * 63 * @param response the response code 64 * @return the name of the given response code 65 */ 66 inline const char* NS_CP_ResponseName(int16_t response) { 67 switch (response) { 68 CASE_RETURN(REJECT_REQUEST); 69 CASE_RETURN(REJECT_TYPE); 70 CASE_RETURN(REJECT_SERVER); 71 CASE_RETURN(REJECT_OTHER); 72 CASE_RETURN(ACCEPT); 73 default: 74 return "<Unknown Response>"; 75 } 76 } 77 78 /** 79 * Returns a string corresponding to the name of the content type constant, or 80 * "<Unknown Type>" if an unknown content type value is given. 81 * 82 * The return value is static and must not be freed. 83 * 84 * @param contentType the content type code 85 * @return the name of the given content type code 86 */ 87 inline const char* NS_CP_ContentTypeName(nsContentPolicyType contentType) { 88 switch (contentType) { 89 CASE_RETURN(TYPE_OTHER); 90 CASE_RETURN(TYPE_SCRIPT); 91 CASE_RETURN(TYPE_IMAGE); 92 CASE_RETURN(TYPE_STYLESHEET); 93 CASE_RETURN(TYPE_OBJECT); 94 CASE_RETURN(TYPE_DOCUMENT); 95 CASE_RETURN(TYPE_SUBDOCUMENT); 96 CASE_RETURN(TYPE_PING); 97 CASE_RETURN(TYPE_XMLHTTPREQUEST); 98 CASE_RETURN(TYPE_DTD); 99 CASE_RETURN(TYPE_FONT); 100 CASE_RETURN(TYPE_MEDIA); 101 CASE_RETURN(TYPE_WEBSOCKET); 102 CASE_RETURN(TYPE_CSP_REPORT); 103 CASE_RETURN(TYPE_XSLT); 104 CASE_RETURN(TYPE_BEACON); 105 CASE_RETURN(TYPE_FETCH); 106 CASE_RETURN(TYPE_IMAGESET); 107 CASE_RETURN(TYPE_WEB_MANIFEST); 108 CASE_RETURN(TYPE_INTERNAL_SCRIPT); 109 CASE_RETURN(TYPE_INTERNAL_WORKER); 110 CASE_RETURN(TYPE_INTERNAL_SHARED_WORKER); 111 CASE_RETURN(TYPE_INTERNAL_EMBED); 112 CASE_RETURN(TYPE_INTERNAL_OBJECT); 113 CASE_RETURN(TYPE_INTERNAL_FRAME); 114 CASE_RETURN(TYPE_INTERNAL_IFRAME); 115 CASE_RETURN(TYPE_INTERNAL_AUDIO); 116 CASE_RETURN(TYPE_INTERNAL_VIDEO); 117 CASE_RETURN(TYPE_INTERNAL_TRACK); 118 CASE_RETURN(TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC); 119 CASE_RETURN(TYPE_INTERNAL_EVENTSOURCE); 120 CASE_RETURN(TYPE_INTERNAL_SERVICE_WORKER); 121 CASE_RETURN(TYPE_INTERNAL_SCRIPT_PRELOAD); 122 CASE_RETURN(TYPE_INTERNAL_IMAGE); 123 CASE_RETURN(TYPE_INTERNAL_IMAGE_PRELOAD); 124 CASE_RETURN(TYPE_INTERNAL_IMAGE_FAVICON); 125 CASE_RETURN(TYPE_INTERNAL_STYLESHEET); 126 CASE_RETURN(TYPE_INTERNAL_STYLESHEET_PRELOAD); 127 CASE_RETURN(TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS); 128 CASE_RETURN(TYPE_SAVEAS_DOWNLOAD); 129 CASE_RETURN(TYPE_SPECULATIVE); 130 CASE_RETURN(TYPE_INTERNAL_MODULE); 131 CASE_RETURN(TYPE_INTERNAL_MODULE_PRELOAD); 132 CASE_RETURN(TYPE_INTERNAL_DTD); 133 CASE_RETURN(TYPE_INTERNAL_FORCE_ALLOWED_DTD); 134 CASE_RETURN(TYPE_INTERNAL_AUDIOWORKLET); 135 CASE_RETURN(TYPE_INTERNAL_PAINTWORKLET); 136 CASE_RETURN(TYPE_INTERNAL_FONT_PRELOAD); 137 CASE_RETURN(TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT); 138 CASE_RETURN(TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT); 139 CASE_RETURN(TYPE_INTERNAL_FETCH_PRELOAD); 140 CASE_RETURN(TYPE_UA_FONT); 141 CASE_RETURN(TYPE_INTERNAL_WORKER_STATIC_MODULE); 142 CASE_RETURN(TYPE_PROXIED_WEBRTC_MEDIA); 143 CASE_RETURN(TYPE_WEB_IDENTITY); 144 CASE_RETURN(TYPE_WEB_TRANSPORT); 145 CASE_RETURN(TYPE_INTERNAL_XMLHTTPREQUEST_SYNC); 146 CASE_RETURN(TYPE_INTERNAL_EXTERNAL_RESOURCE); 147 CASE_RETURN(TYPE_JSON); 148 CASE_RETURN(TYPE_INTERNAL_JSON_PRELOAD); 149 CASE_RETURN(TYPE_END); 150 case nsIContentPolicy::TYPE_INVALID: 151 break; 152 // Do not add default: so that compilers can catch the missing case. 153 } 154 return "<Unknown Type>"; 155 } 156 157 #undef CASE_RETURN 158 159 inline const char* NS_CP_ContentTypeName(ExtContentPolicyType contentType) { 160 return NS_CP_ContentTypeName(static_cast<nsContentPolicyType>(contentType)); 161 } 162 163 /* Passes on parameters from its "caller"'s context. */ 164 #define CHECK_CONTENT_POLICY(action) \ 165 PR_BEGIN_MACRO \ 166 nsCOMPtr<nsIContentPolicy> policy = \ 167 do_GetService(NS_CONTENTPOLICY_CONTRACTID); \ 168 if (!policy) return NS_ERROR_FAILURE; \ 169 \ 170 return policy->action(contentLocation, loadInfo, decision); \ 171 PR_END_MACRO 172 173 /* Passes on parameters from its "caller"'s context. */ 174 #define CHECK_CONTENT_POLICY_WITH_SERVICE(action, _policy) \ 175 PR_BEGIN_MACRO \ 176 return _policy->action(contentLocation, loadInfo, decision); \ 177 PR_END_MACRO 178 179 /** 180 * Check whether we can short-circuit this check and bail out. If not, get the 181 * origin URI to use. 182 * 183 * Note: requestOrigin is scoped outside the PR_BEGIN_MACRO/PR_END_MACRO on 184 * purpose */ 185 #define CHECK_PRINCIPAL_CSP_AND_DATA(action) \ 186 PR_BEGIN_MACRO \ 187 if (loadingPrincipal && loadingPrincipal->IsSystemPrincipal()) { \ 188 /* We exempt most loads into any document with the system principal \ 189 * from content policy (except CSP) checks, mostly as an optimization. \ 190 * Which means that we need to apply this check to the loading principal, \ 191 * not the principal that triggered the load. */ \ 192 /* Check CSP for System Privileged pages */ \ 193 CSPService::ConsultCSP(contentLocation, loadInfo, decision); \ 194 if (NS_CP_REJECTED(*decision)) { \ 195 return NS_OK; \ 196 } \ 197 if (contentType != nsIContentPolicy::TYPE_DOCUMENT && \ 198 contentType != nsIContentPolicy::TYPE_UA_FONT) { \ 199 *decision = nsIContentPolicy::ACCEPT; \ 200 nsCOMPtr<nsINode> n = do_QueryInterface(context); \ 201 if (!n) { \ 202 nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(context); \ 203 n = win ? win->GetExtantDoc() : nullptr; \ 204 } \ 205 if (n) { \ 206 mozilla::dom::Document* d = n->OwnerDoc(); \ 207 if (d->IsLoadedAsData() || d->IsBeingUsedAsImage() || \ 208 d->IsResourceDoc()) { \ 209 nsCOMPtr<nsIContentPolicy> dataPolicy = \ 210 do_GetService("@mozilla.org/data-document-content-policy;1"); \ 211 if (dataPolicy) { \ 212 dataPolicy->action(contentLocation, loadInfo, decision); \ 213 } \ 214 } \ 215 } \ 216 } \ 217 return NS_OK; \ 218 } \ 219 PR_END_MACRO 220 221 /** 222 * Alias for calling ShouldLoad on the content policy service. Parameters are 223 * the same as nsIContentPolicy::shouldLoad, except for the loadingPrincipal 224 * and triggeringPrincipal parameters (which should be non-null if possible, 225 * and have the same semantics as in nsLoadInfo), and the last parameter, 226 * which can be used to pass in a pointer to a useful service if the caller 227 * already has it. The origin URI to pass to shouldLoad will be the URI of 228 * loadingPrincipal, unless loadingPrincipal is null (in which case a null 229 * origin URI will be passed). 230 */ 231 inline nsresult NS_CheckContentLoadPolicy( 232 nsIURI* contentLocation, nsILoadInfo* loadInfo, int16_t* decision, 233 nsIContentPolicy* policyService = nullptr) { 234 nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal(); 235 nsCOMPtr<nsISupports> context = loadInfo->GetLoadingContext(); 236 nsContentPolicyType contentType = loadInfo->InternalContentPolicyType(); 237 CHECK_PRINCIPAL_CSP_AND_DATA(ShouldLoad); 238 if (policyService) { 239 CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldLoad, policyService); 240 } 241 CHECK_CONTENT_POLICY(ShouldLoad); 242 } 243 244 /** 245 * Alias for calling ShouldProcess on the content policy service. 246 */ 247 inline nsresult NS_CheckContentProcessPolicy( 248 nsIURI* contentLocation, nsILoadInfo* loadInfo, int16_t* decision, 249 nsIContentPolicy* policyService = nullptr) { 250 nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal(); 251 nsCOMPtr<nsISupports> context = loadInfo->GetLoadingContext(); 252 nsContentPolicyType contentType = loadInfo->InternalContentPolicyType(); 253 CHECK_PRINCIPAL_CSP_AND_DATA(ShouldProcess); 254 if (policyService) { 255 CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldProcess, policyService); 256 } 257 CHECK_CONTENT_POLICY(ShouldProcess); 258 } 259 260 #undef CHECK_CONTENT_POLICY 261 #undef CHECK_CONTENT_POLICY_WITH_SERVICE 262 263 /** 264 * Helper function to get an nsIDocShell given a context. 265 * If the context is a document or window, the corresponding docshell will be 266 * returned. 267 * If the context is a non-document DOM node, the docshell of its ownerDocument 268 * will be returned. 269 * 270 * @param aContext the context to find a docshell for (can be null) 271 * 272 * @return a WEAK pointer to the docshell, or nullptr if it could 273 * not be obtained 274 * 275 * @note As of this writing, calls to nsIContentPolicy::Should{Load,Process} 276 * for TYPE_DOCUMENT and TYPE_SUBDOCUMENT pass in an aContext that either 277 * points to the frameElement of the window the load is happening in 278 * (in which case NS_CP_GetDocShellFromContext will return the parent of the 279 * docshell the load is happening in), or points to the window the load is 280 * happening in (in which case NS_CP_GetDocShellFromContext will return 281 * the docshell the load is happening in). It's up to callers to QI aContext 282 * and handle things accordingly if they want the docshell the load is 283 * happening in. These are somewhat odd semantics, and bug 466687 has been 284 * filed to consider improving them. 285 */ 286 inline nsIDocShell* NS_CP_GetDocShellFromContext(nsISupports* aContext) { 287 if (!aContext) { 288 return nullptr; 289 } 290 291 nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aContext); 292 293 if (!window) { 294 // Our context might be a document. 295 nsCOMPtr<mozilla::dom::Document> doc = do_QueryInterface(aContext); 296 if (!doc) { 297 // we were not a document after all, get our ownerDocument, 298 // hopefully 299 nsCOMPtr<nsIContent> content = do_QueryInterface(aContext); 300 if (content) { 301 doc = content->OwnerDoc(); 302 } 303 } 304 305 if (doc) { 306 if (doc->GetDisplayDocument()) { 307 doc = doc->GetDisplayDocument(); 308 } 309 310 window = doc->GetWindow(); 311 } 312 } 313 314 if (!window) { 315 return nullptr; 316 } 317 318 return window->GetDocShell(); 319 } 320 321 #endif /* __nsContentPolicyUtils_h__ */