Bug 1528152 [wpt PR 15381] - Quirks: move vertical-align-in-quirks.html to historical...
[gecko.git] / caps / nsScriptSecurityManager.cpp
blob8909eb7c2680bf789c80d03e8276aa852fcd3a26
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsScriptSecurityManager.h"
9 #include "mozilla/ArrayUtils.h"
11 #include "xpcpublic.h"
12 #include "XPCWrapper.h"
13 #include "nsIInputStreamChannel.h"
14 #include "nsILoadContext.h"
15 #include "nsIServiceManager.h"
16 #include "nsIScriptObjectPrincipal.h"
17 #include "nsIScriptContext.h"
18 #include "nsIScriptError.h"
19 #include "nsIURL.h"
20 #include "nsIURIMutator.h"
21 #include "nsINestedURI.h"
22 #include "nspr.h"
23 #include "nsJSPrincipals.h"
24 #include "mozilla/BasePrincipal.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 "nsDocShell.h"
32 #include "nsError.h"
33 #include "nsGlobalWindowInner.h"
34 #include "nsDOMCID.h"
35 #include "nsTextFormatter.h"
36 #include "nsIStringBundle.h"
37 #include "nsNetUtil.h"
38 #include "nsIEffectiveTLDService.h"
39 #include "nsIProperties.h"
40 #include "nsDirectoryServiceDefs.h"
41 #include "nsIFile.h"
42 #include "nsIFileURL.h"
43 #include "nsIZipReader.h"
44 #include "nsIScriptGlobalObject.h"
45 #include "nsPIDOMWindow.h"
46 #include "nsIDocShell.h"
47 #include "nsIPrompt.h"
48 #include "nsIWindowWatcher.h"
49 #include "nsIConsoleService.h"
50 #include "nsIOService.h"
51 #include "nsIContent.h"
52 #include "nsDOMJSUtils.h"
53 #include "nsAboutProtocolUtils.h"
54 #include "nsIClassInfo.h"
55 #include "nsIURIFixup.h"
56 #include "nsIChromeRegistry.h"
57 #include "nsIResProtocolHandler.h"
58 #include "nsIContentSecurityPolicy.h"
59 #include "nsIAsyncVerifyRedirectCallback.h"
60 #include "mozilla/Components.h"
61 #include "mozilla/Preferences.h"
62 #include "mozilla/dom/BindingUtils.h"
63 #include "mozilla/NullPrincipal.h"
64 #include <stdint.h>
65 #include "mozilla/dom/nsCSPContext.h"
66 #include "mozilla/dom/ScriptSettings.h"
67 #include "mozilla/ClearOnShutdown.h"
68 #include "mozilla/StaticPtr.h"
69 #include "mozilla/dom/WorkerCommon.h"
70 #include "mozilla/dom/WorkerPrivate.h"
71 #include "nsContentUtils.h"
72 #include "nsJSUtils.h"
73 #include "nsILoadInfo.h"
74 #include "nsIDOMXULCommandDispatcher.h"
75 #include "nsITreeSelection.h"
77 // This should be probably defined on some other place... but I couldn't find it
78 #define WEBAPPS_PERM_NAME "webapps-manage"
80 using namespace mozilla;
81 using namespace mozilla::dom;
83 nsIIOService* nsScriptSecurityManager::sIOService = nullptr;
84 JSContext* nsScriptSecurityManager::sContext = nullptr;
85 bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
87 namespace {
89 class BundleHelper {
90 public:
91 NS_INLINE_DECL_REFCOUNTING(BundleHelper)
93 static nsIStringBundle* GetOrCreate() {
94 MOZ_ASSERT(!sShutdown);
96 // Already shutting down. Nothing should require the use of the string
97 // bundle when shutting down.
98 if (sShutdown) {
99 return nullptr;
102 if (!sSelf) {
103 sSelf = new BundleHelper();
106 return sSelf->GetOrCreateInternal();
109 static void Shutdown() {
110 sSelf = nullptr;
111 sShutdown = true;
114 private:
115 ~BundleHelper() = default;
117 nsIStringBundle* GetOrCreateInternal() {
118 if (!mBundle) {
119 nsCOMPtr<nsIStringBundleService> bundleService =
120 mozilla::services::GetStringBundleService();
121 if (NS_WARN_IF(!bundleService)) {
122 return nullptr;
125 nsresult rv = bundleService->CreateBundle(
126 "chrome://global/locale/security/caps.properties",
127 getter_AddRefs(mBundle));
128 if (NS_WARN_IF(NS_FAILED(rv))) {
129 return nullptr;
133 return mBundle;
136 nsCOMPtr<nsIStringBundle> mBundle;
138 static StaticRefPtr<BundleHelper> sSelf;
139 static bool sShutdown;
142 StaticRefPtr<BundleHelper> BundleHelper::sSelf;
143 bool BundleHelper::sShutdown = false;
145 } // namespace
147 ///////////////////////////
148 // Convenience Functions //
149 ///////////////////////////
151 class nsAutoInPrincipalDomainOriginSetter {
152 public:
153 nsAutoInPrincipalDomainOriginSetter() { ++sInPrincipalDomainOrigin; }
154 ~nsAutoInPrincipalDomainOriginSetter() { --sInPrincipalDomainOrigin; }
155 static uint32_t sInPrincipalDomainOrigin;
157 uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin;
159 static nsresult GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin) {
160 if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) {
161 // Allow a single recursive call to GetPrincipalDomainOrigin, since that
162 // might be happening on a different principal from the first call. But
163 // after that, cut off the recursion; it just indicates that something
164 // we're doing in this method causes us to reenter a security check here.
165 return NS_ERROR_NOT_AVAILABLE;
168 nsAutoInPrincipalDomainOriginSetter autoSetter;
170 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
171 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
173 nsAutoCString hostPort;
175 nsresult rv = uri->GetHostPort(hostPort);
176 if (NS_SUCCEEDED(rv)) {
177 nsAutoCString scheme;
178 rv = uri->GetScheme(scheme);
179 NS_ENSURE_SUCCESS(rv, rv);
180 aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
181 } else {
182 // Some URIs (e.g., nsSimpleURI) don't support host. Just
183 // get the full spec.
184 rv = uri->GetSpec(aOrigin);
185 NS_ENSURE_SUCCESS(rv, rv);
188 return NS_OK;
191 static nsresult GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
192 nsACString& aOrigin) {
193 nsCOMPtr<nsIURI> uri;
194 aPrincipal->GetDomain(getter_AddRefs(uri));
195 if (!uri) {
196 aPrincipal->GetURI(getter_AddRefs(uri));
198 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
200 return GetOriginFromURI(uri, aOrigin);
203 inline void SetPendingExceptionASCII(JSContext* cx, const char* aMsg) {
204 JS_ReportErrorASCII(cx, "%s", aMsg);
207 inline void SetPendingException(JSContext* cx, const char16_t* aMsg) {
208 NS_ConvertUTF16toUTF8 msg(aMsg);
209 JS_ReportErrorUTF8(cx, "%s", msg.get());
212 /* static */
213 bool nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
214 nsIURI* aTargetURI) {
215 return NS_SecurityCompareURIs(aSourceURI, aTargetURI,
216 sStrictFileOriginPolicy);
219 // SecurityHashURI is consistent with SecurityCompareURIs because
220 // NS_SecurityHashURI is consistent with NS_SecurityCompareURIs. See
221 // nsNetUtil.h.
222 uint32_t nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI) {
223 return NS_SecurityHashURI(aURI);
227 * GetChannelResultPrincipal will return the principal that the resource
228 * returned by this channel will use. For example, if the resource is in
229 * a sandbox, it will return the nullprincipal. If the resource is forced
230 * to inherit principal, it will return the principal of its parent. If
231 * the load doesn't require sandboxing or inheriting, it will return the same
232 * principal as GetChannelURIPrincipal. Namely the principal of the URI
233 * that is being loaded.
235 NS_IMETHODIMP
236 nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
237 nsIPrincipal** aPrincipal) {
238 return GetChannelResultPrincipal(aChannel, aPrincipal,
239 /*aIgnoreSandboxing*/ false);
242 nsresult nsScriptSecurityManager::GetChannelResultPrincipalIfNotSandboxed(
243 nsIChannel* aChannel, nsIPrincipal** aPrincipal) {
244 return GetChannelResultPrincipal(aChannel, aPrincipal,
245 /*aIgnoreSandboxing*/ true);
248 static void InheritAndSetCSPOnPrincipalIfNeeded(nsIChannel* aChannel,
249 nsIPrincipal* aPrincipal) {
250 // loading a data: URI into an iframe, or loading frame[srcdoc] need
251 // to inherit the CSP (see Bug 1073952, 1381761).
252 MOZ_ASSERT(aChannel && aPrincipal, "need a valid channel and principal");
253 if (!aChannel) {
254 return;
257 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
258 if (loadInfo->GetExternalContentPolicyType() !=
259 nsIContentPolicy::TYPE_SUBDOCUMENT) {
260 return;
263 nsCOMPtr<nsIURI> uri;
264 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
265 NS_ENSURE_SUCCESS_VOID(rv);
266 nsAutoCString URISpec;
267 rv = uri->GetSpec(URISpec);
268 NS_ENSURE_SUCCESS_VOID(rv);
270 bool isSrcDoc = URISpec.EqualsLiteral("about:srcdoc");
271 bool isData = (NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData);
273 if (!isSrcDoc && !isData) {
274 return;
277 nsCOMPtr<nsIPrincipal> principalToInherit =
278 loadInfo->FindPrincipalToInherit(aChannel);
280 nsCOMPtr<nsIContentSecurityPolicy> originalCSP;
281 principalToInherit->GetCsp(getter_AddRefs(originalCSP));
282 if (!originalCSP) {
283 return;
286 // if the principalToInherit had a CSP, add it to the before
287 // created NullPrincipal (unless it already has one)
288 MOZ_ASSERT(aPrincipal->GetIsNullPrincipal(),
289 "inheriting the CSP only valid for NullPrincipal");
290 nsCOMPtr<nsIContentSecurityPolicy> nullPrincipalCSP;
291 aPrincipal->GetCsp(getter_AddRefs(nullPrincipalCSP));
292 if (nullPrincipalCSP) {
293 MOZ_ASSERT(nsCSPContext::Equals(originalCSP, nullPrincipalCSP));
294 // CSPs are equal, no need to set it again.
295 return;
298 // After 965637 all that magical CSP inheritance goes away. For now,
299 // we have to create a clone of the current CSP and have to manually
300 // set it on the Principal.
301 uint32_t count = 0;
302 rv = originalCSP->GetPolicyCount(&count);
303 if (NS_WARN_IF(NS_FAILED(rv))) {
304 return;
307 if (count == 0) {
308 // fast path: if there is nothing to inherit, we can return here.
309 return;
312 RefPtr<nsCSPContext> newCSP = new nsCSPContext();
313 nsWeakPtr loadingContext =
314 static_cast<nsCSPContext*>(originalCSP.get())->GetLoadingContext();
315 nsCOMPtr<Document> doc = do_QueryReferent(loadingContext);
317 rv = doc ? newCSP->SetRequestContext(doc, nullptr)
318 : newCSP->SetRequestContext(nullptr, aPrincipal);
319 if (NS_WARN_IF(NS_FAILED(rv))) {
320 return;
323 for (uint32_t i = 0; i < count; ++i) {
324 const nsCSPPolicy* policy = originalCSP->GetPolicy(i);
325 MOZ_ASSERT(policy);
327 nsAutoString policyString;
328 policy->toString(policyString);
330 rv = newCSP->AppendPolicy(policyString, policy->getReportOnlyFlag(),
331 policy->getDeliveredViaMetaTagFlag());
332 if (NS_WARN_IF(NS_FAILED(rv))) {
333 return;
336 aPrincipal->SetCsp(newCSP);
339 nsresult nsScriptSecurityManager::GetChannelResultPrincipal(
340 nsIChannel* aChannel, nsIPrincipal** aPrincipal, bool aIgnoreSandboxing) {
341 MOZ_ASSERT(aChannel, "Must have channel!");
343 // Check whether we have an nsILoadInfo that says what we should do.
344 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
345 if (loadInfo->GetForceInheritPrincipalOverruleOwner()) {
346 nsCOMPtr<nsIPrincipal> principalToInherit =
347 loadInfo->FindPrincipalToInherit(aChannel);
348 principalToInherit.forget(aPrincipal);
349 return NS_OK;
352 nsCOMPtr<nsISupports> owner;
353 aChannel->GetOwner(getter_AddRefs(owner));
354 if (owner) {
355 CallQueryInterface(owner, aPrincipal);
356 if (*aPrincipal) {
357 return NS_OK;
361 if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
362 nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal =
363 loadInfo->GetSandboxedLoadingPrincipal();
364 MOZ_ASSERT(sandboxedLoadingPrincipal);
365 InheritAndSetCSPOnPrincipalIfNeeded(aChannel, sandboxedLoadingPrincipal);
366 sandboxedLoadingPrincipal.forget(aPrincipal);
367 return NS_OK;
370 bool forceInherit = loadInfo->GetForceInheritPrincipal();
371 if (aIgnoreSandboxing && !forceInherit) {
372 // Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
373 // sandboxing:
374 if (loadInfo->GetLoadingSandboxed() &&
375 loadInfo->GetForceInheritPrincipalDropped()) {
376 forceInherit = true;
379 if (forceInherit) {
380 nsCOMPtr<nsIPrincipal> principalToInherit =
381 loadInfo->FindPrincipalToInherit(aChannel);
382 principalToInherit.forget(aPrincipal);
383 return NS_OK;
386 auto securityMode = loadInfo->GetSecurityMode();
387 // The data: inheritance flags should only apply to the initial load,
388 // not to loads that it might have redirected to.
389 if (loadInfo->RedirectChain().IsEmpty() &&
390 (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
391 securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
392 securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
393 nsCOMPtr<nsIURI> uri;
394 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
395 NS_ENSURE_SUCCESS(rv, rv);
397 nsCOMPtr<nsIPrincipal> principalToInherit =
398 loadInfo->FindPrincipalToInherit(aChannel);
399 bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
401 if (nsContentUtils::ChannelShouldInheritPrincipal(
402 principalToInherit, uri, inheritForAboutBlank, false)) {
403 principalToInherit.forget(aPrincipal);
404 return NS_OK;
407 nsresult rv = GetChannelURIPrincipal(aChannel, aPrincipal);
408 NS_ENSURE_SUCCESS(rv, rv);
409 InheritAndSetCSPOnPrincipalIfNeeded(aChannel, *aPrincipal);
410 return NS_OK;
413 /* The principal of the URI that this channel is loading. This is never
414 * affected by things like sandboxed loads, or loads where we forcefully
415 * inherit the principal. Think of this as the principal of the server
416 * which this channel is loading from. Most callers should use
417 * GetChannelResultPrincipal instead of GetChannelURIPrincipal. Only
418 * call GetChannelURIPrincipal if you are sure that you want the
419 * principal that matches the uri, even in cases when the load is
420 * sandboxed or when the load could be a blob or data uri (i.e even when
421 * you encounter loads that may or may not be sandboxed and loads
422 * that may or may not inherit)."
424 NS_IMETHODIMP
425 nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel,
426 nsIPrincipal** aPrincipal) {
427 MOZ_ASSERT(aChannel, "Must have channel!");
429 // Get the principal from the URI. Make sure this does the same thing
430 // as Document::Reset and XULDocument::StartDocumentLoad.
431 nsCOMPtr<nsIURI> uri;
432 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
433 NS_ENSURE_SUCCESS(rv, rv);
435 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
437 // Inherit the origin attributes from loadInfo.
438 // If this is a top-level document load, the origin attributes of the
439 // loadInfo will be set from nsDocShell::DoURILoad.
440 // For subresource loading, the origin attributes of the loadInfo is from
441 // its loadingPrincipal.
442 OriginAttributes attrs = loadInfo->GetOriginAttributes();
444 nsCOMPtr<nsIPrincipal> prin =
445 BasePrincipal::CreateCodebasePrincipal(uri, attrs);
446 prin.forget(aPrincipal);
447 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
450 NS_IMETHODIMP
451 nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
452 bool* aIsSystem) {
453 *aIsSystem = (aPrincipal == mSystemPrincipal);
454 return NS_OK;
457 /////////////////////////////
458 // nsScriptSecurityManager //
459 /////////////////////////////
461 ////////////////////////////////////
462 // Methods implementing ISupports //
463 ////////////////////////////////////
464 NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager)
466 ///////////////////////////////////////////////////
467 // Methods implementing nsIScriptSecurityManager //
468 ///////////////////////////////////////////////////
470 ///////////////// Security Checks /////////////////
472 #if defined(DEBUG) && !defined(ANDROID)
473 static void AssertEvalNotUsingSystemPrincipal(nsIPrincipal* subjectPrincipal,
474 JSContext* cx) {
475 if (!nsContentUtils::IsSystemPrincipal(subjectPrincipal)) {
476 return;
479 if (Preferences::GetBool("security.allow_eval_with_system_principal")) {
480 return;
483 static StaticAutoPtr<nsTArray<nsCString>> sUrisAllowEval;
484 JS::AutoFilename scriptFilename;
485 if (JS::DescribeScriptedCaller(cx, &scriptFilename)) {
486 if (!sUrisAllowEval) {
487 sUrisAllowEval = new nsTArray<nsCString>();
488 nsAutoCString urisAllowEval;
489 Preferences::GetCString("security.uris_using_eval_with_system_principal",
490 urisAllowEval);
491 for (const nsACString& filenameString : urisAllowEval.Split(',')) {
492 sUrisAllowEval->AppendElement(filenameString);
494 ClearOnShutdown(&sUrisAllowEval);
497 nsAutoCString fileName;
498 fileName = nsAutoCString(scriptFilename.get());
499 // Extract file name alone if scriptFilename contains line number
500 // separated by multiple space delimiters in few cases.
501 int32_t fileNameIndex = fileName.FindChar(' ');
502 if (fileNameIndex != -1) {
503 fileName = Substring(fileName, 0, fileNameIndex);
505 ToLowerCase(fileName);
507 for (auto& uriEntry : *sUrisAllowEval) {
508 if (StringEndsWith(fileName, uriEntry)) {
509 return;
514 MOZ_ASSERT(false, "do not use eval with system privileges");
516 #endif
518 bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
519 JSContext* cx, JS::HandleValue aValue) {
520 MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
521 nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
523 #if defined(DEBUG) && !defined(ANDROID)
524 AssertEvalNotUsingSystemPrincipal(subjectPrincipal, cx);
525 #endif
527 nsCOMPtr<nsIContentSecurityPolicy> csp;
528 nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
529 NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
531 // don't do anything unless there's a CSP
532 if (!csp) return true;
534 nsCOMPtr<nsICSPEventListener> cspEventListener;
535 if (!NS_IsMainThread()) {
536 WorkerPrivate* workerPrivate =
537 mozilla::dom::GetWorkerPrivateFromContext(cx);
538 if (workerPrivate) {
539 cspEventListener = workerPrivate->CSPEventListener();
543 bool evalOK = true;
544 bool reportViolation = false;
545 rv = csp->GetAllowsEval(&reportViolation, &evalOK);
547 if (NS_FAILED(rv)) {
548 NS_WARNING("CSP: failed to get allowsEval");
549 return true; // fail open to not break sites.
552 if (reportViolation) {
553 JS::Rooted<JSString*> jsString(cx, JS::ToString(cx, aValue));
554 if (NS_WARN_IF(!jsString)) {
555 JS_ClearPendingException(cx);
556 return false;
559 nsAutoJSString scriptSample;
560 if (NS_WARN_IF(!scriptSample.init(cx, jsString))) {
561 JS_ClearPendingException(cx);
562 return false;
565 JS::AutoFilename scriptFilename;
566 nsAutoString fileName;
567 unsigned lineNum = 0;
568 unsigned columnNum = 0;
569 if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum, &columnNum)) {
570 if (const char* file = scriptFilename.get()) {
571 CopyUTF8toUTF16(nsDependentCString(file), fileName);
573 } else {
574 MOZ_ASSERT(!JS_IsExceptionPending(cx));
576 csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
577 nullptr, // triggering element
578 cspEventListener, fileName, scriptSample, lineNum,
579 columnNum, EmptyString(), EmptyString());
582 return evalOK;
585 // static
586 bool nsScriptSecurityManager::JSPrincipalsSubsume(JSPrincipals* first,
587 JSPrincipals* second) {
588 return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second));
591 NS_IMETHODIMP
592 nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
593 nsIURI* aTargetURI,
594 bool reportError,
595 bool aFromPrivateWindow) {
596 // Please note that aFromPrivateWindow is only 100% accurate if
597 // reportError is true.
598 if (!SecurityCompareURIs(aSourceURI, aTargetURI)) {
599 if (reportError) {
600 ReportError("CheckSameOriginError", aSourceURI, aTargetURI,
601 aFromPrivateWindow);
603 return NS_ERROR_DOM_BAD_URI;
605 return NS_OK;
608 /*static*/
609 uint32_t nsScriptSecurityManager::HashPrincipalByOrigin(
610 nsIPrincipal* aPrincipal) {
611 nsCOMPtr<nsIURI> uri;
612 aPrincipal->GetDomain(getter_AddRefs(uri));
613 if (!uri) aPrincipal->GetURI(getter_AddRefs(uri));
614 return SecurityHashURI(uri);
617 NS_IMETHODIMP
618 nsScriptSecurityManager::CheckLoadURIFromScript(JSContext* cx, nsIURI* aURI) {
619 // Get principal of currently executing script.
620 MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
621 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
622 nsresult rv = CheckLoadURIWithPrincipal(principal, aURI,
623 nsIScriptSecurityManager::STANDARD);
624 if (NS_SUCCEEDED(rv)) {
625 // OK to load
626 return NS_OK;
629 // Report error.
630 nsAutoCString spec;
631 if (NS_FAILED(aURI->GetAsciiSpec(spec))) return NS_ERROR_FAILURE;
632 nsAutoCString msg("Access to '");
633 msg.Append(spec);
634 msg.AppendLiteral("' from script denied");
635 SetPendingExceptionASCII(cx, msg.get());
636 return NS_ERROR_DOM_BAD_URI;
640 * Helper method to handle cases where a flag passed to
641 * CheckLoadURIWithPrincipal means denying loading if the given URI has certain
642 * nsIProtocolHandler flags set.
643 * @return if success, access is allowed. Otherwise, deny access
645 static nsresult DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags) {
646 MOZ_ASSERT(aURI, "Must have URI!");
648 bool uriHasFlags;
649 nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags);
650 NS_ENSURE_SUCCESS(rv, rv);
652 if (uriHasFlags) {
653 return NS_ERROR_DOM_BAD_URI;
656 return NS_OK;
659 static bool EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase) {
660 nsresult rv;
661 nsCOMPtr<nsIURI> probe = aProbeArg;
663 nsCOMPtr<nsIEffectiveTLDService> tldService =
664 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
665 NS_ENSURE_TRUE(tldService, false);
666 while (true) {
667 if (nsScriptSecurityManager::SecurityCompareURIs(probe, aBase)) {
668 return true;
671 nsAutoCString host, newHost;
672 rv = probe->GetHost(host);
673 NS_ENSURE_SUCCESS(rv, false);
675 rv = tldService->GetNextSubDomain(host, newHost);
676 if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
677 return false;
679 NS_ENSURE_SUCCESS(rv, false);
680 rv = NS_MutateURI(probe).SetHost(newHost).Finalize(probe);
681 NS_ENSURE_SUCCESS(rv, false);
685 NS_IMETHODIMP
686 nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
687 nsIURI* aTargetURI,
688 uint32_t aFlags) {
689 MOZ_ASSERT(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
691 // If someone passes a flag that we don't understand, we should
692 // fail, because they may need a security check that we don't
693 // provide.
694 NS_ENSURE_FALSE(
695 aFlags &
696 ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
697 nsIScriptSecurityManager::ALLOW_CHROME |
698 nsIScriptSecurityManager::DISALLOW_SCRIPT |
699 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
700 nsIScriptSecurityManager::DONT_REPORT_ERRORS),
701 NS_ERROR_UNEXPECTED);
702 NS_ENSURE_ARG_POINTER(aPrincipal);
703 NS_ENSURE_ARG_POINTER(aTargetURI);
705 // If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which
706 // would do such inheriting. That would be URIs that do not have their own
707 // security context. We do this even for the system principal.
708 if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) {
709 nsresult rv = DenyAccessIfURIHasFlags(
710 aTargetURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT);
711 NS_ENSURE_SUCCESS(rv, rv);
714 if (aPrincipal == mSystemPrincipal) {
715 // Allow access
716 return NS_OK;
719 nsCOMPtr<nsIURI> sourceURI;
720 aPrincipal->GetURI(getter_AddRefs(sourceURI));
721 if (!sourceURI) {
722 auto* basePrin = BasePrincipal::Cast(aPrincipal);
723 if (basePrin->Is<ExpandedPrincipal>()) {
724 auto expanded = basePrin->As<ExpandedPrincipal>();
725 for (auto& prin : expanded->AllowList()) {
726 nsresult rv = CheckLoadURIWithPrincipal(prin, aTargetURI, aFlags);
727 if (NS_SUCCEEDED(rv)) {
728 // Allow access if it succeeded with one of the allowlisted principals
729 return NS_OK;
732 // None of our allowlisted principals worked.
733 return NS_ERROR_DOM_BAD_URI;
735 NS_ERROR(
736 "Non-system principals or expanded principal passed to "
737 "CheckLoadURIWithPrincipal "
738 "must have a URI!");
739 return NS_ERROR_UNEXPECTED;
742 // Automatic loads are not allowed from certain protocols.
743 if (aFlags &
744 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) {
745 nsresult rv = DenyAccessIfURIHasFlags(
746 sourceURI,
747 nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT);
748 NS_ENSURE_SUCCESS(rv, rv);
751 // If either URI is a nested URI, get the base URI
752 nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI);
753 nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
755 //-- get the target scheme
756 nsAutoCString targetScheme;
757 nsresult rv = targetBaseURI->GetScheme(targetScheme);
758 if (NS_FAILED(rv)) return rv;
760 //-- Some callers do not allow loading javascript:
761 if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) &&
762 targetScheme.EqualsLiteral("javascript")) {
763 return NS_ERROR_DOM_BAD_URI;
766 // Check for uris that are only loadable by principals that subsume them
767 bool hasFlags;
768 rv = NS_URIChainHasFlags(
769 targetBaseURI, nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS, &hasFlags);
770 NS_ENSURE_SUCCESS(rv, rv);
772 if (hasFlags) {
773 // check nothing else in the URI chain has flags that prevent
774 // access:
775 rv = CheckLoadURIFlags(
776 sourceURI, aTargetURI, sourceBaseURI, targetBaseURI, aFlags,
777 aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0);
778 NS_ENSURE_SUCCESS(rv, rv);
779 // Check the principal is allowed to load the target.
780 return aPrincipal->CheckMayLoad(targetBaseURI, true, false);
783 //-- get the source scheme
784 nsAutoCString sourceScheme;
785 rv = sourceBaseURI->GetScheme(sourceScheme);
786 if (NS_FAILED(rv)) return rv;
788 // When comparing schemes, if the relevant pref is set, view-source URIs
789 // are reachable from same-protocol (so e.g. file: can link to
790 // view-source:file). This is required for reftests.
791 static bool sViewSourceReachableFromInner = false;
792 static bool sCachedViewSourcePref = false;
793 if (!sCachedViewSourcePref) {
794 sCachedViewSourcePref = true;
795 mozilla::Preferences::AddBoolVarCache(
796 &sViewSourceReachableFromInner,
797 "security.view-source.reachable-from-inner-protocol");
800 bool targetIsViewSource = false;
802 if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
803 // A null principal can target its own URI.
804 if (sourceURI == aTargetURI) {
805 return NS_OK;
807 } else if (sViewSourceReachableFromInner &&
808 sourceScheme.EqualsIgnoreCase(targetScheme.get()) &&
809 NS_SUCCEEDED(
810 aTargetURI->SchemeIs("view-source", &targetIsViewSource)) &&
811 targetIsViewSource) {
812 // exception for foo: linking to view-source:foo for reftests...
813 return NS_OK;
814 } else if (sourceScheme.EqualsIgnoreCase("file") &&
815 targetScheme.EqualsIgnoreCase("moz-icon")) {
816 // exception for file: linking to moz-icon://.ext?size=...
817 // Note that because targetScheme is the base (innermost) URI scheme,
818 // this does NOT allow file -> moz-icon:file:///... links.
819 // This is intentional.
820 return NS_OK;
823 // Check for webextension
824 rv = NS_URIChainHasFlags(
825 aTargetURI, nsIProtocolHandler::URI_LOADABLE_BY_EXTENSIONS, &hasFlags);
826 NS_ENSURE_SUCCESS(rv, rv);
828 if (hasFlags && BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
829 return NS_OK;
832 // If we get here, check all the schemes can link to each other, from the top
833 // down:
834 nsCaseInsensitiveCStringComparator stringComparator;
835 nsCOMPtr<nsIURI> currentURI = sourceURI;
836 nsCOMPtr<nsIURI> currentOtherURI = aTargetURI;
838 bool denySameSchemeLinks = false;
839 rv = NS_URIChainHasFlags(aTargetURI,
840 nsIProtocolHandler::URI_SCHEME_NOT_SELF_LINKABLE,
841 &denySameSchemeLinks);
842 if (NS_FAILED(rv)) return rv;
844 while (currentURI && currentOtherURI) {
845 nsAutoCString scheme, otherScheme;
846 currentURI->GetScheme(scheme);
847 currentOtherURI->GetScheme(otherScheme);
849 bool schemesMatch = scheme.Equals(otherScheme, stringComparator);
850 bool isSamePage = false;
851 // about: URIs are special snowflakes.
852 if (scheme.EqualsLiteral("about") && schemesMatch) {
853 nsAutoCString moduleName, otherModuleName;
854 // about: pages can always link to themselves:
855 isSamePage =
856 NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, moduleName)) &&
857 NS_SUCCEEDED(
858 NS_GetAboutModuleName(currentOtherURI, otherModuleName)) &&
859 moduleName.Equals(otherModuleName);
860 if (!isSamePage) {
861 // We will have allowed the load earlier if the source page has
862 // system principal. So we know the source has a content
863 // principal, and it's trying to link to something else.
864 // Linkable about: pages are always reachable, even if we hit
865 // the CheckLoadURIFlags call below.
866 // We punch only 1 other hole: iff the source is unlinkable,
867 // we let them link to other pages explicitly marked SAFE
868 // for content. This avoids world-linkable about: pages linking
869 // to non-world-linkable about: pages.
870 nsCOMPtr<nsIAboutModule> module, otherModule;
871 bool knowBothModules =
872 NS_SUCCEEDED(
873 NS_GetAboutModule(currentURI, getter_AddRefs(module))) &&
874 NS_SUCCEEDED(NS_GetAboutModule(currentOtherURI,
875 getter_AddRefs(otherModule)));
876 uint32_t aboutModuleFlags = 0;
877 uint32_t otherAboutModuleFlags = 0;
878 knowBothModules =
879 knowBothModules &&
880 NS_SUCCEEDED(module->GetURIFlags(currentURI, &aboutModuleFlags)) &&
881 NS_SUCCEEDED(otherModule->GetURIFlags(currentOtherURI,
882 &otherAboutModuleFlags));
883 if (knowBothModules) {
884 isSamePage = !(aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) &&
885 (otherAboutModuleFlags &
886 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT);
887 if (isSamePage &&
888 otherAboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) {
889 // XXXgijs: this is a hack. The target will be nested
890 // (with innerURI of moz-safe-about:whatever), and
891 // the source isn't, so we won't pass if we finish
892 // the loop. We *should* pass, though, so return here.
893 // This hack can go away when bug 1228118 is fixed.
894 return NS_OK;
898 } else {
899 bool equalExceptRef = false;
900 rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef);
901 isSamePage = NS_SUCCEEDED(rv) && equalExceptRef;
904 // If schemes are not equal, or they're equal but the target URI
905 // is different from the source URI and doesn't always allow linking
906 // from the same scheme, check if the URI flags of the current target
907 // URI allow the current source URI to link to it.
908 // The policy is specified by the protocol flags on both URIs.
909 if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
910 return CheckLoadURIFlags(
911 currentURI, currentOtherURI, sourceBaseURI, targetBaseURI, aFlags,
912 aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0);
914 // Otherwise... check if we can nest another level:
915 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
916 nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
918 // If schemes match and neither URI is nested further, we're OK.
919 if (!nestedURI && !nestedOtherURI) {
920 return NS_OK;
922 // If one is nested and the other isn't, something is wrong.
923 if (!nestedURI != !nestedOtherURI) {
924 return NS_ERROR_DOM_BAD_URI;
926 // Otherwise, both should be nested and we'll go through the loop again.
927 nestedURI->GetInnerURI(getter_AddRefs(currentURI));
928 nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI));
931 // We should never get here. We should always return from inside the loop.
932 return NS_ERROR_DOM_BAD_URI;
936 * Helper method to check whether the target URI and its innermost ("base") URI
937 * has protocol flags that should stop it from being loaded by the source URI
938 * (and/or the source URI's innermost ("base") URI), taking into account any
939 * nsIScriptSecurityManager flags originally passed to
940 * CheckLoadURIWithPrincipal and friends.
942 * @return if success, access is allowed. Otherwise, deny access
944 nsresult nsScriptSecurityManager::CheckLoadURIFlags(
945 nsIURI* aSourceURI, nsIURI* aTargetURI, nsIURI* aSourceBaseURI,
946 nsIURI* aTargetBaseURI, uint32_t aFlags, bool aFromPrivateWindow) {
947 // Note that the order of policy checks here is very important!
948 // We start from most restrictive and work our way down.
949 bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS);
950 const char* errorTag = "CheckLoadURIError";
952 nsAutoCString targetScheme;
953 nsresult rv = aTargetBaseURI->GetScheme(targetScheme);
954 if (NS_FAILED(rv)) return rv;
956 // Check for system target URI
957 rv = DenyAccessIfURIHasFlags(aTargetURI,
958 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
959 if (NS_FAILED(rv)) {
960 // Deny access, since the origin principal is not system
961 if (reportErrors) {
962 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
964 return rv;
967 // Used by ExtensionProtocolHandler to prevent loading extension resources
968 // in private contexts if the extension does not have permission.
969 if (aFromPrivateWindow) {
970 rv = DenyAccessIfURIHasFlags(
971 aTargetURI, nsIProtocolHandler::URI_DISALLOW_IN_PRIVATE_CONTEXT);
972 if (NS_FAILED(rv)) {
973 if (reportErrors) {
974 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
976 return rv;
980 // Check for chrome target URI
981 bool hasFlags = false;
982 rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
983 &hasFlags);
984 NS_ENSURE_SUCCESS(rv, rv);
985 if (hasFlags) {
986 if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
987 // Allow a URI_IS_UI_RESOURCE source to link to a URI_IS_UI_RESOURCE
988 // target if ALLOW_CHROME is set.
990 // ALLOW_CHROME is a flag that we pass on all loads _except_ docshell
991 // loads (since docshell loads run the loaded content with its origin
992 // principal). So we're effectively allowing resource://, chrome://,
993 // and moz-icon:// source URIs to load resource://, chrome://, and
994 // moz-icon:// files, so long as they're not loading it as a document.
995 bool sourceIsUIResource;
996 rv = NS_URIChainHasFlags(aSourceBaseURI,
997 nsIProtocolHandler::URI_IS_UI_RESOURCE,
998 &sourceIsUIResource);
999 NS_ENSURE_SUCCESS(rv, rv);
1000 if (sourceIsUIResource) {
1001 return NS_OK;
1004 if (targetScheme.EqualsLiteral("resource")) {
1005 // Mochitests that need to load resource:// URIs not declared
1006 // content-accessible in manifests should set the preference
1007 // "security.all_resource_uri_content_accessible" true.
1008 static bool sSecurityPrefCached = false;
1009 static bool sAllResourceUriContentAccessible = false;
1010 if (!sSecurityPrefCached) {
1011 sSecurityPrefCached = true;
1012 Preferences::AddBoolVarCache(
1013 &sAllResourceUriContentAccessible,
1014 "security.all_resource_uri_content_accessible", false);
1016 if (sAllResourceUriContentAccessible) {
1017 return NS_OK;
1020 nsCOMPtr<nsIProtocolHandler> ph;
1021 rv = sIOService->GetProtocolHandler("resource", getter_AddRefs(ph));
1022 NS_ENSURE_SUCCESS(rv, rv);
1023 if (!ph) {
1024 return NS_ERROR_DOM_BAD_URI;
1027 nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph);
1028 if (!rph) {
1029 return NS_ERROR_DOM_BAD_URI;
1032 bool accessAllowed = false;
1033 rph->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
1034 if (accessAllowed) {
1035 return NS_OK;
1037 } else if (targetScheme.EqualsLiteral("chrome")) {
1038 // Allow the load only if the chrome package is allowlisted.
1039 nsCOMPtr<nsIXULChromeRegistry> reg(
1040 do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
1041 if (reg) {
1042 bool accessAllowed = false;
1043 reg->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
1044 if (accessAllowed) {
1045 return NS_OK;
1051 if (reportErrors) {
1052 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
1054 return NS_ERROR_DOM_BAD_URI;
1057 // Check for target URI pointing to a file
1058 rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_LOCAL_FILE,
1059 &hasFlags);
1060 NS_ENSURE_SUCCESS(rv, rv);
1061 if (hasFlags) {
1062 // Allow domains that were allowlisted in the prefs. In 99.9% of cases,
1063 // this array is empty.
1064 bool isAllowlisted;
1065 MOZ_ALWAYS_SUCCEEDS(InFileURIAllowlist(aSourceURI, &isAllowlisted));
1066 if (isAllowlisted) {
1067 return NS_OK;
1070 // Allow chrome://
1071 bool isChrome = false;
1072 if (NS_SUCCEEDED(aSourceBaseURI->SchemeIs("chrome", &isChrome)) &&
1073 isChrome) {
1074 return NS_OK;
1077 // Nothing else.
1078 if (reportErrors) {
1079 ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
1081 return NS_ERROR_DOM_BAD_URI;
1084 // OK, everyone is allowed to load this, since unflagged handlers are
1085 // deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
1086 // need to warn. At some point we'll want to make this warning into an
1087 // error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
1088 rv = NS_URIChainHasFlags(
1089 aTargetBaseURI, nsIProtocolHandler::URI_LOADABLE_BY_ANYONE, &hasFlags);
1090 NS_ENSURE_SUCCESS(rv, rv);
1091 // NB: we also get here if the base URI is URI_LOADABLE_BY_SUBSUMERS,
1092 // and none of the rest of the nested chain of URIs for aTargetURI
1093 // prohibits the load, so avoid warning in that case:
1094 bool hasSubsumersFlag = false;
1095 rv = NS_URIChainHasFlags(aTargetBaseURI,
1096 nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
1097 &hasSubsumersFlag);
1098 NS_ENSURE_SUCCESS(rv, rv);
1099 if (!hasFlags && !hasSubsumersFlag) {
1100 nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
1101 if (bundle) {
1102 nsAutoString message;
1103 NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
1104 const char16_t* formatStrings[] = {ucsTargetScheme.get()};
1105 rv = bundle->FormatStringFromName("ProtocolFlagError", formatStrings,
1106 ArrayLength(formatStrings), message);
1107 if (NS_SUCCEEDED(rv)) {
1108 nsCOMPtr<nsIConsoleService> console(
1109 do_GetService("@mozilla.org/consoleservice;1"));
1110 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
1112 console->LogStringMessage(message.get());
1117 return NS_OK;
1120 nsresult nsScriptSecurityManager::ReportError(const char* aMessageTag,
1121 nsIURI* aSource, nsIURI* aTarget,
1122 bool aFromPrivateWindow) {
1123 nsresult rv;
1124 NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
1126 // Get the source URL spec
1127 nsAutoCString sourceSpec;
1128 rv = aSource->GetAsciiSpec(sourceSpec);
1129 NS_ENSURE_SUCCESS(rv, rv);
1131 // Get the target URL spec
1132 nsAutoCString targetSpec;
1133 rv = aTarget->GetAsciiSpec(targetSpec);
1134 NS_ENSURE_SUCCESS(rv, rv);
1136 nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
1137 if (NS_WARN_IF(!bundle)) {
1138 return NS_OK;
1141 // Localize the error message
1142 nsAutoString message;
1143 NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec);
1144 NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
1145 const char16_t* formatStrings[] = {ucsSourceSpec.get(), ucsTargetSpec.get()};
1146 rv = bundle->FormatStringFromName(aMessageTag, formatStrings,
1147 ArrayLength(formatStrings), message);
1148 NS_ENSURE_SUCCESS(rv, rv);
1150 nsCOMPtr<nsIConsoleService> console(
1151 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
1152 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
1153 nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
1154 NS_ENSURE_TRUE(error, NS_ERROR_FAILURE);
1156 // using category of "SOP" so we can link to MDN
1157 rv = error->Init(message, EmptyString(), EmptyString(), 0, 0,
1158 nsIScriptError::errorFlag, "SOP", aFromPrivateWindow);
1159 NS_ENSURE_SUCCESS(rv, rv);
1160 console->LogMessage(error);
1161 return NS_OK;
1164 NS_IMETHODIMP
1165 nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(
1166 nsIPrincipal* aPrincipal, const nsACString& aTargetURIStr,
1167 uint32_t aFlags) {
1168 nsresult rv;
1169 nsCOMPtr<nsIURI> target;
1170 rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr, nullptr, nullptr,
1171 sIOService);
1172 NS_ENSURE_SUCCESS(rv, rv);
1174 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
1175 if (rv == NS_ERROR_DOM_BAD_URI) {
1176 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
1177 // return values.
1178 return rv;
1180 NS_ENSURE_SUCCESS(rv, rv);
1182 // Now start testing fixup -- since aTargetURIStr is a string, not
1183 // an nsIURI, we may well end up fixing it up before loading.
1184 // Note: This needs to stay in sync with the nsIURIFixup api.
1185 nsCOMPtr<nsIURIFixup> fixup = components::URIFixup::Service();
1186 if (!fixup) {
1187 return rv;
1190 uint32_t flags[] = {nsIURIFixup::FIXUP_FLAG_NONE,
1191 nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS,
1192 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
1193 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
1194 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
1195 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI};
1197 for (uint32_t i = 0; i < ArrayLength(flags); ++i) {
1198 rv = fixup->CreateFixupURI(aTargetURIStr, flags[i], nullptr,
1199 getter_AddRefs(target));
1200 NS_ENSURE_SUCCESS(rv, rv);
1202 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
1203 if (rv == NS_ERROR_DOM_BAD_URI) {
1204 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
1205 // return values.
1206 return rv;
1208 NS_ENSURE_SUCCESS(rv, rv);
1211 return rv;
1214 NS_IMETHODIMP
1215 nsScriptSecurityManager::InFileURIAllowlist(nsIURI* aUri, bool* aResult) {
1216 MOZ_ASSERT(aUri);
1217 MOZ_ASSERT(aResult);
1219 *aResult = false;
1220 for (nsIURI* uri : EnsureFileURIAllowlist()) {
1221 if (EqualOrSubdomain(aUri, uri)) {
1222 *aResult = true;
1223 return NS_OK;
1227 return NS_OK;
1230 ///////////////// Principals ///////////////////////
1232 NS_IMETHODIMP
1233 nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal** result) {
1234 NS_ADDREF(*result = mSystemPrincipal);
1236 return NS_OK;
1239 NS_IMETHODIMP
1240 nsScriptSecurityManager::CreateCodebasePrincipal(
1241 nsIURI* aURI, JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx,
1242 nsIPrincipal** aPrincipal) {
1243 OriginAttributes attrs;
1244 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1245 return NS_ERROR_INVALID_ARG;
1247 nsCOMPtr<nsIPrincipal> prin =
1248 BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
1249 prin.forget(aPrincipal);
1250 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1253 NS_IMETHODIMP
1254 nsScriptSecurityManager::CreateCodebasePrincipalFromOrigin(
1255 const nsACString& aOrigin, nsIPrincipal** aPrincipal) {
1256 if (StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("["))) {
1257 return NS_ERROR_INVALID_ARG;
1260 if (StringBeginsWith(aOrigin,
1261 NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":"))) {
1262 return NS_ERROR_INVALID_ARG;
1265 nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aOrigin);
1266 prin.forget(aPrincipal);
1267 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1270 NS_IMETHODIMP
1271 nsScriptSecurityManager::CreateNullPrincipal(
1272 JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx,
1273 nsIPrincipal** aPrincipal) {
1274 OriginAttributes attrs;
1275 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1276 return NS_ERROR_INVALID_ARG;
1278 nsCOMPtr<nsIPrincipal> prin = NullPrincipal::Create(attrs);
1279 prin.forget(aPrincipal);
1280 return NS_OK;
1283 NS_IMETHODIMP
1284 nsScriptSecurityManager::GetLoadContextCodebasePrincipal(
1285 nsIURI* aURI, nsILoadContext* aLoadContext, nsIPrincipal** aPrincipal) {
1286 NS_ENSURE_STATE(aLoadContext);
1287 OriginAttributes docShellAttrs;
1288 aLoadContext->GetOriginAttributes(docShellAttrs);
1290 nsCOMPtr<nsIPrincipal> prin =
1291 BasePrincipal::CreateCodebasePrincipal(aURI, docShellAttrs);
1292 prin.forget(aPrincipal);
1293 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1296 NS_IMETHODIMP
1297 nsScriptSecurityManager::GetDocShellCodebasePrincipal(
1298 nsIURI* aURI, nsIDocShell* aDocShell, nsIPrincipal** aPrincipal) {
1299 nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(
1300 aURI, nsDocShell::Cast(aDocShell)->GetOriginAttributes());
1301 prin.forget(aPrincipal);
1302 return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1305 NS_IMETHODIMP
1306 nsScriptSecurityManager::PrincipalWithOA(
1307 nsIPrincipal* aPrincipal, JS::Handle<JS::Value> aOriginAttributes,
1308 JSContext* aCx, nsIPrincipal** aReturnPrincipal) {
1309 if (!aPrincipal) {
1310 return NS_OK;
1312 if (aPrincipal->GetIsCodebasePrincipal()) {
1313 OriginAttributes attrs;
1314 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1315 return NS_ERROR_INVALID_ARG;
1317 RefPtr<ContentPrincipal> copy = new ContentPrincipal();
1318 ContentPrincipal* contentPrincipal =
1319 static_cast<ContentPrincipal*>(aPrincipal);
1320 nsresult rv = copy->Init(contentPrincipal, attrs);
1321 NS_ENSURE_SUCCESS(rv, rv);
1322 copy.forget(aReturnPrincipal);
1323 } else {
1324 // We do this for null principals, system principals (both fine)
1325 // ... and expanded principals, where we should probably do something
1326 // cleverer, but I also don't think we care too much.
1327 nsCOMPtr<nsIPrincipal> prin = aPrincipal;
1328 prin.forget(aReturnPrincipal);
1331 return *aReturnPrincipal ? NS_OK : NS_ERROR_FAILURE;
1334 NS_IMETHODIMP
1335 nsScriptSecurityManager::CanCreateWrapper(JSContext* cx, const nsIID& aIID,
1336 nsISupports* aObj,
1337 nsIClassInfo* aClassInfo) {
1338 // XXX Special case for Exception ?
1340 // We give remote-XUL allowlisted domains a free pass here. See bug 932906.
1341 JS::Rooted<JS::Realm*> contextRealm(cx, JS::GetCurrentRealmOrNull(cx));
1342 MOZ_RELEASE_ASSERT(contextRealm);
1343 if (!xpc::AllowContentXBLScope(contextRealm)) {
1344 return NS_OK;
1347 if (nsContentUtils::IsCallerChrome()) {
1348 return NS_OK;
1351 //-- Access denied, report an error
1352 nsAutoCString originUTF8;
1353 nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal();
1354 GetPrincipalDomainOrigin(subjectPrincipal, originUTF8);
1355 NS_ConvertUTF8toUTF16 originUTF16(originUTF8);
1356 nsAutoCString classInfoNameUTF8;
1357 if (aClassInfo) {
1358 aClassInfo->GetClassDescription(classInfoNameUTF8);
1360 if (classInfoNameUTF8.IsEmpty()) {
1361 classInfoNameUTF8.AssignLiteral("UnnamedClass");
1364 nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
1365 if (NS_WARN_IF(!bundle)) {
1366 return NS_OK;
1369 NS_ConvertUTF8toUTF16 classInfoUTF16(classInfoNameUTF8);
1370 nsresult rv;
1371 nsAutoString errorMsg;
1372 if (originUTF16.IsEmpty()) {
1373 const char16_t* formatStrings[] = {classInfoUTF16.get()};
1374 rv = bundle->FormatStringFromName("CreateWrapperDenied", formatStrings, 1,
1375 errorMsg);
1376 } else {
1377 const char16_t* formatStrings[] = {classInfoUTF16.get(), originUTF16.get()};
1378 rv = bundle->FormatStringFromName("CreateWrapperDeniedForOrigin",
1379 formatStrings, 2, errorMsg);
1381 NS_ENSURE_SUCCESS(rv, rv);
1383 SetPendingException(cx, errorMsg.get());
1384 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1387 NS_IMETHODIMP
1388 nsScriptSecurityManager::CanCreateInstance(JSContext* cx, const nsCID& aCID) {
1389 if (nsContentUtils::IsCallerChrome()) {
1390 return NS_OK;
1393 //-- Access denied, report an error
1394 nsAutoCString errorMsg("Permission denied to create instance of class. CID=");
1395 char cidStr[NSID_LENGTH];
1396 aCID.ToProvidedString(cidStr);
1397 errorMsg.Append(cidStr);
1398 SetPendingExceptionASCII(cx, errorMsg.get());
1399 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1402 NS_IMETHODIMP
1403 nsScriptSecurityManager::CanGetService(JSContext* cx, const nsCID& aCID) {
1404 if (nsContentUtils::IsCallerChrome()) {
1405 return NS_OK;
1408 //-- Access denied, report an error
1409 nsAutoCString errorMsg("Permission denied to get service. CID=");
1410 char cidStr[NSID_LENGTH];
1411 aCID.ToProvidedString(cidStr);
1412 errorMsg.Append(cidStr);
1413 SetPendingExceptionASCII(cx, errorMsg.get());
1414 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1417 const char sJSEnabledPrefName[] = "javascript.enabled";
1418 const char sFileOriginPolicyPrefName[] =
1419 "security.fileuri.strict_origin_policy";
1421 static const char* kObservedPrefs[] = {sJSEnabledPrefName,
1422 sFileOriginPolicyPrefName,
1423 "capability.policy.", nullptr};
1425 /////////////////////////////////////////////
1426 // Constructor, Destructor, Initialization //
1427 /////////////////////////////////////////////
1428 nsScriptSecurityManager::nsScriptSecurityManager(void)
1429 : mPrefInitialized(false), mIsJavaScriptEnabled(false) {
1430 static_assert(
1431 sizeof(intptr_t) == sizeof(void*),
1432 "intptr_t and void* have different lengths on this platform. "
1433 "This may cause a security failure with the SecurityLevel union.");
1436 nsresult nsScriptSecurityManager::Init() {
1437 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
1438 NS_ENSURE_SUCCESS(rv, rv);
1440 InitPrefs();
1442 // Create our system principal singleton
1443 RefPtr<SystemPrincipal> system = SystemPrincipal::Create();
1445 mSystemPrincipal = system;
1447 //-- Register security check callback in the JS engine
1448 // Currently this is used to control access to function.caller
1449 sContext = danger::GetJSContext();
1451 static const JSSecurityCallbacks securityCallbacks = {
1452 ContentSecurityPolicyPermitsJSAction,
1453 JSPrincipalsSubsume,
1456 MOZ_ASSERT(!JS_GetSecurityCallbacks(sContext));
1457 JS_SetSecurityCallbacks(sContext, &securityCallbacks);
1458 JS_InitDestroyPrincipalsCallback(sContext, nsJSPrincipals::Destroy);
1460 JS_SetTrustedPrincipals(sContext, system);
1462 return NS_OK;
1465 static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
1467 nsScriptSecurityManager::~nsScriptSecurityManager(void) {
1468 Preferences::UnregisterPrefixCallbacks(
1469 PREF_CHANGE_METHOD(nsScriptSecurityManager::ScriptSecurityPrefChanged),
1470 kObservedPrefs, this);
1471 if (mDomainPolicy) {
1472 mDomainPolicy->Deactivate();
1474 // ContentChild might hold a reference to the domain policy,
1475 // and it might release it only after the security manager is
1476 // gone. But we can still assert this for the main process.
1477 MOZ_ASSERT_IF(XRE_IsParentProcess(), !mDomainPolicy);
1480 void nsScriptSecurityManager::Shutdown() {
1481 if (sContext) {
1482 JS_SetSecurityCallbacks(sContext, nullptr);
1483 JS_SetTrustedPrincipals(sContext, nullptr);
1484 sContext = nullptr;
1487 NS_IF_RELEASE(sIOService);
1488 BundleHelper::Shutdown();
1491 nsScriptSecurityManager* nsScriptSecurityManager::GetScriptSecurityManager() {
1492 return gScriptSecMan;
1495 /* static */
1496 void nsScriptSecurityManager::InitStatics() {
1497 RefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
1498 nsresult rv = ssManager->Init();
1499 if (NS_FAILED(rv)) {
1500 MOZ_CRASH("ssManager->Init() failed");
1503 ClearOnShutdown(&gScriptSecMan);
1504 gScriptSecMan = ssManager;
1507 // Currently this nsGenericFactory constructor is used only from FastLoad
1508 // (XPCOM object deserialization) code, when "creating" the system principal
1509 // singleton.
1510 already_AddRefed<SystemPrincipal>
1511 nsScriptSecurityManager::SystemPrincipalSingletonConstructor() {
1512 if (gScriptSecMan)
1513 return do_AddRef(gScriptSecMan->mSystemPrincipal)
1514 .downcast<SystemPrincipal>();
1515 return nullptr;
1518 struct IsWhitespace {
1519 static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); };
1521 struct IsWhitespaceOrComma {
1522 static bool Test(char aChar) {
1523 return aChar == ',' || NS_IsAsciiWhitespace(aChar);
1527 template <typename Predicate>
1528 uint32_t SkipPast(const nsCString& str, uint32_t base) {
1529 while (base < str.Length() && Predicate::Test(str[base])) {
1530 ++base;
1532 return base;
1535 template <typename Predicate>
1536 uint32_t SkipUntil(const nsCString& str, uint32_t base) {
1537 while (base < str.Length() && !Predicate::Test(str[base])) {
1538 ++base;
1540 return base;
1543 inline void nsScriptSecurityManager::ScriptSecurityPrefChanged(
1544 const char* aPref) {
1545 MOZ_ASSERT(mPrefInitialized);
1546 mIsJavaScriptEnabled =
1547 Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
1548 sStrictFileOriginPolicy =
1549 Preferences::GetBool(sFileOriginPolicyPrefName, false);
1550 mFileURIAllowlist.reset();
1553 void nsScriptSecurityManager::AddSitesToFileURIAllowlist(
1554 const nsCString& aSiteList) {
1555 for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0;
1556 base < aSiteList.Length();
1557 base = SkipPast<IsWhitespace>(aSiteList, bound)) {
1558 // Grab the current site.
1559 bound = SkipUntil<IsWhitespace>(aSiteList, base);
1560 nsAutoCString site(Substring(aSiteList, base, bound - base));
1562 // Check if the URI is schemeless. If so, add both http and https.
1563 nsAutoCString unused;
1564 if (NS_FAILED(sIOService->ExtractScheme(site, unused))) {
1565 AddSitesToFileURIAllowlist(NS_LITERAL_CSTRING("http://") + site);
1566 AddSitesToFileURIAllowlist(NS_LITERAL_CSTRING("https://") + site);
1567 continue;
1570 // Convert it to a URI and add it to our list.
1571 nsCOMPtr<nsIURI> uri;
1572 nsresult rv =
1573 NS_NewURI(getter_AddRefs(uri), site, nullptr, nullptr, sIOService);
1574 if (NS_SUCCEEDED(rv)) {
1575 mFileURIAllowlist.ref().AppendElement(uri);
1576 } else {
1577 nsCOMPtr<nsIConsoleService> console(
1578 do_GetService("@mozilla.org/consoleservice;1"));
1579 if (console) {
1580 nsAutoString msg =
1581 NS_LITERAL_STRING(
1582 "Unable to to add site to file:// URI allowlist: ") +
1583 NS_ConvertASCIItoUTF16(site);
1584 console->LogStringMessage(msg.get());
1590 nsresult nsScriptSecurityManager::InitPrefs() {
1591 nsIPrefBranch* branch = Preferences::GetRootBranch();
1592 NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
1594 mPrefInitialized = true;
1596 // Set the initial value of the "javascript.enabled" prefs
1597 ScriptSecurityPrefChanged();
1599 // set observer callbacks in case the value of the prefs change
1600 Preferences::RegisterPrefixCallbacks(
1601 PREF_CHANGE_METHOD(nsScriptSecurityManager::ScriptSecurityPrefChanged),
1602 kObservedPrefs, this);
1604 OriginAttributes::InitPrefs();
1606 return NS_OK;
1609 NS_IMETHODIMP
1610 nsScriptSecurityManager::GetDomainPolicyActive(bool* aRv) {
1611 *aRv = !!mDomainPolicy;
1612 return NS_OK;
1615 NS_IMETHODIMP
1616 nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv) {
1617 if (!XRE_IsParentProcess()) {
1618 return NS_ERROR_SERVICE_NOT_AVAILABLE;
1621 return ActivateDomainPolicyInternal(aRv);
1624 NS_IMETHODIMP
1625 nsScriptSecurityManager::ActivateDomainPolicyInternal(nsIDomainPolicy** aRv) {
1626 // We only allow one domain policy at a time. The holder of the previous
1627 // policy must explicitly deactivate it first.
1628 if (mDomainPolicy) {
1629 return NS_ERROR_SERVICE_NOT_AVAILABLE;
1632 mDomainPolicy = new DomainPolicy();
1633 nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy;
1634 ptr.forget(aRv);
1635 return NS_OK;
1638 // Intentionally non-scriptable. Script must have a reference to the
1639 // nsIDomainPolicy to deactivate it.
1640 void nsScriptSecurityManager::DeactivateDomainPolicy() {
1641 mDomainPolicy = nullptr;
1644 void nsScriptSecurityManager::CloneDomainPolicy(DomainPolicyClone* aClone) {
1645 MOZ_ASSERT(aClone);
1646 if (mDomainPolicy) {
1647 mDomainPolicy->CloneDomainPolicy(aClone);
1648 } else {
1649 aClone->active() = false;
1653 NS_IMETHODIMP
1654 nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool* aRv) {
1655 nsresult rv;
1657 // Compute our rule. If we don't have any domain policy set up that might
1658 // provide exceptions to this rule, we're done.
1659 *aRv = mIsJavaScriptEnabled;
1660 if (!mDomainPolicy) {
1661 return NS_OK;
1664 // We have a domain policy. Grab the appropriate set of exceptions to the
1665 // rule (either the blocklist or the allowlist, depending on whether script
1666 // is enabled or disabled by default).
1667 nsCOMPtr<nsIDomainSet> exceptions;
1668 nsCOMPtr<nsIDomainSet> superExceptions;
1669 if (*aRv) {
1670 mDomainPolicy->GetBlocklist(getter_AddRefs(exceptions));
1671 mDomainPolicy->GetSuperBlocklist(getter_AddRefs(superExceptions));
1672 } else {
1673 mDomainPolicy->GetAllowlist(getter_AddRefs(exceptions));
1674 mDomainPolicy->GetSuperAllowlist(getter_AddRefs(superExceptions));
1677 bool contains;
1678 rv = exceptions->Contains(aURI, &contains);
1679 NS_ENSURE_SUCCESS(rv, rv);
1680 if (contains) {
1681 *aRv = !*aRv;
1682 return NS_OK;
1684 rv = superExceptions->ContainsSuperDomain(aURI, &contains);
1685 NS_ENSURE_SUCCESS(rv, rv);
1686 if (contains) {
1687 *aRv = !*aRv;
1690 return NS_OK;
1693 const nsTArray<nsCOMPtr<nsIURI>>&
1694 nsScriptSecurityManager::EnsureFileURIAllowlist() {
1695 if (mFileURIAllowlist.isSome()) {
1696 return mFileURIAllowlist.ref();
1700 // Rebuild the set of principals for which we allow file:// URI loads. This
1701 // implements a small subset of an old pref-based CAPS people that people
1702 // have come to depend on. See bug 995943.
1705 mFileURIAllowlist.emplace();
1706 nsAutoCString policies;
1707 mozilla::Preferences::GetCString("capability.policy.policynames", policies);
1708 for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
1709 base < policies.Length();
1710 base = SkipPast<IsWhitespaceOrComma>(policies, bound)) {
1711 // Grab the current policy name.
1712 bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
1713 auto policyName = Substring(policies, base, bound - base);
1715 // Figure out if this policy allows loading file:// URIs. If not, we can
1716 // skip it.
1717 nsCString checkLoadURIPrefName =
1718 NS_LITERAL_CSTRING("capability.policy.") + policyName +
1719 NS_LITERAL_CSTRING(".checkloaduri.enabled");
1720 nsAutoString value;
1721 nsresult rv = Preferences::GetString(checkLoadURIPrefName.get(), value);
1722 if (NS_FAILED(rv) || !value.LowerCaseEqualsLiteral("allaccess")) {
1723 continue;
1726 // Grab the list of domains associated with this policy.
1727 nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
1728 policyName + NS_LITERAL_CSTRING(".sites");
1729 nsAutoCString siteList;
1730 Preferences::GetCString(domainPrefName.get(), siteList);
1731 AddSitesToFileURIAllowlist(siteList);
1734 return mFileURIAllowlist.ref();