Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : // vim: ft=cpp tw=78 sw=4 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 "mozilla/Logging.h"
12 :
13 : #include "nsISupports.h"
14 : #include "nsXPCOM.h"
15 : #include "nsContentPolicyUtils.h"
16 : #include "mozilla/dom/nsCSPService.h"
17 : #include "nsContentPolicy.h"
18 : #include "nsIURI.h"
19 : #include "nsIDocShell.h"
20 : #include "nsIDOMElement.h"
21 : #include "nsIDOMNode.h"
22 : #include "nsIDOMWindow.h"
23 : #include "nsIContent.h"
24 : #include "nsIImageLoadingContent.h"
25 : #include "nsILoadContext.h"
26 : #include "nsCOMArray.h"
27 : #include "nsContentUtils.h"
28 : #include "mozilla/dom/nsMixedContentBlocker.h"
29 : #include "nsIContentSecurityPolicy.h"
30 : #include "mozilla/dom/TabGroup.h"
31 : #include "mozilla/TaskCategory.h"
32 :
33 : using mozilla::LogLevel;
34 :
35 39 : NS_IMPL_ISUPPORTS(nsContentPolicy, nsIContentPolicy)
36 :
37 : static mozilla::LazyLogModule gConPolLog("nsContentPolicy");
38 :
39 : nsresult
40 3 : NS_NewContentPolicy(nsIContentPolicy **aResult)
41 : {
42 3 : *aResult = new nsContentPolicy;
43 3 : NS_ADDREF(*aResult);
44 3 : return NS_OK;
45 : }
46 :
47 3 : nsContentPolicy::nsContentPolicy()
48 : : mPolicies(NS_CONTENTPOLICY_CATEGORY)
49 : , mSimplePolicies(NS_SIMPLECONTENTPOLICY_CATEGORY)
50 : , mMixedContentBlocker(do_GetService(NS_MIXEDCONTENTBLOCKER_CONTRACTID))
51 3 : , mCSPService(do_GetService(CSPSERVICE_CONTRACTID))
52 : {
53 3 : }
54 :
55 0 : nsContentPolicy::~nsContentPolicy()
56 : {
57 0 : }
58 :
59 : #ifdef DEBUG
60 : #define WARN_IF_URI_UNINITIALIZED(uri,name) \
61 : PR_BEGIN_MACRO \
62 : if ((uri)) { \
63 : nsAutoCString spec; \
64 : (uri)->GetAsciiSpec(spec); \
65 : if (spec.IsEmpty()) { \
66 : NS_WARNING(name " is uninitialized, fix caller"); \
67 : } \
68 : } \
69 : PR_END_MACRO
70 :
71 : #else // ! defined(DEBUG)
72 :
73 : #define WARN_IF_URI_UNINITIALIZED(uri,name)
74 :
75 : #endif // defined(DEBUG)
76 :
77 : inline nsresult
78 23 : nsContentPolicy::CheckPolicy(CPMethod policyMethod,
79 : SCPMethod simplePolicyMethod,
80 : nsContentPolicyType contentType,
81 : nsIURI *contentLocation,
82 : nsIURI *requestingLocation,
83 : nsISupports *requestingContext,
84 : const nsACString &mimeType,
85 : nsISupports *extra,
86 : nsIPrincipal *requestPrincipal,
87 : int16_t *decision)
88 : {
89 : //sanity-check passed-through parameters
90 23 : NS_PRECONDITION(decision, "Null out pointer");
91 23 : WARN_IF_URI_UNINITIALIZED(contentLocation, "Request URI");
92 23 : WARN_IF_URI_UNINITIALIZED(requestingLocation, "Requesting URI");
93 :
94 : #ifdef DEBUG
95 : {
96 46 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(requestingContext));
97 46 : nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(requestingContext));
98 23 : NS_ASSERTION(!requestingContext || node || window,
99 : "Context should be a DOM node or a DOM window!");
100 : }
101 : #endif
102 :
103 : /*
104 : * There might not be a requestinglocation. This can happen for
105 : * iframes with an image as src. Get the uri from the dom node.
106 : * See bug 254510
107 : */
108 23 : if (!requestingLocation) {
109 20 : nsCOMPtr<nsIDocument> doc;
110 20 : nsCOMPtr<nsIContent> node = do_QueryInterface(requestingContext);
111 10 : if (node) {
112 1 : doc = node->OwnerDoc();
113 : }
114 10 : if (!doc) {
115 9 : doc = do_QueryInterface(requestingContext);
116 : }
117 10 : if (doc) {
118 1 : requestingLocation = doc->GetDocumentURI();
119 : }
120 : }
121 :
122 : nsContentPolicyType externalType =
123 23 : nsContentUtils::InternalContentPolicyTypeToExternal(contentType);
124 :
125 : /*
126 : * Enumerate mPolicies and ask each of them, taking the logical AND of
127 : * their permissions.
128 : */
129 : nsresult rv;
130 23 : const nsCOMArray<nsIContentPolicy>& entries = mPolicies.GetCachedEntries();
131 :
132 46 : nsCOMPtr<nsPIDOMWindowOuter> window;
133 46 : if (nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext)) {
134 13 : window = node->OwnerDoc()->GetWindow();
135 : } else {
136 10 : window = do_QueryInterface(requestingContext);
137 : }
138 :
139 23 : if (requestPrincipal) {
140 46 : nsCOMPtr<nsIContentSecurityPolicy> csp;
141 23 : requestPrincipal->GetCsp(getter_AddRefs(csp));
142 23 : if (csp && window) {
143 0 : csp->EnsureEventTarget(window->EventTargetFor(mozilla::TaskCategory::Other));
144 : }
145 : }
146 :
147 23 : int32_t count = entries.Count();
148 184 : for (int32_t i = 0; i < count; i++) {
149 : /* check the appropriate policy */
150 : // Send internal content policy type to CSP and mixed content blocker
151 161 : nsContentPolicyType type = externalType;
152 161 : if (mMixedContentBlocker == entries[i] || mCSPService == entries[i]) {
153 46 : type = contentType;
154 : }
155 322 : rv = (entries[i]->*policyMethod)(type, contentLocation,
156 : requestingLocation, requestingContext,
157 : mimeType, extra, requestPrincipal,
158 322 : decision);
159 :
160 161 : if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) {
161 : // If we are blocking an image, we have to let the
162 : // ImageLoadingContent know that we blocked the load.
163 0 : if (externalType == nsIContentPolicy::TYPE_IMAGE ||
164 : externalType == nsIContentPolicy::TYPE_IMAGESET) {
165 : nsCOMPtr<nsIImageLoadingContent> img =
166 0 : do_QueryInterface(requestingContext);
167 0 : if (img) {
168 0 : img->SetBlockedRequest(*decision);
169 : }
170 : }
171 : /* policy says no, no point continuing to check */
172 0 : return NS_OK;
173 : }
174 : }
175 :
176 46 : nsCOMPtr<nsIDOMElement> topFrameElement;
177 23 : bool isTopLevel = true;
178 :
179 23 : if (window) {
180 30 : nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
181 30 : nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
182 15 : if (loadContext) {
183 15 : loadContext->GetTopFrameElement(getter_AddRefs(topFrameElement));
184 : }
185 :
186 15 : MOZ_ASSERT(window->IsOuterWindow());
187 :
188 15 : if (topFrameElement) {
189 0 : nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetScriptableTop();
190 0 : isTopLevel = topWindow == window;
191 : } else {
192 : // If we don't have a top frame element, then requestingContext is
193 : // part of the top-level XUL document. Presumably it's the <browser>
194 : // element that content is being loaded into, so we call it the
195 : // topFrameElement.
196 15 : topFrameElement = do_QueryInterface(requestingContext);
197 15 : isTopLevel = true;
198 : }
199 : }
200 :
201 : const nsCOMArray<nsISimpleContentPolicy>& simpleEntries =
202 23 : mSimplePolicies.GetCachedEntries();
203 23 : count = simpleEntries.Count();
204 23 : for (int32_t i = 0; i < count; i++) {
205 : /* check the appropriate policy */
206 0 : rv = (simpleEntries[i]->*simplePolicyMethod)(externalType, contentLocation,
207 : requestingLocation,
208 : topFrameElement, isTopLevel,
209 : mimeType, extra, requestPrincipal,
210 0 : decision);
211 :
212 0 : if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) {
213 : // If we are blocking an image, we have to let the
214 : // ImageLoadingContent know that we blocked the load.
215 0 : if (externalType == nsIContentPolicy::TYPE_IMAGE ||
216 : externalType == nsIContentPolicy::TYPE_IMAGESET) {
217 : nsCOMPtr<nsIImageLoadingContent> img =
218 0 : do_QueryInterface(requestingContext);
219 0 : if (img) {
220 0 : img->SetBlockedRequest(*decision);
221 : }
222 : }
223 : /* policy says no, no point continuing to check */
224 0 : return NS_OK;
225 : }
226 : }
227 :
228 : // everyone returned failure, or no policies: sanitize result
229 23 : *decision = nsIContentPolicy::ACCEPT;
230 23 : return NS_OK;
231 : }
232 :
233 : //uses the parameters from ShouldXYZ to produce and log a message
234 : //logType must be a literal string constant
235 : #define LOG_CHECK(logType) \
236 : PR_BEGIN_MACRO \
237 : /* skip all this nonsense if the call failed or logging is disabled */ \
238 : if (NS_SUCCEEDED(rv) && MOZ_LOG_TEST(gConPolLog, LogLevel::Debug)) { \
239 : const char *resultName; \
240 : if (decision) { \
241 : resultName = NS_CP_ResponseName(*decision); \
242 : } else { \
243 : resultName = "(null ptr)"; \
244 : } \
245 : MOZ_LOG(gConPolLog, LogLevel::Debug, \
246 : ("Content Policy: " logType ": <%s> <Ref:%s> result=%s", \
247 : contentLocation ? contentLocation->GetSpecOrDefault().get() \
248 : : "None", \
249 : requestingLocation ? requestingLocation->GetSpecOrDefault().get()\
250 : : "None", \
251 : resultName) \
252 : ); \
253 : } \
254 : PR_END_MACRO
255 :
256 : NS_IMETHODIMP
257 23 : nsContentPolicy::ShouldLoad(uint32_t contentType,
258 : nsIURI *contentLocation,
259 : nsIURI *requestingLocation,
260 : nsISupports *requestingContext,
261 : const nsACString &mimeType,
262 : nsISupports *extra,
263 : nsIPrincipal *requestPrincipal,
264 : int16_t *decision)
265 : {
266 : // ShouldProcess does not need a content location, but we do
267 23 : NS_PRECONDITION(contentLocation, "Must provide request location");
268 23 : nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldLoad,
269 : &nsISimpleContentPolicy::ShouldLoad,
270 : contentType,
271 : contentLocation, requestingLocation,
272 : requestingContext, mimeType, extra,
273 23 : requestPrincipal, decision);
274 23 : LOG_CHECK("ShouldLoad");
275 :
276 23 : return rv;
277 : }
278 :
279 : NS_IMETHODIMP
280 0 : nsContentPolicy::ShouldProcess(uint32_t contentType,
281 : nsIURI *contentLocation,
282 : nsIURI *requestingLocation,
283 : nsISupports *requestingContext,
284 : const nsACString &mimeType,
285 : nsISupports *extra,
286 : nsIPrincipal *requestPrincipal,
287 : int16_t *decision)
288 : {
289 0 : nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldProcess,
290 : &nsISimpleContentPolicy::ShouldProcess,
291 : contentType,
292 : contentLocation, requestingLocation,
293 : requestingContext, mimeType, extra,
294 0 : requestPrincipal, decision);
295 0 : LOG_CHECK("ShouldProcess");
296 :
297 0 : return rv;
298 : }
|