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/. */
10 #include "nsIPrincipal.h"
11 #include "nsIObserver.h"
12 #include "nsIContent.h"
13 #include "nsCSPService.h"
14 #include "nsIContentSecurityPolicy.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;
30 static PRLogModuleInfo
* gCspPRLog
;
33 CSPService::CSPService()
35 Preferences::AddBoolVarCache(&sCSPEnabled
, "security.csp.enable");
39 gCspPRLog
= PR_NewLogModule("CSP");
43 CSPService::~CSPService()
45 mAppStatusCache
.Clear();
48 NS_IMPL_ISUPPORTS(CSPService
, nsIContentPolicy
, nsIChannelEventSink
)
50 /* nsIContentPolicy implementation */
52 CSPService::ShouldLoad(uint32_t aContentType
,
53 nsIURI
*aContentLocation
,
54 nsIURI
*aRequestOrigin
,
55 nsISupports
*aRequestContext
,
56 const nsACString
&aMimeTypeGuess
,
58 nsIPrincipal
*aRequestPrincipal
,
61 if (!aContentLocation
)
62 return NS_ERROR_FAILURE
;
66 nsAutoCString location
;
67 aContentLocation
->GetSpec(location
);
68 PR_LOG(gCspPRLog
, PR_LOG_DEBUG
,
69 ("CSPService::ShouldLoad called for %s", location
.get()));
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
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
);
86 NS_ENSURE_SUCCESS(aContentLocation
->SchemeIs("chrome", &schemeMatch
), NS_OK
);
89 NS_ENSURE_SUCCESS(aContentLocation
->SchemeIs("resource", &schemeMatch
), NS_OK
);
92 NS_ENSURE_SUCCESS(aContentLocation
->SchemeIs("javascript", &schemeMatch
), 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
) {
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
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
;
144 case nsIContentPolicy::TYPE_OBJECT
:
145 *aDecision
= nsIContentPolicy::REJECT_SERVER
;
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
) {
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
;
167 principal
= node
->NodePrincipal();
168 principal
->GetCsp(getter_AddRefs(csp
));
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
++) {
178 csp
->GetPolicy(i
, policy
);
179 PR_LOG(gCspPRLog
, PR_LOG_DEBUG
,
180 ("Document has CSP[%d]: %s", i
,
181 NS_ConvertUTF16toUTF8(policy
).get()));
186 // obtain the enforcement decision
187 // (don't pass aExtra, we use that slot for redirects)
188 csp
->ShouldLoad(aContentType
,
199 nsAutoCString uriSpec
;
200 aContentLocation
->GetSpec(uriSpec
);
201 PR_LOG(gCspPRLog
, PR_LOG_DEBUG
,
202 ("COULD NOT get nsINode for location: %s", uriSpec
.get()));
210 CSPService::ShouldProcess(uint32_t aContentType
,
211 nsIURI
*aContentLocation
,
212 nsIURI
*aRequestOrigin
,
213 nsISupports
*aRequestContext
,
214 const nsACString
&aMimeTypeGuess
,
216 nsIPrincipal
*aRequestPrincipal
,
219 if (!aContentLocation
)
220 return NS_ERROR_FAILURE
;
222 *aDecision
= nsIContentPolicy::ACCEPT
;
226 /* nsIChannelEventSink implementation */
228 CSPService::AsyncOnChannelRedirect(nsIChannel
*oldChannel
,
229 nsIChannel
*newChannel
,
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
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
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)
282 nullptr, // nsISupports
283 EmptyCString(), // ACString - MIME guess
284 originalUri
, // aMimeTypeGuess
289 nsAutoCString
newUriSpec("None");
290 newUri
->GetSpec(newUriSpec
);
291 PR_LOG(gCspPRLog
, PR_LOG_DEBUG
,
292 ("CSPService::AsyncOnChannelRedirect called for %s",
296 PR_LOG(gCspPRLog
, PR_LOG_DEBUG
,
297 ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
299 PR_LOG(gCspPRLog
, PR_LOG_DEBUG
,
300 ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
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
;