Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 et sw=4 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 "js/OldDebugAPI.h"
12 #include "xpcprivate.h"
13 #include "XPCWrapper.h"
14 #include "nsIServiceManager.h"
15 #include "nsIScriptObjectPrincipal.h"
16 #include "nsIScriptContext.h"
17 #include "nsIURL.h"
18 #include "nsINestedURI.h"
19 #include "nspr.h"
20 #include "nsJSPrincipals.h"
21 #include "nsSystemPrincipal.h"
22 #include "nsPrincipal.h"
23 #include "nsNullPrincipal.h"
24 #include "DomainPolicy.h"
25 #include "nsXPIDLString.h"
26 #include "nsCRT.h"
27 #include "nsCRTGlue.h"
28 #include "nsError.h"
29 #include "nsDOMCID.h"
30 #include "nsIXPConnect.h"
31 #include "nsIXPCSecurityManager.h"
32 #include "nsTextFormatter.h"
33 #include "nsIStringBundle.h"
34 #include "nsNetUtil.h"
35 #include "nsIEffectiveTLDService.h"
36 #include "nsIProperties.h"
37 #include "nsDirectoryServiceDefs.h"
38 #include "nsIFile.h"
39 #include "nsIFileURL.h"
40 #include "nsIZipReader.h"
41 #include "nsIXPConnect.h"
42 #include "nsIScriptGlobalObject.h"
43 #include "nsPIDOMWindow.h"
44 #include "nsIDocShell.h"
45 #include "nsIPrompt.h"
46 #include "nsIWindowWatcher.h"
47 #include "nsIConsoleService.h"
48 #include "nsIJSRuntimeService.h"
49 #include "nsIObserverService.h"
50 #include "nsIContent.h"
51 #include "nsAutoPtr.h"
52 #include "nsDOMJSUtils.h"
53 #include "nsAboutProtocolUtils.h"
54 #include "nsIClassInfo.h"
55 #include "nsIURIFixup.h"
56 #include "nsCDefaultURIFixup.h"
57 #include "nsIChromeRegistry.h"
58 #include "nsIContentSecurityPolicy.h"
59 #include "nsIAsyncVerifyRedirectCallback.h"
60 #include "mozilla/Preferences.h"
61 #include "mozilla/dom/BindingUtils.h"
62 #include <stdint.h>
63 #include "mozilla/ClearOnShutdown.h"
64 #include "mozilla/StaticPtr.h"
65 #include "nsContentUtils.h"
66 #include "nsCxPusher.h"
67 #include "nsJSUtils.h"
69 // This should be probably defined on some other place... but I couldn't find it
70 #define WEBAPPS_PERM_NAME "webapps-manage"
72 using namespace mozilla;
73 using namespace mozilla::dom;
75 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
77 nsIIOService *nsScriptSecurityManager::sIOService = nullptr;
78 nsIStringBundle *nsScriptSecurityManager::sStrBundle = nullptr;
79 JSRuntime *nsScriptSecurityManager::sRuntime = 0;
80 bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
82 bool
83 nsScriptSecurityManager::SubjectIsPrivileged()
84 {
85 JSContext *cx = GetCurrentJSContext();
86 if (cx && xpc::IsUniversalXPConnectEnabled(cx))
87 return true;
88 bool isSystem = false;
89 return NS_SUCCEEDED(SubjectPrincipalIsSystem(&isSystem)) && isSystem;
90 }
92 ///////////////////////////
93 // Convenience Functions //
94 ///////////////////////////
95 // Result of this function should not be freed.
96 static inline const char16_t *
97 IDToString(JSContext *cx, jsid id_)
98 {
99 JS::RootedId id(cx, id_);
100 if (JSID_IS_STRING(id))
101 return JS_GetInternedStringChars(JSID_TO_STRING(id));
103 JS::Rooted<JS::Value> idval(cx);
104 if (!JS_IdToValue(cx, id, &idval))
105 return nullptr;
106 JSString *str = JS::ToString(cx, idval);
107 if(!str)
108 return nullptr;
109 return JS_GetStringCharsZ(cx, str);
110 }
112 class nsAutoInPrincipalDomainOriginSetter {
113 public:
114 nsAutoInPrincipalDomainOriginSetter() {
115 ++sInPrincipalDomainOrigin;
116 }
117 ~nsAutoInPrincipalDomainOriginSetter() {
118 --sInPrincipalDomainOrigin;
119 }
120 static uint32_t sInPrincipalDomainOrigin;
121 };
122 uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin;
124 static
125 nsresult
126 GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin)
127 {
128 if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) {
129 // Allow a single recursive call to GetPrincipalDomainOrigin, since that
130 // might be happening on a different principal from the first call. But
131 // after that, cut off the recursion; it just indicates that something
132 // we're doing in this method causes us to reenter a security check here.
133 return NS_ERROR_NOT_AVAILABLE;
134 }
136 nsAutoInPrincipalDomainOriginSetter autoSetter;
138 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
139 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
141 nsAutoCString hostPort;
143 nsresult rv = uri->GetHostPort(hostPort);
144 if (NS_SUCCEEDED(rv)) {
145 nsAutoCString scheme;
146 rv = uri->GetScheme(scheme);
147 NS_ENSURE_SUCCESS(rv, rv);
148 aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
149 }
150 else {
151 // Some URIs (e.g., nsSimpleURI) don't support host. Just
152 // get the full spec.
153 rv = uri->GetSpec(aOrigin);
154 NS_ENSURE_SUCCESS(rv, rv);
155 }
157 return NS_OK;
158 }
160 static
161 nsresult
162 GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
163 nsACString& aOrigin)
164 {
166 nsCOMPtr<nsIURI> uri;
167 aPrincipal->GetDomain(getter_AddRefs(uri));
168 if (!uri) {
169 aPrincipal->GetURI(getter_AddRefs(uri));
170 }
171 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
173 return GetOriginFromURI(uri, aOrigin);
174 }
176 inline void SetPendingException(JSContext *cx, const char *aMsg)
177 {
178 JS_ReportError(cx, "%s", aMsg);
179 }
181 inline void SetPendingException(JSContext *cx, const char16_t *aMsg)
182 {
183 JS_ReportError(cx, "%hs", aMsg);
184 }
186 // Helper class to get stuff from the ClassInfo and not waste extra time with
187 // virtual method calls for things it has already gotten
188 class ClassInfoData
189 {
190 public:
191 ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
192 : mClassInfo(aClassInfo),
193 mName(const_cast<char *>(aName)),
194 mDidGetFlags(false),
195 mMustFreeName(false)
196 {
197 }
199 ~ClassInfoData()
200 {
201 if (mMustFreeName)
202 nsMemory::Free(mName);
203 }
205 uint32_t GetFlags()
206 {
207 if (!mDidGetFlags) {
208 if (mClassInfo) {
209 nsresult rv = mClassInfo->GetFlags(&mFlags);
210 if (NS_FAILED(rv)) {
211 mFlags = 0;
212 }
213 } else {
214 mFlags = 0;
215 }
217 mDidGetFlags = true;
218 }
220 return mFlags;
221 }
223 bool IsDOMClass()
224 {
225 return !!(GetFlags() & nsIClassInfo::DOM_OBJECT);
226 }
228 const char* GetName()
229 {
230 if (!mName) {
231 if (mClassInfo) {
232 mClassInfo->GetClassDescription(&mName);
233 }
235 if (mName) {
236 mMustFreeName = true;
237 } else {
238 mName = const_cast<char *>("UnnamedClass");
239 }
240 }
242 return mName;
243 }
245 private:
246 nsIClassInfo *mClassInfo; // WEAK
247 uint32_t mFlags;
248 char *mName;
249 bool mDidGetFlags;
250 bool mMustFreeName;
251 };
253 JSContext *
254 nsScriptSecurityManager::GetCurrentJSContext()
255 {
256 // Get JSContext from stack.
257 return nsXPConnect::XPConnect()->GetCurrentJSContext();
258 }
260 JSContext *
261 nsScriptSecurityManager::GetSafeJSContext()
262 {
263 // Get JSContext from stack.
264 return nsXPConnect::XPConnect()->GetSafeJSContext();
265 }
267 /* static */
268 bool
269 nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
270 nsIURI* aTargetURI)
271 {
272 return NS_SecurityCompareURIs(aSourceURI, aTargetURI, sStrictFileOriginPolicy);
273 }
275 // SecurityHashURI is consistent with SecurityCompareURIs because NS_SecurityHashURI
276 // is consistent with NS_SecurityCompareURIs. See nsNetUtil.h.
277 uint32_t
278 nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI)
279 {
280 return NS_SecurityHashURI(aURI);
281 }
283 NS_IMETHODIMP
284 nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
285 nsIPrincipal** aPrincipal)
286 {
287 NS_PRECONDITION(aChannel, "Must have channel!");
288 nsCOMPtr<nsISupports> owner;
289 aChannel->GetOwner(getter_AddRefs(owner));
290 if (owner) {
291 CallQueryInterface(owner, aPrincipal);
292 if (*aPrincipal) {
293 return NS_OK;
294 }
295 }
297 // OK, get the principal from the URI. Make sure this does the same thing
298 // as nsDocument::Reset and XULDocument::StartDocumentLoad.
299 nsCOMPtr<nsIURI> uri;
300 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
301 NS_ENSURE_SUCCESS(rv, rv);
303 nsCOMPtr<nsIDocShell> docShell;
304 NS_QueryNotificationCallbacks(aChannel, docShell);
306 if (docShell) {
307 return GetDocShellCodebasePrincipal(uri, docShell, aPrincipal);
308 }
310 return GetCodebasePrincipalInternal(uri, UNKNOWN_APP_ID,
311 /* isInBrowserElement */ false, aPrincipal);
312 }
314 NS_IMETHODIMP
315 nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
316 bool* aIsSystem)
317 {
318 *aIsSystem = (aPrincipal == mSystemPrincipal);
319 return NS_OK;
320 }
322 NS_IMETHODIMP_(nsIPrincipal *)
323 nsScriptSecurityManager::GetCxSubjectPrincipal(JSContext *cx)
324 {
325 NS_ASSERTION(cx == GetCurrentJSContext(),
326 "Uh, cx is not the current JS context!");
328 nsresult rv = NS_ERROR_FAILURE;
329 nsIPrincipal *principal = GetSubjectPrincipal(cx, &rv);
330 if (NS_FAILED(rv))
331 return nullptr;
333 return principal;
334 }
336 /////////////////////////////
337 // nsScriptSecurityManager //
338 /////////////////////////////
340 ////////////////////////////////////
341 // Methods implementing ISupports //
342 ////////////////////////////////////
343 NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
344 nsIScriptSecurityManager,
345 nsIXPCSecurityManager,
346 nsIChannelEventSink,
347 nsIObserver)
349 ///////////////////////////////////////////////////
350 // Methods implementing nsIScriptSecurityManager //
351 ///////////////////////////////////////////////////
353 ///////////////// Security Checks /////////////////
355 bool
356 nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
357 {
358 // Get the security manager
359 nsScriptSecurityManager *ssm =
360 nsScriptSecurityManager::GetScriptSecurityManager();
362 NS_ASSERTION(ssm, "Failed to get security manager service");
363 if (!ssm)
364 return false;
366 nsresult rv;
367 nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
369 NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
370 if (NS_FAILED(rv))
371 return false; // Not just absence of principal, but failure.
373 if (!subjectPrincipal)
374 return true;
376 nsCOMPtr<nsIContentSecurityPolicy> csp;
377 rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
378 NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
380 // don't do anything unless there's a CSP
381 if (!csp)
382 return true;
384 bool evalOK = true;
385 bool reportViolation = false;
386 rv = csp->GetAllowsEval(&reportViolation, &evalOK);
388 if (NS_FAILED(rv))
389 {
390 NS_WARNING("CSP: failed to get allowsEval");
391 return true; // fail open to not break sites.
392 }
394 if (reportViolation) {
395 nsAutoString fileName;
396 unsigned lineNum = 0;
397 NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
399 JS::AutoFilename scriptFilename;
400 if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum)) {
401 if (const char *file = scriptFilename.get()) {
402 CopyUTF8toUTF16(nsDependentCString(file), fileName);
403 }
404 }
405 csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
406 fileName,
407 scriptSample,
408 lineNum,
409 EmptyString(),
410 EmptyString());
411 }
413 return evalOK;
414 }
416 // static
417 bool
418 nsScriptSecurityManager::JSPrincipalsSubsume(JSPrincipals *first,
419 JSPrincipals *second)
420 {
421 return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second));
422 }
424 NS_IMETHODIMP
425 nsScriptSecurityManager::CheckSameOrigin(JSContext* cx,
426 nsIURI* aTargetURI)
427 {
428 nsresult rv;
430 // Get a context if necessary
431 if (!cx)
432 {
433 cx = GetCurrentJSContext();
434 if (!cx)
435 return NS_OK; // No JS context, so allow access
436 }
438 // Get a principal from the context
439 nsIPrincipal* sourcePrincipal = GetSubjectPrincipal(cx, &rv);
440 if (NS_FAILED(rv))
441 return rv;
443 if (!sourcePrincipal)
444 {
445 NS_WARNING("CheckSameOrigin called on script w/o principals; should this happen?");
446 return NS_OK;
447 }
449 if (sourcePrincipal == mSystemPrincipal)
450 {
451 // This is a system (chrome) script, so allow access
452 return NS_OK;
453 }
455 // Get the original URI from the source principal.
456 // This has the effect of ignoring any change to document.domain
457 // which must be done to avoid DNS spoofing (bug 154930)
458 nsCOMPtr<nsIURI> sourceURI;
459 sourcePrincipal->GetDomain(getter_AddRefs(sourceURI));
460 if (!sourceURI) {
461 sourcePrincipal->GetURI(getter_AddRefs(sourceURI));
462 NS_ENSURE_TRUE(sourceURI, NS_ERROR_FAILURE);
463 }
465 // Compare origins
466 if (!SecurityCompareURIs(sourceURI, aTargetURI))
467 {
468 ReportError(cx, NS_LITERAL_STRING("CheckSameOriginError"), sourceURI, aTargetURI);
469 return NS_ERROR_DOM_BAD_URI;
470 }
471 return NS_OK;
472 }
474 NS_IMETHODIMP
475 nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
476 nsIURI* aTargetURI,
477 bool reportError)
478 {
479 if (!SecurityCompareURIs(aSourceURI, aTargetURI))
480 {
481 if (reportError) {
482 ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"),
483 aSourceURI, aTargetURI);
484 }
485 return NS_ERROR_DOM_BAD_URI;
486 }
487 return NS_OK;
488 }
490 /*static*/ uint32_t
491 nsScriptSecurityManager::HashPrincipalByOrigin(nsIPrincipal* aPrincipal)
492 {
493 nsCOMPtr<nsIURI> uri;
494 aPrincipal->GetDomain(getter_AddRefs(uri));
495 if (!uri)
496 aPrincipal->GetURI(getter_AddRefs(uri));
497 return SecurityHashURI(uri);
498 }
500 /* static */ bool
501 nsScriptSecurityManager::AppAttributesEqual(nsIPrincipal* aFirst,
502 nsIPrincipal* aSecond)
503 {
504 MOZ_ASSERT(aFirst && aSecond, "Don't pass null pointers!");
506 uint32_t firstAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
507 if (!aFirst->GetUnknownAppId()) {
508 firstAppId = aFirst->GetAppId();
509 }
511 uint32_t secondAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
512 if (!aSecond->GetUnknownAppId()) {
513 secondAppId = aSecond->GetAppId();
514 }
516 return ((firstAppId == secondAppId) &&
517 (aFirst->GetIsInBrowserElement() == aSecond->GetIsInBrowserElement()));
518 }
520 NS_IMETHODIMP
521 nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
522 {
523 // Get principal of currently executing script.
524 nsresult rv;
525 nsIPrincipal* principal = GetSubjectPrincipal(cx, &rv);
526 if (NS_FAILED(rv))
527 return rv;
529 // Native code can load all URIs.
530 if (!principal)
531 return NS_OK;
533 rv = CheckLoadURIWithPrincipal(principal, aURI,
534 nsIScriptSecurityManager::STANDARD);
535 if (NS_SUCCEEDED(rv)) {
536 // OK to load
537 return NS_OK;
538 }
540 // See if we're attempting to load a file: URI. If so, let a
541 // UniversalXPConnect capability trump the above check.
542 bool isFile = false;
543 bool isRes = false;
544 if (NS_FAILED(aURI->SchemeIs("file", &isFile)) ||
545 NS_FAILED(aURI->SchemeIs("resource", &isRes)))
546 return NS_ERROR_FAILURE;
547 if (isFile || isRes)
548 {
549 if (SubjectIsPrivileged())
550 return NS_OK;
551 }
553 // Report error.
554 nsAutoCString spec;
555 if (NS_FAILED(aURI->GetAsciiSpec(spec)))
556 return NS_ERROR_FAILURE;
557 nsAutoCString msg("Access to '");
558 msg.Append(spec);
559 msg.AppendLiteral("' from script denied");
560 SetPendingException(cx, msg.get());
561 return NS_ERROR_DOM_BAD_URI;
562 }
564 /**
565 * Helper method to handle cases where a flag passed to
566 * CheckLoadURIWithPrincipal means denying loading if the given URI has certain
567 * nsIProtocolHandler flags set.
568 * @return if success, access is allowed. Otherwise, deny access
569 */
570 static nsresult
571 DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags)
572 {
573 NS_PRECONDITION(aURI, "Must have URI!");
575 bool uriHasFlags;
576 nsresult rv =
577 NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags);
578 NS_ENSURE_SUCCESS(rv, rv);
580 if (uriHasFlags) {
581 return NS_ERROR_DOM_BAD_URI;
582 }
584 return NS_OK;
585 }
587 static bool
588 EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase)
589 {
590 // Make a clone of the incoming URI, because we're going to mutate it.
591 nsCOMPtr<nsIURI> probe;
592 nsresult rv = aProbeArg->Clone(getter_AddRefs(probe));
593 NS_ENSURE_SUCCESS(rv, false);
595 nsCOMPtr<nsIEffectiveTLDService> tldService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
596 NS_ENSURE_TRUE(tldService, false);
597 while (true) {
598 if (nsScriptSecurityManager::SecurityCompareURIs(probe, aBase)) {
599 return true;
600 }
602 nsAutoCString host, newHost;
603 nsresult rv = probe->GetHost(host);
604 NS_ENSURE_SUCCESS(rv, false);
606 rv = tldService->GetNextSubDomain(host, newHost);
607 if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
608 return false;
609 }
610 NS_ENSURE_SUCCESS(rv, false);
611 rv = probe->SetHost(newHost);
612 NS_ENSURE_SUCCESS(rv, false);
613 }
614 }
616 NS_IMETHODIMP
617 nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
618 nsIURI *aTargetURI,
619 uint32_t aFlags)
620 {
621 NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
622 // If someone passes a flag that we don't understand, we should
623 // fail, because they may need a security check that we don't
624 // provide.
625 NS_ENSURE_FALSE(aFlags & ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
626 nsIScriptSecurityManager::ALLOW_CHROME |
627 nsIScriptSecurityManager::DISALLOW_SCRIPT |
628 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
629 nsIScriptSecurityManager::DONT_REPORT_ERRORS),
630 NS_ERROR_UNEXPECTED);
631 NS_ENSURE_ARG_POINTER(aPrincipal);
632 NS_ENSURE_ARG_POINTER(aTargetURI);
634 // If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which
635 // would do such inheriting. That would be URIs that do not have their own
636 // security context. We do this even for the system principal.
637 if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) {
638 nsresult rv =
639 DenyAccessIfURIHasFlags(aTargetURI,
640 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT);
641 NS_ENSURE_SUCCESS(rv, rv);
642 }
644 if (aPrincipal == mSystemPrincipal) {
645 // Allow access
646 return NS_OK;
647 }
649 nsCOMPtr<nsIURI> sourceURI;
650 aPrincipal->GetURI(getter_AddRefs(sourceURI));
651 if (!sourceURI) {
652 nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aPrincipal);
653 if (expanded) {
654 nsTArray< nsCOMPtr<nsIPrincipal> > *whiteList;
655 expanded->GetWhiteList(&whiteList);
656 for (uint32_t i = 0; i < whiteList->Length(); ++i) {
657 nsresult rv = CheckLoadURIWithPrincipal((*whiteList)[i],
658 aTargetURI,
659 aFlags);
660 if (NS_SUCCEEDED(rv)) {
661 // Allow access if it succeeded with one of the white listed principals
662 return NS_OK;
663 }
664 }
665 // None of our whitelisted principals worked.
666 return NS_ERROR_DOM_BAD_URI;
667 }
668 NS_ERROR("Non-system principals or expanded principal passed to CheckLoadURIWithPrincipal "
669 "must have a URI!");
670 return NS_ERROR_UNEXPECTED;
671 }
673 // Automatic loads are not allowed from certain protocols.
674 if (aFlags & nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) {
675 nsresult rv =
676 DenyAccessIfURIHasFlags(sourceURI,
677 nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT);
678 NS_ENSURE_SUCCESS(rv, rv);
679 }
681 // If either URI is a nested URI, get the base URI
682 nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI);
683 nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
685 //-- get the target scheme
686 nsAutoCString targetScheme;
687 nsresult rv = targetBaseURI->GetScheme(targetScheme);
688 if (NS_FAILED(rv)) return rv;
690 //-- Some callers do not allow loading javascript:
691 if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) &&
692 targetScheme.EqualsLiteral("javascript"))
693 {
694 return NS_ERROR_DOM_BAD_URI;
695 }
697 NS_NAMED_LITERAL_STRING(errorTag, "CheckLoadURIError");
698 bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS);
700 // Check for uris that are only loadable by principals that subsume them
701 bool hasFlags;
702 rv = NS_URIChainHasFlags(targetBaseURI,
703 nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
704 &hasFlags);
705 NS_ENSURE_SUCCESS(rv, rv);
707 if (hasFlags) {
708 return aPrincipal->CheckMayLoad(targetBaseURI, true, false);
709 }
711 //-- get the source scheme
712 nsAutoCString sourceScheme;
713 rv = sourceBaseURI->GetScheme(sourceScheme);
714 if (NS_FAILED(rv)) return rv;
716 if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
717 // A null principal can target its own URI.
718 if (sourceURI == aTargetURI) {
719 return NS_OK;
720 }
721 }
722 else if (targetScheme.Equals(sourceScheme,
723 nsCaseInsensitiveCStringComparator()))
724 {
725 // every scheme can access another URI from the same scheme,
726 // as long as they don't represent null principals...
727 // Or they don't require an special permission to do so
728 // See bug#773886
730 bool hasFlags;
731 rv = NS_URIChainHasFlags(targetBaseURI,
732 nsIProtocolHandler::URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM,
733 &hasFlags);
734 NS_ENSURE_SUCCESS(rv, rv);
736 if (hasFlags) {
737 // In this case, we allow opening only if the source and target URIS
738 // are on the same domain, or the opening URI has the webapps
739 // permision granted
740 if (!SecurityCompareURIs(sourceBaseURI,targetBaseURI) &&
741 !nsContentUtils::IsExactSitePermAllow(aPrincipal,WEBAPPS_PERM_NAME)){
742 return NS_ERROR_DOM_BAD_URI;
743 }
744 }
745 return NS_OK;
746 }
748 // If the schemes don't match, the policy is specified by the protocol
749 // flags on the target URI. Note that the order of policy checks here is
750 // very important! We start from most restrictive and work our way down.
751 // Note that since we're working with the innermost URI, we can just use
752 // the methods that work on chains of nested URIs and they will only look
753 // at the flags for our one URI.
755 // Check for system target URI
756 rv = DenyAccessIfURIHasFlags(targetBaseURI,
757 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
758 if (NS_FAILED(rv)) {
759 // Deny access, since the origin principal is not system
760 if (reportErrors) {
761 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
762 }
763 return rv;
764 }
766 // Check for chrome target URI
767 rv = NS_URIChainHasFlags(targetBaseURI,
768 nsIProtocolHandler::URI_IS_UI_RESOURCE,
769 &hasFlags);
770 NS_ENSURE_SUCCESS(rv, rv);
771 if (hasFlags) {
772 if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
773 if (!targetScheme.EqualsLiteral("chrome")) {
774 // for now don't change behavior for resource: or moz-icon:
775 return NS_OK;
776 }
778 // allow load only if chrome package is whitelisted
779 nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(
780 NS_CHROMEREGISTRY_CONTRACTID));
781 if (reg) {
782 bool accessAllowed = false;
783 reg->AllowContentToAccess(targetBaseURI, &accessAllowed);
784 if (accessAllowed) {
785 return NS_OK;
786 }
787 }
788 }
790 // resource: and chrome: are equivalent, securitywise
791 // That's bogus!! Fix this. But watch out for
792 // the view-source stylesheet?
793 bool sourceIsChrome;
794 rv = NS_URIChainHasFlags(sourceBaseURI,
795 nsIProtocolHandler::URI_IS_UI_RESOURCE,
796 &sourceIsChrome);
797 NS_ENSURE_SUCCESS(rv, rv);
798 if (sourceIsChrome) {
799 return NS_OK;
800 }
801 if (reportErrors) {
802 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
803 }
804 return NS_ERROR_DOM_BAD_URI;
805 }
807 // Check for target URI pointing to a file
808 rv = NS_URIChainHasFlags(targetBaseURI,
809 nsIProtocolHandler::URI_IS_LOCAL_FILE,
810 &hasFlags);
811 NS_ENSURE_SUCCESS(rv, rv);
812 if (hasFlags) {
813 // Allow domains that were whitelisted in the prefs. In 99.9% of cases,
814 // this array is empty.
815 for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) {
816 if (EqualOrSubdomain(sourceURI, mFileURIWhitelist[i])) {
817 return NS_OK;
818 }
819 }
821 // resource: and chrome: are equivalent, securitywise
822 // That's bogus!! Fix this. But watch out for
823 // the view-source stylesheet?
824 bool sourceIsChrome;
825 rv = NS_URIChainHasFlags(sourceURI,
826 nsIProtocolHandler::URI_IS_UI_RESOURCE,
827 &sourceIsChrome);
828 NS_ENSURE_SUCCESS(rv, rv);
829 if (sourceIsChrome) {
830 return NS_OK;
831 }
833 if (reportErrors) {
834 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
835 }
836 return NS_ERROR_DOM_BAD_URI;
837 }
839 // OK, everyone is allowed to load this, since unflagged handlers are
840 // deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
841 // need to warn. At some point we'll want to make this warning into an
842 // error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
843 rv = NS_URIChainHasFlags(targetBaseURI,
844 nsIProtocolHandler::URI_LOADABLE_BY_ANYONE,
845 &hasFlags);
846 NS_ENSURE_SUCCESS(rv, rv);
847 if (!hasFlags) {
848 nsXPIDLString message;
849 NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
850 const char16_t* formatStrings[] = { ucsTargetScheme.get() };
851 rv = sStrBundle->
852 FormatStringFromName(MOZ_UTF16("ProtocolFlagError"),
853 formatStrings,
854 ArrayLength(formatStrings),
855 getter_Copies(message));
856 if (NS_SUCCEEDED(rv)) {
857 nsCOMPtr<nsIConsoleService> console(
858 do_GetService("@mozilla.org/consoleservice;1"));
859 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
861 console->LogStringMessage(message.get());
862 }
863 }
865 return NS_OK;
866 }
868 nsresult
869 nsScriptSecurityManager::ReportError(JSContext* cx, const nsAString& messageTag,
870 nsIURI* aSource, nsIURI* aTarget)
871 {
872 nsresult rv;
873 NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
875 // Get the source URL spec
876 nsAutoCString sourceSpec;
877 rv = aSource->GetAsciiSpec(sourceSpec);
878 NS_ENSURE_SUCCESS(rv, rv);
880 // Get the target URL spec
881 nsAutoCString targetSpec;
882 rv = aTarget->GetAsciiSpec(targetSpec);
883 NS_ENSURE_SUCCESS(rv, rv);
885 // Localize the error message
886 nsXPIDLString message;
887 NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec);
888 NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
889 const char16_t *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() };
890 rv = sStrBundle->FormatStringFromName(PromiseFlatString(messageTag).get(),
891 formatStrings,
892 ArrayLength(formatStrings),
893 getter_Copies(message));
894 NS_ENSURE_SUCCESS(rv, rv);
896 // If a JS context was passed in, set a JS exception.
897 // Otherwise, print the error message directly to the JS console
898 // and to standard output
899 if (cx)
900 {
901 SetPendingException(cx, message.get());
902 }
903 else // Print directly to the console
904 {
905 nsCOMPtr<nsIConsoleService> console(
906 do_GetService("@mozilla.org/consoleservice;1"));
907 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
909 console->LogStringMessage(message.get());
910 }
911 return NS_OK;
912 }
914 NS_IMETHODIMP
915 nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
916 const nsACString& aTargetURIStr,
917 uint32_t aFlags)
918 {
919 nsresult rv;
920 nsCOMPtr<nsIURI> target;
921 rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr,
922 nullptr, nullptr, sIOService);
923 NS_ENSURE_SUCCESS(rv, rv);
925 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
926 if (rv == NS_ERROR_DOM_BAD_URI) {
927 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
928 // return values.
929 return rv;
930 }
931 NS_ENSURE_SUCCESS(rv, rv);
933 // Now start testing fixup -- since aTargetURIStr is a string, not
934 // an nsIURI, we may well end up fixing it up before loading.
935 // Note: This needs to stay in sync with the nsIURIFixup api.
936 nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
937 if (!fixup) {
938 return rv;
939 }
941 uint32_t flags[] = {
942 nsIURIFixup::FIXUP_FLAG_NONE,
943 nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS,
944 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
945 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
946 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
947 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI
948 };
950 for (uint32_t i = 0; i < ArrayLength(flags); ++i) {
951 rv = fixup->CreateFixupURI(aTargetURIStr, flags[i], nullptr,
952 getter_AddRefs(target));
953 NS_ENSURE_SUCCESS(rv, rv);
955 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
956 if (rv == NS_ERROR_DOM_BAD_URI) {
957 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
958 // return values.
959 return rv;
960 }
961 NS_ENSURE_SUCCESS(rv, rv);
962 }
964 return rv;
965 }
967 bool
968 nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
969 {
970 MOZ_ASSERT(aGlobal);
971 MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
972 AutoJSContext cx;
973 JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
975 // Check the bits on the compartment private.
976 return xpc::Scriptability::Get(aGlobal).Allowed();
977 }
979 ///////////////// Principals ///////////////////////
980 NS_IMETHODIMP
981 nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **aSubjectPrincipal)
982 {
983 nsresult rv;
984 *aSubjectPrincipal = doGetSubjectPrincipal(&rv);
985 if (NS_SUCCEEDED(rv))
986 NS_IF_ADDREF(*aSubjectPrincipal);
987 return rv;
988 }
990 nsIPrincipal*
991 nsScriptSecurityManager::doGetSubjectPrincipal(nsresult* rv)
992 {
993 NS_PRECONDITION(rv, "Null out param");
994 JSContext *cx = GetCurrentJSContext();
995 if (!cx)
996 {
997 *rv = NS_OK;
998 return nullptr;
999 }
1000 return GetSubjectPrincipal(cx, rv);
1001 }
1003 NS_IMETHODIMP
1004 nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
1005 {
1006 NS_ADDREF(*result = mSystemPrincipal);
1008 return NS_OK;
1009 }
1011 NS_IMETHODIMP
1012 nsScriptSecurityManager::SubjectPrincipalIsSystem(bool* aIsSystem)
1013 {
1014 NS_ENSURE_ARG_POINTER(aIsSystem);
1015 *aIsSystem = false;
1017 if (!mSystemPrincipal)
1018 return NS_OK;
1020 nsCOMPtr<nsIPrincipal> subject;
1021 nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject));
1022 if (NS_FAILED(rv))
1023 return rv;
1025 if(!subject)
1026 {
1027 // No subject principal means no JS is running;
1028 // this is the equivalent of system principal code
1029 *aIsSystem = true;
1030 return NS_OK;
1031 }
1033 return mSystemPrincipal->Equals(subject, aIsSystem);
1034 }
1036 nsresult
1037 nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId,
1038 bool aInMozBrowser,
1039 nsIPrincipal **result)
1040 {
1041 // I _think_ it's safe to not create null principals here based on aURI.
1042 // At least all the callers would do the right thing in those cases, as far
1043 // as I can tell. --bz
1045 nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
1046 if (uriPrinc) {
1047 nsCOMPtr<nsIPrincipal> principal;
1048 uriPrinc->GetPrincipal(getter_AddRefs(principal));
1049 if (!principal || principal == mSystemPrincipal) {
1050 return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
1051 }
1053 principal.forget(result);
1055 return NS_OK;
1056 }
1058 nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
1059 if (!codebase)
1060 return NS_ERROR_OUT_OF_MEMORY;
1062 nsresult rv = codebase->Init(aURI, aAppId, aInMozBrowser);
1063 if (NS_FAILED(rv))
1064 return rv;
1066 NS_ADDREF(*result = codebase);
1068 return NS_OK;
1069 }
1071 NS_IMETHODIMP
1072 nsScriptSecurityManager::GetSimpleCodebasePrincipal(nsIURI* aURI,
1073 nsIPrincipal** aPrincipal)
1074 {
1075 return GetCodebasePrincipalInternal(aURI,
1076 nsIScriptSecurityManager::UNKNOWN_APP_ID,
1077 false, aPrincipal);
1078 }
1080 NS_IMETHODIMP
1081 nsScriptSecurityManager::GetNoAppCodebasePrincipal(nsIURI* aURI,
1082 nsIPrincipal** aPrincipal)
1083 {
1084 return GetCodebasePrincipalInternal(aURI, nsIScriptSecurityManager::NO_APP_ID,
1085 false, aPrincipal);
1086 }
1088 NS_IMETHODIMP
1089 nsScriptSecurityManager::GetCodebasePrincipal(nsIURI* aURI,
1090 nsIPrincipal** aPrincipal)
1091 {
1092 return GetNoAppCodebasePrincipal(aURI, aPrincipal);
1093 }
1095 NS_IMETHODIMP
1096 nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI,
1097 uint32_t aAppId,
1098 bool aInMozBrowser,
1099 nsIPrincipal** aPrincipal)
1100 {
1101 NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
1102 NS_ERROR_INVALID_ARG);
1104 return GetCodebasePrincipalInternal(aURI, aAppId, aInMozBrowser, aPrincipal);
1105 }
1107 NS_IMETHODIMP
1108 nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
1109 nsIDocShell* aDocShell,
1110 nsIPrincipal** aPrincipal)
1111 {
1112 return GetCodebasePrincipalInternal(aURI,
1113 aDocShell->GetAppId(),
1114 aDocShell->GetIsInBrowserElement(),
1115 aPrincipal);
1116 }
1118 nsresult
1119 nsScriptSecurityManager::GetCodebasePrincipalInternal(nsIURI *aURI,
1120 uint32_t aAppId,
1121 bool aInMozBrowser,
1122 nsIPrincipal **result)
1123 {
1124 NS_ENSURE_ARG(aURI);
1126 bool inheritsPrincipal;
1127 nsresult rv =
1128 NS_URIChainHasFlags(aURI,
1129 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
1130 &inheritsPrincipal);
1131 if (NS_FAILED(rv) || inheritsPrincipal) {
1132 return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
1133 }
1135 nsCOMPtr<nsIPrincipal> principal;
1136 rv = CreateCodebasePrincipal(aURI, aAppId, aInMozBrowser,
1137 getter_AddRefs(principal));
1138 NS_ENSURE_SUCCESS(rv, rv);
1139 NS_IF_ADDREF(*result = principal);
1141 return NS_OK;
1142 }
1144 nsIPrincipal*
1145 nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
1146 nsresult* rv)
1147 {
1148 *rv = NS_OK;
1149 JSCompartment *compartment = js::GetContextCompartment(cx);
1151 // The context should always be in a compartment, either one it has entered
1152 // or the one associated with its global.
1153 MOZ_ASSERT(!!compartment);
1155 JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
1156 return nsJSPrincipals::get(principals);
1157 }
1159 // static
1160 nsIPrincipal*
1161 nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
1162 {
1163 JSCompartment *compartment = js::GetObjectCompartment(aObj);
1164 JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
1165 return nsJSPrincipals::get(principals);
1166 }
1168 ////////////////////////////////////////////////
1169 // Methods implementing nsIXPCSecurityManager //
1170 ////////////////////////////////////////////////
1172 NS_IMETHODIMP
1173 nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
1174 const nsIID &aIID,
1175 nsISupports *aObj,
1176 nsIClassInfo *aClassInfo)
1177 {
1178 // XXX Special case for nsIXPCException ?
1179 ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nullptr);
1180 if (objClassInfo.IsDOMClass())
1181 {
1182 return NS_OK;
1183 }
1185 // We give remote-XUL whitelisted domains a free pass here. See bug 932906.
1186 if (!xpc::AllowXBLScope(js::GetContextCompartment(cx)))
1187 {
1188 return NS_OK;
1189 }
1191 if (SubjectIsPrivileged())
1192 {
1193 return NS_OK;
1194 }
1196 //-- Access denied, report an error
1197 NS_ConvertUTF8toUTF16 strName("CreateWrapperDenied");
1198 nsAutoCString origin;
1199 nsresult rv2;
1200 nsIPrincipal* subjectPrincipal = doGetSubjectPrincipal(&rv2);
1201 if (NS_SUCCEEDED(rv2) && subjectPrincipal) {
1202 GetPrincipalDomainOrigin(subjectPrincipal, origin);
1203 }
1204 NS_ConvertUTF8toUTF16 originUnicode(origin);
1205 NS_ConvertUTF8toUTF16 classInfoName(objClassInfo.GetName());
1206 const char16_t* formatStrings[] = {
1207 classInfoName.get(),
1208 originUnicode.get()
1209 };
1210 uint32_t length = ArrayLength(formatStrings);
1211 if (originUnicode.IsEmpty()) {
1212 --length;
1213 } else {
1214 strName.AppendLiteral("ForOrigin");
1215 }
1216 nsXPIDLString errorMsg;
1217 // We need to keep our existing failure rv and not override it
1218 // with a likely success code from the following string bundle
1219 // call in order to throw the correct security exception later.
1220 rv2 = sStrBundle->FormatStringFromName(strName.get(),
1221 formatStrings,
1222 length,
1223 getter_Copies(errorMsg));
1224 NS_ENSURE_SUCCESS(rv2, rv2);
1226 SetPendingException(cx, errorMsg.get());
1227 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1228 }
1230 NS_IMETHODIMP
1231 nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
1232 const nsCID &aCID)
1233 {
1234 if (SubjectIsPrivileged()) {
1235 return NS_OK;
1236 }
1238 //-- Access denied, report an error
1239 nsAutoCString errorMsg("Permission denied to create instance of class. CID=");
1240 char cidStr[NSID_LENGTH];
1241 aCID.ToProvidedString(cidStr);
1242 errorMsg.Append(cidStr);
1243 SetPendingException(cx, errorMsg.get());
1244 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1245 }
1247 NS_IMETHODIMP
1248 nsScriptSecurityManager::CanGetService(JSContext *cx,
1249 const nsCID &aCID)
1250 {
1251 if (SubjectIsPrivileged()) {
1252 return NS_OK;
1253 }
1255 //-- Access denied, report an error
1256 nsAutoCString errorMsg("Permission denied to get service. CID=");
1257 char cidStr[NSID_LENGTH];
1258 aCID.ToProvidedString(cidStr);
1259 errorMsg.Append(cidStr);
1260 SetPendingException(cx, errorMsg.get());
1261 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1262 }
1264 /////////////////////////////////////////////
1265 // Method implementing nsIChannelEventSink //
1266 /////////////////////////////////////////////
1267 NS_IMETHODIMP
1268 nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel,
1269 nsIChannel* newChannel,
1270 uint32_t redirFlags,
1271 nsIAsyncVerifyRedirectCallback *cb)
1272 {
1273 nsCOMPtr<nsIPrincipal> oldPrincipal;
1274 GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
1276 nsCOMPtr<nsIURI> newURI;
1277 newChannel->GetURI(getter_AddRefs(newURI));
1278 nsCOMPtr<nsIURI> newOriginalURI;
1279 newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
1281 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
1283 const uint32_t flags =
1284 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
1285 nsIScriptSecurityManager::DISALLOW_SCRIPT;
1286 nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
1287 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
1288 rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
1289 }
1291 if (NS_FAILED(rv))
1292 return rv;
1294 cb->OnRedirectVerifyCallback(NS_OK);
1295 return NS_OK;
1296 }
1299 /////////////////////////////////////
1300 // Method implementing nsIObserver //
1301 /////////////////////////////////////
1302 const char sJSEnabledPrefName[] = "javascript.enabled";
1303 const char sFileOriginPolicyPrefName[] =
1304 "security.fileuri.strict_origin_policy";
1306 static const char* kObservedPrefs[] = {
1307 sJSEnabledPrefName,
1308 sFileOriginPolicyPrefName,
1309 "capability.policy.",
1310 nullptr
1311 };
1314 NS_IMETHODIMP
1315 nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
1316 const char16_t* aMessage)
1317 {
1318 ScriptSecurityPrefChanged();
1319 return NS_OK;
1320 }
1322 /////////////////////////////////////////////
1323 // Constructor, Destructor, Initialization //
1324 /////////////////////////////////////////////
1325 nsScriptSecurityManager::nsScriptSecurityManager(void)
1326 : mPrefInitialized(false)
1327 , mIsJavaScriptEnabled(false)
1328 {
1329 static_assert(sizeof(intptr_t) == sizeof(void*),
1330 "intptr_t and void* have different lengths on this platform. "
1331 "This may cause a security failure with the SecurityLevel union.");
1332 }
1334 nsresult nsScriptSecurityManager::Init()
1335 {
1336 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
1337 NS_ENSURE_SUCCESS(rv, rv);
1339 InitPrefs();
1341 nsCOMPtr<nsIStringBundleService> bundleService =
1342 mozilla::services::GetStringBundleService();
1343 if (!bundleService)
1344 return NS_ERROR_FAILURE;
1346 rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
1347 NS_ENSURE_SUCCESS(rv, rv);
1349 // Create our system principal singleton
1350 nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
1351 NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
1353 mSystemPrincipal = system;
1355 //-- Register security check callback in the JS engine
1356 // Currently this is used to control access to function.caller
1357 rv = nsXPConnect::XPConnect()->GetRuntime(&sRuntime);
1358 NS_ENSURE_SUCCESS(rv, rv);
1360 static const JSSecurityCallbacks securityCallbacks = {
1361 ContentSecurityPolicyPermitsJSAction,
1362 JSPrincipalsSubsume,
1363 };
1365 MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
1366 JS_SetSecurityCallbacks(sRuntime, &securityCallbacks);
1367 JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy);
1369 JS_SetTrustedPrincipals(sRuntime, system);
1371 return NS_OK;
1372 }
1374 static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
1376 nsScriptSecurityManager::~nsScriptSecurityManager(void)
1377 {
1378 Preferences::RemoveObservers(this, kObservedPrefs);
1379 if (mDomainPolicy)
1380 mDomainPolicy->Deactivate();
1381 MOZ_ASSERT(!mDomainPolicy);
1382 }
1384 void
1385 nsScriptSecurityManager::Shutdown()
1386 {
1387 if (sRuntime) {
1388 JS_SetSecurityCallbacks(sRuntime, nullptr);
1389 JS_SetTrustedPrincipals(sRuntime, nullptr);
1390 sRuntime = nullptr;
1391 }
1393 NS_IF_RELEASE(sIOService);
1394 NS_IF_RELEASE(sStrBundle);
1395 }
1397 nsScriptSecurityManager *
1398 nsScriptSecurityManager::GetScriptSecurityManager()
1399 {
1400 if (!gScriptSecMan && nsXPConnect::XPConnect())
1401 {
1402 nsRefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
1404 nsresult rv;
1405 rv = ssManager->Init();
1406 if (NS_FAILED(rv)) {
1407 return nullptr;
1408 }
1410 rv = nsXPConnect::XPConnect()->
1411 SetDefaultSecurityManager(ssManager);
1412 if (NS_FAILED(rv)) {
1413 NS_WARNING("Failed to install xpconnect security manager!");
1414 return nullptr;
1415 }
1417 ClearOnShutdown(&gScriptSecMan);
1418 gScriptSecMan = ssManager;
1419 }
1420 return gScriptSecMan;
1421 }
1423 // Currently this nsGenericFactory constructor is used only from FastLoad
1424 // (XPCOM object deserialization) code, when "creating" the system principal
1425 // singleton.
1426 nsSystemPrincipal *
1427 nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
1428 {
1429 nsIPrincipal *sysprin = nullptr;
1430 if (gScriptSecMan)
1431 NS_ADDREF(sysprin = gScriptSecMan->mSystemPrincipal);
1432 return static_cast<nsSystemPrincipal*>(sysprin);
1433 }
1435 struct IsWhitespace {
1436 static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); };
1437 };
1438 struct IsWhitespaceOrComma {
1439 static bool Test(char aChar) { return aChar == ',' || NS_IsAsciiWhitespace(aChar); };
1440 };
1442 template <typename Predicate>
1443 uint32_t SkipPast(const nsCString& str, uint32_t base)
1444 {
1445 while (base < str.Length() && Predicate::Test(str[base])) {
1446 ++base;
1447 }
1448 return base;
1449 }
1451 template <typename Predicate>
1452 uint32_t SkipUntil(const nsCString& str, uint32_t base)
1453 {
1454 while (base < str.Length() && !Predicate::Test(str[base])) {
1455 ++base;
1456 }
1457 return base;
1458 }
1460 inline void
1461 nsScriptSecurityManager::ScriptSecurityPrefChanged()
1462 {
1463 MOZ_ASSERT(mPrefInitialized);
1464 mIsJavaScriptEnabled =
1465 Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
1466 sStrictFileOriginPolicy =
1467 Preferences::GetBool(sFileOriginPolicyPrefName, false);
1469 //
1470 // Rebuild the set of principals for which we allow file:// URI loads. This
1471 // implements a small subset of an old pref-based CAPS people that people
1472 // have come to depend on. See bug 995943.
1473 //
1475 mFileURIWhitelist.Clear();
1476 auto policies = mozilla::Preferences::GetCString("capability.policy.policynames");
1477 for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
1478 base < policies.Length();
1479 base = SkipPast<IsWhitespaceOrComma>(policies, bound))
1480 {
1481 // Grab the current policy name.
1482 bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
1483 auto policyName = Substring(policies, base, bound - base);
1485 // Figure out if this policy allows loading file:// URIs. If not, we can skip it.
1486 nsCString checkLoadURIPrefName = NS_LITERAL_CSTRING("capability.policy.") +
1487 policyName +
1488 NS_LITERAL_CSTRING(".checkloaduri.enabled");
1489 if (!Preferences::GetString(checkLoadURIPrefName.get()).LowerCaseEqualsLiteral("allaccess")) {
1490 continue;
1491 }
1493 // Grab the list of domains associated with this policy.
1494 nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
1495 policyName +
1496 NS_LITERAL_CSTRING(".sites");
1497 auto siteList = Preferences::GetCString(domainPrefName.get());
1498 AddSitesToFileURIWhitelist(siteList);
1499 }
1500 }
1502 void
1503 nsScriptSecurityManager::AddSitesToFileURIWhitelist(const nsCString& aSiteList)
1504 {
1505 for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0;
1506 base < aSiteList.Length();
1507 base = SkipPast<IsWhitespace>(aSiteList, bound))
1508 {
1509 // Grab the current site.
1510 bound = SkipUntil<IsWhitespace>(aSiteList, base);
1511 nsAutoCString site(Substring(aSiteList, base, bound - base));
1513 // Check if the URI is schemeless. If so, add both http and https.
1514 nsAutoCString unused;
1515 if (NS_FAILED(sIOService->ExtractScheme(site, unused))) {
1516 AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("http://") + site);
1517 AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("https://") + site);
1518 continue;
1519 }
1521 // Convert it to a URI and add it to our list.
1522 nsCOMPtr<nsIURI> uri;
1523 nsresult rv = NS_NewURI(getter_AddRefs(uri), site, nullptr, nullptr, sIOService);
1524 if (NS_SUCCEEDED(rv)) {
1525 mFileURIWhitelist.AppendElement(uri);
1526 } else {
1527 nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
1528 if (console) {
1529 nsAutoString msg = NS_LITERAL_STRING("Unable to to add site to file:// URI whitelist: ") +
1530 NS_ConvertASCIItoUTF16(site);
1531 console->LogStringMessage(msg.get());
1532 }
1533 }
1534 }
1535 }
1537 nsresult
1538 nsScriptSecurityManager::InitPrefs()
1539 {
1540 nsIPrefBranch* branch = Preferences::GetRootBranch();
1541 NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
1543 mPrefInitialized = true;
1545 // Set the initial value of the "javascript.enabled" prefs
1546 ScriptSecurityPrefChanged();
1548 // set observer callbacks in case the value of the prefs change
1549 Preferences::AddStrongObservers(this, kObservedPrefs);
1551 return NS_OK;
1552 }
1554 namespace mozilla {
1556 void
1557 GetJarPrefix(uint32_t aAppId, bool aInMozBrowser, nsACString& aJarPrefix)
1558 {
1559 MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
1561 if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
1562 aAppId = nsIScriptSecurityManager::NO_APP_ID;
1563 }
1565 aJarPrefix.Truncate();
1567 // Fallback.
1568 if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInMozBrowser) {
1569 return;
1570 }
1572 // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
1573 aJarPrefix.AppendInt(aAppId);
1574 aJarPrefix.Append('+');
1575 aJarPrefix.Append(aInMozBrowser ? 't' : 'f');
1576 aJarPrefix.Append('+');
1578 return;
1579 }
1581 } // namespace mozilla
1583 NS_IMETHODIMP
1584 nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId,
1585 bool aInMozBrowser,
1586 nsACString& aJarPrefix)
1587 {
1588 MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
1590 mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
1591 return NS_OK;
1592 }
1594 NS_IMETHODIMP
1595 nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
1596 {
1597 *aRv = !!mDomainPolicy;
1598 return NS_OK;
1599 }
1601 NS_IMETHODIMP
1602 nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv)
1603 {
1604 // We only allow one domain policy at a time. The holder of the previous
1605 // policy must explicitly deactivate it first.
1606 if (mDomainPolicy) {
1607 return NS_ERROR_SERVICE_NOT_AVAILABLE;
1608 }
1610 mDomainPolicy = new DomainPolicy();
1611 nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy;
1612 ptr.forget(aRv);
1613 return NS_OK;
1614 }
1616 // Intentionally non-scriptable. Script must have a reference to the
1617 // nsIDomainPolicy to deactivate it.
1618 void
1619 nsScriptSecurityManager::DeactivateDomainPolicy()
1620 {
1621 mDomainPolicy = nullptr;
1622 }
1624 NS_IMETHODIMP
1625 nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv)
1626 {
1627 nsresult rv;
1629 // Compute our rule. If we don't have any domain policy set up that might
1630 // provide exceptions to this rule, we're done.
1631 *aRv = mIsJavaScriptEnabled;
1632 if (!mDomainPolicy) {
1633 return NS_OK;
1634 }
1636 // We have a domain policy. Grab the appropriate set of exceptions to the
1637 // rule (either the blacklist or the whitelist, depending on whether script
1638 // is enabled or disabled by default).
1639 nsCOMPtr<nsIDomainSet> exceptions;
1640 nsCOMPtr<nsIDomainSet> superExceptions;
1641 if (*aRv) {
1642 mDomainPolicy->GetBlacklist(getter_AddRefs(exceptions));
1643 mDomainPolicy->GetSuperBlacklist(getter_AddRefs(superExceptions));
1644 } else {
1645 mDomainPolicy->GetWhitelist(getter_AddRefs(exceptions));
1646 mDomainPolicy->GetSuperWhitelist(getter_AddRefs(superExceptions));
1647 }
1649 bool contains;
1650 rv = exceptions->Contains(aURI, &contains);
1651 NS_ENSURE_SUCCESS(rv, rv);
1652 if (contains) {
1653 *aRv = !*aRv;
1654 return NS_OK;
1655 }
1656 rv = superExceptions->ContainsSuperDomain(aURI, &contains);
1657 NS_ENSURE_SUCCESS(rv, rv);
1658 if (contains) {
1659 *aRv = !*aRv;
1660 }
1662 return NS_OK;
1663 }