1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
7 #include "mozilla/Logging.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/StaticPrefs_security.h"
13 #include "nsIContent.h"
14 #include "nsCSPService.h"
15 #include "nsIContentSecurityPolicy.h"
17 #include "nsIAsyncVerifyRedirectCallback.h"
18 #include "nsAsyncRedirectVerifyHelper.h"
19 #include "nsContentUtils.h"
20 #include "nsContentPolicyUtils.h"
21 #include "nsNetUtil.h"
22 #include "nsIProtocolHandler.h"
23 #include "nsQueryObject.h"
24 #include "mozilla/net/DocumentLoadListener.h"
25 #include "mozilla/net/DocumentChannel.h"
27 using namespace mozilla
;
29 static LazyLogModule
gCspPRLog("CSP");
31 CSPService::CSPService() = default;
33 CSPService::~CSPService() = default;
35 NS_IMPL_ISUPPORTS(CSPService
, nsIContentPolicy
, nsIChannelEventSink
)
37 // Helper function to identify protocols and content types not subject to CSP.
38 bool subjectToCSP(nsIURI
* aURI
, nsContentPolicyType aContentType
) {
39 ExtContentPolicyType contentType
=
40 nsContentUtils::InternalContentPolicyTypeToExternal(aContentType
);
42 // These content types are not subject to CSP content policy checks:
43 // TYPE_CSP_REPORT -- csp can't block csp reports
44 // TYPE_DOCUMENT -- used for frame-ancestors
45 if (contentType
== ExtContentPolicy::TYPE_CSP_REPORT
||
46 contentType
== ExtContentPolicy::TYPE_DOCUMENT
) {
50 // The three protocols: data:, blob: and filesystem: share the same
51 // protocol flag (URI_IS_LOCAL_RESOURCE) with other protocols,
52 // but those three protocols get special attention in CSP and
53 // are subject to CSP, hence we have to make sure those
54 // protocols are subject to CSP, see:
55 // http://www.w3.org/TR/CSP2/#source-list-guid-matching
56 if (aURI
->SchemeIs("data") || aURI
->SchemeIs("blob") ||
57 aURI
->SchemeIs("filesystem")) {
61 // Finally we have to allowlist "about:" which does not fall into
62 // the category underneath and also "javascript:" which is not
63 // subject to CSP content loading rules.
64 if (aURI
->SchemeIs("about") || aURI
->SchemeIs("javascript")) {
68 // Please note that it should be possible for websites to
69 // allowlist their own protocol handlers with respect to CSP,
70 // hence we use protocol flags to accomplish that, but we also
71 // want resource:, chrome: and moz-icon to be subject to CSP
72 // (which also use URI_IS_LOCAL_RESOURCE).
73 // Exception to the rule are images, styles, and localization
74 // DTDs using a scheme of resource: or chrome:
75 bool isImgOrStyleOrDTD
= contentType
== ExtContentPolicy::TYPE_IMAGE
||
76 contentType
== ExtContentPolicy::TYPE_STYLESHEET
||
77 contentType
== ExtContentPolicy::TYPE_DTD
;
78 if (aURI
->SchemeIs("resource")) {
79 nsAutoCString uriSpec
;
80 aURI
->GetSpec(uriSpec
);
81 // Exempt pdf.js from being subject to a page's CSP.
82 if (StringBeginsWith(uriSpec
, "resource://pdf.js/"_ns
)) {
85 if (!isImgOrStyleOrDTD
) {
89 if (aURI
->SchemeIs("chrome") && !isImgOrStyleOrDTD
) {
92 if (aURI
->SchemeIs("moz-icon")) {
96 nsresult rv
= NS_URIChainHasFlags(
97 aURI
, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE
, &match
);
98 if (NS_SUCCEEDED(rv
) && match
) {
101 // all other protocols are subject To CSP.
105 /* static */ nsresult
CSPService::ConsultCSP(nsIURI
* aContentLocation
,
106 nsILoadInfo
* aLoadInfo
,
107 int16_t* aDecision
) {
108 if (!aContentLocation
) {
109 return NS_ERROR_FAILURE
;
112 nsContentPolicyType contentType
= aLoadInfo
->InternalContentPolicyType();
114 nsCOMPtr
<nsICSPEventListener
> cspEventListener
;
116 aLoadInfo
->GetCspEventListener(getter_AddRefs(cspEventListener
));
117 NS_ENSURE_SUCCESS(rv
, rv
);
119 if (MOZ_LOG_TEST(gCspPRLog
, LogLevel::Debug
)) {
120 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
121 ("CSPService::ShouldLoad called for %s",
122 aContentLocation
->GetSpecOrDefault().get()));
125 // default decision, CSP can revise it if there's a policy to enforce
126 *aDecision
= nsIContentPolicy::ACCEPT
;
128 // No need to continue processing if CSP is disabled or if the protocol
129 // or type is *not* subject to CSP.
130 // Please note, the correct way to opt-out of CSP using a custom
131 // protocolHandler is to set one of the nsIProtocolHandler flags
132 // that are allowlistet in subjectToCSP()
133 if (!subjectToCSP(aContentLocation
, contentType
)) {
137 // 1) Apply speculate CSP for preloads
138 bool isPreload
= nsContentUtils::IsPreloadType(contentType
);
141 nsCOMPtr
<nsIContentSecurityPolicy
> preloadCsp
= aLoadInfo
->GetPreloadCsp();
143 // obtain the enforcement decision
144 rv
= preloadCsp
->ShouldLoad(
145 contentType
, cspEventListener
, aLoadInfo
, aContentLocation
,
146 nullptr, // no redirect, aOriginal URL is null.
148 NS_ENSURE_SUCCESS(rv
, rv
);
150 // if the preload policy already denied the load, then there
151 // is no point in checking the real policy
152 if (NS_CP_REJECTED(*aDecision
)) {
153 NS_SetRequestBlockingReason(
154 aLoadInfo
, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_PRELOAD
);
161 // 2) Apply actual CSP to all loads. Please note that in case
162 // the csp should be overruled (e.g. by an ExpandedPrincipal)
163 // then loadinfo->GetCsp() returns that CSP instead of the
165 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aLoadInfo
->GetCsp();
168 // Generally aOriginalURI denotes the URI before a redirect and hence
169 // will always be a nullptr here. Only exception are frame navigations
170 // which we want to treat as a redirect for the purpose of CSP reporting
171 // and in particular the `blocked-uri` in the CSP report where we want
172 // to report the prePath information.
173 nsCOMPtr
<nsIURI
> originalURI
= nullptr;
174 ExtContentPolicyType extType
=
175 nsContentUtils::InternalContentPolicyTypeToExternal(contentType
);
176 if (extType
== ExtContentPolicy::TYPE_SUBDOCUMENT
&&
177 !aLoadInfo
->GetOriginalFrameSrcLoad() &&
178 mozilla::StaticPrefs::
179 security_csp_truncate_blocked_uri_for_frame_navigations()) {
180 nsAutoCString prePathStr
;
181 nsresult rv
= aContentLocation
->GetPrePath(prePathStr
);
182 NS_ENSURE_SUCCESS(rv
, rv
);
183 rv
= NS_NewURI(getter_AddRefs(originalURI
), prePathStr
);
184 NS_ENSURE_SUCCESS(rv
, rv
);
187 // obtain the enforcement decision
188 rv
= csp
->ShouldLoad(
189 contentType
, cspEventListener
, aLoadInfo
, aContentLocation
,
190 originalURI
, // no redirect, unless it's a frame navigation.
191 !isPreload
&& aLoadInfo
->GetSendCSPViolationEvents(), aDecision
);
193 if (NS_CP_REJECTED(*aDecision
)) {
194 NS_SetRequestBlockingReason(
195 aLoadInfo
, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL
);
198 NS_ENSURE_SUCCESS(rv
, rv
);
203 /* nsIContentPolicy implementation */
205 CSPService::ShouldLoad(nsIURI
* aContentLocation
, nsILoadInfo
* aLoadInfo
,
206 int16_t* aDecision
) {
207 return ConsultCSP(aContentLocation
, aLoadInfo
, aDecision
);
211 CSPService::ShouldProcess(nsIURI
* aContentLocation
, nsILoadInfo
* aLoadInfo
,
212 int16_t* aDecision
) {
213 if (!aContentLocation
) {
214 return NS_ERROR_FAILURE
;
216 nsContentPolicyType contentType
= aLoadInfo
->InternalContentPolicyType();
218 if (MOZ_LOG_TEST(gCspPRLog
, LogLevel::Debug
)) {
219 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
220 ("CSPService::ShouldProcess called for %s",
221 aContentLocation
->GetSpecOrDefault().get()));
224 // ShouldProcess is only relevant to TYPE_OBJECT, so let's convert the
225 // internal contentPolicyType to the mapping external one.
226 // If it is not TYPE_OBJECT, we can return at this point.
227 // Note that we should still pass the internal contentPolicyType
228 // (contentType) to ShouldLoad().
229 ExtContentPolicyType policyType
=
230 nsContentUtils::InternalContentPolicyTypeToExternal(contentType
);
232 if (policyType
!= ExtContentPolicy::TYPE_OBJECT
) {
233 *aDecision
= nsIContentPolicy::ACCEPT
;
237 return ShouldLoad(aContentLocation
, aLoadInfo
, aDecision
);
240 /* nsIChannelEventSink implementation */
242 CSPService::AsyncOnChannelRedirect(nsIChannel
* oldChannel
,
243 nsIChannel
* newChannel
, uint32_t flags
,
244 nsIAsyncVerifyRedirectCallback
* callback
) {
245 net::nsAsyncRedirectAutoCallback
autoCallback(callback
);
247 if (XRE_IsE10sParentProcess()) {
248 nsCOMPtr
<nsIParentChannel
> parentChannel
;
249 NS_QueryNotificationCallbacks(oldChannel
, parentChannel
);
250 RefPtr
<net::DocumentLoadListener
> docListener
=
251 do_QueryObject(parentChannel
);
252 // Since this is an IPC'd channel we do not have access to the request
253 // context. In turn, we do not have an event target for policy violations.
254 // Enforce the CSP check in the content process where we have that info.
255 // We allow redirect checks to run for document loads via
256 // DocumentLoadListener, since these are fully supported and we don't
257 // expose the redirects to the content process. We can't do this for all
258 // request types yet because we don't serialize nsICSPEventListener.
259 if (parentChannel
&& !docListener
) {
264 // Don't do these checks if we're switching from DocumentChannel
265 // to a real channel. In that case, we should already have done
266 // the checks in the parent process. AsyncOnChannelRedirect
267 // isn't called in the content process if we switch process,
268 // so checking here would just hide bugs in the process switch
270 if (RefPtr
<net::DocumentChannel
> docChannel
= do_QueryObject(oldChannel
)) {
274 nsCOMPtr
<nsIURI
> newUri
;
275 nsresult rv
= newChannel
->GetURI(getter_AddRefs(newUri
));
276 NS_ENSURE_SUCCESS(rv
, rv
);
278 nsCOMPtr
<nsILoadInfo
> loadInfo
= oldChannel
->LoadInfo();
280 /* Since redirecting channels don't call into nsIContentPolicy, we call our
281 * Content Policy implementation directly when redirects occur using the
282 * information set in the LoadInfo when channels are created.
284 * We check if the CSP permits this host for this type of load, if not,
285 * we cancel the load now.
287 nsCOMPtr
<nsIURI
> originalUri
;
288 rv
= oldChannel
->GetOriginalURI(getter_AddRefs(originalUri
));
290 autoCallback
.DontCallback();
291 oldChannel
->Cancel(NS_ERROR_DOM_BAD_URI
);
295 Maybe
<nsresult
> cancelCode
;
296 rv
= ConsultCSPForRedirect(originalUri
, newUri
, loadInfo
, cancelCode
);
298 oldChannel
->Cancel(*cancelCode
);
301 autoCallback
.DontCallback();
307 nsresult
CSPService::ConsultCSPForRedirect(nsIURI
* aOriginalURI
,
309 nsILoadInfo
* aLoadInfo
,
310 Maybe
<nsresult
>& aCancelCode
) {
311 // No need to continue processing if CSP is disabled or if the protocol
312 // is *not* subject to CSP.
313 // Please note, the correct way to opt-out of CSP using a custom
314 // protocolHandler is to set one of the nsIProtocolHandler flags
315 // that are allowlistet in subjectToCSP()
316 nsContentPolicyType policyType
= aLoadInfo
->InternalContentPolicyType();
317 if (!subjectToCSP(aNewURI
, policyType
)) {
321 nsCOMPtr
<nsICSPEventListener
> cspEventListener
;
323 aLoadInfo
->GetCspEventListener(getter_AddRefs(cspEventListener
));
324 MOZ_ALWAYS_SUCCEEDS(rv
);
326 bool isPreload
= nsContentUtils::IsPreloadType(policyType
);
328 /* On redirect, if the content policy is a preload type, rejecting the
329 * preload results in the load silently failing, so we pass true to
330 * the aSendViolationReports parameter. See Bug 1219453.
333 int16_t decision
= nsIContentPolicy::ACCEPT
;
335 // 1) Apply speculative CSP for preloads
337 nsCOMPtr
<nsIContentSecurityPolicy
> preloadCsp
= aLoadInfo
->GetPreloadCsp();
339 // Pass originalURI to indicate the redirect
340 preloadCsp
->ShouldLoad(
341 policyType
, // load type per nsIContentPolicy (uint32_t)
342 cspEventListener
, aLoadInfo
,
344 aOriginalURI
, // Original nsIURI
345 true, // aSendViolationReports
348 // if the preload policy already denied the load, then there
349 // is no point in checking the real policy
350 if (NS_CP_REJECTED(decision
)) {
351 aCancelCode
= Some(NS_ERROR_DOM_BAD_URI
);
352 return NS_BINDING_FAILED
;
357 // 2) Apply actual CSP to all loads
358 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aLoadInfo
->GetCsp();
360 // Pass originalURI to indicate the redirect
361 csp
->ShouldLoad(policyType
, // load type per nsIContentPolicy (uint32_t)
362 cspEventListener
, aLoadInfo
,
364 aOriginalURI
, // Original nsIURI
365 true, // aSendViolationReports
367 if (NS_CP_REJECTED(decision
)) {
368 aCancelCode
= Some(NS_ERROR_DOM_BAD_URI
);
369 return NS_BINDING_FAILED
;