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