nsScriptSecurityManager.cpp (67710B)
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 "nsScriptSecurityManager.h" 8 9 #include "mozilla/SourceLocation.h" 10 #include "mozilla/StaticPrefs_extensions.h" 11 #include "mozilla/StaticPrefs_security.h" 12 #include "mozilla/StoragePrincipalHelper.h" 13 14 #include "xpcpublic.h" 15 #include "XPCWrapper.h" 16 #include "nsILoadContext.h" 17 #include "nsIScriptObjectPrincipal.h" 18 #include "nsIScriptContext.h" 19 #include "nsIScriptError.h" 20 #include "nsINestedURI.h" 21 #include "nspr.h" 22 #include "nsJSPrincipals.h" 23 #include "mozilla/BasePrincipal.h" 24 #include "mozilla/ContentPrincipal.h" 25 #include "ExpandedPrincipal.h" 26 #include "SystemPrincipal.h" 27 #include "DomainPolicy.h" 28 #include "nsString.h" 29 #include "nsCRT.h" 30 #include "nsCRTGlue.h" 31 #include "nsContentSecurityUtils.h" 32 #include "nsDocShell.h" 33 #include "nsError.h" 34 #include "nsGlobalWindowInner.h" 35 #include "nsDOMCID.h" 36 #include "nsTextFormatter.h" 37 #include "nsIStringBundle.h" 38 #include "nsNetUtil.h" 39 #include "nsIEffectiveTLDService.h" 40 #include "nsDirectoryServiceDefs.h" 41 #include "nsIScriptGlobalObject.h" 42 #include "nsPIDOMWindow.h" 43 #include "nsIDocShell.h" 44 #include "nsIConsoleService.h" 45 #include "nsIOService.h" 46 #include "nsIContent.h" 47 #include "nsDOMJSUtils.h" 48 #include "nsAboutProtocolUtils.h" 49 #include "nsIClassInfo.h" 50 #include "nsIURIFixup.h" 51 #include "nsIURIMutator.h" 52 #include "nsIChromeRegistry.h" 53 #include "nsIResProtocolHandler.h" 54 #include "nsIContentSecurityPolicy.h" 55 #include "mozilla/Components.h" 56 #include "mozilla/Preferences.h" 57 #include "mozilla/dom/BindingUtils.h" 58 #include "mozilla/NullPrincipal.h" 59 #include <stdint.h> 60 #include "mozilla/dom/ContentChild.h" 61 #include "mozilla/dom/ContentParent.h" 62 #include "mozilla/dom/Exceptions.h" 63 #include "mozilla/dom/nsCSPContext.h" 64 #include "mozilla/dom/PolicyContainer.h" 65 #include "mozilla/dom/ScriptSettings.h" 66 #include "mozilla/ClearOnShutdown.h" 67 #include "mozilla/ExtensionPolicyService.h" 68 #include "mozilla/StaticPtr.h" 69 #include "mozilla/dom/TrustedTypeUtils.h" 70 #include "mozilla/dom/WorkerCommon.h" 71 #include "mozilla/dom/WorkerPrivate.h" 72 #include "nsContentUtils.h" 73 #include "nsJSUtils.h" 74 #include "nsILoadInfo.h" 75 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin 76 #include "js/GCVector.h" 77 #include "js/Value.h" 78 79 // This should be probably defined on some other place... but I couldn't find it 80 #define WEBAPPS_PERM_NAME "webapps-manage" 81 82 using namespace mozilla; 83 using namespace mozilla::dom; 84 85 StaticRefPtr<nsIIOService> nsScriptSecurityManager::sIOService; 86 std::atomic<bool> nsScriptSecurityManager::sStrictFileOriginPolicy = true; 87 88 namespace { 89 90 class BundleHelper { 91 public: 92 NS_INLINE_DECL_REFCOUNTING(BundleHelper) 93 94 static nsIStringBundle* GetOrCreate() { 95 MOZ_ASSERT(!sShutdown); 96 97 // Already shutting down. Nothing should require the use of the string 98 // bundle when shutting down. 99 if (sShutdown) { 100 return nullptr; 101 } 102 103 if (!sSelf) { 104 sSelf = new BundleHelper(); 105 } 106 107 return sSelf->GetOrCreateInternal(); 108 } 109 110 static void Shutdown() { 111 sSelf = nullptr; 112 sShutdown = true; 113 } 114 115 private: 116 ~BundleHelper() = default; 117 118 nsIStringBundle* GetOrCreateInternal() { 119 if (!mBundle) { 120 nsCOMPtr<nsIStringBundleService> bundleService = 121 mozilla::components::StringBundle::Service(); 122 if (NS_WARN_IF(!bundleService)) { 123 return nullptr; 124 } 125 126 nsresult rv = bundleService->CreateBundle( 127 "chrome://global/locale/security/caps.properties", 128 getter_AddRefs(mBundle)); 129 if (NS_WARN_IF(NS_FAILED(rv))) { 130 return nullptr; 131 } 132 } 133 134 return mBundle; 135 } 136 137 nsCOMPtr<nsIStringBundle> mBundle; 138 139 static StaticRefPtr<BundleHelper> sSelf; 140 static bool sShutdown; 141 }; 142 143 StaticRefPtr<BundleHelper> BundleHelper::sSelf; 144 bool BundleHelper::sShutdown = false; 145 146 } // namespace 147 148 /////////////////////////// 149 // Convenience Functions // 150 /////////////////////////// 151 152 class nsAutoInPrincipalDomainOriginSetter { 153 public: 154 nsAutoInPrincipalDomainOriginSetter() { ++sInPrincipalDomainOrigin; } 155 ~nsAutoInPrincipalDomainOriginSetter() { --sInPrincipalDomainOrigin; } 156 static uint32_t sInPrincipalDomainOrigin; 157 }; 158 uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin; 159 160 static nsresult GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin) { 161 if (!aURI) { 162 return NS_ERROR_NULL_POINTER; 163 } 164 if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) { 165 // Allow a single recursive call to GetPrincipalDomainOrigin, since that 166 // might be happening on a different principal from the first call. But 167 // after that, cut off the recursion; it just indicates that something 168 // we're doing in this method causes us to reenter a security check here. 169 return NS_ERROR_NOT_AVAILABLE; 170 } 171 172 nsAutoInPrincipalDomainOriginSetter autoSetter; 173 174 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI); 175 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); 176 177 nsAutoCString hostPort; 178 179 nsresult rv = uri->GetHostPort(hostPort); 180 if (NS_SUCCEEDED(rv)) { 181 nsAutoCString scheme; 182 rv = uri->GetScheme(scheme); 183 NS_ENSURE_SUCCESS(rv, rv); 184 aOrigin = scheme + "://"_ns + hostPort; 185 } else { 186 // Some URIs (e.g., nsSimpleURI) don't support host. Just 187 // get the full spec. 188 rv = uri->GetSpec(aOrigin); 189 NS_ENSURE_SUCCESS(rv, rv); 190 } 191 192 return NS_OK; 193 } 194 195 static nsresult GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal, 196 nsACString& aOrigin) { 197 aOrigin.Truncate(); 198 nsCOMPtr<nsIURI> uri; 199 aPrincipal->GetDomain(getter_AddRefs(uri)); 200 nsresult rv = GetOriginFromURI(uri, aOrigin); 201 if (NS_SUCCEEDED(rv)) { 202 return rv; 203 } 204 // If there is no Domain fallback to the Principals Origin 205 return aPrincipal->GetOriginNoSuffix(aOrigin); 206 } 207 208 inline void SetPendingExceptionASCII(JSContext* cx, const char* aMsg) { 209 JS_ReportErrorASCII(cx, "%s", aMsg); 210 } 211 212 inline void SetPendingException(JSContext* cx, const char16_t* aMsg) { 213 NS_ConvertUTF16toUTF8 msg(aMsg); 214 JS_ReportErrorUTF8(cx, "%s", msg.get()); 215 } 216 217 /* static */ 218 bool nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI, 219 nsIURI* aTargetURI) { 220 return NS_SecurityCompareURIs(aSourceURI, aTargetURI, 221 sStrictFileOriginPolicy); 222 } 223 224 // SecurityHashURI is consistent with SecurityCompareURIs because 225 // NS_SecurityHashURI is consistent with NS_SecurityCompareURIs. See 226 // nsNetUtil.h. 227 uint32_t nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI) { 228 return NS_SecurityHashURI(aURI); 229 } 230 231 bool nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(nsIURI* aUriA, 232 nsIURI* aUriB) { 233 if (!aUriA || !net::SchemeIsHttpOrHttps(aUriA) || !aUriB || 234 !net::SchemeIsHttpOrHttps(aUriB)) { 235 return false; 236 } 237 if (!SecurityCompareURIs(aUriA, aUriB)) { 238 return true; 239 } 240 return false; 241 } 242 243 /* 244 * GetChannelResultPrincipal will return the principal that the resource 245 * returned by this channel will use. For example, if the resource is in 246 * a sandbox, it will return the nullprincipal. If the resource is forced 247 * to inherit principal, it will return the principal of its parent. If 248 * the load doesn't require sandboxing or inheriting, it will return the same 249 * principal as GetChannelURIPrincipal. Namely the principal of the URI 250 * that is being loaded. 251 */ 252 NS_IMETHODIMP 253 nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel, 254 nsIPrincipal** aPrincipal) { 255 return GetChannelResultPrincipal(aChannel, aPrincipal, 256 /*aIgnoreSandboxing*/ false); 257 } 258 259 nsresult nsScriptSecurityManager::GetChannelResultPrincipalIfNotSandboxed( 260 nsIChannel* aChannel, nsIPrincipal** aPrincipal) { 261 return GetChannelResultPrincipal(aChannel, aPrincipal, 262 /*aIgnoreSandboxing*/ true); 263 } 264 265 NS_IMETHODIMP 266 nsScriptSecurityManager::GetChannelResultStoragePrincipal( 267 nsIChannel* aChannel, nsIPrincipal** aPrincipal) { 268 nsCOMPtr<nsIPrincipal> principal; 269 nsresult rv = GetChannelResultPrincipal(aChannel, getter_AddRefs(principal), 270 /*aIgnoreSandboxing*/ false); 271 if (NS_WARN_IF(NS_FAILED(rv) || !principal)) { 272 return rv; 273 } 274 275 if (!(principal->GetIsContentPrincipal())) { 276 // If for some reason we don't have a content principal here, just reuse our 277 // principal for the storage principal too, since attempting to create a 278 // storage principal would fail anyway. 279 principal.forget(aPrincipal); 280 return NS_OK; 281 } 282 283 return StoragePrincipalHelper::Create( 284 aChannel, principal, /* aForceIsolation */ false, aPrincipal); 285 } 286 287 NS_IMETHODIMP 288 nsScriptSecurityManager::GetChannelResultPrincipals( 289 nsIChannel* aChannel, nsIPrincipal** aPrincipal, 290 nsIPrincipal** aPartitionedPrincipal) { 291 nsresult rv = GetChannelResultPrincipal(aChannel, aPrincipal, 292 /*aIgnoreSandboxing*/ false); 293 if (NS_WARN_IF(NS_FAILED(rv))) { 294 return rv; 295 } 296 297 if (!(*aPrincipal)->GetIsContentPrincipal()) { 298 // If for some reason we don't have a content principal here, just reuse our 299 // principal for the storage principal too, since attempting to create a 300 // storage principal would fail anyway. 301 nsCOMPtr<nsIPrincipal> copy = *aPrincipal; 302 copy.forget(aPartitionedPrincipal); 303 return NS_OK; 304 } 305 306 return StoragePrincipalHelper::Create( 307 aChannel, *aPrincipal, /* aForceIsolation */ true, aPartitionedPrincipal); 308 } 309 310 nsresult nsScriptSecurityManager::GetChannelResultPrincipal( 311 nsIChannel* aChannel, nsIPrincipal** aPrincipal, bool aIgnoreSandboxing) { 312 MOZ_ASSERT(aChannel, "Must have channel!"); 313 314 // Check whether we have an nsILoadInfo that says what we should do. 315 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 316 if (loadInfo->GetForceInheritPrincipalOverruleOwner()) { 317 nsCOMPtr<nsIPrincipal> principalToInherit = 318 loadInfo->FindPrincipalToInherit(aChannel); 319 principalToInherit.forget(aPrincipal); 320 return NS_OK; 321 } 322 323 nsCOMPtr<nsISupports> owner; 324 aChannel->GetOwner(getter_AddRefs(owner)); 325 if (owner) { 326 CallQueryInterface(owner, aPrincipal); 327 if (*aPrincipal) { 328 return NS_OK; 329 } 330 } 331 332 if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) { 333 // Determine the unsandboxed result principal to use as this null 334 // principal's precursor. Ignore errors here, as the precursor isn't 335 // required. 336 nsCOMPtr<nsIPrincipal> precursor; 337 GetChannelResultPrincipal(aChannel, getter_AddRefs(precursor), 338 /*aIgnoreSandboxing*/ true); 339 340 // Construct a deterministic null principal URI from the precursor and the 341 // loadinfo's nullPrincipalID. 342 nsCOMPtr<nsIURI> nullPrincipalURI = NullPrincipal::CreateURI( 343 precursor, &loadInfo->GetSandboxedNullPrincipalID()); 344 345 // Use the URI to construct the sandboxed result principal. 346 OriginAttributes attrs; 347 loadInfo->GetOriginAttributes(&attrs); 348 nsCOMPtr<nsIPrincipal> sandboxedPrincipal = 349 NullPrincipal::Create(attrs, nullPrincipalURI); 350 sandboxedPrincipal.forget(aPrincipal); 351 return NS_OK; 352 } 353 354 bool forceInherit = loadInfo->GetForceInheritPrincipal(); 355 if (aIgnoreSandboxing && !forceInherit) { 356 // Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of 357 // sandboxing: 358 if (loadInfo->GetLoadingSandboxed() && 359 loadInfo->GetForceInheritPrincipalDropped()) { 360 forceInherit = true; 361 } 362 } 363 if (forceInherit) { 364 nsCOMPtr<nsIPrincipal> principalToInherit = 365 loadInfo->FindPrincipalToInherit(aChannel); 366 principalToInherit.forget(aPrincipal); 367 return NS_OK; 368 } 369 370 auto securityMode = loadInfo->GetSecurityMode(); 371 // The data: inheritance flags should only apply to the initial load, 372 // not to loads that it might have redirected to. 373 if (loadInfo->RedirectChain().IsEmpty() && 374 (securityMode == 375 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT || 376 securityMode == 377 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT || 378 securityMode == nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT)) { 379 nsCOMPtr<nsIURI> uri; 380 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 381 NS_ENSURE_SUCCESS(rv, rv); 382 383 nsCOMPtr<nsIPrincipal> principalToInherit = 384 loadInfo->FindPrincipalToInherit(aChannel); 385 bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits(); 386 387 if (nsContentUtils::ChannelShouldInheritPrincipal( 388 principalToInherit, uri, inheritForAboutBlank, false)) { 389 principalToInherit.forget(aPrincipal); 390 return NS_OK; 391 } 392 } 393 return GetChannelURIPrincipal(aChannel, aPrincipal); 394 } 395 396 /* The principal of the URI that this channel is loading. This is never 397 * affected by things like sandboxed loads, or loads where we forcefully 398 * inherit the principal. Think of this as the principal of the server 399 * which this channel is loading from. Most callers should use 400 * GetChannelResultPrincipal instead of GetChannelURIPrincipal. Only 401 * call GetChannelURIPrincipal if you are sure that you want the 402 * principal that matches the uri, even in cases when the load is 403 * sandboxed or when the load could be a blob or data uri (i.e even when 404 * you encounter loads that may or may not be sandboxed and loads 405 * that may or may not inherit)." 406 */ 407 NS_IMETHODIMP 408 nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel, 409 nsIPrincipal** aPrincipal) { 410 MOZ_ASSERT(aChannel, "Must have channel!"); 411 412 // Get the principal from the URI. Make sure this does the same thing 413 // as Document::Reset and PrototypeDocumentContentSink::Init. 414 nsCOMPtr<nsIURI> uri; 415 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 416 NS_ENSURE_SUCCESS(rv, rv); 417 418 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 419 420 // Inherit the origin attributes from loadInfo. 421 // If this is a top-level document load, the origin attributes of the 422 // loadInfo will be set from nsDocShell::DoURILoad. 423 // For subresource loading, the origin attributes of the loadInfo is from 424 // its loadingPrincipal. 425 OriginAttributes attrs = loadInfo->GetOriginAttributes(); 426 427 // If the URI is supposed to inherit the security context of whoever loads it, 428 // we shouldn't make a content principal for it, so instead return a null 429 // principal. 430 bool inheritsPrincipal = false; 431 rv = NS_URIChainHasFlags(uri, 432 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, 433 &inheritsPrincipal); 434 if (NS_FAILED(rv) || inheritsPrincipal) { 435 // Find a precursor principal to credit for the load. This won't impact 436 // security checks, but makes tracking the source of related loads easier. 437 nsCOMPtr<nsIPrincipal> precursorPrincipal = 438 loadInfo->FindPrincipalToInherit(aChannel); 439 nsCOMPtr<nsIURI> nullPrincipalURI = 440 NullPrincipal::CreateURI(precursorPrincipal); 441 *aPrincipal = NullPrincipal::Create(attrs, nullPrincipalURI).take(); 442 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; 443 } 444 445 nsCOMPtr<nsIPrincipal> prin = 446 BasePrincipal::CreateContentPrincipal(uri, attrs); 447 prin.forget(aPrincipal); 448 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; 449 } 450 451 ///////////////////////////// 452 // nsScriptSecurityManager // 453 ///////////////////////////// 454 455 //////////////////////////////////// 456 // Methods implementing ISupports // 457 //////////////////////////////////// 458 NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager) 459 460 /////////////////////////////////////////////////// 461 // Methods implementing nsIScriptSecurityManager // 462 /////////////////////////////////////////////////// 463 464 ///////////////// Security Checks ///////////////// 465 466 bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( 467 JSContext* cx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString, 468 JS::CompilationType aCompilationType, 469 JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings, 470 JS::Handle<JSString*> aBodyString, 471 JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs, 472 JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) { 473 MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); 474 475 nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal(); 476 477 if (aKind == JS::RuntimeCode::JS) { 478 ErrorResult error; 479 bool areArgumentsTrusted = TrustedTypeUtils:: 480 AreArgumentsTrustedForEnsureCSPDoesNotBlockStringCompilation( 481 cx, aCodeString, aCompilationType, aParameterStrings, aBodyString, 482 aParameterArgs, aBodyArg, subjectPrincipal, error); 483 if (error.MaybeSetPendingException(cx)) { 484 return false; 485 } 486 if (!areArgumentsTrusted) { 487 *aOutCanCompileStrings = false; 488 return true; 489 } 490 } 491 492 // Check if Eval is allowed per firefox hardening policy 493 bool contextForbidsEval = 494 (subjectPrincipal->IsSystemPrincipal() || XRE_IsE10sParentProcess()); 495 if (contextForbidsEval) { 496 nsAutoJSString scriptSample; 497 if (aKind == JS::RuntimeCode::JS && 498 NS_WARN_IF(!scriptSample.init(cx, aCodeString))) { 499 return false; 500 } 501 502 if (!nsContentSecurityUtils::IsEvalAllowed( 503 cx, subjectPrincipal->IsSystemPrincipal(), scriptSample)) { 504 *aOutCanCompileStrings = false; 505 return true; 506 } 507 } 508 509 // Get the window, if any, corresponding to the current global 510 nsCOMPtr<nsIContentSecurityPolicy> csp; 511 if (nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(cx)) { 512 csp = PolicyContainer::GetCSP(win->GetPolicyContainer()); 513 } 514 515 if (!csp) { 516 // Get the CSP for addon sandboxes. If the principal is expanded and has a 517 // csp, we're probably in luck. 518 auto* basePrin = BasePrincipal::Cast(subjectPrincipal); 519 // TODO bug 1548468: Move CSP off ExpandedPrincipal. 520 if (basePrin->Is<ExpandedPrincipal>()) { 521 basePrin->As<ExpandedPrincipal>()->GetCsp(getter_AddRefs(csp)); 522 } 523 // don't do anything unless there's a CSP 524 if (!csp) { 525 *aOutCanCompileStrings = true; 526 return true; 527 } 528 } 529 530 nsCOMPtr<nsICSPEventListener> cspEventListener; 531 if (!NS_IsMainThread()) { 532 WorkerPrivate* workerPrivate = 533 mozilla::dom::GetWorkerPrivateFromContext(cx); 534 if (workerPrivate) { 535 cspEventListener = workerPrivate->CSPEventListener(); 536 } 537 } 538 539 bool evalOK = true; 540 bool reportViolation = false; 541 if (aKind == JS::RuntimeCode::JS) { 542 nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK); 543 if (NS_FAILED(rv)) { 544 NS_WARNING("CSP: failed to get allowsEval"); 545 *aOutCanCompileStrings = true; // fail open to not break sites. 546 return true; 547 } 548 } else { 549 if (NS_FAILED(csp->GetAllowsWasmEval(&reportViolation, &evalOK))) { 550 return false; 551 } 552 if (!evalOK) { 553 // Historically, CSP did not block WebAssembly in Firefox, and some 554 // add-ons use wasm and a stricter CSP. To avoid breaking them, ignore 555 // 'wasm-unsafe-eval' violations for MV2 extensions. 556 // TODO bug 1770909: remove this exception. 557 auto* addonPolicy = BasePrincipal::Cast(subjectPrincipal)->AddonPolicy(); 558 if (addonPolicy && addonPolicy->ManifestVersion() == 2) { 559 reportViolation = true; 560 evalOK = true; 561 } 562 } 563 } 564 565 if (reportViolation) { 566 auto caller = JSCallingLocation::Get(cx); 567 nsAutoJSString scriptSample; 568 if (aKind == JS::RuntimeCode::JS && 569 NS_WARN_IF(!scriptSample.init(cx, aCodeString))) { 570 return false; 571 } 572 uint16_t violationType = 573 aKind == JS::RuntimeCode::JS 574 ? nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL 575 : nsIContentSecurityPolicy::VIOLATION_TYPE_WASM_EVAL; 576 csp->LogViolationDetails(violationType, 577 nullptr, // triggering element 578 cspEventListener, caller.FileName(), scriptSample, 579 caller.mLine, caller.mColumn, u""_ns, u""_ns); 580 } 581 582 *aOutCanCompileStrings = evalOK; 583 return true; 584 } 585 586 // static 587 bool nsScriptSecurityManager::JSPrincipalsSubsume(JSPrincipals* first, 588 JSPrincipals* second) { 589 return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second)); 590 } 591 592 NS_IMETHODIMP 593 nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI, 594 nsIURI* aTargetURI, 595 bool reportError, 596 bool aFromPrivateWindow) { 597 // Please note that aFromPrivateWindow is only 100% accurate if 598 // reportError is true. 599 if (!SecurityCompareURIs(aSourceURI, aTargetURI)) { 600 if (reportError) { 601 ReportError("CheckSameOriginError", aSourceURI, aTargetURI, 602 aFromPrivateWindow); 603 } 604 return NS_ERROR_DOM_BAD_URI; 605 } 606 return NS_OK; 607 } 608 609 NS_IMETHODIMP 610 nsScriptSecurityManager::CheckLoadURIFromScript(JSContext* cx, nsIURI* aURI) { 611 // Get principal of currently executing script. 612 MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); 613 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(); 614 nsresult rv = CheckLoadURIWithPrincipal( 615 // Passing 0 for the window ID here is OK, because we will report a 616 // script-visible exception anyway. 617 principal, aURI, nsIScriptSecurityManager::STANDARD, 0); 618 if (NS_SUCCEEDED(rv)) { 619 // OK to load 620 return NS_OK; 621 } 622 623 // Report error. 624 nsAutoCString spec; 625 if (NS_FAILED(aURI->GetAsciiSpec(spec))) return NS_ERROR_FAILURE; 626 nsAutoCString msg("Access to '"); 627 msg.Append(spec); 628 msg.AppendLiteral("' from script denied"); 629 SetPendingExceptionASCII(cx, msg.get()); 630 return NS_ERROR_DOM_BAD_URI; 631 } 632 633 /** 634 * Helper method to handle cases where a flag passed to 635 * CheckLoadURIWithPrincipal means denying loading if the given URI has certain 636 * nsIProtocolHandler flags set. 637 * @return if success, access is allowed. Otherwise, deny access 638 */ 639 static nsresult DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags) { 640 MOZ_ASSERT(aURI, "Must have URI!"); 641 642 bool uriHasFlags; 643 nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags); 644 NS_ENSURE_SUCCESS(rv, rv); 645 646 if (uriHasFlags) { 647 return NS_ERROR_DOM_BAD_URI; 648 } 649 650 return NS_OK; 651 } 652 653 static bool EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase) { 654 nsresult rv; 655 nsCOMPtr<nsIURI> probe = aProbeArg; 656 657 nsCOMPtr<nsIEffectiveTLDService> tldService = 658 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); 659 NS_ENSURE_TRUE(tldService, false); 660 while (true) { 661 if (nsScriptSecurityManager::SecurityCompareURIs(probe, aBase)) { 662 return true; 663 } 664 665 nsAutoCString host, newHost; 666 rv = probe->GetHost(host); 667 NS_ENSURE_SUCCESS(rv, false); 668 669 rv = tldService->GetNextSubDomain(host, newHost); 670 if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { 671 return false; 672 } 673 NS_ENSURE_SUCCESS(rv, false); 674 rv = NS_MutateURI(probe).SetHost(newHost).Finalize(probe); 675 NS_ENSURE_SUCCESS(rv, false); 676 } 677 } 678 679 NS_IMETHODIMP 680 nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, 681 nsIURI* aTargetURI, 682 uint32_t aFlags, 683 uint64_t aInnerWindowID) { 684 MOZ_ASSERT(aPrincipal, "CheckLoadURIWithPrincipal must have a principal"); 685 686 // If someone passes a flag that we don't understand, we should 687 // fail, because they may need a security check that we don't 688 // provide. 689 NS_ENSURE_FALSE( 690 aFlags & 691 ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT | 692 nsIScriptSecurityManager::ALLOW_CHROME | 693 nsIScriptSecurityManager::DISALLOW_SCRIPT | 694 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL | 695 nsIScriptSecurityManager::DONT_REPORT_ERRORS), 696 NS_ERROR_UNEXPECTED); 697 NS_ENSURE_ARG_POINTER(aPrincipal); 698 NS_ENSURE_ARG_POINTER(aTargetURI); 699 700 // If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which 701 // would do such inheriting. That would be URIs that do not have their own 702 // security context. We do this even for the system principal. 703 if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) { 704 nsresult rv = DenyAccessIfURIHasFlags( 705 aTargetURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT); 706 NS_ENSURE_SUCCESS(rv, rv); 707 } 708 709 if (aPrincipal == mSystemPrincipal) { 710 // Allow access 711 return NS_OK; 712 } 713 714 nsCOMPtr<nsIURI> sourceURI; 715 auto* basePrin = BasePrincipal::Cast(aPrincipal); 716 basePrin->GetURI(getter_AddRefs(sourceURI)); 717 if (!sourceURI) { 718 if (basePrin->Is<ExpandedPrincipal>()) { 719 // If the target addon is MV3 or the pref is on we require extension 720 // resources loaded from content to be listed in web_accessible_resources. 721 auto* targetPolicy = 722 ExtensionPolicyService::GetSingleton().GetByURL(aTargetURI); 723 bool contentAccessRequired = 724 targetPolicy && 725 (targetPolicy->ManifestVersion() > 2 || 726 StaticPrefs::extensions_content_web_accessible_enabled()); 727 auto expanded = basePrin->As<ExpandedPrincipal>(); 728 const auto& allowList = expanded->AllowList(); 729 // Only report errors when all principals fail. 730 // With expanded principals, which are used by extension content scripts, 731 // we check only against non-extension principals for access to extension 732 // resource to enforce making those resources explicitly web accessible. 733 uint32_t flags = aFlags | nsIScriptSecurityManager::DONT_REPORT_ERRORS; 734 for (size_t i = 0; i < allowList.Length() - 1; i++) { 735 if (contentAccessRequired && 736 BasePrincipal::Cast(allowList[i])->AddonPolicy()) { 737 continue; 738 } 739 nsresult rv = CheckLoadURIWithPrincipal(allowList[i], aTargetURI, flags, 740 aInnerWindowID); 741 if (NS_SUCCEEDED(rv)) { 742 // Allow access if it succeeded with one of the allowlisted principals 743 return NS_OK; 744 } 745 } 746 747 if (contentAccessRequired && 748 BasePrincipal::Cast(allowList.LastElement())->AddonPolicy()) { 749 bool reportErrors = 750 !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS); 751 if (reportErrors) { 752 ReportError("CheckLoadURI", sourceURI, aTargetURI, 753 allowList.LastElement() 754 ->OriginAttributesRef() 755 .IsPrivateBrowsing(), 756 aInnerWindowID); 757 } 758 return NS_ERROR_DOM_BAD_URI; 759 } 760 // Report errors (if requested) for the last principal. 761 return CheckLoadURIWithPrincipal(allowList.LastElement(), aTargetURI, 762 aFlags, aInnerWindowID); 763 } 764 NS_ERROR( 765 "Non-system principals or expanded principal passed to " 766 "CheckLoadURIWithPrincipal " 767 "must have a URI!"); 768 return NS_ERROR_UNEXPECTED; 769 } 770 771 // Automatic loads are not allowed from certain protocols. 772 if (aFlags & 773 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) { 774 nsresult rv = DenyAccessIfURIHasFlags( 775 sourceURI, 776 nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT); 777 NS_ENSURE_SUCCESS(rv, rv); 778 } 779 780 // If either URI is a nested URI, get the base URI 781 nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI); 782 nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI); 783 784 //-- get the target scheme 785 nsAutoCString targetScheme; 786 nsresult rv = targetBaseURI->GetScheme(targetScheme); 787 if (NS_FAILED(rv)) return rv; 788 789 //-- Some callers do not allow loading javascript: 790 if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) && 791 targetScheme.EqualsLiteral("javascript")) { 792 return NS_ERROR_DOM_BAD_URI; 793 } 794 795 // Check for uris that are only loadable by principals that subsume them 796 bool targetURIIsLoadableBySubsumers = false; 797 rv = NS_URIChainHasFlags(targetBaseURI, 798 nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS, 799 &targetURIIsLoadableBySubsumers); 800 NS_ENSURE_SUCCESS(rv, rv); 801 802 if (targetURIIsLoadableBySubsumers) { 803 // check nothing else in the URI chain has flags that prevent 804 // access: 805 rv = CheckLoadURIFlags( 806 sourceURI, aTargetURI, sourceBaseURI, targetBaseURI, aFlags, 807 aPrincipal->OriginAttributesRef().IsPrivateBrowsing(), aInnerWindowID); 808 NS_ENSURE_SUCCESS(rv, rv); 809 // Check the principal is allowed to load the target. 810 if (aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS) { 811 return aPrincipal->CheckMayLoad(targetBaseURI, false); 812 } 813 return aPrincipal->CheckMayLoadWithReporting(targetBaseURI, false, 814 aInnerWindowID); 815 } 816 817 //-- get the source scheme 818 nsAutoCString sourceScheme; 819 rv = sourceBaseURI->GetScheme(sourceScheme); 820 if (NS_FAILED(rv)) return rv; 821 822 if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) { 823 // A null principal can target its own URI. 824 if (sourceURI == aTargetURI) { 825 return NS_OK; 826 } 827 } else if (sourceScheme.EqualsIgnoreCase("file") && 828 targetScheme.EqualsIgnoreCase("moz-icon")) { 829 // exception for file: linking to moz-icon://.ext?size=... 830 // Note that because targetScheme is the base (innermost) URI scheme, 831 // this does NOT allow file -> moz-icon:file:///... links. 832 // This is intentional. 833 return NS_OK; 834 } 835 836 // Check for webextension 837 bool targetURIIsLoadableByExtensions = false; 838 rv = NS_URIChainHasFlags(aTargetURI, 839 nsIProtocolHandler::URI_LOADABLE_BY_EXTENSIONS, 840 &targetURIIsLoadableByExtensions); 841 NS_ENSURE_SUCCESS(rv, rv); 842 843 if (targetURIIsLoadableByExtensions && 844 BasePrincipal::Cast(aPrincipal)->AddonPolicy()) { 845 return NS_OK; 846 } 847 848 // If we get here, check all the schemes can link to each other, from the top 849 // down: 850 nsCOMPtr<nsIURI> currentURI = sourceURI; 851 nsCOMPtr<nsIURI> currentOtherURI = aTargetURI; 852 853 bool denySameSchemeLinks = false; 854 rv = NS_URIChainHasFlags(aTargetURI, 855 nsIProtocolHandler::URI_SCHEME_NOT_SELF_LINKABLE, 856 &denySameSchemeLinks); 857 if (NS_FAILED(rv)) return rv; 858 859 while (currentURI && currentOtherURI) { 860 nsAutoCString scheme, otherScheme; 861 currentURI->GetScheme(scheme); 862 currentOtherURI->GetScheme(otherScheme); 863 864 bool schemesMatch = 865 scheme.Equals(otherScheme, nsCaseInsensitiveCStringComparator); 866 bool isSamePage = false; 867 bool isExtensionMismatch = false; 868 // about: URIs are special snowflakes. 869 if (scheme.EqualsLiteral("about") && schemesMatch) { 870 nsAutoCString moduleName, otherModuleName; 871 // about: pages can always link to themselves: 872 isSamePage = 873 NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, moduleName)) && 874 NS_SUCCEEDED( 875 NS_GetAboutModuleName(currentOtherURI, otherModuleName)) && 876 moduleName.Equals(otherModuleName); 877 if (!isSamePage) { 878 // We will have allowed the load earlier if the source page has 879 // system principal. So we know the source has a content 880 // principal, and it's trying to link to something else. 881 // Linkable about: pages are always reachable, even if we hit 882 // the CheckLoadURIFlags call below. 883 // We punch only 1 other hole: iff the source is unlinkable, 884 // we let them link to other pages explicitly marked SAFE 885 // for content. This avoids world-linkable about: pages linking 886 // to non-world-linkable about: pages. 887 nsCOMPtr<nsIAboutModule> module, otherModule; 888 bool knowBothModules = 889 NS_SUCCEEDED( 890 NS_GetAboutModule(currentURI, getter_AddRefs(module))) && 891 NS_SUCCEEDED(NS_GetAboutModule(currentOtherURI, 892 getter_AddRefs(otherModule))); 893 uint32_t aboutModuleFlags = 0; 894 uint32_t otherAboutModuleFlags = 0; 895 knowBothModules = 896 knowBothModules && 897 NS_SUCCEEDED(module->GetURIFlags(currentURI, &aboutModuleFlags)) && 898 NS_SUCCEEDED(otherModule->GetURIFlags(currentOtherURI, 899 &otherAboutModuleFlags)); 900 if (knowBothModules) { 901 isSamePage = !(aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) && 902 (otherAboutModuleFlags & 903 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT); 904 if (isSamePage && 905 otherAboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) { 906 // XXXgijs: this is a hack. The target will be nested 907 // (with innerURI of moz-safe-about:whatever), and 908 // the source isn't, so we won't pass if we finish 909 // the loop. We *should* pass, though, so return here. 910 // This hack can go away when bug 1228118 is fixed. 911 return NS_OK; 912 } 913 } 914 } 915 } else if (schemesMatch && scheme.EqualsLiteral("moz-extension")) { 916 // If it is not the same exension, we want to ensure we end up 917 // calling CheckLoadURIFlags 918 nsAutoCString host, otherHost; 919 currentURI->GetHost(host); 920 currentOtherURI->GetHost(otherHost); 921 isExtensionMismatch = !host.Equals(otherHost); 922 } else { 923 bool equalExceptRef = false; 924 rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef); 925 isSamePage = NS_SUCCEEDED(rv) && equalExceptRef; 926 } 927 928 // If schemes are not equal, or they're equal but the target URI 929 // is different from the source URI and doesn't always allow linking 930 // from the same scheme, or this is two different extensions, check 931 // if the URI flags of the current target URI allow the current 932 // source URI to link to it. 933 // The policy is specified by the protocol flags on both URIs. 934 if (!schemesMatch || (denySameSchemeLinks && !isSamePage) || 935 isExtensionMismatch) { 936 return CheckLoadURIFlags( 937 currentURI, currentOtherURI, sourceBaseURI, targetBaseURI, aFlags, 938 aPrincipal->OriginAttributesRef().IsPrivateBrowsing(), 939 aInnerWindowID); 940 } 941 // Otherwise... check if we can nest another level: 942 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI); 943 nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI); 944 945 // If schemes match and neither URI is nested further, we're OK. 946 if (!nestedURI && !nestedOtherURI) { 947 return NS_OK; 948 } 949 // If one is nested and the other isn't, something is wrong. 950 if (!nestedURI != !nestedOtherURI) { 951 return NS_ERROR_DOM_BAD_URI; 952 } 953 // Otherwise, both should be nested and we'll go through the loop again. 954 nestedURI->GetInnerURI(getter_AddRefs(currentURI)); 955 nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI)); 956 } 957 958 // We should never get here. We should always return from inside the loop. 959 return NS_ERROR_DOM_BAD_URI; 960 } 961 962 /** 963 * Helper method to check whether the target URI and its innermost ("base") URI 964 * has protocol flags that should stop it from being loaded by the source URI 965 * (and/or the source URI's innermost ("base") URI), taking into account any 966 * nsIScriptSecurityManager flags originally passed to 967 * CheckLoadURIWithPrincipal and friends. 968 * 969 * @return if success, access is allowed. Otherwise, deny access 970 */ 971 nsresult nsScriptSecurityManager::CheckLoadURIFlags( 972 nsIURI* aSourceURI, nsIURI* aTargetURI, nsIURI* aSourceBaseURI, 973 nsIURI* aTargetBaseURI, uint32_t aFlags, bool aFromPrivateWindow, 974 uint64_t aInnerWindowID) { 975 // Note that the order of policy checks here is very important! 976 // We start from most restrictive and work our way down. 977 bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS); 978 const char* errorTag = "CheckLoadURIError"; 979 980 nsAutoCString targetScheme; 981 nsresult rv = aTargetBaseURI->GetScheme(targetScheme); 982 if (NS_FAILED(rv)) return rv; 983 984 // Check for system target URI. 985 rv = DenyAccessIfURIHasFlags(aTargetURI, 986 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD); 987 if (NS_FAILED(rv)) { 988 // Deny access, since the origin principal is not system 989 if (reportErrors) { 990 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow, 991 aInnerWindowID); 992 } 993 return rv; 994 } 995 996 // WebExtension URIs are only accessible if the ExtensionPolicyService allows 997 // the source URI to load them. 998 bool targetURIIsWebExtensionResource = false; 999 rv = NS_URIChainHasFlags(aTargetURI, 1000 nsIProtocolHandler::URI_IS_WEBEXTENSION_RESOURCE, 1001 &targetURIIsWebExtensionResource); 1002 NS_ENSURE_SUCCESS(rv, rv); 1003 if (targetURIIsWebExtensionResource) { 1004 bool isAccessible = false; 1005 rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI( 1006 aSourceURI, aTargetURI, aFromPrivateWindow, &isAccessible); 1007 if (NS_SUCCEEDED(rv) && isAccessible) { 1008 return NS_OK; 1009 } 1010 if (reportErrors) { 1011 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow, 1012 aInnerWindowID); 1013 } 1014 return NS_ERROR_DOM_BAD_URI; 1015 } 1016 1017 // Check for chrome target URI 1018 bool targetURIIsUIResource = false; 1019 rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, 1020 &targetURIIsUIResource); 1021 NS_ENSURE_SUCCESS(rv, rv); 1022 if (targetURIIsUIResource) { 1023 // ALLOW_CHROME is a flag that we pass on all loads _except_ docshell 1024 // loads (since docshell loads run the loaded content with its origin 1025 // principal). We are effectively allowing resource:// and chrome:// 1026 // URIs to load as long as they are content accessible and as long 1027 // they're not loading it as a document. 1028 if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) { 1029 bool sourceIsUIResource = false; 1030 rv = NS_URIChainHasFlags(aSourceBaseURI, 1031 nsIProtocolHandler::URI_IS_UI_RESOURCE, 1032 &sourceIsUIResource); 1033 NS_ENSURE_SUCCESS(rv, rv); 1034 if (sourceIsUIResource) { 1035 // Special case for moz-icon URIs loaded by a local resources like 1036 // e.g. chrome: or resource: 1037 if (targetScheme.EqualsLiteral("moz-icon")) { 1038 return NS_OK; 1039 } 1040 } 1041 1042 // Only allow some "about:" pages to have access to contentaccessible 1043 // "chrome://branding/" assets. Otherwise web pages could easily and 1044 // consistently detect the differences between channels when their 1045 // branding differs. See tor-browser#43308 and tor-browser#42319. 1046 // NOTE: The same assets under the alternative URI 1047 // "resource:///chrome/browser/content/branding/" should already be 1048 // inaccessible to web content, so we only add a condition for the chrome 1049 // path. 1050 if (targetScheme.EqualsLiteral("chrome")) { 1051 nsAutoCString targetHost; 1052 rv = aTargetBaseURI->GetHost(targetHost); 1053 NS_ENSURE_SUCCESS(rv, rv); 1054 if (targetHost.EqualsLiteral("branding")) { 1055 // Disallow any Principal whose scheme is not "about", or is a 1056 // contentaccessible "about" URI ("about:blank" or "about:srcdoc"). 1057 // NOTE: "about:blank" and "about:srcdoc" would be unexpected here 1058 // since such a document spawned by a web document should inherit the 1059 // same Principal URI. I.e. they would be "http:" or "https:" schemes. 1060 // But we add this condition for extra assurances. 1061 // NOTE: Documents with null Principals, like "about:blank" typed by 1062 // the user, would also be excluded since the Principal URI would be 1063 // "moz-nullprincipal:". 1064 if (!aSourceBaseURI->SchemeIs("about") || 1065 NS_IsContentAccessibleAboutURI(aSourceBaseURI)) { 1066 return NS_ERROR_DOM_BAD_URI; 1067 } 1068 // Also exclude "about:reader" from accessing branding assets. I.e. if 1069 // a web page includes `<img src="chrome://branding/..." />` we do not 1070 // want it to render within "about:reader" either. 1071 // Though it is unknown whether the information within "about:reader" 1072 // would be exploitable by a web page, we also want to exclude 1073 // "about:reader" for consistency: if it does not display in the 1074 // original web page, it should not display in "about:reader" either. 1075 nsAutoCString sourcePath; 1076 rv = aSourceBaseURI->GetFilePath(sourcePath); 1077 NS_ENSURE_SUCCESS(rv, rv); 1078 if (sourcePath.EqualsLiteral("reader")) { 1079 return NS_ERROR_DOM_BAD_URI; 1080 } 1081 } 1082 } 1083 1084 if (targetScheme.EqualsLiteral("resource")) { 1085 if (StaticPrefs::security_all_resource_uri_content_accessible()) { 1086 return NS_OK; 1087 } 1088 1089 nsCOMPtr<nsIProtocolHandler> ph; 1090 rv = sIOService->GetProtocolHandler("resource", getter_AddRefs(ph)); 1091 NS_ENSURE_SUCCESS(rv, rv); 1092 if (!ph) { 1093 return NS_ERROR_DOM_BAD_URI; 1094 } 1095 1096 nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph); 1097 if (!rph) { 1098 return NS_ERROR_DOM_BAD_URI; 1099 } 1100 1101 bool accessAllowed = false; 1102 rph->AllowContentToAccess(aTargetBaseURI, &accessAllowed); 1103 if (accessAllowed) { 1104 return NS_OK; 1105 } 1106 } else if (targetScheme.EqualsLiteral("chrome")) { 1107 // Allow the load only if the chrome package is allowlisted. 1108 nsCOMPtr<nsIXULChromeRegistry> reg( 1109 do_GetService(NS_CHROMEREGISTRY_CONTRACTID)); 1110 if (reg) { 1111 bool accessAllowed = false; 1112 reg->AllowContentToAccess(aTargetBaseURI, &accessAllowed); 1113 if (accessAllowed) { 1114 return NS_OK; 1115 } 1116 } 1117 } else if (targetScheme.EqualsLiteral("moz-page-thumb") || 1118 targetScheme.EqualsLiteral("page-icon") || 1119 targetScheme.EqualsLiteral("moz-newtab-wallpaper")) { 1120 if (XRE_IsParentProcess()) { 1121 return NS_OK; 1122 } 1123 1124 auto& remoteType = dom::ContentChild::GetSingleton()->GetRemoteType(); 1125 if (remoteType == PRIVILEGEDABOUT_REMOTE_TYPE) { 1126 return NS_OK; 1127 } 1128 } 1129 } 1130 1131 if (reportErrors) { 1132 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow, 1133 aInnerWindowID); 1134 } 1135 return NS_ERROR_DOM_BAD_URI; 1136 } 1137 1138 // Check for target URI pointing to a file 1139 bool targetURIIsLocalFile = false; 1140 rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, 1141 &targetURIIsLocalFile); 1142 NS_ENSURE_SUCCESS(rv, rv); 1143 if (targetURIIsLocalFile) { 1144 // Allow domains that were allowlisted in the prefs. In 99.9% of cases, 1145 // this array is empty. 1146 bool isAllowlisted; 1147 MOZ_ALWAYS_SUCCEEDS(InFileURIAllowlist(aSourceURI, &isAllowlisted)); 1148 if (isAllowlisted) { 1149 return NS_OK; 1150 } 1151 1152 // Allow chrome:// 1153 if (aSourceBaseURI->SchemeIs("chrome")) { 1154 return NS_OK; 1155 } 1156 1157 // Nothing else. 1158 if (reportErrors) { 1159 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow, 1160 aInnerWindowID); 1161 } 1162 return NS_ERROR_DOM_BAD_URI; 1163 } 1164 1165 #ifdef DEBUG 1166 { 1167 // Everyone is allowed to load this. The case URI_LOADABLE_BY_SUBSUMERS 1168 // is handled by the caller which is just delegating to us as a helper. 1169 bool hasSubsumersFlag = false; 1170 NS_URIChainHasFlags(aTargetBaseURI, 1171 nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS, 1172 &hasSubsumersFlag); 1173 bool hasLoadableByAnyone = false; 1174 NS_URIChainHasFlags(aTargetBaseURI, 1175 nsIProtocolHandler::URI_LOADABLE_BY_ANYONE, 1176 &hasLoadableByAnyone); 1177 MOZ_ASSERT(hasLoadableByAnyone || hasSubsumersFlag, 1178 "why do we get here and do not have any of the two flags set?"); 1179 } 1180 #endif 1181 1182 return NS_OK; 1183 } 1184 1185 nsresult nsScriptSecurityManager::ReportError(const char* aMessageTag, 1186 const nsACString& aSourceSpec, 1187 const nsACString& aTargetSpec, 1188 bool aFromPrivateWindow, 1189 uint64_t aInnerWindowID) { 1190 if (aSourceSpec.IsEmpty() || aTargetSpec.IsEmpty()) { 1191 return NS_OK; 1192 } 1193 1194 nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate(); 1195 if (NS_WARN_IF(!bundle)) { 1196 return NS_OK; 1197 } 1198 1199 // Localize the error message 1200 nsAutoString message; 1201 AutoTArray<nsString, 2> formatStrings; 1202 CopyASCIItoUTF16(aSourceSpec, *formatStrings.AppendElement()); 1203 CopyASCIItoUTF16(aTargetSpec, *formatStrings.AppendElement()); 1204 nsresult rv = 1205 bundle->FormatStringFromName(aMessageTag, formatStrings, message); 1206 NS_ENSURE_SUCCESS(rv, rv); 1207 1208 nsCOMPtr<nsIConsoleService> console( 1209 do_GetService(NS_CONSOLESERVICE_CONTRACTID)); 1210 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE); 1211 nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); 1212 NS_ENSURE_TRUE(error, NS_ERROR_FAILURE); 1213 1214 // using category of "SOP" so we can link to MDN 1215 if (aInnerWindowID != 0) { 1216 rv = error->InitWithWindowID( 1217 message, ""_ns, 0, 0, nsIScriptError::errorFlag, "SOP"_ns, 1218 aInnerWindowID, true /* From chrome context */); 1219 } else { 1220 rv = error->Init(message, ""_ns, 0, 0, nsIScriptError::errorFlag, "SOP"_ns, 1221 aFromPrivateWindow, true /* From chrome context */); 1222 } 1223 NS_ENSURE_SUCCESS(rv, rv); 1224 console->LogMessage(error); 1225 return NS_OK; 1226 } 1227 1228 nsresult nsScriptSecurityManager::ReportError(const char* aMessageTag, 1229 nsIURI* aSource, nsIURI* aTarget, 1230 bool aFromPrivateWindow, 1231 uint64_t aInnerWindowID) { 1232 NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER); 1233 1234 // Get the source URL spec 1235 nsAutoCString sourceSpec; 1236 nsresult rv = aSource->GetAsciiSpec(sourceSpec); 1237 NS_ENSURE_SUCCESS(rv, rv); 1238 1239 // Get the target URL spec 1240 nsAutoCString targetSpec; 1241 rv = aTarget->GetAsciiSpec(targetSpec); 1242 NS_ENSURE_SUCCESS(rv, rv); 1243 1244 return ReportError(aMessageTag, sourceSpec, targetSpec, aFromPrivateWindow, 1245 aInnerWindowID); 1246 } 1247 1248 NS_IMETHODIMP 1249 nsScriptSecurityManager::CheckLoadURIStrWithPrincipal( 1250 nsIPrincipal* aPrincipal, const nsACString& aTargetURIStr, 1251 uint32_t aFlags) { 1252 nsresult rv; 1253 nsCOMPtr<nsIURI> target; 1254 rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr); 1255 NS_ENSURE_SUCCESS(rv, rv); 1256 1257 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags, 0); 1258 if (rv == NS_ERROR_DOM_BAD_URI) { 1259 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected 1260 // return values. 1261 return rv; 1262 } 1263 NS_ENSURE_SUCCESS(rv, rv); 1264 1265 // Now start testing fixup -- since aTargetURIStr is a string, not 1266 // an nsIURI, we may well end up fixing it up before loading. 1267 // Note: This needs to stay in sync with the nsIURIFixup api. 1268 nsCOMPtr<nsIURIFixup> fixup = components::URIFixup::Service(); 1269 if (!fixup) { 1270 return rv; 1271 } 1272 1273 // URIFixup's keyword and alternate flags can only fixup to http/https, so we 1274 // can skip testing them. This simplifies our life because this code can be 1275 // invoked from the content process where the search service would not be 1276 // available. 1277 uint32_t flags[] = {nsIURIFixup::FIXUP_FLAG_NONE, 1278 nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS}; 1279 for (uint32_t i = 0; i < std::size(flags); ++i) { 1280 uint32_t fixupFlags = flags[i]; 1281 if (aPrincipal->OriginAttributesRef().IsPrivateBrowsing()) { 1282 fixupFlags |= nsIURIFixup::FIXUP_FLAG_PRIVATE_CONTEXT; 1283 } 1284 nsCOMPtr<nsIURIFixupInfo> fixupInfo; 1285 rv = fixup->GetFixupURIInfo(aTargetURIStr, fixupFlags, 1286 getter_AddRefs(fixupInfo)); 1287 NS_ENSURE_SUCCESS(rv, rv); 1288 rv = fixupInfo->GetPreferredURI(getter_AddRefs(target)); 1289 NS_ENSURE_SUCCESS(rv, rv); 1290 1291 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags, 0); 1292 if (rv == NS_ERROR_DOM_BAD_URI) { 1293 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected 1294 // return values. 1295 return rv; 1296 } 1297 NS_ENSURE_SUCCESS(rv, rv); 1298 } 1299 1300 return rv; 1301 } 1302 1303 NS_IMETHODIMP 1304 nsScriptSecurityManager::CheckLoadURIWithPrincipalFromJS( 1305 nsIPrincipal* aPrincipal, nsIURI* aTargetURI, uint32_t aFlags, 1306 uint64_t aInnerWindowID, JSContext* aCx) { 1307 MOZ_ASSERT(aPrincipal, 1308 "CheckLoadURIWithPrincipalFromJS must have a principal"); 1309 NS_ENSURE_ARG_POINTER(aPrincipal); 1310 NS_ENSURE_ARG_POINTER(aTargetURI); 1311 1312 nsresult rv = 1313 CheckLoadURIWithPrincipal(aPrincipal, aTargetURI, aFlags, aInnerWindowID); 1314 if (NS_FAILED(rv)) { 1315 nsAutoCString uriStr; 1316 (void)aTargetURI->GetSpec(uriStr); 1317 1318 nsAutoCString message("Load of "); 1319 message.Append(uriStr); 1320 1321 nsAutoCString principalStr; 1322 (void)aPrincipal->GetSpec(principalStr); 1323 if (!principalStr.IsEmpty()) { 1324 message.AppendPrintf(" from %s", principalStr.get()); 1325 } 1326 1327 message.Append(" denied"); 1328 1329 dom::Throw(aCx, rv, message); 1330 } 1331 1332 return rv; 1333 } 1334 1335 NS_IMETHODIMP 1336 nsScriptSecurityManager::CheckLoadURIStrWithPrincipalFromJS( 1337 nsIPrincipal* aPrincipal, const nsACString& aTargetURIStr, uint32_t aFlags, 1338 JSContext* aCx) { 1339 nsCOMPtr<nsIURI> targetURI; 1340 MOZ_TRY(NS_NewURI(getter_AddRefs(targetURI), aTargetURIStr)); 1341 1342 return CheckLoadURIWithPrincipalFromJS(aPrincipal, targetURI, aFlags, 0, aCx); 1343 } 1344 1345 NS_IMETHODIMP 1346 nsScriptSecurityManager::InFileURIAllowlist(nsIURI* aUri, bool* aResult) { 1347 MOZ_ASSERT(aUri); 1348 MOZ_ASSERT(aResult); 1349 1350 *aResult = false; 1351 for (nsIURI* uri : EnsureFileURIAllowlist()) { 1352 if (EqualOrSubdomain(aUri, uri)) { 1353 *aResult = true; 1354 return NS_OK; 1355 } 1356 } 1357 1358 return NS_OK; 1359 } 1360 1361 ///////////////// Principals /////////////////////// 1362 1363 NS_IMETHODIMP 1364 nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal** result) { 1365 NS_ADDREF(*result = mSystemPrincipal); 1366 1367 return NS_OK; 1368 } 1369 1370 NS_IMETHODIMP 1371 nsScriptSecurityManager::CreateContentPrincipal( 1372 nsIURI* aURI, JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx, 1373 nsIPrincipal** aPrincipal) { 1374 OriginAttributes attrs; 1375 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { 1376 return NS_ERROR_INVALID_ARG; 1377 } 1378 nsCOMPtr<nsIPrincipal> prin = 1379 BasePrincipal::CreateContentPrincipal(aURI, attrs); 1380 prin.forget(aPrincipal); 1381 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; 1382 } 1383 1384 NS_IMETHODIMP 1385 nsScriptSecurityManager::CreateContentPrincipalFromOrigin( 1386 const nsACString& aOrigin, nsIPrincipal** aPrincipal) { 1387 if (StringBeginsWith(aOrigin, "["_ns)) { 1388 return NS_ERROR_INVALID_ARG; 1389 } 1390 1391 if (StringBeginsWith(aOrigin, 1392 nsLiteralCString(NS_NULLPRINCIPAL_SCHEME ":"))) { 1393 return NS_ERROR_INVALID_ARG; 1394 } 1395 1396 nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateContentPrincipal(aOrigin); 1397 prin.forget(aPrincipal); 1398 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; 1399 } 1400 1401 NS_IMETHODIMP 1402 nsScriptSecurityManager::PrincipalToJSON(nsIPrincipal* aPrincipal, 1403 nsACString& aJSON) { 1404 aJSON.Truncate(); 1405 if (!aPrincipal) { 1406 return NS_ERROR_FAILURE; 1407 } 1408 1409 BasePrincipal::Cast(aPrincipal)->ToJSON(aJSON); 1410 1411 if (aJSON.IsEmpty()) { 1412 return NS_ERROR_FAILURE; 1413 } 1414 1415 return NS_OK; 1416 } 1417 1418 NS_IMETHODIMP 1419 nsScriptSecurityManager::JSONToPrincipal(const nsACString& aJSON, 1420 nsIPrincipal** aPrincipal) { 1421 if (aJSON.IsEmpty()) { 1422 return NS_ERROR_FAILURE; 1423 } 1424 1425 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(aJSON); 1426 1427 if (!principal) { 1428 return NS_ERROR_FAILURE; 1429 } 1430 1431 principal.forget(aPrincipal); 1432 return NS_OK; 1433 } 1434 1435 NS_IMETHODIMP 1436 nsScriptSecurityManager::CreateNullPrincipal( 1437 JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx, 1438 nsIPrincipal** aPrincipal) { 1439 OriginAttributes attrs; 1440 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { 1441 return NS_ERROR_INVALID_ARG; 1442 } 1443 nsCOMPtr<nsIPrincipal> prin = NullPrincipal::Create(attrs); 1444 prin.forget(aPrincipal); 1445 return NS_OK; 1446 } 1447 1448 NS_IMETHODIMP 1449 nsScriptSecurityManager::GetLoadContextContentPrincipal( 1450 nsIURI* aURI, nsILoadContext* aLoadContext, nsIPrincipal** aPrincipal) { 1451 NS_ENSURE_STATE(aLoadContext); 1452 OriginAttributes docShellAttrs; 1453 aLoadContext->GetOriginAttributes(docShellAttrs); 1454 1455 nsCOMPtr<nsIPrincipal> prin = 1456 BasePrincipal::CreateContentPrincipal(aURI, docShellAttrs); 1457 prin.forget(aPrincipal); 1458 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; 1459 } 1460 1461 NS_IMETHODIMP 1462 nsScriptSecurityManager::GetDocShellContentPrincipal( 1463 nsIURI* aURI, nsIDocShell* aDocShell, nsIPrincipal** aPrincipal) { 1464 nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateContentPrincipal( 1465 aURI, nsDocShell::Cast(aDocShell)->GetOriginAttributes()); 1466 prin.forget(aPrincipal); 1467 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; 1468 } 1469 1470 NS_IMETHODIMP 1471 nsScriptSecurityManager::PrincipalWithOA( 1472 nsIPrincipal* aPrincipal, JS::Handle<JS::Value> aOriginAttributes, 1473 JSContext* aCx, nsIPrincipal** aReturnPrincipal) { 1474 if (!aPrincipal) { 1475 return NS_OK; 1476 } 1477 if (aPrincipal->GetIsContentPrincipal()) { 1478 OriginAttributes attrs; 1479 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { 1480 return NS_ERROR_INVALID_ARG; 1481 } 1482 auto* contentPrincipal = static_cast<ContentPrincipal*>(aPrincipal); 1483 RefPtr<ContentPrincipal> copy = 1484 new ContentPrincipal(contentPrincipal, attrs); 1485 NS_ENSURE_TRUE(copy, NS_ERROR_FAILURE); 1486 copy.forget(aReturnPrincipal); 1487 } else { 1488 // We do this for null principals, system principals (both fine) 1489 // ... and expanded principals, where we should probably do something 1490 // cleverer, but I also don't think we care too much. 1491 nsCOMPtr<nsIPrincipal> prin = aPrincipal; 1492 prin.forget(aReturnPrincipal); 1493 } 1494 1495 return *aReturnPrincipal ? NS_OK : NS_ERROR_FAILURE; 1496 } 1497 1498 NS_IMETHODIMP 1499 nsScriptSecurityManager::CanCreateWrapper(JSContext* cx, const nsIID& aIID, 1500 nsISupports* aObj, 1501 nsIClassInfo* aClassInfo) { 1502 // XXX Special case for Exception ? 1503 1504 // We give remote-XUL allowlisted domains a free pass here. See bug 932906. 1505 JS::Rooted<JS::Realm*> contextRealm(cx, JS::GetCurrentRealmOrNull(cx)); 1506 MOZ_RELEASE_ASSERT(contextRealm); 1507 if (!xpc::AllowContentXBLScope(contextRealm)) { 1508 return NS_OK; 1509 } 1510 1511 if (nsContentUtils::IsCallerChrome()) { 1512 return NS_OK; 1513 } 1514 1515 //-- Access denied, report an error 1516 nsAutoCString originUTF8; 1517 nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal(); 1518 GetPrincipalDomainOrigin(subjectPrincipal, originUTF8); 1519 NS_ConvertUTF8toUTF16 originUTF16(originUTF8); 1520 nsAutoCString classInfoNameUTF8; 1521 if (aClassInfo) { 1522 aClassInfo->GetClassDescription(classInfoNameUTF8); 1523 } 1524 if (classInfoNameUTF8.IsEmpty()) { 1525 classInfoNameUTF8.AssignLiteral("UnnamedClass"); 1526 } 1527 1528 nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate(); 1529 if (NS_WARN_IF(!bundle)) { 1530 return NS_OK; 1531 } 1532 1533 NS_ConvertUTF8toUTF16 classInfoUTF16(classInfoNameUTF8); 1534 nsresult rv; 1535 nsAutoString errorMsg; 1536 if (originUTF16.IsEmpty()) { 1537 AutoTArray<nsString, 1> formatStrings = {classInfoUTF16}; 1538 rv = bundle->FormatStringFromName("CreateWrapperDenied", formatStrings, 1539 errorMsg); 1540 } else { 1541 AutoTArray<nsString, 2> formatStrings = {classInfoUTF16, originUTF16}; 1542 rv = bundle->FormatStringFromName("CreateWrapperDeniedForOrigin", 1543 formatStrings, errorMsg); 1544 } 1545 NS_ENSURE_SUCCESS(rv, rv); 1546 1547 SetPendingException(cx, errorMsg.get()); 1548 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED; 1549 } 1550 1551 NS_IMETHODIMP 1552 nsScriptSecurityManager::CanCreateInstance(JSContext* cx, const nsCID& aCID) { 1553 if (nsContentUtils::IsCallerChrome()) { 1554 return NS_OK; 1555 } 1556 1557 //-- Access denied, report an error 1558 nsAutoCString errorMsg("Permission denied to create instance of class. CID="); 1559 char cidStr[NSID_LENGTH]; 1560 aCID.ToProvidedString(cidStr); 1561 errorMsg.Append(cidStr); 1562 SetPendingExceptionASCII(cx, errorMsg.get()); 1563 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED; 1564 } 1565 1566 NS_IMETHODIMP 1567 nsScriptSecurityManager::CanGetService(JSContext* cx, const nsCID& aCID) { 1568 if (nsContentUtils::IsCallerChrome()) { 1569 return NS_OK; 1570 } 1571 1572 //-- Access denied, report an error 1573 nsAutoCString errorMsg("Permission denied to get service. CID="); 1574 char cidStr[NSID_LENGTH]; 1575 aCID.ToProvidedString(cidStr); 1576 errorMsg.Append(cidStr); 1577 SetPendingExceptionASCII(cx, errorMsg.get()); 1578 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED; 1579 } 1580 1581 const char sJSEnabledPrefName[] = "javascript.enabled"; 1582 const char sFileOriginPolicyPrefName[] = 1583 "security.fileuri.strict_origin_policy"; 1584 1585 static const char* kObservedPrefs[] = {sJSEnabledPrefName, 1586 sFileOriginPolicyPrefName, 1587 "capability.policy.", nullptr}; 1588 1589 ///////////////////////////////////////////// 1590 // Constructor, Destructor, Initialization // 1591 ///////////////////////////////////////////// 1592 nsScriptSecurityManager::nsScriptSecurityManager(void) 1593 : mPrefInitialized(false), mIsJavaScriptEnabled(false) { 1594 static_assert( 1595 sizeof(intptr_t) == sizeof(void*), 1596 "intptr_t and void* have different lengths on this platform. " 1597 "This may cause a security failure with the SecurityLevel union."); 1598 } 1599 1600 nsresult nsScriptSecurityManager::Init() { 1601 nsresult rv; 1602 RefPtr<nsIIOService> io = mozilla::components::IO::Service(&rv); 1603 if (NS_FAILED(rv)) { 1604 return rv; 1605 } 1606 sIOService = std::move(io); 1607 InitPrefs(); 1608 1609 // Create our system principal singleton 1610 mSystemPrincipal = SystemPrincipal::Init(); 1611 1612 return NS_OK; 1613 } 1614 1615 void nsScriptSecurityManager::InitJSCallbacks(JSContext* aCx) { 1616 //-- Register security check callback in the JS engine 1617 // Currently this is used to control access to function.caller 1618 1619 static const JSSecurityCallbacks securityCallbacks = { 1620 ContentSecurityPolicyPermitsJSAction, 1621 TrustedTypeUtils::HostGetCodeForEval, 1622 JSPrincipalsSubsume, 1623 }; 1624 1625 MOZ_ASSERT(!JS_GetSecurityCallbacks(aCx)); 1626 JS_SetSecurityCallbacks(aCx, &securityCallbacks); 1627 JS_InitDestroyPrincipalsCallback(aCx, nsJSPrincipals::Destroy); 1628 1629 JS_SetTrustedPrincipals(aCx, BasePrincipal::Cast(mSystemPrincipal)); 1630 } 1631 1632 /* static */ 1633 void nsScriptSecurityManager::ClearJSCallbacks(JSContext* aCx) { 1634 JS_SetSecurityCallbacks(aCx, nullptr); 1635 JS_SetTrustedPrincipals(aCx, nullptr); 1636 } 1637 1638 static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan; 1639 1640 nsScriptSecurityManager::~nsScriptSecurityManager(void) { 1641 Preferences::UnregisterPrefixCallbacks( 1642 nsScriptSecurityManager::ScriptSecurityPrefChanged, kObservedPrefs, this); 1643 if (mDomainPolicy) { 1644 mDomainPolicy->Deactivate(); 1645 } 1646 // ContentChild might hold a reference to the domain policy, 1647 // and it might release it only after the security manager is 1648 // gone. But we can still assert this for the main process. 1649 MOZ_ASSERT_IF(XRE_IsParentProcess(), !mDomainPolicy); 1650 } 1651 1652 void nsScriptSecurityManager::Shutdown() { 1653 sIOService = nullptr; 1654 BundleHelper::Shutdown(); 1655 SystemPrincipal::Shutdown(); 1656 } 1657 1658 nsScriptSecurityManager* nsScriptSecurityManager::GetScriptSecurityManager() { 1659 return gScriptSecMan; 1660 } 1661 1662 /* static */ 1663 void nsScriptSecurityManager::InitStatics() { 1664 RefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager(); 1665 nsresult rv = ssManager->Init(); 1666 if (NS_FAILED(rv)) { 1667 MOZ_CRASH("ssManager->Init() failed"); 1668 } 1669 1670 ClearOnShutdown(&gScriptSecMan); 1671 gScriptSecMan = ssManager; 1672 } 1673 1674 // Currently this nsGenericFactory constructor is used only from FastLoad 1675 // (XPCOM object deserialization) code, when "creating" the system principal 1676 // singleton. 1677 already_AddRefed<SystemPrincipal> 1678 nsScriptSecurityManager::SystemPrincipalSingletonConstructor() { 1679 if (gScriptSecMan) 1680 return do_AddRef(gScriptSecMan->mSystemPrincipal) 1681 .downcast<SystemPrincipal>(); 1682 return nullptr; 1683 } 1684 1685 struct IsWhitespace { 1686 static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); }; 1687 }; 1688 struct IsWhitespaceOrComma { 1689 static bool Test(char aChar) { 1690 return aChar == ',' || NS_IsAsciiWhitespace(aChar); 1691 }; 1692 }; 1693 1694 template <typename Predicate> 1695 uint32_t SkipPast(const nsCString& str, uint32_t base) { 1696 while (base < str.Length() && Predicate::Test(str[base])) { 1697 ++base; 1698 } 1699 return base; 1700 } 1701 1702 template <typename Predicate> 1703 uint32_t SkipUntil(const nsCString& str, uint32_t base) { 1704 while (base < str.Length() && !Predicate::Test(str[base])) { 1705 ++base; 1706 } 1707 return base; 1708 } 1709 1710 // static 1711 void nsScriptSecurityManager::ScriptSecurityPrefChanged(const char* aPref, 1712 void* aSelf) { 1713 static_cast<nsScriptSecurityManager*>(aSelf)->ScriptSecurityPrefChanged( 1714 aPref); 1715 } 1716 1717 inline void nsScriptSecurityManager::ScriptSecurityPrefChanged( 1718 const char* aPref) { 1719 MOZ_ASSERT(mPrefInitialized); 1720 mIsJavaScriptEnabled = 1721 Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled); 1722 sStrictFileOriginPolicy = 1723 Preferences::GetBool(sFileOriginPolicyPrefName, false); 1724 mFileURIAllowlist.reset(); 1725 } 1726 1727 void nsScriptSecurityManager::AddSitesToFileURIAllowlist( 1728 const nsCString& aSiteList) { 1729 for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0; 1730 base < aSiteList.Length(); 1731 base = SkipPast<IsWhitespace>(aSiteList, bound)) { 1732 // Grab the current site. 1733 bound = SkipUntil<IsWhitespace>(aSiteList, base); 1734 nsAutoCString site(Substring(aSiteList, base, bound - base)); 1735 1736 // Check if the URI is schemeless. If so, add both http and https. 1737 nsAutoCString unused; 1738 if (NS_FAILED(sIOService->ExtractScheme(site, unused))) { 1739 AddSitesToFileURIAllowlist("http://"_ns + site); 1740 AddSitesToFileURIAllowlist("https://"_ns + site); 1741 continue; 1742 } 1743 1744 // Convert it to a URI and add it to our list. 1745 nsCOMPtr<nsIURI> uri; 1746 nsresult rv = NS_NewURI(getter_AddRefs(uri), site); 1747 if (NS_SUCCEEDED(rv)) { 1748 mFileURIAllowlist.ref().AppendElement(uri); 1749 } else { 1750 nsCOMPtr<nsIConsoleService> console( 1751 do_GetService("@mozilla.org/consoleservice;1")); 1752 if (console) { 1753 nsAutoString msg = 1754 u"Unable to to add site to file:// URI allowlist: "_ns + 1755 NS_ConvertASCIItoUTF16(site); 1756 console->LogStringMessage(msg.get()); 1757 } 1758 } 1759 } 1760 } 1761 1762 nsresult nsScriptSecurityManager::InitPrefs() { 1763 nsIPrefBranch* branch = Preferences::GetRootBranch(); 1764 NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE); 1765 1766 mPrefInitialized = true; 1767 1768 // Set the initial value of the "javascript.enabled" prefs 1769 ScriptSecurityPrefChanged(); 1770 1771 // set observer callbacks in case the value of the prefs change 1772 Preferences::RegisterPrefixCallbacks( 1773 nsScriptSecurityManager::ScriptSecurityPrefChanged, kObservedPrefs, this); 1774 1775 return NS_OK; 1776 } 1777 1778 NS_IMETHODIMP 1779 nsScriptSecurityManager::GetDomainPolicyActive(bool* aRv) { 1780 *aRv = !!mDomainPolicy; 1781 return NS_OK; 1782 } 1783 1784 NS_IMETHODIMP 1785 nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv) { 1786 if (!XRE_IsParentProcess()) { 1787 return NS_ERROR_SERVICE_NOT_AVAILABLE; 1788 } 1789 1790 return ActivateDomainPolicyInternal(aRv); 1791 } 1792 1793 NS_IMETHODIMP 1794 nsScriptSecurityManager::ActivateDomainPolicyInternal(nsIDomainPolicy** aRv) { 1795 // We only allow one domain policy at a time. The holder of the previous 1796 // policy must explicitly deactivate it first. 1797 if (mDomainPolicy) { 1798 return NS_ERROR_SERVICE_NOT_AVAILABLE; 1799 } 1800 1801 mDomainPolicy = new DomainPolicy(); 1802 nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy; 1803 ptr.forget(aRv); 1804 return NS_OK; 1805 } 1806 1807 // Intentionally non-scriptable. Script must have a reference to the 1808 // nsIDomainPolicy to deactivate it. 1809 void nsScriptSecurityManager::DeactivateDomainPolicy() { 1810 mDomainPolicy = nullptr; 1811 } 1812 1813 void nsScriptSecurityManager::CloneDomainPolicy(DomainPolicyClone* aClone) { 1814 MOZ_ASSERT(aClone); 1815 if (mDomainPolicy) { 1816 mDomainPolicy->CloneDomainPolicy(aClone); 1817 } else { 1818 aClone->active() = false; 1819 } 1820 } 1821 1822 NS_IMETHODIMP 1823 nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool* aRv) { 1824 nsresult rv; 1825 1826 // Compute our rule. If we don't have any domain policy set up that might 1827 // provide exceptions to this rule, we're done. 1828 *aRv = mIsJavaScriptEnabled; 1829 if (!mDomainPolicy) { 1830 return NS_OK; 1831 } 1832 1833 // We have a domain policy. Grab the appropriate set of exceptions to the 1834 // rule (either the blocklist or the allowlist, depending on whether script 1835 // is enabled or disabled by default). 1836 nsCOMPtr<nsIDomainSet> exceptions; 1837 nsCOMPtr<nsIDomainSet> superExceptions; 1838 if (*aRv) { 1839 mDomainPolicy->GetBlocklist(getter_AddRefs(exceptions)); 1840 mDomainPolicy->GetSuperBlocklist(getter_AddRefs(superExceptions)); 1841 } else { 1842 mDomainPolicy->GetAllowlist(getter_AddRefs(exceptions)); 1843 mDomainPolicy->GetSuperAllowlist(getter_AddRefs(superExceptions)); 1844 } 1845 1846 bool contains; 1847 rv = exceptions->Contains(aURI, &contains); 1848 NS_ENSURE_SUCCESS(rv, rv); 1849 if (contains) { 1850 *aRv = !*aRv; 1851 return NS_OK; 1852 } 1853 rv = superExceptions->ContainsSuperDomain(aURI, &contains); 1854 NS_ENSURE_SUCCESS(rv, rv); 1855 if (contains) { 1856 *aRv = !*aRv; 1857 } 1858 1859 return NS_OK; 1860 } 1861 1862 const nsTArray<nsCOMPtr<nsIURI>>& 1863 nsScriptSecurityManager::EnsureFileURIAllowlist() { 1864 if (mFileURIAllowlist.isSome()) { 1865 return mFileURIAllowlist.ref(); 1866 } 1867 1868 // 1869 // Rebuild the set of principals for which we allow file:// URI loads. This 1870 // implements a small subset of an old pref-based CAPS people that people 1871 // have come to depend on. See bug 995943. 1872 // 1873 1874 mFileURIAllowlist.emplace(); 1875 nsAutoCString policies; 1876 mozilla::Preferences::GetCString("capability.policy.policynames", policies); 1877 for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0; 1878 base < policies.Length(); 1879 base = SkipPast<IsWhitespaceOrComma>(policies, bound)) { 1880 // Grab the current policy name. 1881 bound = SkipUntil<IsWhitespaceOrComma>(policies, base); 1882 auto policyName = Substring(policies, base, bound - base); 1883 1884 // Figure out if this policy allows loading file:// URIs. If not, we can 1885 // skip it. 1886 nsCString checkLoadURIPrefName = 1887 "capability.policy."_ns + policyName + ".checkloaduri.enabled"_ns; 1888 nsAutoString value; 1889 nsresult rv = Preferences::GetString(checkLoadURIPrefName.get(), value); 1890 if (NS_FAILED(rv) || !value.LowerCaseEqualsLiteral("allaccess")) { 1891 continue; 1892 } 1893 1894 // Grab the list of domains associated with this policy. 1895 nsCString domainPrefName = 1896 "capability.policy."_ns + policyName + ".sites"_ns; 1897 nsAutoCString siteList; 1898 Preferences::GetCString(domainPrefName.get(), siteList); 1899 AddSitesToFileURIAllowlist(siteList); 1900 } 1901 1902 return mFileURIAllowlist.ref(); 1903 } 1904 1905 NS_IMETHODIMP 1906 nsScriptSecurityManager::GetFirstUnexpectedJavaScriptLoad( 1907 nsACString& aScriptFilename) { 1908 aScriptFilename.Truncate(); 1909 return nsContentSecurityUtils::GetVeryFirstUnexpectedScriptFilename( 1910 aScriptFilename); 1911 }