Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; 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/. */
6 #include "prlog.h"
7 #include "nsString.h"
8 #include "nsCOMPtr.h"
9 #include "nsIURI.h"
10 #include "nsIPrincipal.h"
11 #include "nsIObserver.h"
12 #include "nsIContent.h"
13 #include "nsCSPService.h"
14 #include "nsIContentSecurityPolicy.h"
15 #include "nsIChannelPolicy.h"
16 #include "nsIChannelEventSink.h"
17 #include "nsIPropertyBag2.h"
18 #include "nsIWritablePropertyBag2.h"
19 #include "nsError.h"
20 #include "nsChannelProperties.h"
21 #include "nsIAsyncVerifyRedirectCallback.h"
22 #include "nsAsyncRedirectVerifyHelper.h"
23 #include "mozilla/Preferences.h"
24 #include "nsIScriptError.h"
25 #include "nsContentUtils.h"
27 using namespace mozilla;
29 /* Keeps track of whether or not CSP is enabled */
30 bool CSPService::sCSPEnabled = true;
32 #ifdef PR_LOGGING
33 static PRLogModuleInfo* gCspPRLog;
34 #endif
36 CSPService::CSPService()
37 {
38 Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable");
40 #ifdef PR_LOGGING
41 if (!gCspPRLog)
42 gCspPRLog = PR_NewLogModule("CSP");
43 #endif
44 }
46 CSPService::~CSPService()
47 {
48 mAppStatusCache.Clear();
49 }
51 NS_IMPL_ISUPPORTS(CSPService, nsIContentPolicy, nsIChannelEventSink)
53 /* nsIContentPolicy implementation */
54 NS_IMETHODIMP
55 CSPService::ShouldLoad(uint32_t aContentType,
56 nsIURI *aContentLocation,
57 nsIURI *aRequestOrigin,
58 nsISupports *aRequestContext,
59 const nsACString &aMimeTypeGuess,
60 nsISupports *aExtra,
61 nsIPrincipal *aRequestPrincipal,
62 int16_t *aDecision)
63 {
64 if (!aContentLocation)
65 return NS_ERROR_FAILURE;
67 #ifdef PR_LOGGING
68 {
69 nsAutoCString location;
70 aContentLocation->GetSpec(location);
71 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
72 ("CSPService::ShouldLoad called for %s", location.get()));
73 }
74 #endif
76 // default decision, CSP can revise it if there's a policy to enforce
77 *aDecision = nsIContentPolicy::ACCEPT;
79 // No need to continue processing if CSP is disabled
80 if (!sCSPEnabled)
81 return NS_OK;
83 // shortcut for about: chrome: and resource: and javascript: uris since
84 // they're not subject to CSP content policy checks.
85 bool schemeMatch = false;
86 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
87 if (schemeMatch)
88 return NS_OK;
89 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
90 if (schemeMatch)
91 return NS_OK;
92 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
93 if (schemeMatch)
94 return NS_OK;
95 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
96 if (schemeMatch)
97 return NS_OK;
100 // These content types are not subject to CSP content policy checks:
101 // TYPE_CSP_REPORT, TYPE_REFRESH, TYPE_DOCUMENT
102 // (their mappings are null in contentSecurityPolicy.js)
103 if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
104 aContentType == nsIContentPolicy::TYPE_REFRESH ||
105 aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
106 return NS_OK;
107 }
109 // ----- THIS IS A TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
110 // ----- PLEASE REMOVE ONCE bug 925004 LANDS. -----
112 // Cache the app status for this origin.
113 uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
114 nsAutoCString contentOrigin;
115 aContentLocation->GetPrePath(contentOrigin);
116 if (aRequestPrincipal && !mAppStatusCache.Get(contentOrigin, &status)) {
117 aRequestPrincipal->GetAppStatus(&status);
118 mAppStatusCache.Put(contentOrigin, status);
119 }
121 if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
122 // The CSP for certified apps is :
123 // "default-src *; script-src 'self'; object-src 'none'; style-src 'self'"
124 // That means we can optimize for this case by:
125 // - loading only same origin scripts and stylesheets.
126 // - never loading objects.
127 // - accepting everything else.
129 switch (aContentType) {
130 case nsIContentPolicy::TYPE_SCRIPT:
131 case nsIContentPolicy::TYPE_STYLESHEET:
132 {
133 nsAutoCString sourceOrigin;
134 aRequestOrigin->GetPrePath(sourceOrigin);
135 if (!sourceOrigin.Equals(contentOrigin)) {
136 *aDecision = nsIContentPolicy::REJECT_SERVER;
137 }
138 }
139 break;
141 case nsIContentPolicy::TYPE_OBJECT:
142 *aDecision = nsIContentPolicy::REJECT_SERVER;
143 break;
145 default:
146 *aDecision = nsIContentPolicy::ACCEPT;
147 }
149 // Only cache and return if we are successful. If not, we want the error
150 // to be reported, and thus fallback to the slow path.
151 if (*aDecision == nsIContentPolicy::ACCEPT) {
152 return NS_OK;
153 }
154 }
156 // ----- END OF TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
158 // find the principal of the document that initiated this request and see
159 // if it has a CSP policy object
160 nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
161 nsCOMPtr<nsIPrincipal> principal;
162 nsCOMPtr<nsIContentSecurityPolicy> csp;
163 if (node) {
164 principal = node->NodePrincipal();
165 principal->GetCsp(getter_AddRefs(csp));
167 if (csp) {
168 #ifdef PR_LOGGING
169 {
170 int numPolicies = 0;
171 nsresult rv = csp->GetPolicyCount(&numPolicies);
172 if (NS_SUCCEEDED(rv)) {
173 for (int i=0; i<numPolicies; i++) {
174 nsAutoString policy;
175 csp->GetPolicy(i, policy);
176 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
177 ("Document has CSP[%d]: %s", i,
178 NS_ConvertUTF16toUTF8(policy).get()));
179 }
180 }
181 }
182 #endif
183 // obtain the enforcement decision
184 // (don't pass aExtra, we use that slot for redirects)
185 csp->ShouldLoad(aContentType,
186 aContentLocation,
187 aRequestOrigin,
188 aRequestContext,
189 aMimeTypeGuess,
190 nullptr,
191 aDecision);
192 }
193 }
194 #ifdef PR_LOGGING
195 else {
196 nsAutoCString uriSpec;
197 aContentLocation->GetSpec(uriSpec);
198 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
199 ("COULD NOT get nsINode for location: %s", uriSpec.get()));
200 }
201 #endif
203 return NS_OK;
204 }
206 NS_IMETHODIMP
207 CSPService::ShouldProcess(uint32_t aContentType,
208 nsIURI *aContentLocation,
209 nsIURI *aRequestOrigin,
210 nsISupports *aRequestContext,
211 const nsACString &aMimeTypeGuess,
212 nsISupports *aExtra,
213 nsIPrincipal *aRequestPrincipal,
214 int16_t *aDecision)
215 {
216 if (!aContentLocation)
217 return NS_ERROR_FAILURE;
219 // default decision is to accept the item
220 *aDecision = nsIContentPolicy::ACCEPT;
222 // No need to continue processing if CSP is disabled
223 if (!sCSPEnabled)
224 return NS_OK;
226 // find the nsDocument that initiated this request and see if it has a
227 // CSP policy object
228 nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
229 nsCOMPtr<nsIPrincipal> principal;
230 nsCOMPtr<nsIContentSecurityPolicy> csp;
231 if (node) {
232 principal = node->NodePrincipal();
233 principal->GetCsp(getter_AddRefs(csp));
235 if (csp) {
236 #ifdef PR_LOGGING
237 {
238 int numPolicies = 0;
239 nsresult rv = csp->GetPolicyCount(&numPolicies);
240 if (NS_SUCCEEDED(rv)) {
241 for (int i=0; i<numPolicies; i++) {
242 nsAutoString policy;
243 csp->GetPolicy(i, policy);
244 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
245 ("shouldProcess - document has policy[%d]: %s", i,
246 NS_ConvertUTF16toUTF8(policy).get()));
247 }
248 }
249 }
250 #endif
251 // obtain the enforcement decision
252 csp->ShouldProcess(aContentType,
253 aContentLocation,
254 aRequestOrigin,
255 aRequestContext,
256 aMimeTypeGuess,
257 aExtra,
258 aDecision);
259 }
260 }
261 #ifdef PR_LOGGING
262 else {
263 nsAutoCString uriSpec;
264 aContentLocation->GetSpec(uriSpec);
265 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
266 ("COULD NOT get nsINode for location: %s", uriSpec.get()));
267 }
268 #endif
269 return NS_OK;
270 }
272 /* nsIChannelEventSink implementation */
273 NS_IMETHODIMP
274 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
275 nsIChannel *newChannel,
276 uint32_t flags,
277 nsIAsyncVerifyRedirectCallback *callback)
278 {
279 nsAsyncRedirectAutoCallback autoCallback(callback);
281 // get the Content Security Policy and load type from the property bag
282 nsCOMPtr<nsISupports> policyContainer;
283 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
284 if (!props)
285 return NS_OK;
287 props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
288 NS_GET_IID(nsISupports),
289 getter_AddRefs(policyContainer));
291 // see if we have a valid nsIChannelPolicy containing CSP and load type
292 nsCOMPtr<nsIChannelPolicy> channelPolicy(do_QueryInterface(policyContainer));
293 if (!channelPolicy)
294 return NS_OK;
296 nsCOMPtr<nsISupports> supports;
297 nsCOMPtr<nsIContentSecurityPolicy> csp;
298 channelPolicy->GetContentSecurityPolicy(getter_AddRefs(supports));
299 csp = do_QueryInterface(supports);
300 uint32_t loadType;
301 channelPolicy->GetLoadType(&loadType);
303 // if no CSP in the channelPolicy, nothing for us to add to the channel
304 if (!csp)
305 return NS_OK;
307 /* Since redirecting channels don't call into nsIContentPolicy, we call our
308 * Content Policy implementation directly when redirects occur. When channels
309 * are created using NS_NewChannel(), callers can optionally pass in a
310 * nsIChannelPolicy containing a CSP object and load type, which is placed in
311 * the new channel's property bag. This container is propagated forward when
312 * channels redirect.
313 */
315 // Does the CSP permit this host for this type of load?
316 // If not, cancel the load now.
317 nsCOMPtr<nsIURI> newUri;
318 newChannel->GetURI(getter_AddRefs(newUri));
319 nsCOMPtr<nsIURI> originalUri;
320 oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
321 int16_t aDecision = nsIContentPolicy::ACCEPT;
322 csp->ShouldLoad(loadType, // load type per nsIContentPolicy (uint32_t)
323 newUri, // nsIURI
324 nullptr, // nsIURI
325 nullptr, // nsISupports
326 EmptyCString(), // ACString - MIME guess
327 originalUri, // nsISupports - extra
328 &aDecision);
330 #ifdef PR_LOGGING
331 if (newUri) {
332 nsAutoCString newUriSpec("None");
333 newUri->GetSpec(newUriSpec);
334 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
335 ("CSPService::AsyncOnChannelRedirect called for %s",
336 newUriSpec.get()));
337 }
338 if (aDecision == 1)
339 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
340 ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
341 else
342 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
343 ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
344 #endif
346 // if ShouldLoad doesn't accept the load, cancel the request
347 if (aDecision != 1) {
348 autoCallback.DontCallback();
349 return NS_BINDING_FAILED;
350 }
352 // the redirect is permitted, so propagate the Content Security Policy
353 // and load type to the redirecting channel
354 nsresult rv;
355 nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel);
356 if (props2) {
357 rv = props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
358 channelPolicy);
359 if (NS_SUCCEEDED(rv)) {
360 return NS_OK;
361 }
362 }
364 // The redirecting channel isn't a writable property bag, we won't be able
365 // to enforce the load policy if it redirects again, so we stop it now.
366 nsAutoCString newUriSpec;
367 rv = newUri->GetSpec(newUriSpec);
368 const char16_t *formatParams[] = { NS_ConvertUTF8toUTF16(newUriSpec).get() };
369 if (NS_SUCCEEDED(rv)) {
370 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
371 NS_LITERAL_CSTRING("Redirect Error"), nullptr,
372 nsContentUtils::eDOM_PROPERTIES,
373 "InvalidRedirectChannelWarning",
374 formatParams, 1);
375 }
377 return NS_BINDING_FAILED;
378 }