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