nsContentSecurityManager.cpp (68908B)
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/. */ 6 7 #include "nsContentSecurityManager.h" 8 9 #include "js/RegExp.h" 10 #include "jsapi.h" 11 #include "mozilla/BasePrincipal.h" 12 #include "mozilla/ClearOnShutdown.h" 13 #include "mozilla/CmdLineAndEnvUtils.h" 14 #include "mozilla/Components.h" 15 #include "mozilla/ExtensionPolicyService.h" 16 #include "mozilla/Logging.h" 17 #include "mozilla/Maybe.h" 18 #include "mozilla/Preferences.h" 19 #include "mozilla/StaticPrefs_content.h" 20 #include "mozilla/StaticPrefs_dom.h" 21 #include "mozilla/StaticPrefs_security.h" 22 #include "mozilla/dom/BrowserChild.h" 23 #include "mozilla/dom/ContentChild.h" 24 #include "mozilla/dom/ContentParent.h" 25 #include "mozilla/dom/Document.h" 26 #include "mozilla/dom/Element.h" 27 #include "mozilla/dom/PolicyContainer.h" 28 #include "mozilla/dom/nsMixedContentBlocker.h" 29 #include "mozilla/extensions/WebExtensionPolicy.h" 30 #include "mozilla/glean/DomSecurityMetrics.h" 31 #include "nsAboutProtocolUtils.h" 32 #include "nsArray.h" 33 #include "nsCORSListenerProxy.h" 34 #include "nsContentPolicyUtils.h" 35 #include "nsContentSecurityUtils.h" 36 #include "nsContentUtils.h" 37 #include "nsDataHandler.h" 38 #include "nsEscape.h" 39 #include "nsIChannel.h" 40 #include "nsIContentPolicy.h" 41 #include "nsIHttpChannelInternal.h" 42 #include "nsILoadInfo.h" 43 #include "nsIMIMEService.h" 44 #include "nsINode.h" 45 #include "nsIOService.h" 46 #include "nsIParentChannel.h" 47 #include "nsIRedirectHistoryEntry.h" 48 #include "nsIStreamListener.h" 49 #include "nsIXPConnect.h" 50 #include "nsIXULRuntime.h" 51 #include "nsMimeTypes.h" 52 #include "nsNetUtil.h" 53 #include "nsReadableUtils.h" 54 #include "nsSandboxFlags.h" 55 #include "nsScriptSecurityManager.h" 56 #include "xpcpublic.h" 57 58 using namespace mozilla; 59 using namespace mozilla::dom; 60 61 NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager, 62 nsIChannelEventSink) 63 64 mozilla::LazyLogModule sCSMLog("CSMLog"); 65 mozilla::LazyLogModule sUELLog("UnexpectedLoad"); 66 67 // These first two are used for off-the-main-thread checks of 68 // general.config.filename 69 // (which can't be checked off-main-thread). 70 Atomic<bool, mozilla::Relaxed> sJSHacksChecked(false); 71 Atomic<bool, mozilla::Relaxed> sJSHacksPresent(false); 72 Atomic<bool, mozilla::Relaxed> sCSSHacksChecked(false); 73 Atomic<bool, mozilla::Relaxed> sCSSHacksPresent(false); 74 75 /* static */ 76 bool nsContentSecurityManager::AllowTopLevelNavigationToDataURI( 77 nsIChannel* aChannel) { 78 // Let's block all toplevel document navigations to a data: URI. 79 // In all cases where the toplevel document is navigated to a 80 // data: URI the triggeringPrincipal is a contentPrincipal, or 81 // a NullPrincipal. In other cases, e.g. typing a data: URL into 82 // the URL-Bar, the triggeringPrincipal is a SystemPrincipal; 83 // we don't want to block those loads. Only exception, loads coming 84 // from an external applicaton (e.g. Thunderbird) don't load 85 // using a contentPrincipal, but we want to block those loads. 86 if (!StaticPrefs::security_data_uri_block_toplevel_data_uri_navigations()) { 87 return true; 88 } 89 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 90 if (loadInfo->GetExternalContentPolicyType() != 91 ExtContentPolicy::TYPE_DOCUMENT) { 92 return true; 93 } 94 if (loadInfo->GetForceAllowDataURI()) { 95 // if the loadinfo explicitly allows the data URI navigation, let's allow it 96 // now 97 return true; 98 } 99 nsCOMPtr<nsIURI> uri; 100 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 101 NS_ENSURE_SUCCESS(rv, true); 102 if (!uri->SchemeIs("data")) { 103 return true; 104 } 105 106 nsAutoCString spec; 107 rv = uri->GetSpec(spec); 108 NS_ENSURE_SUCCESS(rv, true); 109 nsAutoCString contentType; 110 bool base64; 111 rv = nsDataHandler::ParseURI(spec, contentType, nullptr, base64, nullptr); 112 NS_ENSURE_SUCCESS(rv, true); 113 114 // Allow data: images as long as they are not SVGs 115 if (StringBeginsWith(contentType, "image/"_ns) && 116 !contentType.EqualsLiteral(IMAGE_SVG_XML)) { 117 return true; 118 } 119 // Allow all data: PDFs. or JSON documents 120 if (contentType.EqualsLiteral(APPLICATION_JSON) || 121 contentType.EqualsLiteral(TEXT_JSON) || 122 contentType.EqualsLiteral(APPLICATION_PDF)) { 123 return true; 124 } 125 // Redirecting to a toplevel data: URI is not allowed, hence we make 126 // sure the RedirectChain is empty. 127 if (!loadInfo->GetLoadTriggeredFromExternal() && 128 loadInfo->TriggeringPrincipal()->IsSystemPrincipal() && 129 loadInfo->RedirectChain().IsEmpty()) { 130 return true; 131 } 132 133 ReportBlockedDataURI(uri, loadInfo); 134 135 return false; 136 } 137 138 void nsContentSecurityManager::ReportBlockedDataURI(nsIURI* aURI, 139 nsILoadInfo* aLoadInfo, 140 bool aIsRedirect) { 141 // We're going to block the request, construct the localized error message to 142 // report to the console. 143 nsAutoCString dataSpec; 144 aURI->GetSpec(dataSpec); 145 if (dataSpec.Length() > 50) { 146 dataSpec.Truncate(50); 147 dataSpec.AppendLiteral("..."); 148 } 149 AutoTArray<nsString, 1> params; 150 CopyUTF8toUTF16(NS_UnescapeURL(dataSpec), *params.AppendElement()); 151 nsAutoString errorText; 152 const char* stringID = 153 aIsRedirect ? "BlockRedirectToDataURI" : "BlockTopLevelDataURINavigation"; 154 nsresult rv = nsContentUtils::FormatLocalizedString( 155 nsContentUtils::eSECURITY_PROPERTIES, stringID, params, errorText); 156 if (NS_FAILED(rv)) { 157 return; 158 } 159 160 // Report the localized error message to the console for the loading 161 // BrowsingContext's current inner window. 162 RefPtr<BrowsingContext> target = aLoadInfo->GetBrowsingContext(); 163 nsContentUtils::ReportToConsoleByWindowID( 164 errorText, nsIScriptError::warningFlag, "DATA_URI_BLOCKED"_ns, 165 target ? target->GetCurrentInnerWindowId() : 0); 166 } 167 168 /* static */ 169 bool nsContentSecurityManager::AllowInsecureRedirectToDataURI( 170 nsIChannel* aNewChannel) { 171 nsCOMPtr<nsILoadInfo> loadInfo = aNewChannel->LoadInfo(); 172 if (loadInfo->GetExternalContentPolicyType() != 173 ExtContentPolicy::TYPE_SCRIPT) { 174 return true; 175 } 176 nsCOMPtr<nsIURI> newURI; 177 nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); 178 if (NS_FAILED(rv) || !newURI || !newURI->SchemeIs("data")) { 179 return true; 180 } 181 182 // Web Extensions are exempt from that restriction and are allowed to redirect 183 // a channel to a data: URI. When a web extension redirects a channel, we set 184 // a flag on the loadInfo which allows us to identify such redirects here. 185 if (loadInfo->GetAllowInsecureRedirectToDataURI()) { 186 return true; 187 } 188 189 ReportBlockedDataURI(newURI, loadInfo, true); 190 191 return false; 192 } 193 194 static nsresult ValidateSecurityFlags(nsILoadInfo* aLoadInfo) { 195 nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode(); 196 197 // We should never perform a security check on a loadInfo that uses the flag 198 // SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, because that is only used for 199 // temporary loadInfos used for explicit nsIContentPolicy checks, but never be 200 // set as a security flag on an actual channel. 201 if (securityMode != 202 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT && 203 securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED && 204 securityMode != 205 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT && 206 securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL && 207 securityMode != nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) { 208 MOZ_ASSERT( 209 false, 210 "need one securityflag from nsILoadInfo to perform security checks"); 211 return NS_ERROR_FAILURE; 212 } 213 214 // all good, found the right security flags 215 return NS_OK; 216 } 217 218 static already_AddRefed<nsIPrincipal> GetExtensionSandboxPrincipal( 219 nsILoadInfo* aLoadInfo) { 220 // An extension is allowed to load resources from itself when its pages are 221 // loaded into a sandboxed frame. Extension resources in a sandbox have 222 // a null principal and no access to extension APIs. See "sandbox" in 223 // MDN extension docs for more information. 224 if (!aLoadInfo->TriggeringPrincipal()->GetIsNullPrincipal()) { 225 return nullptr; 226 } 227 RefPtr<Document> doc; 228 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc)); 229 if (!doc || !(doc->GetSandboxFlags() & SANDBOXED_ORIGIN)) { 230 return nullptr; 231 } 232 233 // node principal is also a null principal here, so we need to 234 // create a principal using documentURI, which is the moz-extension 235 // uri for the page if this is an extension sandboxed page. 236 nsCOMPtr<nsIPrincipal> docPrincipal = BasePrincipal::CreateContentPrincipal( 237 doc->GetDocumentURI(), doc->NodePrincipal()->OriginAttributesRef()); 238 239 if (!BasePrincipal::Cast(docPrincipal)->AddonPolicy()) { 240 return nullptr; 241 } 242 return docPrincipal.forget(); 243 } 244 245 static bool IsImageLoadInEditorAppType(nsILoadInfo* aLoadInfo) { 246 // Editor apps get special treatment here, editors can load images 247 // from anywhere. This allows editor to insert images from file:// 248 // into documents that are being edited. 249 nsContentPolicyType type = aLoadInfo->InternalContentPolicyType(); 250 if (type != nsIContentPolicy::TYPE_INTERNAL_IMAGE && 251 type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD && 252 type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON && 253 type != nsIContentPolicy::TYPE_IMAGESET) { 254 return false; 255 } 256 257 auto appType = nsIDocShell::APP_TYPE_UNKNOWN; 258 nsINode* node = aLoadInfo->LoadingNode(); 259 if (!node) { 260 return false; 261 } 262 Document* doc = node->OwnerDoc(); 263 if (!doc) { 264 return false; 265 } 266 267 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell(); 268 if (!docShellTreeItem) { 269 return false; 270 } 271 272 nsCOMPtr<nsIDocShellTreeItem> root; 273 docShellTreeItem->GetInProcessRootTreeItem(getter_AddRefs(root)); 274 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root)); 275 if (docShell) { 276 appType = docShell->GetAppType(); 277 } 278 279 return appType == nsIDocShell::APP_TYPE_EDITOR; 280 } 281 282 static nsresult DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) { 283 // In practice, these DTDs are just used for localization, so applying the 284 // same principal check as Fluent. 285 if (aLoadInfo->InternalContentPolicyType() == 286 nsIContentPolicy::TYPE_INTERNAL_DTD) { 287 RefPtr<Document> doc; 288 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc)); 289 bool allowed = false; 290 aLoadInfo->TriggeringPrincipal()->IsL10nAllowed( 291 doc ? doc->GetDocumentURI() : nullptr, &allowed); 292 293 return allowed ? NS_OK : NS_ERROR_DOM_BAD_URI; 294 } 295 296 // This is used in order to allow a privileged DOMParser to parse documents 297 // that need to access localization DTDs. We just allow through 298 // TYPE_INTERNAL_FORCE_ALLOWED_DTD no matter what the triggering principal is. 299 if (aLoadInfo->InternalContentPolicyType() == 300 nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD) { 301 return NS_OK; 302 } 303 304 if (IsImageLoadInEditorAppType(aLoadInfo)) { 305 return NS_OK; 306 } 307 308 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal(); 309 nsCOMPtr<nsIPrincipal> addonPrincipal = 310 GetExtensionSandboxPrincipal(aLoadInfo); 311 if (addonPrincipal) { 312 // call CheckLoadURIWithPrincipal() as below to continue other checks, but 313 // with the addon principal. 314 triggeringPrincipal = addonPrincipal; 315 } 316 317 // Only call CheckLoadURIWithPrincipal() using the TriggeringPrincipal and not 318 // the LoadingPrincipal when SEC_ALLOW_CROSS_ORIGIN_* security flags are set, 319 // to allow, e.g. user stylesheets to load chrome:// URIs. 320 return nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal( 321 triggeringPrincipal, aURI, aLoadInfo->CheckLoadURIFlags(), 322 aLoadInfo->GetInnerWindowID()); 323 } 324 325 static bool URIHasFlags(nsIURI* aURI, uint32_t aURIFlags) { 326 bool hasFlags; 327 nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags); 328 NS_ENSURE_SUCCESS(rv, false); 329 330 return hasFlags; 331 } 332 333 static nsresult DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo, 334 nsIChannel* aChannel) { 335 if (aLoadInfo->GetAllowChrome() && 336 (URIHasFlags(aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE) || 337 nsContentUtils::SchemeIs(aURI, "moz-safe-about"))) { 338 // UI resources are allowed. 339 return DoCheckLoadURIChecks(aURI, aLoadInfo); 340 } 341 342 if (NS_HasBeenCrossOrigin(aChannel, true)) { 343 NS_SetRequestBlockingReason(aLoadInfo, 344 nsILoadInfo::BLOCKING_REASON_NOT_SAME_ORIGIN); 345 return NS_ERROR_DOM_BAD_URI; 346 } 347 348 return NS_OK; 349 } 350 351 // Determine which principal to use in DoCORSChecks. Normally, we do CORS 352 // checks using the LoadingPrincipal (whose Origin comes from the host 353 // document). But under certain configurations/situations, we instead use 354 // the TriggeringPrincipal() (whose Origin comes from the specific resource 355 // that initiated the request). 356 static nsIPrincipal* DeterminePrincipalForCORSChecks(nsILoadInfo* aLoadInfo) { 357 nsIPrincipal* const triggeringPrincipal = aLoadInfo->TriggeringPrincipal(); 358 359 if (StaticPrefs::content_cors_use_triggering_principal()) { 360 // This pref forces us to use the TriggeringPrincipal. 361 // TODO(dholbert): Remove this special-case, perhaps right after we 362 // fix bug 1982916 which requires it for a test. 363 return triggeringPrincipal; 364 } 365 366 if (!StaticPrefs::extensions_content_web_accessible_enabled() && 367 triggeringPrincipal->GetIsAddonOrExpandedAddonPrincipal()) { 368 // If we get here, then we know: 369 // * we want to allow MV2 WebExtensions to access their own resources 370 // regardless of whether those are listed in 'web_accessible_resources' in 371 // their manifest (this is nonstandard but it's a legacy thing we allow). 372 // * this load was initiated by a WebExtension (possibly running in a 373 // content script in the context of a web page). 374 // 375 // Hence: in this case, we use the TriggeringPrincipal for our CORS checks 376 // (so that a WebExtension requesting its own resources will be treated as 377 // same-origin, rather than being rejected as a cross-origin request from 378 // the page's origin). 379 // 380 // NOTE: Technically we should also check whether the extension uses MV2 381 // here, since this pref is specific to MV2. But that's not strictly 382 // necessary because we already unconditionally block MV3-WebExtension 383 // content-loads of this type at a different level (in 384 // nsScriptSecurityManager::CheckLoadURIWithPrincipal). 385 return triggeringPrincipal; 386 } 387 388 // Otherwise we use the LoadingPrincipal. 389 return aLoadInfo->GetLoadingPrincipal(); 390 } 391 392 static nsresult DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo, 393 nsCOMPtr<nsIStreamListener>& aInAndOutListener) { 394 MOZ_RELEASE_ASSERT(aInAndOutListener, 395 "can not perform CORS checks without a listener"); 396 397 // No need to set up CORS if TriggeringPrincipal is the SystemPrincipal. 398 if (aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { 399 return NS_OK; 400 } 401 402 nsIPrincipal* principalForCORSCheck = 403 DeterminePrincipalForCORSChecks(aLoadInfo); 404 405 RefPtr<nsCORSListenerProxy> corsListener = new nsCORSListenerProxy( 406 aInAndOutListener, principalForCORSCheck, 407 aLoadInfo->GetCookiePolicy() == nsILoadInfo::SEC_COOKIES_INCLUDE); 408 // XXX: @arg: DataURIHandling::Allow 409 // lets use DataURIHandling::Allow for now and then decide on callsite basis. 410 // see also: 411 // http://mxr.mozilla.org/mozilla-central/source/dom/security/nsCORSListenerProxy.h#33 412 nsresult rv = corsListener->Init(aChannel, DataURIHandling::Allow); 413 NS_ENSURE_SUCCESS(rv, rv); 414 aInAndOutListener = corsListener; 415 return NS_OK; 416 } 417 418 static nsresult DoContentSecurityChecks(nsIChannel* aChannel, 419 nsILoadInfo* aLoadInfo) { 420 ExtContentPolicyType contentPolicyType = 421 aLoadInfo->GetExternalContentPolicyType(); 422 423 nsCOMPtr<nsIURI> uri; 424 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 425 NS_ENSURE_SUCCESS(rv, rv); 426 427 switch (contentPolicyType) { 428 case ExtContentPolicy::TYPE_XMLHTTPREQUEST: { 429 #ifdef DEBUG 430 { 431 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode(); 432 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE, 433 "type_xml requires requestingContext of type Document"); 434 } 435 #endif 436 break; 437 } 438 439 case ExtContentPolicy::TYPE_DTD: { 440 #ifdef DEBUG 441 { 442 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode(); 443 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE, 444 "type_dtd requires requestingContext of type Document"); 445 } 446 #endif 447 break; 448 } 449 450 case ExtContentPolicy::TYPE_MEDIA: { 451 #ifdef DEBUG 452 { 453 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode(); 454 MOZ_ASSERT(!node || node->NodeType() == nsINode::ELEMENT_NODE, 455 "type_media requires requestingContext of type Element"); 456 } 457 #endif 458 break; 459 } 460 461 case ExtContentPolicy::TYPE_WEBSOCKET: { 462 // Websockets have to use the proxied URI: 463 // ws:// instead of http:// for CSP checks 464 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = 465 do_QueryInterface(aChannel); 466 MOZ_ASSERT(httpChannelInternal); 467 if (httpChannelInternal) { 468 rv = httpChannelInternal->GetProxyURI(getter_AddRefs(uri)); 469 MOZ_ASSERT(NS_SUCCEEDED(rv)); 470 } 471 break; 472 } 473 474 case ExtContentPolicy::TYPE_XSLT: { 475 #ifdef DEBUG 476 { 477 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode(); 478 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE, 479 "type_xslt requires requestingContext of type Document"); 480 } 481 #endif 482 break; 483 } 484 485 case ExtContentPolicy::TYPE_BEACON: { 486 #ifdef DEBUG 487 { 488 nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode(); 489 MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE, 490 "type_beacon requires requestingContext of type Document"); 491 } 492 #endif 493 break; 494 } 495 496 case ExtContentPolicy::TYPE_OTHER: 497 case ExtContentPolicy::TYPE_SCRIPT: 498 case ExtContentPolicy::TYPE_IMAGE: 499 case ExtContentPolicy::TYPE_STYLESHEET: 500 case ExtContentPolicy::TYPE_OBJECT: 501 case ExtContentPolicy::TYPE_DOCUMENT: 502 case ExtContentPolicy::TYPE_SUBDOCUMENT: 503 case ExtContentPolicy::TYPE_PING: 504 case ExtContentPolicy::TYPE_FONT: 505 case ExtContentPolicy::TYPE_UA_FONT: 506 case ExtContentPolicy::TYPE_CSP_REPORT: 507 case ExtContentPolicy::TYPE_WEB_MANIFEST: 508 case ExtContentPolicy::TYPE_FETCH: 509 case ExtContentPolicy::TYPE_IMAGESET: 510 case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD: 511 case ExtContentPolicy::TYPE_SPECULATIVE: 512 case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA: 513 case ExtContentPolicy::TYPE_WEB_TRANSPORT: 514 case ExtContentPolicy::TYPE_WEB_IDENTITY: 515 case ExtContentPolicy::TYPE_JSON: 516 break; 517 518 case ExtContentPolicy::TYPE_INVALID: 519 MOZ_ASSERT(false, 520 "can not perform security check without a valid contentType"); 521 // Do not add default: so that compilers can catch the missing case. 522 } 523 524 int16_t shouldLoad = nsIContentPolicy::ACCEPT; 525 rv = NS_CheckContentLoadPolicy(uri, aLoadInfo, &shouldLoad, 526 nsContentUtils::GetContentPolicy()); 527 528 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { 529 NS_SetRequestBlockingReasonIfNull( 530 aLoadInfo, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL); 531 532 if (NS_SUCCEEDED(rv) && 533 (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT || 534 contentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT)) { 535 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) { 536 // for docshell loads we might have to return SHOW_ALT. 537 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT; 538 } 539 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) { 540 return NS_ERROR_BLOCKED_BY_POLICY; 541 } 542 } 543 return NS_ERROR_CONTENT_BLOCKED; 544 } 545 546 return NS_OK; 547 } 548 549 static void LogHTTPSOnlyInfo(nsILoadInfo* aLoadInfo) { 550 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" httpsOnlyFirstStatus:")); 551 uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); 552 553 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) { 554 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - HTTPS_ONLY_UNINITIALIZED")); 555 } 556 if (httpsOnlyStatus & 557 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) { 558 MOZ_LOG(sCSMLog, LogLevel::Verbose, 559 (" - HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED")); 560 } 561 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED) { 562 MOZ_LOG(sCSMLog, LogLevel::Verbose, 563 (" - HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED")); 564 } 565 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { 566 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - HTTPS_ONLY_EXEMPT")); 567 } 568 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS) { 569 MOZ_LOG(sCSMLog, LogLevel::Verbose, 570 (" - HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS")); 571 } 572 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DOWNLOAD_IN_PROGRESS) { 573 MOZ_LOG(sCSMLog, LogLevel::Verbose, 574 (" - HTTPS_ONLY_DOWNLOAD_IN_PROGRESS")); 575 } 576 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) { 577 MOZ_LOG(sCSMLog, LogLevel::Verbose, 578 (" - HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE")); 579 } 580 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST) { 581 MOZ_LOG(sCSMLog, LogLevel::Verbose, 582 (" - HTTPS_ONLY_UPGRADED_HTTPS_FIRST")); 583 } 584 } 585 586 static void LogPrincipal(nsIPrincipal* aPrincipal, 587 const nsAString& aPrincipalName, 588 const uint8_t& aNestingLevel) { 589 nsPrintfCString aIndentationString("%*s", aNestingLevel * 2, ""); 590 591 if (aPrincipal && aPrincipal->IsSystemPrincipal()) { 592 MOZ_LOG(sCSMLog, LogLevel::Debug, 593 ("%s%s: SystemPrincipal\n", aIndentationString.get(), 594 NS_ConvertUTF16toUTF8(aPrincipalName).get())); 595 return; 596 } 597 if (aPrincipal) { 598 if (aPrincipal->GetIsNullPrincipal()) { 599 MOZ_LOG(sCSMLog, LogLevel::Debug, 600 ("%s%s: NullPrincipal\n", aIndentationString.get(), 601 NS_ConvertUTF16toUTF8(aPrincipalName).get())); 602 return; 603 } 604 if (aPrincipal->GetIsExpandedPrincipal()) { 605 nsCOMPtr<nsIExpandedPrincipal> expanded(do_QueryInterface(aPrincipal)); 606 nsAutoCString origin; 607 origin.AssignLiteral("[Expanded Principal ["); 608 609 StringJoinAppend(origin, ", "_ns, expanded->AllowList(), 610 [](nsACString& dest, nsIPrincipal* principal) { 611 nsAutoCString subOrigin; 612 DebugOnly<nsresult> rv = 613 principal->GetOrigin(subOrigin); 614 MOZ_ASSERT(NS_SUCCEEDED(rv)); 615 dest.Append(subOrigin); 616 }); 617 618 origin.AppendLiteral("]]"); 619 620 MOZ_LOG(sCSMLog, LogLevel::Debug, 621 ("%s%s: %s\n", aIndentationString.get(), 622 NS_ConvertUTF16toUTF8(aPrincipalName).get(), origin.get())); 623 return; 624 } 625 nsAutoCString principalSpec; 626 aPrincipal->GetAsciiSpec(principalSpec); 627 if (aPrincipalName.IsEmpty()) { 628 MOZ_LOG(sCSMLog, LogLevel::Debug, 629 ("%s - \"%s\"\n", aIndentationString.get(), principalSpec.get())); 630 } else { 631 MOZ_LOG( 632 sCSMLog, LogLevel::Debug, 633 ("%s%s: \"%s\"\n", aIndentationString.get(), 634 NS_ConvertUTF16toUTF8(aPrincipalName).get(), principalSpec.get())); 635 } 636 return; 637 } 638 MOZ_LOG(sCSMLog, LogLevel::Debug, 639 ("%s%s: nullptr\n", aIndentationString.get(), 640 NS_ConvertUTF16toUTF8(aPrincipalName).get())); 641 } 642 643 static void LogSecurityFlags(nsSecurityFlags securityFlags) { 644 struct DebugSecFlagType { 645 unsigned long secFlag; 646 char secTypeStr[128]; 647 }; 648 static const DebugSecFlagType secTypes[] = { 649 {nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 650 "SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK"}, 651 {nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, 652 "SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT"}, 653 {nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED, 654 "SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED"}, 655 {nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT, 656 "SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT"}, 657 {nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 658 "SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL"}, 659 {nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT, 660 "SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT"}, 661 {nsILoadInfo::SEC_COOKIES_DEFAULT, "SEC_COOKIES_DEFAULT"}, 662 {nsILoadInfo::SEC_COOKIES_INCLUDE, "SEC_COOKIES_INCLUDE"}, 663 {nsILoadInfo::SEC_COOKIES_SAME_ORIGIN, "SEC_COOKIES_SAME_ORIGIN"}, 664 {nsILoadInfo::SEC_COOKIES_OMIT, "SEC_COOKIES_OMIT"}, 665 {nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, "SEC_FORCE_INHERIT_PRINCIPAL"}, 666 {nsILoadInfo::SEC_ABOUT_BLANK_INHERITS, "SEC_ABOUT_BLANK_INHERITS"}, 667 {nsILoadInfo::SEC_ALLOW_CHROME, "SEC_ALLOW_CHROME"}, 668 {nsILoadInfo::SEC_DISALLOW_SCRIPT, "SEC_DISALLOW_SCRIPT"}, 669 {nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS, "SEC_DONT_FOLLOW_REDIRECTS"}, 670 {nsILoadInfo::SEC_LOAD_ERROR_PAGE, "SEC_LOAD_ERROR_PAGE"}, 671 {nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER, 672 "SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER"}}; 673 674 for (const DebugSecFlagType& flag : secTypes) { 675 if (securityFlags & flag.secFlag) { 676 // the logging level should be in sync with the logging level in 677 // DebugDoContentSecurityCheck() 678 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" - %s\n", flag.secTypeStr)); 679 } 680 } 681 } 682 static void DebugDoContentSecurityCheck(nsIChannel* aChannel, 683 nsILoadInfo* aLoadInfo) { 684 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel)); 685 686 MOZ_LOG(sCSMLog, LogLevel::Debug, ("\n#DebugDoContentSecurityCheck Begin\n")); 687 688 // we only log http channels, unless loglevel is 5. 689 if (httpChannel || MOZ_LOG_TEST(sCSMLog, LogLevel::Verbose)) { 690 MOZ_LOG(sCSMLog, LogLevel::Verbose, ("doContentSecurityCheck:\n")); 691 692 nsAutoCString remoteType; 693 if (XRE_IsParentProcess()) { 694 nsCOMPtr<nsIParentChannel> parentChannel; 695 NS_QueryNotificationCallbacks(aChannel, parentChannel); 696 if (parentChannel) { 697 parentChannel->GetRemoteType(remoteType); 698 } 699 } else { 700 remoteType.Assign( 701 mozilla::dom::ContentChild::GetSingleton()->GetRemoteType()); 702 } 703 MOZ_LOG(sCSMLog, LogLevel::Verbose, 704 (" processType: \"%s\"\n", remoteType.get())); 705 706 nsCOMPtr<nsIURI> channelURI; 707 nsAutoCString channelSpec; 708 nsAutoCString channelMethod; 709 NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI)); 710 if (channelURI) { 711 channelURI->GetSpec(channelSpec); 712 } 713 MOZ_LOG(sCSMLog, LogLevel::Verbose, 714 (" channelURI: \"%s\"\n", channelSpec.get())); 715 716 // Log HTTP-specific things 717 if (httpChannel) { 718 nsresult rv; 719 rv = httpChannel->GetRequestMethod(channelMethod); 720 if (!NS_FAILED(rv)) { 721 MOZ_LOG(sCSMLog, LogLevel::Verbose, 722 (" httpMethod: %s\n", channelMethod.get())); 723 } 724 } 725 726 // Log Principals 727 nsCOMPtr<nsIPrincipal> requestPrincipal = aLoadInfo->TriggeringPrincipal(); 728 LogPrincipal(aLoadInfo->GetLoadingPrincipal(), u"loadingPrincipal"_ns, 1); 729 LogPrincipal(requestPrincipal, u"triggeringPrincipal"_ns, 1); 730 LogPrincipal(aLoadInfo->PrincipalToInherit(), u"principalToInherit"_ns, 1); 731 732 // Log Redirect Chain 733 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" redirectChain:\n")); 734 for (nsIRedirectHistoryEntry* redirectHistoryEntry : 735 aLoadInfo->RedirectChain()) { 736 nsCOMPtr<nsIPrincipal> principal; 737 redirectHistoryEntry->GetPrincipal(getter_AddRefs(principal)); 738 LogPrincipal(principal, u""_ns, 2); 739 } 740 741 MOZ_LOG(sCSMLog, LogLevel::Verbose, 742 (" internalContentPolicyType: %s\n", 743 NS_CP_ContentTypeName(aLoadInfo->InternalContentPolicyType()))); 744 MOZ_LOG(sCSMLog, LogLevel::Verbose, 745 (" externalContentPolicyType: %s\n", 746 NS_CP_ContentTypeName(aLoadInfo->GetExternalContentPolicyType()))); 747 MOZ_LOG(sCSMLog, LogLevel::Verbose, 748 (" upgradeInsecureRequests: %s\n", 749 aLoadInfo->GetUpgradeInsecureRequests() ? "true" : "false")); 750 MOZ_LOG(sCSMLog, LogLevel::Verbose, 751 (" initialSecurityChecksDone: %s\n", 752 aLoadInfo->GetInitialSecurityCheckDone() ? "true" : "false")); 753 MOZ_LOG(sCSMLog, LogLevel::Verbose, 754 (" allowDeprecatedSystemRequests: %s\n", 755 aLoadInfo->GetAllowDeprecatedSystemRequests() ? "true" : "false")); 756 MOZ_LOG(sCSMLog, LogLevel::Verbose, 757 (" schemelessInput: %d\n", aLoadInfo->GetSchemelessInput())); 758 759 // Log CSPrequestPrincipal 760 nsCOMPtr<nsIPolicyContainer> policyContainer = 761 aLoadInfo->GetPolicyContainer(); 762 nsCOMPtr<nsIContentSecurityPolicy> csp = 763 PolicyContainer::GetCSP(policyContainer); 764 MOZ_LOG(sCSMLog, LogLevel::Debug, (" CSP:")); 765 if (csp) { 766 nsAutoString parsedPolicyStr; 767 uint32_t count = 0; 768 csp->GetPolicyCount(&count); 769 for (uint32_t i = 0; i < count; ++i) { 770 csp->GetPolicyString(i, parsedPolicyStr); 771 // we need to add quotation marks, as otherwise yaml parsers may fail 772 // with CSP directives 773 // no need to escape quote marks in the parsed policy string, as URLs in 774 // there are already encoded 775 MOZ_LOG(sCSMLog, LogLevel::Debug, 776 (" - \"%s\"\n", NS_ConvertUTF16toUTF8(parsedPolicyStr).get())); 777 } 778 } 779 780 // Security Flags 781 MOZ_LOG(sCSMLog, LogLevel::Verbose, (" securityFlags:")); 782 LogSecurityFlags(aLoadInfo->GetSecurityFlags()); 783 // HTTPS-Only 784 LogHTTPSOnlyInfo(aLoadInfo); 785 786 MOZ_LOG(sCSMLog, LogLevel::Debug, ("\n#DebugDoContentSecurityCheck End\n")); 787 } 788 } 789 790 /* static */ 791 void nsContentSecurityManager::MeasureUnexpectedPrivilegedLoads( 792 nsILoadInfo* aLoadInfo, nsIURI* aFinalURI, const nsACString& aRemoteType) { 793 if (!StaticPrefs::dom_security_unexpected_system_load_telemetry_enabled()) { 794 return; 795 } 796 nsContentSecurityUtils::DetectJsHacks(); 797 nsContentSecurityUtils::DetectCssHacks(); 798 // The detection only work on the main-thread. 799 // To avoid races and early reports, we need to ensure the checks actually 800 // happened. 801 if (MOZ_UNLIKELY(sJSHacksPresent || !sJSHacksChecked || sCSSHacksPresent || 802 !sCSSHacksChecked)) { 803 return; 804 } 805 806 ExtContentPolicyType contentPolicyType = 807 aLoadInfo->GetExternalContentPolicyType(); 808 // restricting reported types to script, styles and documents 809 // to be continued in follow-ups of bug 1697163. 810 if (contentPolicyType != ExtContentPolicyType::TYPE_SCRIPT && 811 contentPolicyType != ExtContentPolicyType::TYPE_STYLESHEET && 812 contentPolicyType != ExtContentPolicyType::TYPE_DOCUMENT) { 813 return; 814 } 815 816 // Gather redirected schemes in string 817 nsAutoCString loggedRedirects; 818 const nsTArray<nsCOMPtr<nsIRedirectHistoryEntry>>& redirects = 819 aLoadInfo->RedirectChain(); 820 if (!redirects.IsEmpty()) { 821 nsCOMPtr<nsIRedirectHistoryEntry> end = redirects.LastElement(); 822 for (nsIRedirectHistoryEntry* entry : redirects) { 823 nsCOMPtr<nsIPrincipal> principal; 824 entry->GetPrincipal(getter_AddRefs(principal)); 825 if (principal) { 826 nsAutoCString scheme; 827 principal->GetScheme(scheme); 828 loggedRedirects.Append(scheme); 829 if (entry != end) { 830 loggedRedirects.AppendLiteral(", "); 831 } 832 } 833 } 834 } 835 836 nsAutoCString uriString; 837 if (aFinalURI) { 838 aFinalURI->GetAsciiSpec(uriString); 839 } 840 FilenameTypeAndDetails fileNameTypeAndDetails = 841 nsContentSecurityUtils::FilenameToFilenameType(uriString, true); 842 843 nsCString loggedFileDetails = "unknown"_ns; 844 if (fileNameTypeAndDetails.second.isSome()) { 845 loggedFileDetails.Assign(fileNameTypeAndDetails.second.value()); 846 } 847 // sanitize remoteType because it may contain sensitive 848 // info, like URLs. e.g. `webIsolated=https://example.com` 849 nsAutoCString loggedRemoteType(dom::RemoteTypePrefix(aRemoteType)); 850 nsAutoCString loggedContentType(NS_CP_ContentTypeName(contentPolicyType)); 851 852 MOZ_LOG(sUELLog, LogLevel::Debug, ("UnexpectedPrivilegedLoadTelemetry:\n")); 853 MOZ_LOG(sUELLog, LogLevel::Debug, 854 ("- contentType: %s\n", loggedContentType.get())); 855 MOZ_LOG(sUELLog, LogLevel::Debug, 856 ("- URL (not to be reported): %s\n", uriString.get())); 857 MOZ_LOG(sUELLog, LogLevel::Debug, 858 ("- remoteType: %s\n", loggedRemoteType.get())); 859 MOZ_LOG(sUELLog, LogLevel::Debug, 860 ("- fileInfo: %s\n", fileNameTypeAndDetails.first.get())); 861 MOZ_LOG(sUELLog, LogLevel::Debug, 862 ("- fileDetails: %s\n", loggedFileDetails.get())); 863 MOZ_LOG(sUELLog, LogLevel::Debug, 864 ("- redirects: %s\n\n", loggedRedirects.get())); 865 866 glean::security::UnexpectedLoadExtra extra = { 867 .contenttype = Some(loggedContentType), 868 .filedetails = Some(loggedFileDetails), 869 .redirects = Some(loggedRedirects), 870 .remotetype = Some(loggedRemoteType), 871 .value = Some(fileNameTypeAndDetails.first), 872 }; 873 glean::security::unexpected_load.Record(Some(extra)); 874 } 875 876 /* static */ 877 nsSecurityFlags nsContentSecurityManager::ComputeSecurityFlags( 878 mozilla::CORSMode aCORSMode, CORSSecurityMapping aCORSSecurityMapping) { 879 if (aCORSSecurityMapping == CORSSecurityMapping::DISABLE_CORS_CHECKS) { 880 return nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; 881 } 882 883 switch (aCORSMode) { 884 case CORS_NONE: 885 if (aCORSSecurityMapping == CORSSecurityMapping::REQUIRE_CORS_CHECKS) { 886 // CORS_NONE gets treated like CORS_ANONYMOUS in this mode 887 return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | 888 nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; 889 } else if (aCORSSecurityMapping == 890 CORSSecurityMapping::CORS_NONE_MAPS_TO_INHERITED_CONTEXT) { 891 // CORS_NONE inherits 892 return nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT; 893 } else { 894 // CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS, the only remaining enum 895 // variant. CORSSecurityMapping::DISABLE_CORS_CHECKS returned early. 896 MOZ_ASSERT(aCORSSecurityMapping == 897 CORSSecurityMapping::CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS); 898 return nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; 899 } 900 case CORS_ANONYMOUS: 901 return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | 902 nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; 903 case CORS_USE_CREDENTIALS: 904 return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | 905 nsILoadInfo::SEC_COOKIES_INCLUDE; 906 break; 907 default: 908 MOZ_ASSERT_UNREACHABLE("Invalid aCORSMode enum value"); 909 return nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | 910 nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; 911 } 912 } 913 914 /* static */ 915 nsSecurityFlags nsContentSecurityManager::ComputeSecurityMode( 916 nsSecurityFlags aSecurityFlags) { 917 return aSecurityFlags & 918 (nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT | 919 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED | 920 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT | 921 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL | 922 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT); 923 } 924 925 /* static */ 926 mozilla::dom::RequestMode nsContentSecurityManager::SecurityModeToRequestMode( 927 uint32_t aSecurityMode) { 928 if (aSecurityMode == 929 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT || 930 aSecurityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED) { 931 return mozilla::dom::RequestMode::Same_origin; 932 } 933 934 if (aSecurityMode == nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) { 935 return mozilla::dom::RequestMode::Cors; 936 } 937 938 // If it's not one of the security modes above, then we ensure it's 939 // at least one of the others defined in nsILoadInfo 940 MOZ_ASSERT(aSecurityMode == 941 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT || 942 aSecurityMode == 943 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 944 "unhandled security mode"); 945 946 return mozilla::dom::RequestMode::No_cors; 947 } 948 949 /* static */ 950 nsresult nsContentSecurityManager::CheckAllowLoadInSystemPrivilegedContext( 951 nsIChannel* aChannel) { 952 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 953 nsCOMPtr<nsIPrincipal> inspectedPrincipal = loadInfo->GetLoadingPrincipal(); 954 if (!inspectedPrincipal) { 955 return NS_OK; 956 } 957 // Check if we are actually dealing with a privileged request 958 if (!inspectedPrincipal->IsSystemPrincipal()) { 959 return NS_OK; 960 } 961 // loads with the allow flag are waived through 962 // until refactored (e.g., Shavar, OCSP) 963 if (loadInfo->GetAllowDeprecatedSystemRequests()) { 964 return NS_OK; 965 } 966 ExtContentPolicyType contentPolicyType = 967 loadInfo->GetExternalContentPolicyType(); 968 // For now, let's not inspect top-level document loads 969 if (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT) { 970 return NS_OK; 971 } 972 973 // allowing some fetches due to their lowered risk 974 // i.e., data & downloads fetches do limited parsing, no rendering 975 // remote images are too widely used (favicons, about:addons etc.) 976 if ((contentPolicyType == ExtContentPolicy::TYPE_FETCH) || 977 (contentPolicyType == ExtContentPolicy::TYPE_XMLHTTPREQUEST) || 978 (contentPolicyType == ExtContentPolicy::TYPE_WEBSOCKET) || 979 (contentPolicyType == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) || 980 (contentPolicyType == ExtContentPolicy::TYPE_IMAGE)) { 981 return NS_OK; 982 } 983 984 // Allow the user interface (e.g., schemes like chrome, resource) 985 nsCOMPtr<nsIURI> finalURI; 986 NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI)); 987 bool isUiResource = false; 988 if (NS_SUCCEEDED(NS_URIChainHasFlags( 989 finalURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isUiResource)) && 990 isUiResource) { 991 return NS_OK; 992 } 993 // For about: and extension-based URIs, which don't get 994 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present. 995 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(finalURI); 996 997 nsAutoCString remoteType; 998 if (XRE_IsParentProcess()) { 999 nsCOMPtr<nsIParentChannel> parentChannel; 1000 NS_QueryNotificationCallbacks(aChannel, parentChannel); 1001 if (parentChannel) { 1002 parentChannel->GetRemoteType(remoteType); 1003 } 1004 } else { 1005 remoteType.Assign( 1006 mozilla::dom::ContentChild::GetSingleton()->GetRemoteType()); 1007 } 1008 1009 // GetInnerURI can return null for malformed nested URIs like moz-icon:trash 1010 if (!innerURI) { 1011 MeasureUnexpectedPrivilegedLoads(loadInfo, innerURI, remoteType); 1012 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1013 return NS_ERROR_CONTENT_BLOCKED; 1014 } 1015 // loads of userContent.css during startup and tests that show up as file: 1016 if (innerURI->SchemeIs("file")) { 1017 if ((contentPolicyType == ExtContentPolicy::TYPE_STYLESHEET) || 1018 (contentPolicyType == ExtContentPolicy::TYPE_OTHER)) { 1019 return NS_OK; 1020 } 1021 } 1022 // (1) loads from within omni.ja and system add-ons use jar: 1023 // this is safe to allow, because we do not support remote jar. 1024 // (2) about: resources are always allowed: they are part of the build. 1025 // (3) extensions are signed or the user has made bad decisions. 1026 if (innerURI->SchemeIs("jar") || innerURI->SchemeIs("about") || 1027 innerURI->SchemeIs("moz-extension") || 1028 innerURI->SchemeIs("moz-safe-about")) { 1029 return NS_OK; 1030 } 1031 1032 nsAutoCString requestedURL; 1033 innerURI->GetAsciiSpec(requestedURL); 1034 MOZ_LOG(sUELLog, LogLevel::Warning, 1035 ("SystemPrincipal should not load remote resources. URL: %s, type %d", 1036 requestedURL.get(), int(contentPolicyType))); 1037 1038 // The load types that we want to disallow, will extend over time and 1039 // prioritized by risk. The most risky/dangerous are load-types are documents, 1040 // subdocuments, scripts and styles in that order. The most dangerous URL 1041 // schemes to cover are HTTP, HTTPS, data, blob in that order. Meta bug 1042 // 1725112 will track upcoming restrictions 1043 1044 // Telemetry for unexpected privileged loads. 1045 // pref check & data sanitization happens in the called function 1046 MeasureUnexpectedPrivilegedLoads(loadInfo, innerURI, remoteType); 1047 1048 // Relaxing restrictions for our test suites: 1049 // (1) AreNonLocalConnectionsDisabled() disables network, so 1050 // http://mochitest is actually local and allowed. (2) The marionette test 1051 // framework uses injections and data URLs to execute scripts, checking for 1052 // the environment variable breaks the attack but not the tests. 1053 if (xpc::AreNonLocalConnectionsDisabled() || 1054 mozilla::EnvHasValue("MOZ_MARIONETTE")) { 1055 bool disallowSystemPrincipalRemoteDocuments = Preferences::GetBool( 1056 "security.disallow_non_local_systemprincipal_in_tests"); 1057 if (disallowSystemPrincipalRemoteDocuments) { 1058 // our own mochitest needs NS_ASSERTION instead of MOZ_ASSERT 1059 NS_ASSERTION(false, "SystemPrincipal must not load remote documents."); 1060 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1061 return NS_ERROR_CONTENT_BLOCKED; 1062 } 1063 // but other mochitest are exempt from this 1064 return NS_OK; 1065 } 1066 1067 if (contentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) { 1068 if (net::SchemeIsHttpOrHttps(innerURI)) { 1069 MOZ_ASSERT( 1070 false, 1071 "Disallowing SystemPrincipal load of subdocuments on HTTP(S)."); 1072 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1073 return NS_ERROR_CONTENT_BLOCKED; 1074 } 1075 if (innerURI->SchemeIs("data")) { 1076 MOZ_ASSERT( 1077 false, 1078 "Disallowing SystemPrincipal load of subdocuments on data URL."); 1079 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1080 return NS_ERROR_CONTENT_BLOCKED; 1081 } 1082 } 1083 if (contentPolicyType == ExtContentPolicy::TYPE_SCRIPT) { 1084 if (net::SchemeIsHttpOrHttps(innerURI)) { 1085 MOZ_ASSERT(false, 1086 "Disallowing SystemPrincipal load of scripts on HTTP(S)."); 1087 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1088 return NS_ERROR_CONTENT_BLOCKED; 1089 } 1090 } 1091 if (contentPolicyType == ExtContentPolicy::TYPE_STYLESHEET) { 1092 if (net::SchemeIsHttpOrHttps(innerURI)) { 1093 MOZ_ASSERT(false, 1094 "Disallowing SystemPrincipal load of stylesheets on HTTP(S)."); 1095 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1096 return NS_ERROR_CONTENT_BLOCKED; 1097 } 1098 } 1099 return NS_OK; 1100 } 1101 1102 /* 1103 * Disallow about pages in the privilegedaboutcontext (e.g., password manager, 1104 * newtab etc.) to load remote scripts. Regardless of whether this is coming 1105 * from the contentprincipal or the systemprincipal. 1106 */ 1107 /* static */ 1108 nsresult nsContentSecurityManager::CheckAllowLoadInPrivilegedAboutContext( 1109 nsIChannel* aChannel) { 1110 // If remote scripts aren't disallowed, then bail out. 1111 if (!StaticPrefs::security_disallow_privilegedabout_remote_script_loads()) { 1112 return NS_OK; 1113 } 1114 1115 nsAutoCString remoteType; 1116 if (XRE_IsParentProcess()) { 1117 nsCOMPtr<nsIParentChannel> parentChannel; 1118 NS_QueryNotificationCallbacks(aChannel, parentChannel); 1119 if (parentChannel) { 1120 parentChannel->GetRemoteType(remoteType); 1121 } 1122 } else { 1123 remoteType.Assign( 1124 mozilla::dom::ContentChild::GetSingleton()->GetRemoteType()); 1125 } 1126 1127 // only perform check for privileged about process 1128 if (!remoteType.Equals(PRIVILEGEDABOUT_REMOTE_TYPE)) { 1129 return NS_OK; 1130 } 1131 1132 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1133 ExtContentPolicyType contentPolicyType = 1134 loadInfo->GetExternalContentPolicyType(); 1135 // only check for script loads 1136 if (contentPolicyType != ExtContentPolicy::TYPE_SCRIPT) { 1137 return NS_OK; 1138 } 1139 1140 nsCOMPtr<nsIURI> finalURI; 1141 NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI)); 1142 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(finalURI); 1143 1144 bool isLocal; 1145 NS_URIChainHasFlags(innerURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, 1146 &isLocal); 1147 // We allow URLs that are URI_IS_LOCAL (but that includes `data` 1148 // and `blob` which are also undesirable. 1149 if (isLocal && !innerURI->SchemeIs("data") && !innerURI->SchemeIs("blob")) { 1150 return NS_OK; 1151 } 1152 MOZ_ASSERT( 1153 false, 1154 "Disallowing privileged about process to load scripts on HTTP(S)."); 1155 aChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1156 return NS_ERROR_CONTENT_BLOCKED; 1157 } 1158 1159 /* 1160 * Every protocol handler must set one of the six security flags 1161 * defined in nsIProtocolHandler - if not - deny the load. 1162 */ 1163 nsresult nsContentSecurityManager::CheckChannelHasProtocolSecurityFlag( 1164 nsIChannel* aChannel) { 1165 nsCOMPtr<nsIURI> uri; 1166 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 1167 NS_ENSURE_SUCCESS(rv, rv); 1168 1169 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv); 1170 NS_ENSURE_SUCCESS(rv, rv); 1171 1172 uint32_t flags; 1173 rv = ios->GetDynamicProtocolFlags(uri, &flags); 1174 NS_ENSURE_SUCCESS(rv, rv); 1175 1176 uint32_t securityFlagsSet = 0; 1177 if (flags & nsIProtocolHandler::URI_IS_WEBEXTENSION_RESOURCE) { 1178 securityFlagsSet += 1; 1179 } 1180 if (flags & nsIProtocolHandler::URI_LOADABLE_BY_ANYONE) { 1181 securityFlagsSet += 1; 1182 } 1183 if (flags & nsIProtocolHandler::URI_DANGEROUS_TO_LOAD) { 1184 securityFlagsSet += 1; 1185 } 1186 if (flags & nsIProtocolHandler::URI_IS_UI_RESOURCE) { 1187 securityFlagsSet += 1; 1188 } 1189 if (flags & nsIProtocolHandler::URI_IS_LOCAL_FILE) { 1190 securityFlagsSet += 1; 1191 } 1192 if (flags & nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS) { 1193 securityFlagsSet += 1; 1194 } 1195 1196 // Ensure that only "1" valid security flags is set. 1197 if (securityFlagsSet == 1) { 1198 return NS_OK; 1199 } 1200 1201 MOZ_ASSERT(false, "protocol must use one valid security flag"); 1202 return NS_ERROR_CONTENT_BLOCKED; 1203 } 1204 1205 // We should not allow loading non-JavaScript files as scripts using 1206 // a file:// URL. 1207 static nsresult CheckAllowFileProtocolScriptLoad(nsIChannel* aChannel) { 1208 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1209 ExtContentPolicyType type = loadInfo->GetExternalContentPolicyType(); 1210 1211 // Only check script loads. 1212 if (type != ExtContentPolicy::TYPE_SCRIPT) { 1213 return NS_OK; 1214 } 1215 1216 if (!StaticPrefs::security_block_fileuri_script_with_wrong_mime()) { 1217 return NS_OK; 1218 } 1219 1220 nsCOMPtr<nsIURI> uri; 1221 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 1222 NS_ENSURE_SUCCESS(rv, rv); 1223 if (!uri || !uri->SchemeIs("file")) { 1224 return NS_OK; 1225 } 1226 1227 nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv); 1228 NS_ENSURE_SUCCESS(rv, rv); 1229 1230 // GetTypeFromURI fails for missing or unknown file-extensions. 1231 nsAutoCString contentType; 1232 rv = mime->GetTypeFromURI(uri, contentType); 1233 if (NS_FAILED(rv) || !nsContentUtils::IsJavascriptMIMEType( 1234 NS_ConvertUTF8toUTF16(contentType))) { 1235 nsCOMPtr<Document> doc; 1236 if (nsINode* node = loadInfo->LoadingNode()) { 1237 doc = node->OwnerDoc(); 1238 } 1239 1240 nsAutoCString spec; 1241 uri->GetSpec(spec); 1242 1243 AutoTArray<nsString, 1> params; 1244 CopyUTF8toUTF16(NS_UnescapeURL(spec), *params.AppendElement()); 1245 CopyUTF8toUTF16(NS_UnescapeURL(contentType), *params.AppendElement()); 1246 1247 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, 1248 "FILE_SCRIPT_BLOCKED"_ns, doc, 1249 nsContentUtils::eSECURITY_PROPERTIES, 1250 "BlockFileScriptWithWrongMimeType", params); 1251 1252 return NS_ERROR_CONTENT_BLOCKED; 1253 } 1254 1255 return NS_OK; 1256 } 1257 1258 // We should not allow loading non-JavaScript files as scripts using 1259 // a moz-extension:// URL. 1260 static nsresult CheckAllowExtensionProtocolScriptLoad(nsIChannel* aChannel) { 1261 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1262 ExtContentPolicyType type = loadInfo->GetExternalContentPolicyType(); 1263 1264 // Only check script loads. 1265 if (type != ExtContentPolicy::TYPE_SCRIPT) { 1266 return NS_OK; 1267 } 1268 1269 nsCOMPtr<nsIURI> uri; 1270 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 1271 NS_ENSURE_SUCCESS(rv, rv); 1272 if (!uri || !uri->SchemeIs("moz-extension")) { 1273 return NS_OK; 1274 } 1275 1276 // We expect this code to never be hit off-the-main-thread (even worker 1277 // scripts are currently hitting only on the main thread, see 1278 // WorkerScriptLoader::DispatchLoadScript calling NS_DispatchToMainThread 1279 // internally), this diagnostic assertion is meant to let us notice if that 1280 // isn't the case anymore. 1281 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(), 1282 "Unexpected off-the-main-thread call to " 1283 "CheckAllowFileProtocolScriptLoad"); 1284 1285 nsAutoCString host; 1286 rv = uri->GetHost(host); 1287 NS_ENSURE_SUCCESS(rv, rv); 1288 1289 RefPtr<extensions::WebExtensionPolicyCore> targetPolicy = 1290 ExtensionPolicyService::GetCoreByHost(host); 1291 1292 if (NS_WARN_IF(!targetPolicy) || targetPolicy->ManifestVersion() < 3) { 1293 return NS_OK; 1294 } 1295 1296 nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv); 1297 NS_ENSURE_SUCCESS(rv, rv); 1298 1299 // GetDefaultTypeFromExtension fails for missing or unknown file-extensions. 1300 nsAutoCString contentType; 1301 rv = mime->GetDefaultTypeFromURI(uri, contentType); 1302 if (NS_FAILED(rv) || !nsContentUtils::IsJavascriptMIMEType( 1303 NS_ConvertUTF8toUTF16(contentType))) { 1304 nsCOMPtr<Document> doc; 1305 if (nsINode* node = loadInfo->LoadingNode()) { 1306 doc = node->OwnerDoc(); 1307 } 1308 1309 nsAutoCString spec; 1310 uri->GetSpec(spec); 1311 1312 AutoTArray<nsString, 1> params; 1313 CopyUTF8toUTF16(NS_UnescapeURL(spec), *params.AppendElement()); 1314 1315 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, 1316 "EXTENSION_SCRIPT_BLOCKED"_ns, doc, 1317 nsContentUtils::eSECURITY_PROPERTIES, 1318 "BlockExtensionScriptWithWrongExt", params); 1319 1320 return NS_ERROR_CONTENT_BLOCKED; 1321 } 1322 1323 return NS_OK; 1324 } 1325 1326 // Validate that a load should be allowed based on its remote type. This 1327 // intentionally prevents some loads from occuring even using the system 1328 // principal, if they were started in a content process. 1329 static nsresult CheckAllowLoadByTriggeringRemoteType(nsIChannel* aChannel) { 1330 MOZ_ASSERT(aChannel); 1331 1332 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1333 1334 // For now, only restrict loads for documents. We currently have no 1335 // interesting subresource checks for protocols which are are not fully 1336 // handled within the content process. 1337 ExtContentPolicy contentPolicyType = loadInfo->GetExternalContentPolicyType(); 1338 if (contentPolicyType != ExtContentPolicy::TYPE_DOCUMENT && 1339 contentPolicyType != ExtContentPolicy::TYPE_SUBDOCUMENT && 1340 contentPolicyType != ExtContentPolicy::TYPE_OBJECT) { 1341 return NS_OK; 1342 } 1343 1344 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(), 1345 "Unexpected off-the-main-thread call to " 1346 "CheckAllowLoadByTriggeringRemoteType"); 1347 1348 // Due to the way that session history is handled without SHIP, we cannot run 1349 // these checks when SHIP is disabled. 1350 if (!mozilla::SessionHistoryInParent()) { 1351 return NS_OK; 1352 } 1353 1354 nsAutoCString triggeringRemoteType; 1355 nsresult rv = loadInfo->GetTriggeringRemoteType(triggeringRemoteType); 1356 NS_ENSURE_SUCCESS(rv, rv); 1357 1358 // For now, only restrict loads coming from web remote types. In the future we 1359 // may want to expand this a bit. 1360 if (!StringBeginsWith(triggeringRemoteType, WEB_REMOTE_TYPE)) { 1361 return NS_OK; 1362 } 1363 1364 nsCOMPtr<nsIURI> finalURI; 1365 rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI)); 1366 NS_ENSURE_SUCCESS(rv, rv); 1367 1368 // Don't allow web content processes to load non-remote about pages. 1369 // NOTE: URIs with a `moz-safe-about:` inner scheme are safe to link to, so 1370 // it's OK we miss them here. 1371 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(finalURI); 1372 if (innermostURI->SchemeIs("about")) { 1373 nsCOMPtr<nsIAboutModule> aboutModule; 1374 rv = NS_GetAboutModule(innermostURI, getter_AddRefs(aboutModule)); 1375 NS_ENSURE_SUCCESS(rv, rv); 1376 1377 uint32_t aboutModuleFlags = 0; 1378 rv = aboutModule->GetURIFlags(innermostURI, &aboutModuleFlags); 1379 NS_ENSURE_SUCCESS(rv, rv); 1380 1381 if (!(aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) && 1382 !(aboutModuleFlags & nsIAboutModule::URI_CAN_LOAD_IN_CHILD) && 1383 !(aboutModuleFlags & nsIAboutModule::URI_MUST_LOAD_IN_CHILD)) { 1384 NS_WARNING(nsPrintfCString("Blocking load of about URI (%s) which cannot " 1385 "be linked to in web content process", 1386 finalURI->GetSpecOrDefault().get()) 1387 .get()); 1388 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 1389 if (NS_SUCCEEDED( 1390 loadInfo->TriggeringPrincipal()->CheckMayLoad(finalURI, true))) { 1391 nsAutoCString aboutModuleName; 1392 MOZ_ALWAYS_SUCCEEDS( 1393 NS_GetAboutModuleName(innermostURI, aboutModuleName)); 1394 MOZ_CRASH_UNSAFE_PRINTF( 1395 "Blocking load of about uri by content process which may have " 1396 "otherwise succeeded [aboutModule=%s, isSystemPrincipal=%d]", 1397 aboutModuleName.get(), 1398 loadInfo->TriggeringPrincipal()->IsSystemPrincipal()); 1399 } 1400 #endif 1401 return NS_ERROR_CONTENT_BLOCKED; 1402 } 1403 return NS_OK; 1404 } 1405 1406 // Don't allow web content processes to load file documents. Loads of file 1407 // URIs as subresources will be handled by the sandbox, and may be allowed in 1408 // some cases. 1409 bool localFile = false; 1410 rv = NS_URIChainHasFlags(finalURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, 1411 &localFile); 1412 NS_ENSURE_SUCCESS(rv, rv); 1413 if (localFile) { 1414 NS_WARNING( 1415 nsPrintfCString( 1416 "Blocking document load of file URI (%s) from web content process", 1417 innermostURI->GetSpecOrDefault().get()) 1418 .get()); 1419 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 1420 if (NS_SUCCEEDED( 1421 loadInfo->TriggeringPrincipal()->CheckMayLoad(finalURI, true))) { 1422 MOZ_CRASH_UNSAFE_PRINTF( 1423 "Blocking document load of file URI by content process which may " 1424 "have otherwise succeeded [isSystemPrincipal=%d]", 1425 loadInfo->TriggeringPrincipal()->IsSystemPrincipal()); 1426 } 1427 #endif 1428 return NS_ERROR_CONTENT_BLOCKED; 1429 } 1430 1431 return NS_OK; 1432 } 1433 1434 /* 1435 * Based on the security flags provided in the loadInfo of the channel, 1436 * doContentSecurityCheck() performs the following content security checks 1437 * before opening the channel: 1438 * 1439 * (1) Same Origin Policy Check (if applicable) 1440 * (2) Allow Cross Origin but perform sanity checks whether a principal 1441 * is allowed to access the following URL. 1442 * (3) Perform CORS check (if applicable) 1443 * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...) 1444 * 1445 * @param aChannel 1446 * The channel to perform the security checks on. 1447 * @param aInAndOutListener 1448 * The streamListener that is passed to channel->AsyncOpen() that is now 1449 * potentially wrappend within nsCORSListenerProxy() and becomes the 1450 * corsListener that now needs to be set as new streamListener on the channel. 1451 */ 1452 nsresult nsContentSecurityManager::doContentSecurityCheck( 1453 nsIChannel* aChannel, nsCOMPtr<nsIStreamListener>& aInAndOutListener) { 1454 NS_ENSURE_ARG(aChannel); 1455 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1456 if (MOZ_UNLIKELY(MOZ_LOG_TEST(sCSMLog, LogLevel::Verbose))) { 1457 DebugDoContentSecurityCheck(aChannel, loadInfo); 1458 } 1459 1460 nsresult rv = CheckAllowLoadInSystemPrivilegedContext(aChannel); 1461 NS_ENSURE_SUCCESS(rv, rv); 1462 1463 rv = CheckAllowLoadInPrivilegedAboutContext(aChannel); 1464 NS_ENSURE_SUCCESS(rv, rv); 1465 1466 // We want to also check redirected requests to ensure 1467 // the target maintains the proper javascript file extensions. 1468 rv = CheckAllowExtensionProtocolScriptLoad(aChannel); 1469 NS_ENSURE_SUCCESS(rv, rv); 1470 1471 rv = CheckChannelHasProtocolSecurityFlag(aChannel); 1472 NS_ENSURE_SUCCESS(rv, rv); 1473 1474 rv = CheckAllowLoadByTriggeringRemoteType(aChannel); 1475 NS_ENSURE_SUCCESS(rv, rv); 1476 1477 rv = CheckForIncoherentResultPrincipal(aChannel); 1478 NS_ENSURE_SUCCESS(rv, rv); 1479 1480 // if dealing with a redirected channel then we have already installed 1481 // streamlistener and redirect proxies and so we are done. 1482 if (loadInfo->GetInitialSecurityCheckDone()) { 1483 return NS_OK; 1484 } 1485 1486 // make sure that only one of the five security flags is set in the loadinfo 1487 // e.g. do not require same origin and allow cross origin at the same time 1488 rv = ValidateSecurityFlags(loadInfo); 1489 NS_ENSURE_SUCCESS(rv, rv); 1490 1491 if (loadInfo->GetSecurityMode() == 1492 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) { 1493 rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener); 1494 NS_ENSURE_SUCCESS(rv, rv); 1495 } 1496 1497 rv = CheckChannel(aChannel); 1498 NS_ENSURE_SUCCESS(rv, rv); 1499 1500 // Perform all ContentPolicy checks (MixedContent, CSP, ...) 1501 rv = DoContentSecurityChecks(aChannel, loadInfo); 1502 NS_ENSURE_SUCCESS(rv, rv); 1503 1504 rv = CheckAllowFileProtocolScriptLoad(aChannel); 1505 NS_ENSURE_SUCCESS(rv, rv); 1506 1507 // now lets set the initialSecurityFlag for subsequent calls 1508 loadInfo->SetInitialSecurityCheckDone(true); 1509 1510 // all security checks passed - lets allow the load 1511 return NS_OK; 1512 } 1513 1514 NS_IMETHODIMP 1515 nsContentSecurityManager::AsyncOnChannelRedirect( 1516 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aRedirFlags, 1517 nsIAsyncVerifyRedirectCallback* aCb) { 1518 // Since we compare the principal from the loadInfo to the URI's 1519 // princicpal, it's possible that the checks fail when doing an internal 1520 // redirect. We can just return early instead, since we should never 1521 // need to block an internal redirect. 1522 if (aRedirFlags & nsIChannelEventSink::REDIRECT_INTERNAL) { 1523 aCb->OnRedirectVerifyCallback(NS_OK); 1524 return NS_OK; 1525 } 1526 1527 nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->LoadInfo(); 1528 nsresult rv = CheckChannel(aNewChannel); 1529 if (NS_FAILED(rv)) { 1530 aOldChannel->Cancel(rv); 1531 return rv; 1532 } 1533 1534 // Also verify that the redirecting server is allowed to redirect to the 1535 // given URI 1536 nsCOMPtr<nsIPrincipal> oldPrincipal; 1537 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 1538 aOldChannel, getter_AddRefs(oldPrincipal)); 1539 1540 nsCOMPtr<nsIURI> newURI; 1541 (void)NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); 1542 NS_ENSURE_STATE(oldPrincipal && newURI); 1543 1544 // Do not allow insecure redirects to data: URIs 1545 if (!AllowInsecureRedirectToDataURI(aNewChannel)) { 1546 // cancel the old channel and return an error 1547 aOldChannel->Cancel(NS_ERROR_CONTENT_BLOCKED); 1548 return NS_ERROR_CONTENT_BLOCKED; 1549 } 1550 1551 const uint32_t flags = 1552 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT | 1553 nsIScriptSecurityManager::DISALLOW_SCRIPT; 1554 rv = nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal( 1555 oldPrincipal, newURI, flags, loadInfo->GetInnerWindowID()); 1556 NS_ENSURE_SUCCESS(rv, rv); 1557 1558 aCb->OnRedirectVerifyCallback(NS_OK); 1559 return NS_OK; 1560 } 1561 1562 static void AddLoadFlags(nsIRequest* aRequest, nsLoadFlags aNewFlags) { 1563 nsLoadFlags flags; 1564 aRequest->GetLoadFlags(&flags); 1565 flags |= aNewFlags; 1566 aRequest->SetLoadFlags(flags); 1567 } 1568 1569 /* 1570 * Check that this channel passes all security checks. Returns an error code 1571 * if this requesst should not be permitted. 1572 */ 1573 nsresult nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) { 1574 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1575 nsCOMPtr<nsIURI> uri; 1576 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 1577 NS_ENSURE_SUCCESS(rv, rv); 1578 1579 // Handle cookie policies 1580 uint32_t cookiePolicy = loadInfo->GetCookiePolicy(); 1581 if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) { 1582 // We shouldn't have the SEC_COOKIES_SAME_ORIGIN flag for top level loads 1583 MOZ_ASSERT(loadInfo->GetExternalContentPolicyType() != 1584 ExtContentPolicy::TYPE_DOCUMENT); 1585 nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal(); 1586 1587 // It doesn't matter what we pass for the second, data-inherits, argument. 1588 // Any protocol which inherits won't pay attention to cookies anyway. 1589 rv = loadingPrincipal->CheckMayLoad(uri, false); 1590 if (NS_FAILED(rv)) { 1591 AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS); 1592 } 1593 } else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) { 1594 AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS); 1595 } 1596 1597 if (!CrossOriginEmbedderPolicyAllowsCredentials(aChannel)) { 1598 AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS); 1599 } 1600 1601 nsSecurityFlags securityMode = loadInfo->GetSecurityMode(); 1602 1603 // CORS mode is handled by nsCORSListenerProxy 1604 if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT) { 1605 if (NS_HasBeenCrossOrigin(aChannel)) { 1606 loadInfo->MaybeIncreaseTainting(LoadTainting::CORS); 1607 } 1608 return NS_OK; 1609 } 1610 1611 // Allow subresource loads if TriggeringPrincipal is the SystemPrincipal. 1612 if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal() && 1613 loadInfo->GetExternalContentPolicyType() != 1614 ExtContentPolicy::TYPE_DOCUMENT && 1615 loadInfo->GetExternalContentPolicyType() != 1616 ExtContentPolicy::TYPE_SUBDOCUMENT) { 1617 return NS_OK; 1618 } 1619 1620 // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply 1621 if ((securityMode == 1622 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT) || 1623 (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) { 1624 rv = DoSOPChecks(uri, loadInfo, aChannel); 1625 NS_ENSURE_SUCCESS(rv, rv); 1626 } 1627 1628 if ((securityMode == 1629 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) || 1630 (securityMode == 1631 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL)) { 1632 if (NS_HasBeenCrossOrigin(aChannel)) { 1633 NS_ENSURE_FALSE(loadInfo->GetDontFollowRedirects(), NS_ERROR_DOM_BAD_URI); 1634 loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque); 1635 } 1636 // Please note that DoCheckLoadURIChecks should only be enforced for 1637 // cross origin requests. If the flag SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT 1638 // is set within the loadInfo, then CheckLoadURIWithPrincipal is performed 1639 // within nsCorsListenerProxy 1640 rv = DoCheckLoadURIChecks(uri, loadInfo); 1641 NS_ENSURE_SUCCESS(rv, rv); 1642 // TODO: Bug 1371237 1643 // consider calling SetBlockedRequest in 1644 // nsContentSecurityManager::CheckChannel 1645 } 1646 1647 return NS_OK; 1648 } 1649 1650 // https://fetch.spec.whatwg.org/#ref-for-cross-origin-embedder-policy-allows-credentials 1651 bool nsContentSecurityManager::CrossOriginEmbedderPolicyAllowsCredentials( 1652 nsIChannel* aChannel) { 1653 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1654 1655 // 1. If request’s mode is not "no-cors", then return true. 1656 // 1657 // `no-cors` check applies to document navigation such that if it is 1658 // an document navigation, this check should return true to allow 1659 // credentials. 1660 if (loadInfo->GetExternalContentPolicyType() == 1661 ExtContentPolicy::TYPE_DOCUMENT || 1662 loadInfo->GetExternalContentPolicyType() == 1663 ExtContentPolicy::TYPE_SUBDOCUMENT || 1664 loadInfo->GetExternalContentPolicyType() == 1665 ExtContentPolicy::TYPE_WEBSOCKET) { 1666 return true; 1667 } 1668 1669 if (loadInfo->GetSecurityMode() != 1670 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL && 1671 loadInfo->GetSecurityMode() != 1672 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) { 1673 return true; 1674 } 1675 1676 // If request’s client’s policy container’s embedder policy’s value is not 1677 // "credentialless", then return true. 1678 if (loadInfo->GetLoadingEmbedderPolicy() != 1679 nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) { 1680 return true; 1681 } 1682 1683 // If request’s origin is same origin with request’s current URL’s origin and 1684 // request does not have a redirect-tainted origin, then return true. 1685 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 1686 nsCOMPtr<nsIPrincipal> resourcePrincipal; 1687 ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(resourcePrincipal)); 1688 1689 bool sameOrigin = resourcePrincipal->Equals(loadInfo->TriggeringPrincipal()); 1690 nsAutoCString serializedOrigin; 1691 GetSerializedOrigin(loadInfo->TriggeringPrincipal(), resourcePrincipal, 1692 serializedOrigin, loadInfo); 1693 if (sameOrigin && !serializedOrigin.IsEmpty()) { 1694 return true; 1695 } 1696 1697 return false; 1698 } 1699 1700 // https://fetch.spec.whatwg.org/#serializing-a-request-origin 1701 void nsContentSecurityManager::GetSerializedOrigin( 1702 nsIPrincipal* aOrigin, nsIPrincipal* aResourceOrigin, 1703 nsACString& aSerializedOrigin, nsILoadInfo* aLoadInfo) { 1704 // The following for loop performs the 1705 // https://fetch.spec.whatwg.org/#ref-for-concept-request-tainted-origin 1706 nsCOMPtr<nsIPrincipal> lastOrigin; 1707 for (nsIRedirectHistoryEntry* entry : aLoadInfo->RedirectChain()) { 1708 if (!lastOrigin) { 1709 entry->GetPrincipal(getter_AddRefs(lastOrigin)); 1710 continue; 1711 } 1712 1713 nsCOMPtr<nsIPrincipal> currentOrigin; 1714 entry->GetPrincipal(getter_AddRefs(currentOrigin)); 1715 1716 if (!currentOrigin->Equals(lastOrigin) && !lastOrigin->Equals(aOrigin)) { 1717 aSerializedOrigin.AssignLiteral("null"); 1718 return; 1719 } 1720 lastOrigin = currentOrigin; 1721 } 1722 1723 // When the redirectChain is empty, it means this is the first redirect. 1724 // So according to the #serializing-a-request-origin spec, we don't 1725 // have a redirect-tainted origin, so we return the origin of the request 1726 // here. 1727 if (!lastOrigin) { 1728 aOrigin->GetWebExposedOriginSerialization(aSerializedOrigin); 1729 return; 1730 } 1731 1732 // Same as above, redirectChain doesn't contain the current redirect, 1733 // so we have to do the check one last time here. 1734 if (!lastOrigin->Equals(aResourceOrigin) && !lastOrigin->Equals(aOrigin)) { 1735 aSerializedOrigin.AssignLiteral("null"); 1736 return; 1737 } 1738 1739 aOrigin->GetWebExposedOriginSerialization(aSerializedOrigin); 1740 } 1741 1742 // https://html.spec.whatwg.org/multipage/browsers.html#compatible-with-cross-origin-isolation 1743 bool nsContentSecurityManager::IsCompatibleWithCrossOriginIsolation( 1744 nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) { 1745 return aPolicy == nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS || 1746 aPolicy == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP; 1747 } 1748 1749 // ==== nsIContentSecurityManager implementation ===== 1750 1751 NS_IMETHODIMP 1752 nsContentSecurityManager::PerformSecurityCheck( 1753 nsIChannel* aChannel, nsIStreamListener* aStreamListener, 1754 nsIStreamListener** outStreamListener) { 1755 nsCOMPtr<nsIStreamListener> inAndOutListener = aStreamListener; 1756 nsresult rv = doContentSecurityCheck(aChannel, inAndOutListener); 1757 NS_ENSURE_SUCCESS(rv, rv); 1758 1759 inAndOutListener.forget(outStreamListener); 1760 return NS_OK; 1761 } 1762 1763 nsresult nsContentSecurityManager::CheckForIncoherentResultPrincipal( 1764 nsIChannel* aChannel) { 1765 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 1766 ExtContentPolicyType contentPolicyType = 1767 loadInfo->GetExternalContentPolicyType(); 1768 if (contentPolicyType != ExtContentPolicyType::TYPE_DOCUMENT && 1769 contentPolicyType != ExtContentPolicyType::TYPE_SUBDOCUMENT && 1770 contentPolicyType != ExtContentPolicyType::TYPE_OBJECT) { 1771 return NS_OK; 1772 } 1773 1774 nsCOMPtr<nsIPrincipal> resultOrPrecursor; 1775 nsresult rv = nsScriptSecurityManager::GetScriptSecurityManager() 1776 ->GetChannelResultPrincipalIfNotSandboxed( 1777 aChannel, getter_AddRefs(resultOrPrecursor)); 1778 NS_ENSURE_SUCCESS(rv, rv); 1779 NS_ENSURE_STATE(resultOrPrecursor); 1780 1781 if (nsCOMPtr<nsIPrincipal> precursor = 1782 resultOrPrecursor->GetPrecursorPrincipal()) { 1783 resultOrPrecursor = precursor; 1784 } 1785 1786 if (!resultOrPrecursor->GetIsContentPrincipal()) { 1787 return NS_OK; 1788 } 1789 1790 nsAutoCString resultSiteOriginNoSuffix; 1791 rv = resultOrPrecursor->GetSiteOriginNoSuffix(resultSiteOriginNoSuffix); 1792 NS_ENSURE_SUCCESS(rv, rv); 1793 1794 nsCOMPtr<nsIURI> resultSiteOriginURI; 1795 NS_NewURI(getter_AddRefs(resultSiteOriginURI), resultSiteOriginNoSuffix); 1796 NS_ENSURE_STATE(resultSiteOriginURI); 1797 1798 nsCOMPtr<nsIURI> channelURI; 1799 aChannel->GetURI(getter_AddRefs(channelURI)); 1800 NS_ENSURE_STATE(channelURI); 1801 1802 nsCOMPtr<nsIPrincipal> channelUriPrincipal = 1803 BasePrincipal::CreateContentPrincipal(channelURI, {}); 1804 NS_ENSURE_STATE(channelUriPrincipal); 1805 1806 nsAutoCString channelUriSiteOrigin; 1807 rv = channelUriPrincipal->GetSiteOriginNoSuffix(channelUriSiteOrigin); 1808 NS_ENSURE_SUCCESS(rv, rv); 1809 1810 nsCOMPtr<nsIURI> channelSiteOriginURI; 1811 NS_NewURI(getter_AddRefs(channelSiteOriginURI), channelUriSiteOrigin); 1812 NS_ENSURE_STATE(channelSiteOriginURI); 1813 1814 if (nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin( 1815 resultSiteOriginURI, channelSiteOriginURI) || 1816 (!net::SchemeIsHttpOrHttps(resultSiteOriginURI) && 1817 net::SchemeIsHttpOrHttps(channelSiteOriginURI))) { 1818 return NS_ERROR_CONTENT_BLOCKED; 1819 } 1820 1821 return NS_OK; 1822 }