Bug 1073336 part 5 - Add AnimationPlayerCollection::PlayerUpdated; r=dbaron
[gecko.git] / dom / security / nsCSPService.cpp
blob0683e78db0d0f1d3a40e14de5662c06402064030
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 "nsError.h"
16 #include "nsIAsyncVerifyRedirectCallback.h"
17 #include "nsAsyncRedirectVerifyHelper.h"
18 #include "mozilla/Preferences.h"
19 #include "nsIScriptError.h"
20 #include "nsContentUtils.h"
21 #include "nsContentPolicyUtils.h"
22 #include "nsPrincipal.h"
24 using namespace mozilla;
26 /* Keeps track of whether or not CSP is enabled */
27 bool CSPService::sCSPEnabled = true;
29 #ifdef PR_LOGGING
30 static PRLogModuleInfo* gCspPRLog;
31 #endif
33 CSPService::CSPService()
35 Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable");
37 #ifdef PR_LOGGING
38 if (!gCspPRLog)
39 gCspPRLog = PR_NewLogModule("CSP");
40 #endif
43 CSPService::~CSPService()
45 mAppStatusCache.Clear();
48 NS_IMPL_ISUPPORTS(CSPService, nsIContentPolicy, nsIChannelEventSink)
50 /* nsIContentPolicy implementation */
51 NS_IMETHODIMP
52 CSPService::ShouldLoad(uint32_t aContentType,
53 nsIURI *aContentLocation,
54 nsIURI *aRequestOrigin,
55 nsISupports *aRequestContext,
56 const nsACString &aMimeTypeGuess,
57 nsISupports *aExtra,
58 nsIPrincipal *aRequestPrincipal,
59 int16_t *aDecision)
61 if (!aContentLocation)
62 return NS_ERROR_FAILURE;
64 #ifdef PR_LOGGING
66 nsAutoCString location;
67 aContentLocation->GetSpec(location);
68 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
69 ("CSPService::ShouldLoad called for %s", location.get()));
71 #endif
73 // default decision, CSP can revise it if there's a policy to enforce
74 *aDecision = nsIContentPolicy::ACCEPT;
76 // No need to continue processing if CSP is disabled
77 if (!sCSPEnabled)
78 return NS_OK;
80 // shortcut for about: chrome: and resource: and javascript: uris since
81 // they're not subject to CSP content policy checks.
82 bool schemeMatch = false;
83 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
84 if (schemeMatch)
85 return NS_OK;
86 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
87 if (schemeMatch)
88 return NS_OK;
89 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
90 if (schemeMatch)
91 return NS_OK;
92 NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
93 if (schemeMatch)
94 return NS_OK;
97 // These content types are not subject to CSP content policy checks:
98 // TYPE_CSP_REPORT -- csp can't block csp reports
99 // TYPE_REFRESH -- never passed to ShouldLoad (see nsIContentPolicy.idl)
100 // TYPE_DOCUMENT -- used for frame-ancestors
101 if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
102 aContentType == nsIContentPolicy::TYPE_REFRESH ||
103 aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
104 return NS_OK;
107 // ----- THIS IS A TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
108 // ----- PLEASE REMOVE ONCE bug 925004 LANDS. -----
110 // Cache the app status for this origin.
111 uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
112 nsAutoCString contentOrigin;
113 aContentLocation->GetPrePath(contentOrigin);
114 if (aRequestPrincipal && !mAppStatusCache.Get(contentOrigin, &status)) {
115 aRequestPrincipal->GetAppStatus(&status);
116 mAppStatusCache.Put(contentOrigin, status);
119 if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
120 // The CSP for certified apps is :
121 // "default-src *; script-src 'self'; object-src 'none'; style-src 'self' app://theme.gaiamobile.org:*"
122 // That means we can optimize for this case by:
123 // - loading same origin scripts and stylesheets, and stylesheets from the
124 // theme url space.
125 // - never loading objects.
126 // - accepting everything else.
128 switch (aContentType) {
129 case nsIContentPolicy::TYPE_SCRIPT:
130 case nsIContentPolicy::TYPE_STYLESHEET:
132 // Whitelist the theme resources.
133 auto themeOrigin = Preferences::GetCString("b2g.theme.origin");
134 nsAutoCString sourceOrigin;
135 aRequestOrigin->GetPrePath(sourceOrigin);
137 if (!(sourceOrigin.Equals(contentOrigin) ||
138 (themeOrigin && themeOrigin.Equals(contentOrigin)))) {
139 *aDecision = nsIContentPolicy::REJECT_SERVER;
142 break;
144 case nsIContentPolicy::TYPE_OBJECT:
145 *aDecision = nsIContentPolicy::REJECT_SERVER;
146 break;
148 default:
149 *aDecision = nsIContentPolicy::ACCEPT;
152 // Only cache and return if we are successful. If not, we want the error
153 // to be reported, and thus fallback to the slow path.
154 if (*aDecision == nsIContentPolicy::ACCEPT) {
155 return NS_OK;
159 // ----- END OF TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
161 // find the principal of the document that initiated this request and see
162 // if it has a CSP policy object
163 nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
164 nsCOMPtr<nsIPrincipal> principal;
165 nsCOMPtr<nsIContentSecurityPolicy> csp;
166 if (node) {
167 principal = node->NodePrincipal();
168 principal->GetCsp(getter_AddRefs(csp));
170 if (csp) {
171 #ifdef PR_LOGGING
173 uint32_t numPolicies = 0;
174 nsresult rv = csp->GetPolicyCount(&numPolicies);
175 if (NS_SUCCEEDED(rv)) {
176 for (uint32_t i=0; i<numPolicies; i++) {
177 nsAutoString policy;
178 csp->GetPolicy(i, policy);
179 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
180 ("Document has CSP[%d]: %s", i,
181 NS_ConvertUTF16toUTF8(policy).get()));
185 #endif
186 // obtain the enforcement decision
187 // (don't pass aExtra, we use that slot for redirects)
188 csp->ShouldLoad(aContentType,
189 aContentLocation,
190 aRequestOrigin,
191 aRequestContext,
192 aMimeTypeGuess,
193 nullptr,
194 aDecision);
197 #ifdef PR_LOGGING
198 else {
199 nsAutoCString uriSpec;
200 aContentLocation->GetSpec(uriSpec);
201 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
202 ("COULD NOT get nsINode for location: %s", uriSpec.get()));
204 #endif
206 return NS_OK;
209 NS_IMETHODIMP
210 CSPService::ShouldProcess(uint32_t aContentType,
211 nsIURI *aContentLocation,
212 nsIURI *aRequestOrigin,
213 nsISupports *aRequestContext,
214 const nsACString &aMimeTypeGuess,
215 nsISupports *aExtra,
216 nsIPrincipal *aRequestPrincipal,
217 int16_t *aDecision)
219 if (!aContentLocation)
220 return NS_ERROR_FAILURE;
222 *aDecision = nsIContentPolicy::ACCEPT;
223 return NS_OK;
226 /* nsIChannelEventSink implementation */
227 NS_IMETHODIMP
228 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
229 nsIChannel *newChannel,
230 uint32_t flags,
231 nsIAsyncVerifyRedirectCallback *callback)
233 nsAsyncRedirectAutoCallback autoCallback(callback);
235 nsCOMPtr<nsILoadInfo> loadInfo;
236 nsresult rv = oldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
238 // if no loadInfo on the channel, nothing for us to do
239 if (!loadInfo) {
240 return NS_OK;
243 // The loadInfo must not necessarily contain a Node, hence we try to query
244 // the CSP in the following order:
245 // a) Get the Node, the Principal of that Node, and the CSP of that Principal
246 // b) Get the Principal and the CSP of that Principal
248 nsCOMPtr<nsINode> loadingNode = loadInfo->LoadingNode();
249 nsCOMPtr<nsIPrincipal> principal = loadingNode ?
250 loadingNode->NodePrincipal() :
251 loadInfo->LoadingPrincipal();
252 NS_ASSERTION(principal, "Can not evaluate CSP without a principal");
253 nsCOMPtr<nsIContentSecurityPolicy> csp;
254 rv = principal->GetCsp(getter_AddRefs(csp));
255 NS_ENSURE_SUCCESS(rv, rv);
257 // if there is no CSP, nothing for us to do
258 if (!csp) {
259 return NS_OK;
262 /* Since redirecting channels don't call into nsIContentPolicy, we call our
263 * Content Policy implementation directly when redirects occur using the
264 * information set in the LoadInfo when channels are created.
266 * We check if the CSP permits this host for this type of load, if not,
267 * we cancel the load now.
270 nsCOMPtr<nsIURI> newUri;
271 rv = newChannel->GetURI(getter_AddRefs(newUri));
272 NS_ENSURE_SUCCESS(rv, rv);
273 nsCOMPtr<nsIURI> originalUri;
274 rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
275 NS_ENSURE_SUCCESS(rv, rv);
276 nsContentPolicyType policyType = loadInfo->GetContentPolicyType();
278 int16_t aDecision = nsIContentPolicy::ACCEPT;
279 csp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t)
280 newUri, // nsIURI
281 nullptr, // nsIURI
282 nullptr, // nsISupports
283 EmptyCString(), // ACString - MIME guess
284 originalUri, // aMimeTypeGuess
285 &aDecision);
287 #ifdef PR_LOGGING
288 if (newUri) {
289 nsAutoCString newUriSpec("None");
290 newUri->GetSpec(newUriSpec);
291 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
292 ("CSPService::AsyncOnChannelRedirect called for %s",
293 newUriSpec.get()));
295 if (aDecision == 1)
296 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
297 ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
298 else
299 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
300 ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
301 #endif
303 // if ShouldLoad doesn't accept the load, cancel the request
304 if (!NS_CP_ACCEPTED(aDecision)) {
305 autoCallback.DontCallback();
306 return NS_BINDING_FAILED;
308 return NS_OK;