forked from mirrors/gecko-dev
		
	 a60d06b5ef
			
		
	
	
		a60d06b5ef
		
	
	
	
	
		
			
			Differential Revision: https://phabricator.services.mozilla.com/D19269 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			303 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* 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/. */
 | |
| 
 | |
| /*
 | |
|  * Utility routines for checking content load/process policy settings,
 | |
|  * and routines helpful for content policy implementors.
 | |
|  *
 | |
|  * XXXbz it would be nice if some of this stuff could be out-of-lined in
 | |
|  * nsContentUtils.  That would work for almost all the callers...
 | |
|  */
 | |
| 
 | |
| #ifndef __nsContentPolicyUtils_h__
 | |
| #define __nsContentPolicyUtils_h__
 | |
| 
 | |
| #include "mozilla/BasePrincipal.h"
 | |
| 
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsIContentPolicy.h"
 | |
| #include "nsIContent.h"
 | |
| #include "nsIScriptSecurityManager.h"
 | |
| #include "nsIURI.h"
 | |
| #include "nsServiceManagerUtils.h"
 | |
| #include "nsStringFwd.h"
 | |
| 
 | |
| // XXXtw sadly, this makes consumers of nsContentPolicyUtils depend on widget
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "nsPIDOMWindow.h"
 | |
| 
 | |
| #define NS_CONTENTPOLICY_CONTRACTID "@mozilla.org/layout/content-policy;1"
 | |
| #define NS_CONTENTPOLICY_CATEGORY "content-policy"
 | |
| #define NS_CONTENTPOLICY_CID                         \
 | |
|   {                                                  \
 | |
|     0x0e3afd3d, 0xeb60, 0x4c2b, {                    \
 | |
|       0x96, 0x3b, 0x56, 0xd7, 0xc4, 0x39, 0xf1, 0x24 \
 | |
|     }                                                \
 | |
|   }
 | |
| 
 | |
| /**
 | |
|  * Evaluates to true if val is ACCEPT.
 | |
|  *
 | |
|  * @param val the status returned from shouldProcess/shouldLoad
 | |
|  */
 | |
| #define NS_CP_ACCEPTED(val) ((val) == nsIContentPolicy::ACCEPT)
 | |
| 
 | |
| /**
 | |
|  * Evaluates to true if val is a REJECT_* status
 | |
|  *
 | |
|  * @param val the status returned from shouldProcess/shouldLoad
 | |
|  */
 | |
| #define NS_CP_REJECTED(val) ((val) != nsIContentPolicy::ACCEPT)
 | |
| 
 | |
| // Offer convenient translations of constants -> const char*
 | |
| 
 | |
| // convenience macro to reduce some repetative typing...
 | |
| // name is the name of a constant from this interface
 | |
| #define CASE_RETURN(name)      \
 | |
|   case nsIContentPolicy::name: \
 | |
|     return #name
 | |
| 
 | |
| /**
 | |
|  * Returns a string corresponding to the name of the response constant, or
 | |
|  * "<Unknown Response>" if an unknown response value is given.
 | |
|  *
 | |
|  * The return value is static and must not be freed.
 | |
|  *
 | |
|  * @param response the response code
 | |
|  * @return the name of the given response code
 | |
|  */
 | |
| inline const char *NS_CP_ResponseName(int16_t response) {
 | |
|   switch (response) {
 | |
|     CASE_RETURN(REJECT_REQUEST);
 | |
|     CASE_RETURN(REJECT_TYPE);
 | |
|     CASE_RETURN(REJECT_SERVER);
 | |
|     CASE_RETURN(REJECT_OTHER);
 | |
|     CASE_RETURN(ACCEPT);
 | |
|     default:
 | |
|       return "<Unknown Response>";
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns a string corresponding to the name of the content type constant, or
 | |
|  * "<Unknown Type>" if an unknown content type value is given.
 | |
|  *
 | |
|  * The return value is static and must not be freed.
 | |
|  *
 | |
|  * @param contentType the content type code
 | |
|  * @return the name of the given content type code
 | |
|  */
 | |
| inline const char *NS_CP_ContentTypeName(uint32_t contentType) {
 | |
|   switch (contentType) {
 | |
|     CASE_RETURN(TYPE_OTHER);
 | |
|     CASE_RETURN(TYPE_SCRIPT);
 | |
|     CASE_RETURN(TYPE_IMAGE);
 | |
|     CASE_RETURN(TYPE_STYLESHEET);
 | |
|     CASE_RETURN(TYPE_OBJECT);
 | |
|     CASE_RETURN(TYPE_DOCUMENT);
 | |
|     CASE_RETURN(TYPE_SUBDOCUMENT);
 | |
|     CASE_RETURN(TYPE_REFRESH);
 | |
|     CASE_RETURN(TYPE_XBL);
 | |
|     CASE_RETURN(TYPE_PING);
 | |
|     CASE_RETURN(TYPE_XMLHTTPREQUEST);
 | |
|     CASE_RETURN(TYPE_OBJECT_SUBREQUEST);
 | |
|     CASE_RETURN(TYPE_DTD);
 | |
|     CASE_RETURN(TYPE_FONT);
 | |
|     CASE_RETURN(TYPE_MEDIA);
 | |
|     CASE_RETURN(TYPE_WEBSOCKET);
 | |
|     CASE_RETURN(TYPE_CSP_REPORT);
 | |
|     CASE_RETURN(TYPE_XSLT);
 | |
|     CASE_RETURN(TYPE_BEACON);
 | |
|     CASE_RETURN(TYPE_FETCH);
 | |
|     CASE_RETURN(TYPE_IMAGESET);
 | |
|     CASE_RETURN(TYPE_WEB_MANIFEST);
 | |
|     CASE_RETURN(TYPE_INTERNAL_SCRIPT);
 | |
|     CASE_RETURN(TYPE_INTERNAL_WORKER);
 | |
|     CASE_RETURN(TYPE_INTERNAL_SHARED_WORKER);
 | |
|     CASE_RETURN(TYPE_INTERNAL_EMBED);
 | |
|     CASE_RETURN(TYPE_INTERNAL_OBJECT);
 | |
|     CASE_RETURN(TYPE_INTERNAL_FRAME);
 | |
|     CASE_RETURN(TYPE_INTERNAL_IFRAME);
 | |
|     CASE_RETURN(TYPE_INTERNAL_AUDIO);
 | |
|     CASE_RETURN(TYPE_INTERNAL_VIDEO);
 | |
|     CASE_RETURN(TYPE_INTERNAL_TRACK);
 | |
|     CASE_RETURN(TYPE_INTERNAL_XMLHTTPREQUEST);
 | |
|     CASE_RETURN(TYPE_INTERNAL_EVENTSOURCE);
 | |
|     CASE_RETURN(TYPE_INTERNAL_SERVICE_WORKER);
 | |
|     CASE_RETURN(TYPE_INTERNAL_SCRIPT_PRELOAD);
 | |
|     CASE_RETURN(TYPE_INTERNAL_IMAGE);
 | |
|     CASE_RETURN(TYPE_INTERNAL_IMAGE_PRELOAD);
 | |
|     CASE_RETURN(TYPE_INTERNAL_IMAGE_FAVICON);
 | |
|     CASE_RETURN(TYPE_INTERNAL_STYLESHEET);
 | |
|     CASE_RETURN(TYPE_INTERNAL_STYLESHEET_PRELOAD);
 | |
|     CASE_RETURN(TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS);
 | |
|     CASE_RETURN(TYPE_SAVEAS_DOWNLOAD);
 | |
|     CASE_RETURN(TYPE_SPECULATIVE);
 | |
|     CASE_RETURN(TYPE_INTERNAL_MODULE);
 | |
|     CASE_RETURN(TYPE_INTERNAL_MODULE_PRELOAD);
 | |
|     default:
 | |
|       return "<Unknown Type>";
 | |
|   }
 | |
| }
 | |
| 
 | |
| #undef CASE_RETURN
 | |
| 
 | |
| /* Passes on parameters from its "caller"'s context. */
 | |
| #define CHECK_CONTENT_POLICY(action)                                    \
 | |
|   PR_BEGIN_MACRO                                                        \
 | |
|   nsCOMPtr<nsIContentPolicy> policy =                                   \
 | |
|       do_GetService(NS_CONTENTPOLICY_CONTRACTID);                       \
 | |
|   if (!policy) return NS_ERROR_FAILURE;                                 \
 | |
|                                                                         \
 | |
|   return policy->action(contentLocation, loadInfo, mimeType, decision); \
 | |
|   PR_END_MACRO
 | |
| 
 | |
| /* Passes on parameters from its "caller"'s context. */
 | |
| #define CHECK_CONTENT_POLICY_WITH_SERVICE(action, _policy)               \
 | |
|   PR_BEGIN_MACRO                                                         \
 | |
|   return _policy->action(contentLocation, loadInfo, mimeType, decision); \
 | |
|   PR_END_MACRO
 | |
| 
 | |
| /**
 | |
|  * Check whether we can short-circuit this check and bail out.  If not, get the
 | |
|  * origin URI to use.
 | |
|  *
 | |
|  * Note: requestOrigin is scoped outside the PR_BEGIN_MACRO/PR_END_MACRO on
 | |
|  * purpose */
 | |
| #define CHECK_PRINCIPAL_AND_DATA(action)                                       \
 | |
|   nsCOMPtr<nsIURI> requestOrigin;                                              \
 | |
|   PR_BEGIN_MACRO                                                               \
 | |
|   if (loadingPrincipal) {                                                      \
 | |
|     /* We exempt most loads into any document with the system principal        \
 | |
|      * from content policy checks, mostly as an optimization. Which means      \
 | |
|      * that we need to apply this check to the loading principal, not the      \
 | |
|      * principal that triggered the load. */                                   \
 | |
|     bool isSystem = loadingPrincipal->IsSystemPrincipal();                     \
 | |
|     if (isSystem && contentType != nsIContentPolicy::TYPE_DOCUMENT) {          \
 | |
|       *decision = nsIContentPolicy::ACCEPT;                                    \
 | |
|       nsCOMPtr<nsINode> n = do_QueryInterface(context);                        \
 | |
|       if (!n) {                                                                \
 | |
|         nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(context);         \
 | |
|         n = win ? win->GetExtantDoc() : nullptr;                               \
 | |
|       }                                                                        \
 | |
|       if (n) {                                                                 \
 | |
|         mozilla::dom::Document *d = n->OwnerDoc();                             \
 | |
|         if (d->IsLoadedAsData() || d->IsBeingUsedAsImage() ||                  \
 | |
|             d->IsResourceDoc()) {                                              \
 | |
|           nsCOMPtr<nsIContentPolicy> dataPolicy =                              \
 | |
|               do_GetService("@mozilla.org/data-document-content-policy;1");    \
 | |
|           if (dataPolicy) {                                                    \
 | |
|             dataPolicy->action(contentLocation, loadInfo, mimeType, decision); \
 | |
|           }                                                                    \
 | |
|         }                                                                      \
 | |
|       }                                                                        \
 | |
|       return NS_OK;                                                            \
 | |
|     }                                                                          \
 | |
|     nsresult rv = loadingPrincipal->GetURI(getter_AddRefs(requestOrigin));     \
 | |
|     NS_ENSURE_SUCCESS(rv, rv);                                                 \
 | |
|   }                                                                            \
 | |
|   PR_END_MACRO
 | |
| 
 | |
| /**
 | |
|  * Alias for calling ShouldLoad on the content policy service.  Parameters are
 | |
|  * the same as nsIContentPolicy::shouldLoad, except for the loadingPrincipal
 | |
|  * and triggeringPrincipal parameters (which should be non-null if possible,
 | |
|  * and have the same semantics as in nsLoadInfo), and the last parameter,
 | |
|  * which can be used to pass in a pointer to a useful service if the caller
 | |
|  * already has it.  The origin URI to pass to shouldLoad will be the URI of
 | |
|  * loadingPrincipal, unless loadingPrincipal is null (in which case a null
 | |
|  * origin URI will be passed).
 | |
|  */
 | |
| inline nsresult NS_CheckContentLoadPolicy(
 | |
|     nsIURI *contentLocation, nsILoadInfo *loadInfo, const nsACString &mimeType,
 | |
|     int16_t *decision, nsIContentPolicy *policyService = nullptr) {
 | |
|   nsIPrincipal *loadingPrincipal = loadInfo->LoadingPrincipal();
 | |
|   nsCOMPtr<nsISupports> context = loadInfo->GetLoadingContext();
 | |
|   nsContentPolicyType contentType = loadInfo->InternalContentPolicyType();
 | |
|   CHECK_PRINCIPAL_AND_DATA(ShouldLoad);
 | |
|   if (policyService) {
 | |
|     CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldLoad, policyService);
 | |
|   }
 | |
|   CHECK_CONTENT_POLICY(ShouldLoad);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Alias for calling ShouldProcess on the content policy service.
 | |
|  */
 | |
| inline nsresult NS_CheckContentProcessPolicy(
 | |
|     nsIURI *contentLocation, nsILoadInfo *loadInfo, const nsACString &mimeType,
 | |
|     int16_t *decision, nsIContentPolicy *policyService = nullptr) {
 | |
|   nsIPrincipal *loadingPrincipal = loadInfo->LoadingPrincipal();
 | |
|   nsCOMPtr<nsISupports> context = loadInfo->GetLoadingContext();
 | |
|   nsContentPolicyType contentType = loadInfo->InternalContentPolicyType();
 | |
|   CHECK_PRINCIPAL_AND_DATA(ShouldProcess);
 | |
|   if (policyService) {
 | |
|     CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldProcess, policyService);
 | |
|   }
 | |
|   CHECK_CONTENT_POLICY(ShouldProcess);
 | |
| }
 | |
| 
 | |
| #undef CHECK_CONTENT_POLICY
 | |
| #undef CHECK_CONTENT_POLICY_WITH_SERVICE
 | |
| 
 | |
| /**
 | |
|  * Helper function to get an nsIDocShell given a context.
 | |
|  * If the context is a document or window, the corresponding docshell will be
 | |
|  * returned.
 | |
|  * If the context is a non-document DOM node, the docshell of its ownerDocument
 | |
|  * will be returned.
 | |
|  *
 | |
|  * @param aContext the context to find a docshell for (can be null)
 | |
|  *
 | |
|  * @return a WEAK pointer to the docshell, or nullptr if it could
 | |
|  *     not be obtained
 | |
|  *
 | |
|  * @note  As of this writing, calls to nsIContentPolicy::Should{Load,Process}
 | |
|  * for TYPE_DOCUMENT and TYPE_SUBDOCUMENT pass in an aContext that either
 | |
|  * points to the frameElement of the window the load is happening in
 | |
|  * (in which case NS_CP_GetDocShellFromContext will return the parent of the
 | |
|  * docshell the load is happening in), or points to the window the load is
 | |
|  * happening in (in which case NS_CP_GetDocShellFromContext will return
 | |
|  * the docshell the load is happening in).  It's up to callers to QI aContext
 | |
|  * and handle things accordingly if they want the docshell the load is
 | |
|  * happening in.  These are somewhat odd semantics, and bug 466687 has been
 | |
|  * filed to consider improving them.
 | |
|  */
 | |
| inline nsIDocShell *NS_CP_GetDocShellFromContext(nsISupports *aContext) {
 | |
|   if (!aContext) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aContext);
 | |
| 
 | |
|   if (!window) {
 | |
|     // Our context might be a document.
 | |
|     nsCOMPtr<mozilla::dom::Document> doc = do_QueryInterface(aContext);
 | |
|     if (!doc) {
 | |
|       // we were not a document after all, get our ownerDocument,
 | |
|       // hopefully
 | |
|       nsCOMPtr<nsIContent> content = do_QueryInterface(aContext);
 | |
|       if (content) {
 | |
|         doc = content->OwnerDoc();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (doc) {
 | |
|       if (doc->GetDisplayDocument()) {
 | |
|         doc = doc->GetDisplayDocument();
 | |
|       }
 | |
| 
 | |
|       window = doc->GetWindow();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!window) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return window->GetDocShell();
 | |
| }
 | |
| 
 | |
| #endif /* __nsContentPolicyUtils_h__ */
 |