Bug 1731274 [wpt PR 30792] - Add WebIDL.idl as a dependency for webtransport idlharne...
[gecko.git] / dom / security / nsContentSecurityManager.cpp
blobfec54b0688e206106e7e88255cb824933f1e4c39
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 "nsArray.h"
8 #include "nsContentSecurityManager.h"
9 #include "nsContentSecurityUtils.h"
10 #include "nsContentPolicyUtils.h"
11 #include "nsEscape.h"
12 #include "nsDataHandler.h"
13 #include "nsIChannel.h"
14 #include "nsIContentPolicy.h"
15 #include "nsIHttpChannelInternal.h"
16 #include "nsINode.h"
17 #include "nsIStreamListener.h"
18 #include "nsILoadInfo.h"
19 #include "nsIOService.h"
20 #include "nsContentUtils.h"
21 #include "nsCORSListenerProxy.h"
22 #include "nsIParentChannel.h"
23 #include "nsIStreamListener.h"
24 #include "nsIRedirectHistoryEntry.h"
25 #include "nsNetUtil.h"
26 #include "nsReadableUtils.h"
27 #include "nsSandboxFlags.h"
28 #include "nsIXPConnect.h"
30 #include "mozilla/BasePrincipal.h"
31 #include "mozilla/ClearOnShutdown.h"
32 #include "mozilla/CmdLineAndEnvUtils.h"
33 #include "mozilla/dom/Element.h"
34 #include "mozilla/dom/nsMixedContentBlocker.h"
35 #include "mozilla/dom/BrowserChild.h"
36 #include "mozilla/dom/ContentChild.h"
37 #include "mozilla/dom/ContentParent.h"
38 #include "mozilla/dom/Document.h"
39 #include "mozilla/Components.h"
40 #include "mozilla/Logging.h"
41 #include "mozilla/Maybe.h"
42 #include "mozilla/Preferences.h"
43 #include "mozilla/StaticPrefs_dom.h"
44 #include "mozilla/StaticPrefs_security.h"
45 #include "mozilla/Telemetry.h"
46 #include "mozilla/TelemetryComms.h"
47 #include "xpcpublic.h"
48 #include "nsMimeTypes.h"
50 #include "jsapi.h"
51 #include "js/RegExp.h"
53 using namespace mozilla;
54 using namespace mozilla::dom;
55 using namespace mozilla::Telemetry;
57 NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager,
58 nsIChannelEventSink)
60 mozilla::LazyLogModule sCSMLog("CSMLog");
62 // These first two are used for off-the-main-thread checks of
63 // general.config.filename
64 // (which can't be checked off-main-thread).
65 Atomic<bool, mozilla::Relaxed> sJSHacksChecked(false);
66 Atomic<bool, mozilla::Relaxed> sJSHacksPresent(false);
67 Atomic<bool, mozilla::Relaxed> sTelemetryEventEnabled(false);
69 /* static */
70 bool nsContentSecurityManager::AllowTopLevelNavigationToDataURI(
71 nsIChannel* aChannel) {
72 // Let's block all toplevel document navigations to a data: URI.
73 // In all cases where the toplevel document is navigated to a
74 // data: URI the triggeringPrincipal is a contentPrincipal, or
75 // a NullPrincipal. In other cases, e.g. typing a data: URL into
76 // the URL-Bar, the triggeringPrincipal is a SystemPrincipal;
77 // we don't want to block those loads. Only exception, loads coming
78 // from an external applicaton (e.g. Thunderbird) don't load
79 // using a contentPrincipal, but we want to block those loads.
80 if (!StaticPrefs::security_data_uri_block_toplevel_data_uri_navigations()) {
81 return true;
83 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
84 if (loadInfo->GetExternalContentPolicyType() !=
85 ExtContentPolicy::TYPE_DOCUMENT) {
86 return true;
88 if (loadInfo->GetForceAllowDataURI()) {
89 // if the loadinfo explicitly allows the data URI navigation, let's allow it
90 // now
91 return true;
93 nsCOMPtr<nsIURI> uri;
94 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
95 NS_ENSURE_SUCCESS(rv, true);
96 bool isDataURI = uri->SchemeIs("data");
97 if (!isDataURI) {
98 return true;
101 nsAutoCString spec;
102 rv = uri->GetSpec(spec);
103 NS_ENSURE_SUCCESS(rv, true);
104 nsAutoCString contentType;
105 bool base64;
106 rv = nsDataHandler::ParseURI(spec, contentType, nullptr, base64, nullptr);
107 NS_ENSURE_SUCCESS(rv, true);
109 // Allow data: images as long as they are not SVGs
110 if (StringBeginsWith(contentType, "image/"_ns) &&
111 !contentType.EqualsLiteral("image/svg+xml")) {
112 return true;
114 // Allow all data: PDFs. or JSON documents
115 if (contentType.EqualsLiteral(APPLICATION_JSON) ||
116 contentType.EqualsLiteral(TEXT_JSON) ||
117 contentType.EqualsLiteral(APPLICATION_PDF)) {
118 return true;
120 // Redirecting to a toplevel data: URI is not allowed, hence we make
121 // sure the RedirectChain is empty.
122 if (!loadInfo->GetLoadTriggeredFromExternal() &&
123 loadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
124 loadInfo->RedirectChain().IsEmpty()) {
125 return true;
127 nsAutoCString dataSpec;
128 uri->GetSpec(dataSpec);
129 if (dataSpec.Length() > 50) {
130 dataSpec.Truncate(50);
131 dataSpec.AppendLiteral("...");
133 nsCOMPtr<nsISupports> context = loadInfo->ContextForTopLevelLoad();
134 nsCOMPtr<nsIBrowserChild> browserChild = do_QueryInterface(context);
135 nsCOMPtr<Document> doc;
136 if (browserChild) {
137 doc = static_cast<mozilla::dom::BrowserChild*>(browserChild.get())
138 ->GetTopLevelDocument();
140 AutoTArray<nsString, 1> params;
141 CopyUTF8toUTF16(NS_UnescapeURL(dataSpec), *params.AppendElement());
142 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
143 "DATA_URI_BLOCKED"_ns, doc,
144 nsContentUtils::eSECURITY_PROPERTIES,
145 "BlockTopLevelDataURINavigation", params);
146 return false;
149 /* static */
150 bool nsContentSecurityManager::AllowInsecureRedirectToDataURI(
151 nsIChannel* aNewChannel) {
152 nsCOMPtr<nsILoadInfo> loadInfo = aNewChannel->LoadInfo();
153 if (loadInfo->GetExternalContentPolicyType() !=
154 ExtContentPolicy::TYPE_SCRIPT) {
155 return true;
157 nsCOMPtr<nsIURI> newURI;
158 nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
159 if (NS_FAILED(rv) || !newURI) {
160 return true;
162 bool isDataURI = newURI->SchemeIs("data");
163 if (!isDataURI) {
164 return true;
167 // Web Extensions are exempt from that restriction and are allowed to redirect
168 // a channel to a data: URI. When a web extension redirects a channel, we set
169 // a flag on the loadInfo which allows us to identify such redirects here.
170 if (loadInfo->GetAllowInsecureRedirectToDataURI()) {
171 return true;
174 nsAutoCString dataSpec;
175 newURI->GetSpec(dataSpec);
176 if (dataSpec.Length() > 50) {
177 dataSpec.Truncate(50);
178 dataSpec.AppendLiteral("...");
180 nsCOMPtr<Document> doc;
181 nsINode* node = loadInfo->LoadingNode();
182 if (node) {
183 doc = node->OwnerDoc();
185 AutoTArray<nsString, 1> params;
186 CopyUTF8toUTF16(NS_UnescapeURL(dataSpec), *params.AppendElement());
187 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
188 "DATA_URI_BLOCKED"_ns, doc,
189 nsContentUtils::eSECURITY_PROPERTIES,
190 "BlockSubresourceRedirectToData", params);
191 return false;
194 /* static */
195 nsresult nsContentSecurityManager::CheckFTPSubresourceLoad(
196 nsIChannel* aChannel) {
197 // We dissallow using FTP resources as a subresource everywhere.
198 // The only valid way to use FTP resources is loading it as
199 // a top level document.
201 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
202 ExtContentPolicyType type = loadInfo->GetExternalContentPolicyType();
204 // Allow top-level FTP documents and save-as download of FTP files on
205 // HTTP pages.
206 if (type == ExtContentPolicy::TYPE_DOCUMENT ||
207 type == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) {
208 return NS_OK;
211 // Allow the system principal to load everything. This is meant to
212 // temporarily fix downloads and pdf.js.
213 nsIPrincipal* triggeringPrincipal = loadInfo->TriggeringPrincipal();
214 if (triggeringPrincipal->IsSystemPrincipal()) {
215 return NS_OK;
218 nsCOMPtr<nsIURI> uri;
219 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
220 NS_ENSURE_SUCCESS(rv, rv);
221 if (!uri) {
222 return NS_OK;
225 bool isFtpURI = uri->SchemeIs("ftp");
226 if (!isFtpURI) {
227 return NS_OK;
230 nsCOMPtr<Document> doc;
231 if (nsINode* node = loadInfo->LoadingNode()) {
232 doc = node->OwnerDoc();
235 nsAutoCString spec;
236 uri->GetSpec(spec);
237 AutoTArray<nsString, 1> params;
238 CopyUTF8toUTF16(NS_UnescapeURL(spec), *params.AppendElement());
240 nsContentUtils::ReportToConsole(
241 nsIScriptError::warningFlag, "FTP_URI_BLOCKED"_ns, doc,
242 nsContentUtils::eSECURITY_PROPERTIES, "BlockSubresourceFTP", params);
244 return NS_ERROR_CONTENT_BLOCKED;
247 static nsresult ValidateSecurityFlags(nsILoadInfo* aLoadInfo) {
248 nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
250 // We should never perform a security check on a loadInfo that uses the flag
251 // SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, because that is only used for
252 // temporary loadInfos used for explicit nsIContentPolicy checks, but never be
253 // set as a security flag on an actual channel.
254 if (securityMode !=
255 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT &&
256 securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
257 securityMode !=
258 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT &&
259 securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
260 securityMode != nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) {
261 MOZ_ASSERT(
262 false,
263 "need one securityflag from nsILoadInfo to perform security checks");
264 return NS_ERROR_FAILURE;
267 // all good, found the right security flags
268 return NS_OK;
271 static already_AddRefed<nsIPrincipal> GetExtensionSandboxPrincipal(
272 nsILoadInfo* aLoadInfo) {
273 // An extension is allowed to load resources from itself when its pages are
274 // loaded into a sandboxed frame. Extension resources in a sandbox have
275 // a null principal and no access to extension APIs. See "sandbox" in
276 // MDN extension docs for more information.
277 if (!aLoadInfo->TriggeringPrincipal()->GetIsNullPrincipal()) {
278 return nullptr;
280 RefPtr<Document> doc;
281 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
282 if (!doc || !(doc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
283 return nullptr;
286 // node principal is also a null principal here, so we need to
287 // create a principal using documentURI, which is the moz-extension
288 // uri for the page if this is an extension sandboxed page.
289 nsCOMPtr<nsIPrincipal> docPrincipal = BasePrincipal::CreateContentPrincipal(
290 doc->GetDocumentURI(), doc->NodePrincipal()->OriginAttributesRef());
292 if (!BasePrincipal::Cast(docPrincipal)->AddonPolicy()) {
293 return nullptr;
295 return docPrincipal.forget();
298 static bool IsImageLoadInEditorAppType(nsILoadInfo* aLoadInfo) {
299 // Editor apps get special treatment here, editors can load images
300 // from anywhere. This allows editor to insert images from file://
301 // into documents that are being edited.
302 nsContentPolicyType type = aLoadInfo->InternalContentPolicyType();
303 if (type != nsIContentPolicy::TYPE_INTERNAL_IMAGE &&
304 type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD &&
305 type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON &&
306 type != nsIContentPolicy::TYPE_IMAGESET) {
307 return false;
310 auto appType = nsIDocShell::APP_TYPE_UNKNOWN;
311 nsINode* node = aLoadInfo->LoadingNode();
312 if (!node) {
313 return false;
315 Document* doc = node->OwnerDoc();
316 if (!doc) {
317 return false;
320 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
321 if (!docShellTreeItem) {
322 return false;
325 nsCOMPtr<nsIDocShellTreeItem> root;
326 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root));
327 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
328 if (docShell) {
329 appType = docShell->GetAppType();
332 return appType == nsIDocShell::APP_TYPE_EDITOR;
335 static nsresult DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
336 // In practice, these DTDs are just used for localization, so applying the
337 // same principal check as Fluent.
338 if (aLoadInfo->InternalContentPolicyType() ==
339 nsIContentPolicy::TYPE_INTERNAL_DTD) {
340 RefPtr<Document> doc;
341 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
342 bool allowed = false;
343 aLoadInfo->TriggeringPrincipal()->IsL10nAllowed(
344 doc ? doc->GetDocumentURI() : nullptr, &allowed);
346 return allowed ? NS_OK : NS_ERROR_DOM_BAD_URI;
349 // This is used in order to allow a privileged DOMParser to parse documents
350 // that need to access localization DTDs. We just allow through
351 // TYPE_INTERNAL_FORCE_ALLOWED_DTD no matter what the triggering principal is.
352 if (aLoadInfo->InternalContentPolicyType() ==
353 nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD) {
354 return NS_OK;
357 if (IsImageLoadInEditorAppType(aLoadInfo)) {
358 return NS_OK;
361 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal();
362 nsCOMPtr<nsIPrincipal> addonPrincipal =
363 GetExtensionSandboxPrincipal(aLoadInfo);
364 if (addonPrincipal) {
365 // call CheckLoadURIWithPrincipal() as below to continue other checks, but
366 // with the addon principal.
367 triggeringPrincipal = addonPrincipal;
370 // Only call CheckLoadURIWithPrincipal() using the TriggeringPrincipal and not
371 // the LoadingPrincipal when SEC_ALLOW_CROSS_ORIGIN_* security flags are set,
372 // to allow, e.g. user stylesheets to load chrome:// URIs.
373 return nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal(
374 triggeringPrincipal, aURI, aLoadInfo->CheckLoadURIFlags(),
375 aLoadInfo->GetInnerWindowID());
378 static bool URIHasFlags(nsIURI* aURI, uint32_t aURIFlags) {
379 bool hasFlags;
380 nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags);
381 NS_ENSURE_SUCCESS(rv, false);
383 return hasFlags;
386 static nsresult DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo,
387 nsIChannel* aChannel) {
388 if (aLoadInfo->GetAllowChrome() &&
389 (URIHasFlags(aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE) ||
390 nsContentUtils::SchemeIs(aURI, "moz-safe-about"))) {
391 // UI resources are allowed.
392 return DoCheckLoadURIChecks(aURI, aLoadInfo);
395 if (NS_HasBeenCrossOrigin(aChannel, true)) {
396 NS_SetRequestBlockingReason(aLoadInfo,
397 nsILoadInfo::BLOCKING_REASON_NOT_SAME_ORIGIN);
398 return NS_ERROR_DOM_BAD_URI;
401 return NS_OK;
404 static nsresult DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
405 nsCOMPtr<nsIStreamListener>& aInAndOutListener) {
406 MOZ_RELEASE_ASSERT(aInAndOutListener,
407 "can not perform CORS checks without a listener");
409 // No need to set up CORS if TriggeringPrincipal is the SystemPrincipal.
410 if (aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
411 return NS_OK;
414 // We use the triggering principal here, rather than the loading principal
415 // to ensure that anonymous CORS content in the browser resources and in
416 // WebExtensions is allowed to load.
417 nsIPrincipal* principal = aLoadInfo->TriggeringPrincipal();
418 RefPtr<nsCORSListenerProxy> corsListener = new nsCORSListenerProxy(
419 aInAndOutListener, principal,
420 aLoadInfo->GetCookiePolicy() == nsILoadInfo::SEC_COOKIES_INCLUDE);
421 // XXX: @arg: DataURIHandling::Allow
422 // lets use DataURIHandling::Allow for now and then decide on callsite basis.
423 // see also:
424 // http://mxr.mozilla.org/mozilla-central/source/dom/security/nsCORSListenerProxy.h#33
425 nsresult rv = corsListener->Init(aChannel, DataURIHandling::Allow);
426 NS_ENSURE_SUCCESS(rv, rv);
427 aInAndOutListener = corsListener;
428 return NS_OK;
431 static nsresult DoContentSecurityChecks(nsIChannel* aChannel,
432 nsILoadInfo* aLoadInfo) {
433 ExtContentPolicyType contentPolicyType =
434 aLoadInfo->GetExternalContentPolicyType();
435 nsContentPolicyType internalContentPolicyType =
436 aLoadInfo->InternalContentPolicyType();
437 nsCString mimeTypeGuess;
439 nsCOMPtr<nsIURI> uri;
440 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
441 NS_ENSURE_SUCCESS(rv, rv);
443 switch (contentPolicyType) {
444 case ExtContentPolicy::TYPE_OTHER: {
445 mimeTypeGuess.Truncate();
446 break;
449 case ExtContentPolicy::TYPE_SCRIPT: {
450 mimeTypeGuess = "application/javascript"_ns;
451 break;
454 case ExtContentPolicy::TYPE_IMAGE: {
455 mimeTypeGuess.Truncate();
456 break;
459 case ExtContentPolicy::TYPE_STYLESHEET: {
460 mimeTypeGuess = "text/css"_ns;
461 break;
464 case ExtContentPolicy::TYPE_OBJECT: {
465 mimeTypeGuess.Truncate();
466 break;
469 case ExtContentPolicy::TYPE_DOCUMENT: {
470 mimeTypeGuess.Truncate();
471 break;
474 case ExtContentPolicy::TYPE_SUBDOCUMENT: {
475 mimeTypeGuess = "text/html"_ns;
476 break;
479 case ExtContentPolicy::TYPE_PING: {
480 mimeTypeGuess.Truncate();
481 break;
484 case ExtContentPolicy::TYPE_XMLHTTPREQUEST: {
485 #ifdef DEBUG
487 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
488 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
489 "type_xml requires requestingContext of type Document");
491 #endif
492 // We're checking for the external TYPE_XMLHTTPREQUEST here in case
493 // an addon creates a request with that type.
494 if (internalContentPolicyType ==
495 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST ||
496 internalContentPolicyType == nsIContentPolicy::TYPE_XMLHTTPREQUEST) {
497 mimeTypeGuess.Truncate();
498 } else {
499 MOZ_ASSERT(internalContentPolicyType ==
500 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE,
501 "can not set mime type guess for unexpected internal type");
502 mimeTypeGuess = nsLiteralCString(TEXT_EVENT_STREAM);
504 break;
507 case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST: {
508 mimeTypeGuess.Truncate();
509 #ifdef DEBUG
511 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
512 MOZ_ASSERT(
513 !node || node->NodeType() == nsINode::ELEMENT_NODE,
514 "type_subrequest requires requestingContext of type Element");
516 #endif
517 break;
520 case ExtContentPolicy::TYPE_DTD: {
521 mimeTypeGuess.Truncate();
522 #ifdef DEBUG
524 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
525 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
526 "type_dtd requires requestingContext of type Document");
528 #endif
529 break;
532 case ExtContentPolicy::TYPE_FONT:
533 case ExtContentPolicy::TYPE_UA_FONT: {
534 mimeTypeGuess.Truncate();
535 break;
538 case ExtContentPolicy::TYPE_MEDIA: {
539 if (internalContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_TRACK) {
540 mimeTypeGuess = "text/vtt"_ns;
541 } else {
542 mimeTypeGuess.Truncate();
544 #ifdef DEBUG
546 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
547 MOZ_ASSERT(!node || node->NodeType() == nsINode::ELEMENT_NODE,
548 "type_media requires requestingContext of type Element");
550 #endif
551 break;
554 case ExtContentPolicy::TYPE_WEBSOCKET: {
555 // Websockets have to use the proxied URI:
556 // ws:// instead of http:// for CSP checks
557 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
558 do_QueryInterface(aChannel);
559 MOZ_ASSERT(httpChannelInternal);
560 if (httpChannelInternal) {
561 rv = httpChannelInternal->GetProxyURI(getter_AddRefs(uri));
562 MOZ_ASSERT(NS_SUCCEEDED(rv));
564 mimeTypeGuess.Truncate();
565 break;
568 case ExtContentPolicy::TYPE_CSP_REPORT: {
569 mimeTypeGuess.Truncate();
570 break;
573 case ExtContentPolicy::TYPE_XSLT: {
574 mimeTypeGuess = "application/xml"_ns;
575 #ifdef DEBUG
577 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
578 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
579 "type_xslt requires requestingContext of type Document");
581 #endif
582 break;
585 case ExtContentPolicy::TYPE_BEACON: {
586 mimeTypeGuess.Truncate();
587 #ifdef DEBUG
589 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
590 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
591 "type_beacon requires requestingContext of type Document");
593 #endif
594 break;
597 case ExtContentPolicy::TYPE_FETCH: {
598 mimeTypeGuess.Truncate();
599 break;
602 case ExtContentPolicy::TYPE_IMAGESET: {
603 mimeTypeGuess.Truncate();
604 break;
607 case ExtContentPolicy::TYPE_WEB_MANIFEST: {
608 mimeTypeGuess = "application/manifest+json"_ns;
609 break;
612 case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD: {
613 mimeTypeGuess.Truncate();
614 break;
617 case ExtContentPolicy::TYPE_SPECULATIVE: {
618 mimeTypeGuess.Truncate();
619 break;
622 case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA: {
623 mimeTypeGuess.Truncate();
624 break;
627 case ExtContentPolicy::TYPE_INVALID:
628 MOZ_ASSERT(false,
629 "can not perform security check without a valid contentType");
630 // Do not add default: so that compilers can catch the missing case.
633 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
634 rv = NS_CheckContentLoadPolicy(uri, aLoadInfo, mimeTypeGuess, &shouldLoad,
635 nsContentUtils::GetContentPolicy());
637 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
638 NS_SetRequestBlockingReasonIfNull(
639 aLoadInfo, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL);
641 if (NS_SUCCEEDED(rv) &&
642 (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT ||
643 contentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT)) {
644 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
645 // for docshell loads we might have to return SHOW_ALT.
646 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
648 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
649 return NS_ERROR_BLOCKED_BY_POLICY;
652 return NS_ERROR_CONTENT_BLOCKED;
655 return NS_OK;
658 static void LogHTTPSOnlyInfo(nsILoadInfo* aLoadInfo) {
659 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - https-only/https-first flags:"));
660 uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
662 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) {
663 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - HTTPS_ONLY_UNINITIALIZED"));
665 if (httpsOnlyStatus &
666 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) {
667 MOZ_LOG(sCSMLog, LogLevel::Verbose,
668 (" - HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED"));
670 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED) {
671 MOZ_LOG(sCSMLog, LogLevel::Verbose,
672 (" - HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED"));
674 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
675 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - HTTPS_ONLY_EXEMPT"));
677 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS) {
678 MOZ_LOG(sCSMLog, LogLevel::Verbose,
679 (" - HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS"));
681 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) {
682 MOZ_LOG(sCSMLog, LogLevel::Verbose,
683 (" - HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE"));
685 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST) {
686 MOZ_LOG(sCSMLog, LogLevel::Verbose,
687 (" - HTTPS_ONLY_UPGRADED_HTTPS_FIRST"));
691 static void LogPrincipal(nsIPrincipal* aPrincipal,
692 const nsAString& aPrincipalName,
693 const uint8_t& aNestingLevel) {
694 nsPrintfCString aIndentationString("%*s", aNestingLevel * 2, "");
696 if (aPrincipal && aPrincipal->IsSystemPrincipal()) {
697 MOZ_LOG(sCSMLog, LogLevel::Debug,
698 ("%s%s: SystemPrincipal\n", aIndentationString.get(),
699 NS_ConvertUTF16toUTF8(aPrincipalName).get()));
700 return;
702 if (aPrincipal) {
703 if (aPrincipal->GetIsNullPrincipal()) {
704 MOZ_LOG(sCSMLog, LogLevel::Debug,
705 ("%s%s: NullPrincipal\n", aIndentationString.get(),
706 NS_ConvertUTF16toUTF8(aPrincipalName).get()));
707 return;
709 if (aPrincipal->GetIsExpandedPrincipal()) {
710 nsCOMPtr<nsIExpandedPrincipal> expanded(do_QueryInterface(aPrincipal));
711 nsAutoCString origin;
712 origin.AssignLiteral("[Expanded Principal [");
714 StringJoinAppend(origin, ", "_ns, expanded->AllowList(),
715 [](nsACString& dest, nsIPrincipal* principal) {
716 nsAutoCString subOrigin;
717 DebugOnly<nsresult> rv =
718 principal->GetOrigin(subOrigin);
719 MOZ_ASSERT(NS_SUCCEEDED(rv));
720 dest.Append(subOrigin);
723 origin.AppendLiteral("]]");
725 MOZ_LOG(sCSMLog, LogLevel::Debug,
726 ("%s%s: %s\n", aIndentationString.get(),
727 NS_ConvertUTF16toUTF8(aPrincipalName).get(), origin.get()));
728 return;
730 nsAutoCString principalSpec;
731 aPrincipal->GetAsciiSpec(principalSpec);
732 MOZ_LOG(sCSMLog, LogLevel::Debug,
733 ("%s%s: %s\n", aIndentationString.get(),
734 NS_ConvertUTF16toUTF8(aPrincipalName).get(), principalSpec.get()));
735 return;
737 MOZ_LOG(sCSMLog, LogLevel::Debug,
738 ("%s%s: nullptr\n", aIndentationString.get(),
739 NS_ConvertUTF16toUTF8(aPrincipalName).get()));
742 static void LogSecurityFlags(nsSecurityFlags securityFlags) {
743 struct DebugSecFlagType {
744 unsigned long secFlag;
745 char secTypeStr[128];
747 static const DebugSecFlagType secTypes[] = {
748 {nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
749 "SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK"},
750 {nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
751 "SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT"},
752 {nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
753 "SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED"},
754 {nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT,
755 "SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT"},
756 {nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
757 "SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL"},
758 {nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT,
759 "SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT"},
760 {nsILoadInfo::SEC_COOKIES_DEFAULT, "SEC_COOKIES_DEFAULT"},
761 {nsILoadInfo::SEC_COOKIES_INCLUDE, "SEC_COOKIES_INCLUDE"},
762 {nsILoadInfo::SEC_COOKIES_SAME_ORIGIN, "SEC_COOKIES_SAME_ORIGIN"},
763 {nsILoadInfo::SEC_COOKIES_OMIT, "SEC_COOKIES_OMIT"},
764 {nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, "SEC_FORCE_INHERIT_PRINCIPAL"},
765 {nsILoadInfo::SEC_ABOUT_BLANK_INHERITS, "SEC_ABOUT_BLANK_INHERITS"},
766 {nsILoadInfo::SEC_ALLOW_CHROME, "SEC_ALLOW_CHROME"},
767 {nsILoadInfo::SEC_DISALLOW_SCRIPT, "SEC_DISALLOW_SCRIPT"},
768 {nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS, "SEC_DONT_FOLLOW_REDIRECTS"},
769 {nsILoadInfo::SEC_LOAD_ERROR_PAGE, "SEC_LOAD_ERROR_PAGE"},
770 {nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER,
771 "SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER"}};
773 for (const DebugSecFlagType& flag : secTypes) {
774 if (securityFlags & flag.secFlag) {
775 // the logging level should be in sync with the logging level in
776 // DebugDoContentSecurityCheck()
777 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - %s\n", flag.secTypeStr));
781 static void DebugDoContentSecurityCheck(nsIChannel* aChannel,
782 nsILoadInfo* aLoadInfo) {
783 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
785 MOZ_LOG(sCSMLog, LogLevel::Debug, ("\n#DebugDoContentSecurityCheck Begin\n"));
787 // we only log http channels, unless loglevel is 5.
788 if (httpChannel || MOZ_LOG_TEST(sCSMLog, LogLevel::Verbose)) {
789 nsCOMPtr<nsIURI> channelURI;
790 nsAutoCString channelSpec;
791 nsAutoCString channelMethod;
792 NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
793 if (channelURI) {
794 channelURI->GetSpec(channelSpec);
797 MOZ_LOG(sCSMLog, LogLevel::Verbose, ("doContentSecurityCheck:\n"));
799 MOZ_LOG(sCSMLog, LogLevel::Verbose,
800 (" - channelURI: %s\n", channelSpec.get()));
802 // Log HTTP-specific things
803 if (httpChannel) {
804 nsresult rv;
805 rv = httpChannel->GetRequestMethod(channelMethod);
806 if (!NS_FAILED(rv)) {
807 MOZ_LOG(sCSMLog, LogLevel::Verbose,
808 (" - httpMethod: %s\n", channelMethod.get()));
812 // Log Principals
813 nsCOMPtr<nsIPrincipal> requestPrincipal = aLoadInfo->TriggeringPrincipal();
814 LogPrincipal(aLoadInfo->GetLoadingPrincipal(), u"- loadingPrincipal"_ns, 1);
815 LogPrincipal(requestPrincipal, u"- triggeringPrincipal"_ns, 1);
816 LogPrincipal(aLoadInfo->PrincipalToInherit(), u"- principalToInherit"_ns,
819 // Log Redirect Chain
820 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - redirectChain:\n"));
821 for (nsIRedirectHistoryEntry* redirectHistoryEntry :
822 aLoadInfo->RedirectChain()) {
823 nsCOMPtr<nsIPrincipal> principal;
824 redirectHistoryEntry->GetPrincipal(getter_AddRefs(principal));
825 LogPrincipal(principal, u"-"_ns, 2);
828 MOZ_LOG(sCSMLog, LogLevel::Verbose,
829 (" - internalContentPolicyType: %s\n",
830 NS_CP_ContentTypeName(aLoadInfo->InternalContentPolicyType())));
831 MOZ_LOG(sCSMLog, LogLevel::Verbose,
832 (" - externalContentPolicyType: %s\n",
833 NS_CP_ContentTypeName(aLoadInfo->GetExternalContentPolicyType())));
834 MOZ_LOG(sCSMLog, LogLevel::Verbose,
835 (" - upgradeInsecureRequests: %s\n",
836 aLoadInfo->GetUpgradeInsecureRequests() ? "true" : "false"));
837 MOZ_LOG(sCSMLog, LogLevel::Verbose,
838 (" - initialSecurityChecksDone: %s\n",
839 aLoadInfo->GetInitialSecurityCheckDone() ? "true" : "false"));
840 MOZ_LOG(sCSMLog, LogLevel::Verbose,
841 (" - allowDeprecatedSystemRequests: %s\n",
842 aLoadInfo->GetAllowDeprecatedSystemRequests() ? "true" : "false"));
844 // Log CSPrequestPrincipal
845 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp();
846 MOZ_LOG(sCSMLog, LogLevel::Debug, (" - CSP:"));
847 if (csp) {
848 nsAutoString parsedPolicyStr;
849 uint32_t count = 0;
850 csp->GetPolicyCount(&count);
851 for (uint32_t i = 0; i < count; ++i) {
852 csp->GetPolicyString(i, parsedPolicyStr);
853 // we need to add quotation marks, as otherwise yaml parsers may fail
854 // with CSP directives
855 // no need to escape quote marks in the parsed policy string, as URLs in
856 // there are already encoded
857 MOZ_LOG(
858 sCSMLog, LogLevel::Debug,
859 (" - \"%s\"\n", NS_ConvertUTF16toUTF8(parsedPolicyStr).get()));
863 // Security Flags
864 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - securityFlags:"));
865 LogSecurityFlags(aLoadInfo->GetSecurityFlags());
866 LogHTTPSOnlyInfo(aLoadInfo);
867 MOZ_LOG(sCSMLog, LogLevel::Debug, ("\n#DebugDoContentSecurityCheck End\n"));
871 /* static */
872 void nsContentSecurityManager::MeasureUnexpectedPrivilegedLoads(
873 nsILoadInfo* aLoadInfo, nsIURI* aFinalURI, const nsACString& aRemoteType) {
874 if (!StaticPrefs::dom_security_unexpected_system_load_telemetry_enabled()) {
875 return;
877 nsContentSecurityUtils::DetectJsHacks();
878 if (MOZ_UNLIKELY(sJSHacksPresent)) {
879 return;
882 ExtContentPolicyType contentPolicyType =
883 aLoadInfo->GetExternalContentPolicyType();
884 // restricting reported types to script, styles and documents
885 // to be continued in follow-ups of bug 1697163.
886 if (contentPolicyType != ExtContentPolicyType::TYPE_SCRIPT &&
887 contentPolicyType != ExtContentPolicyType::TYPE_STYLESHEET &&
888 contentPolicyType != ExtContentPolicyType::TYPE_DOCUMENT) {
889 return;
892 // Gather redirected schemes in string
893 nsAutoCString loggedRedirects;
894 const nsTArray<nsCOMPtr<nsIRedirectHistoryEntry>>& redirects =
895 aLoadInfo->RedirectChain();
896 if (!redirects.IsEmpty()) {
897 nsCOMPtr<nsIRedirectHistoryEntry> end = redirects.LastElement();
898 for (nsIRedirectHistoryEntry* entry : redirects) {
899 nsCOMPtr<nsIPrincipal> principal;
900 entry->GetPrincipal(getter_AddRefs(principal));
901 if (principal) {
902 nsAutoCString scheme;
903 principal->GetScheme(scheme);
904 loggedRedirects.Append(scheme);
905 if (entry != end) {
906 loggedRedirects.AppendLiteral(", ");
912 nsAutoCString uriString;
913 if (aFinalURI) {
914 aFinalURI->GetAsciiSpec(uriString);
915 } else {
916 uriString.AssignLiteral("");
918 FilenameTypeAndDetails fileNameTypeAndDetails =
919 nsContentSecurityUtils::FilenameToFilenameType(
920 NS_ConvertUTF8toUTF16(uriString), true);
922 nsCString loggedFileDetails = "unknown"_ns;
923 if (fileNameTypeAndDetails.second.isSome()) {
924 loggedFileDetails.Assign(
925 NS_ConvertUTF16toUTF8(fileNameTypeAndDetails.second.value()));
927 // sanitize remoteType because it may contain sensitive
928 // info, like URLs. e.g. `webIsolated=https://example.com`
929 nsAutoCString loggedRemoteType(dom::RemoteTypePrefix(aRemoteType));
930 nsAutoCString loggedContentType(NS_CP_ContentTypeName(contentPolicyType));
932 MOZ_LOG(sCSMLog, LogLevel::Debug, ("UnexpectedPrivilegedLoadTelemetry:\n"));
933 MOZ_LOG(sCSMLog, LogLevel::Debug,
934 ("- contentType: %s\n", loggedContentType.get()));
935 MOZ_LOG(sCSMLog, LogLevel::Debug,
936 ("- URL (not to be reported): %s\n", uriString.get()));
937 MOZ_LOG(sCSMLog, LogLevel::Debug,
938 ("- remoteType: %s\n", loggedRemoteType.get()));
939 MOZ_LOG(sCSMLog, LogLevel::Debug,
940 ("- fileInfo: %s\n", fileNameTypeAndDetails.first.get()));
941 MOZ_LOG(sCSMLog, LogLevel::Debug,
942 ("- fileDetails: %s\n", loggedFileDetails.get()));
943 MOZ_LOG(sCSMLog, LogLevel::Debug,
944 ("- redirects: %s\n\n", loggedRedirects.get()));
946 // Send Telemetry
947 auto extra = Some<nsTArray<EventExtraEntry>>(
948 {EventExtraEntry{"contenttype"_ns, loggedContentType},
949 EventExtraEntry{"remotetype"_ns, loggedRemoteType},
950 EventExtraEntry{"filedetails"_ns, loggedFileDetails},
951 EventExtraEntry{"redirects"_ns, loggedRedirects}});
953 if (!sTelemetryEventEnabled.exchange(true)) {
954 Telemetry::SetEventRecordingEnabled("security"_ns, true);
957 Telemetry::EventID eventType =
958 Telemetry::EventID::Security_Unexpectedload_Systemprincipal;
959 Telemetry::RecordEvent(eventType, mozilla::Some(fileNameTypeAndDetails.first),
960 extra);
963 /* static */
964 nsresult nsContentSecurityManager::CheckAllowLoadInSystemPrivilegedContext(
965 nsIChannel* aChannel) {
966 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
968 // loads with the allow flag are waived through
969 // until refactored (e.g., Shavar, OCSP)
970 if (loadInfo->GetAllowDeprecatedSystemRequests()) {
971 return NS_OK;
973 ExtContentPolicyType contentPolicyType =
974 loadInfo->GetExternalContentPolicyType();
976 // For now, let's not inspect top-level document loads
977 if (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT) {
978 return NS_OK;
981 // We mostly care about the triggeringPrincipal,
982 // unless this is a TYPE_DOCUMENT request, which has none.
983 nsCOMPtr<nsIPrincipal> inspectedPrincipal;
984 if (contentPolicyType != ExtContentPolicy::TYPE_DOCUMENT) {
985 inspectedPrincipal = loadInfo->GetLoadingPrincipal();
986 } else {
987 inspectedPrincipal = loadInfo->TriggeringPrincipal();
990 // Check if we are actually dealing with a SystemPrincipal request
991 if (!inspectedPrincipal || !inspectedPrincipal->IsSystemPrincipal()) {
992 return NS_OK;
995 // allowing some fetches due to their lowered risk
996 // i.e., data & downloads fetches do limited parsing, no rendering
997 // remote images are too widely used (favicons, about:addons etc.)
998 if ((contentPolicyType == ExtContentPolicy::TYPE_FETCH) ||
999 (contentPolicyType == ExtContentPolicy::TYPE_XMLHTTPREQUEST) ||
1000 (contentPolicyType == ExtContentPolicy::TYPE_WEBSOCKET) ||
1001 (contentPolicyType == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) ||
1002 (contentPolicyType == ExtContentPolicy::TYPE_IMAGE)) {
1003 return NS_OK;
1006 // Allow the user interface (e.g., schemes like chrome, resource)
1007 nsCOMPtr<nsIURI> finalURI;
1008 NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
1009 bool isUiResource = false;
1010 if (NS_SUCCEEDED(NS_URIChainHasFlags(
1011 finalURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isUiResource)) &&
1012 isUiResource) {
1013 return NS_OK;
1015 // For about: and extension-based URIs, which don't get
1016 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
1017 while (finalURI && finalURI->SchemeIs("view-source")) {
1018 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(finalURI);
1019 if (nested) {
1020 nested->GetInnerURI(getter_AddRefs(finalURI));
1024 nsAutoCString remoteType;
1025 if (XRE_IsParentProcess()) {
1026 nsCOMPtr<nsIParentChannel> parentChannel;
1027 NS_QueryNotificationCallbacks(aChannel, parentChannel);
1028 if (parentChannel) {
1029 parentChannel->GetRemoteType(remoteType);
1031 } else {
1032 remoteType.Assign(
1033 mozilla::dom::ContentChild::GetSingleton()->GetRemoteType());
1036 // This is our escape hatch, if things break in release.
1037 // We expect to remove the pref in bug 1638770
1038 bool cancelNonLocalSystemPrincipal = StaticPrefs::
1039 security_cancel_non_local_loads_triggered_by_systemprincipal();
1041 // GetInnerURI can return null for malformed nested URIs like moz-icon:trash
1042 if (!finalURI) {
1043 MeasureUnexpectedPrivilegedLoads(loadInfo, finalURI, remoteType);
1044 if (cancelNonLocalSystemPrincipal) {
1045 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
1046 return NS_ERROR_CONTENT_BLOCKED;
1049 // loads of userContent.css during startup and tests that show up as file:
1050 if (finalURI->SchemeIs("file")) {
1051 if ((contentPolicyType == ExtContentPolicy::TYPE_STYLESHEET) ||
1052 (contentPolicyType == ExtContentPolicy::TYPE_OTHER)) {
1053 return NS_OK;
1056 // (1)loads from within omni.ja and system add-ons use jar:
1057 // this is safe to allow, because we do not support remote jar.
1058 // (2) about: resources are always allowed: they are part of the build.
1059 // (3) extensions are signed or the user has made bad decisions.
1060 if (finalURI->SchemeIs("jar") || finalURI->SchemeIs("about") ||
1061 finalURI->SchemeIs("moz-extension")) {
1062 return NS_OK;
1064 // Telemetry for unexpected privileged loads.
1065 // pref check & data sanitization happens in the called function
1066 MeasureUnexpectedPrivilegedLoads(loadInfo, finalURI, remoteType);
1068 // Relaxing restrictions for our test suites:
1069 // (1) AreNonLocalConnectionsDisabled() disables network, so http://mochitest
1070 // is actually local and allowed. (2) The marionette test framework uses
1071 // injections and data URLs to execute scripts, checking for the environment
1072 // variable breaks the attack but not the tests.
1073 if (xpc::AreNonLocalConnectionsDisabled() ||
1074 mozilla::EnvHasValue("MOZ_MARIONETTE")) {
1075 bool disallowSystemPrincipalRemoteDocuments = Preferences::GetBool(
1076 "security.disallow_non_local_systemprincipal_in_tests");
1077 if (disallowSystemPrincipalRemoteDocuments) {
1078 // our own mochitest needs NS_ASSERTION instead of MOZ_ASSERT
1079 NS_ASSERTION(false, "SystemPrincipal must not load remote documents.");
1080 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
1081 return NS_ERROR_CONTENT_BLOCKED;
1083 // but other mochitest are exempt from this
1084 return NS_OK;
1087 nsAutoCString requestedURL;
1088 finalURI->GetAsciiSpec(requestedURL);
1089 MOZ_LOG(sCSMLog, LogLevel::Warning,
1090 ("SystemPrincipal should not load remote resources. URL: %s, type %d",
1091 requestedURL.get(), int(contentPolicyType)));
1093 // The load types that we want to disallow, will extend over time and
1094 // prioritized by risk. The most risky/dangerous are load-types are documents,
1095 // subdocuments, scripts and styles in that order. The most dangerous URL
1096 // schemes to cover are HTTP, HTTPS, data, blob in that order. Meta bug
1097 // 1725112 will track upcoming restrictions
1098 if (contentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
1099 if (StaticPrefs::security_disallow_privileged_https_subdocuments_loads() &&
1100 (finalURI->SchemeIs("http") || finalURI->SchemeIs("https"))) {
1101 #ifdef DEBUG
1102 MOZ_CRASH("Disallowing SystemPrincipal load of subdocuments on HTTP(S).");
1103 #endif
1104 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
1105 return NS_ERROR_CONTENT_BLOCKED;
1107 if ((StaticPrefs::security_disallow_privileged_data_subdocuments_loads()) &&
1108 (finalURI->SchemeIs("data"))) {
1109 #ifdef DEBUG
1110 MOZ_CRASH(
1111 "Disallowing SystemPrincipal load of subdocuments on data URL.");
1112 #endif
1113 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
1114 return NS_ERROR_CONTENT_BLOCKED;
1118 if (cancelNonLocalSystemPrincipal) {
1119 MOZ_ASSERT(false, "SystemPrincipal must not load remote documents.");
1120 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
1121 return NS_ERROR_CONTENT_BLOCKED;
1123 return NS_OK;
1127 * Every protocol handler must set one of the five security flags
1128 * defined in nsIProtocolHandler - if not - deny the load.
1130 nsresult nsContentSecurityManager::CheckChannelHasProtocolSecurityFlag(
1131 nsIChannel* aChannel) {
1132 nsCOMPtr<nsIURI> uri;
1133 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
1134 NS_ENSURE_SUCCESS(rv, rv);
1136 nsAutoCString scheme;
1137 rv = uri->GetScheme(scheme);
1138 NS_ENSURE_SUCCESS(rv, rv);
1140 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
1141 NS_ENSURE_SUCCESS(rv, rv);
1143 nsCOMPtr<nsIProtocolHandler> handler;
1144 rv = ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
1145 NS_ENSURE_SUCCESS(rv, rv);
1147 uint32_t flags;
1148 rv = handler->DoGetProtocolFlags(uri, &flags);
1149 NS_ENSURE_SUCCESS(rv, rv);
1151 uint32_t securityFlagsSet = 0;
1152 if (flags & nsIProtocolHandler::URI_LOADABLE_BY_ANYONE) {
1153 securityFlagsSet += 1;
1155 if (flags & nsIProtocolHandler::URI_DANGEROUS_TO_LOAD) {
1156 securityFlagsSet += 1;
1158 if (flags & nsIProtocolHandler::URI_IS_UI_RESOURCE) {
1159 securityFlagsSet += 1;
1161 if (flags & nsIProtocolHandler::URI_IS_LOCAL_FILE) {
1162 securityFlagsSet += 1;
1164 if (flags & nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS) {
1165 securityFlagsSet += 1;
1168 // Ensure that only "1" valid security flags is set.
1169 if (securityFlagsSet == 1) {
1170 return NS_OK;
1173 MOZ_ASSERT(false, "protocol must use one valid security flag");
1174 return NS_ERROR_CONTENT_BLOCKED;
1178 * Based on the security flags provided in the loadInfo of the channel,
1179 * doContentSecurityCheck() performs the following content security checks
1180 * before opening the channel:
1182 * (1) Same Origin Policy Check (if applicable)
1183 * (2) Allow Cross Origin but perform sanity checks whether a principal
1184 * is allowed to access the following URL.
1185 * (3) Perform CORS check (if applicable)
1186 * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...)
1188 * @param aChannel
1189 * The channel to perform the security checks on.
1190 * @param aInAndOutListener
1191 * The streamListener that is passed to channel->AsyncOpen() that is now
1192 * potentially wrappend within nsCORSListenerProxy() and becomes the
1193 * corsListener that now needs to be set as new streamListener on the channel.
1195 nsresult nsContentSecurityManager::doContentSecurityCheck(
1196 nsIChannel* aChannel, nsCOMPtr<nsIStreamListener>& aInAndOutListener) {
1197 NS_ENSURE_ARG(aChannel);
1198 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
1199 if (MOZ_UNLIKELY(MOZ_LOG_TEST(sCSMLog, LogLevel::Verbose))) {
1200 DebugDoContentSecurityCheck(aChannel, loadInfo);
1203 nsresult rv = CheckAllowLoadInSystemPrivilegedContext(aChannel);
1204 NS_ENSURE_SUCCESS(rv, rv);
1206 rv = CheckChannelHasProtocolSecurityFlag(aChannel);
1207 NS_ENSURE_SUCCESS(rv, rv);
1209 // if dealing with a redirected channel then we have already installed
1210 // streamlistener and redirect proxies and so we are done.
1211 if (loadInfo->GetInitialSecurityCheckDone()) {
1212 return NS_OK;
1215 // make sure that only one of the five security flags is set in the loadinfo
1216 // e.g. do not require same origin and allow cross origin at the same time
1217 rv = ValidateSecurityFlags(loadInfo);
1218 NS_ENSURE_SUCCESS(rv, rv);
1220 if (loadInfo->GetSecurityMode() ==
1221 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) {
1222 rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
1223 NS_ENSURE_SUCCESS(rv, rv);
1226 rv = CheckChannel(aChannel);
1227 NS_ENSURE_SUCCESS(rv, rv);
1229 // Perform all ContentPolicy checks (MixedContent, CSP, ...)
1230 rv = DoContentSecurityChecks(aChannel, loadInfo);
1231 NS_ENSURE_SUCCESS(rv, rv);
1233 // Apply this after CSP to match Chrome.
1234 rv = CheckFTPSubresourceLoad(aChannel);
1235 NS_ENSURE_SUCCESS(rv, rv);
1237 // now lets set the initialSecurityFlag for subsequent calls
1238 loadInfo->SetInitialSecurityCheckDone(true);
1240 // all security checks passed - lets allow the load
1241 return NS_OK;
1244 NS_IMETHODIMP
1245 nsContentSecurityManager::AsyncOnChannelRedirect(
1246 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aRedirFlags,
1247 nsIAsyncVerifyRedirectCallback* aCb) {
1248 // Since we compare the principal from the loadInfo to the URI's
1249 // princicpal, it's possible that the checks fail when doing an internal
1250 // redirect. We can just return early instead, since we should never
1251 // need to block an internal redirect.
1252 if (aRedirFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
1253 aCb->OnRedirectVerifyCallback(NS_OK);
1254 return NS_OK;
1257 nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->LoadInfo();
1258 nsresult rv = CheckChannel(aNewChannel);
1259 if (NS_SUCCEEDED(rv)) {
1260 rv = CheckFTPSubresourceLoad(aNewChannel);
1262 if (NS_FAILED(rv)) {
1263 aOldChannel->Cancel(rv);
1264 return rv;
1267 // Also verify that the redirecting server is allowed to redirect to the
1268 // given URI
1269 nsCOMPtr<nsIPrincipal> oldPrincipal;
1270 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
1271 aOldChannel, getter_AddRefs(oldPrincipal));
1273 nsCOMPtr<nsIURI> newURI;
1274 Unused << NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
1275 NS_ENSURE_STATE(oldPrincipal && newURI);
1277 // Do not allow insecure redirects to data: URIs
1278 if (!AllowInsecureRedirectToDataURI(aNewChannel)) {
1279 // cancel the old channel and return an error
1280 aOldChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
1281 return NS_ERROR_CONTENT_BLOCKED;
1284 const uint32_t flags =
1285 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
1286 nsIScriptSecurityManager::DISALLOW_SCRIPT;
1287 rv = nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal(
1288 oldPrincipal, newURI, flags, loadInfo->GetInnerWindowID());
1289 NS_ENSURE_SUCCESS(rv, rv);
1291 aCb->OnRedirectVerifyCallback(NS_OK);
1292 return NS_OK;
1295 static void AddLoadFlags(nsIRequest* aRequest, nsLoadFlags aNewFlags) {
1296 nsLoadFlags flags;
1297 aRequest->GetLoadFlags(&flags);
1298 flags |= aNewFlags;
1299 aRequest->SetLoadFlags(flags);
1303 * Check that this channel passes all security checks. Returns an error code
1304 * if this requesst should not be permitted.
1306 nsresult nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) {
1307 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
1308 nsCOMPtr<nsIURI> uri;
1309 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
1310 NS_ENSURE_SUCCESS(rv, rv);
1312 // Handle cookie policies
1313 uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
1314 if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
1315 // We shouldn't have the SEC_COOKIES_SAME_ORIGIN flag for top level loads
1316 MOZ_ASSERT(loadInfo->GetExternalContentPolicyType() !=
1317 ExtContentPolicy::TYPE_DOCUMENT);
1318 nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal();
1320 // It doesn't matter what we pass for the second, data-inherits, argument.
1321 // Any protocol which inherits won't pay attention to cookies anyway.
1322 rv = loadingPrincipal->CheckMayLoad(uri, false);
1323 if (NS_FAILED(rv)) {
1324 AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
1326 } else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) {
1327 AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
1330 nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
1332 // CORS mode is handled by nsCORSListenerProxy
1333 if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) {
1334 if (NS_HasBeenCrossOrigin(aChannel)) {
1335 loadInfo->MaybeIncreaseTainting(LoadTainting::CORS);
1337 return NS_OK;
1340 // Allow subresource loads if TriggeringPrincipal is the SystemPrincipal.
1341 if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
1342 loadInfo->GetExternalContentPolicyType() !=
1343 ExtContentPolicy::TYPE_DOCUMENT &&
1344 loadInfo->GetExternalContentPolicyType() !=
1345 ExtContentPolicy::TYPE_SUBDOCUMENT) {
1346 return NS_OK;
1349 // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
1350 if ((securityMode ==
1351 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT) ||
1352 (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
1353 rv = DoSOPChecks(uri, loadInfo, aChannel);
1354 NS_ENSURE_SUCCESS(rv, rv);
1357 if ((securityMode ==
1358 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) ||
1359 (securityMode ==
1360 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL)) {
1361 if (NS_HasBeenCrossOrigin(aChannel)) {
1362 NS_ENSURE_FALSE(loadInfo->GetDontFollowRedirects(), NS_ERROR_DOM_BAD_URI);
1363 loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque);
1365 // Please note that DoCheckLoadURIChecks should only be enforced for
1366 // cross origin requests. If the flag SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
1367 // is set within the loadInfo, then then CheckLoadURIWithPrincipal is
1368 // performed within nsCorsListenerProxy
1369 rv = DoCheckLoadURIChecks(uri, loadInfo);
1370 NS_ENSURE_SUCCESS(rv, rv);
1371 // TODO: Bug 1371237
1372 // consider calling SetBlockedRequest in
1373 // nsContentSecurityManager::CheckChannel
1376 return NS_OK;
1379 // ==== nsIContentSecurityManager implementation =====
1381 NS_IMETHODIMP
1382 nsContentSecurityManager::PerformSecurityCheck(
1383 nsIChannel* aChannel, nsIStreamListener* aStreamListener,
1384 nsIStreamListener** outStreamListener) {
1385 nsCOMPtr<nsIStreamListener> inAndOutListener = aStreamListener;
1386 nsresult rv = doContentSecurityCheck(aChannel, inAndOutListener);
1387 NS_ENSURE_SUCCESS(rv, rv);
1389 inAndOutListener.forget(outStreamListener);
1390 return NS_OK;