Backed out changeset 4c2bc5ae8f95 (bug 945562) for device image bustage.
[gecko.git] / caps / src / nsScriptSecurityManager.cpp
blob6223fbfa3946a2af7cd7afacb3d57c7d62c125eb
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/Util.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 "nsIProperties.h"
36 #include "nsDirectoryServiceDefs.h"
37 #include "nsIFile.h"
38 #include "nsIFileURL.h"
39 #include "nsIZipReader.h"
40 #include "nsIXPConnect.h"
41 #include "nsIScriptGlobalObject.h"
42 #include "nsPIDOMWindow.h"
43 #include "nsIDocShell.h"
44 #include "nsIPrompt.h"
45 #include "nsIWindowWatcher.h"
46 #include "nsIConsoleService.h"
47 #include "nsISecurityCheckedComponent.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 // Lazily initialized. Use the getter below.
83 static jsid sEnabledID = JSID_VOID;
84 static JS::HandleId
85 EnabledID()
87 if (sEnabledID != JSID_VOID)
88 return JS::HandleId::fromMarkedLocation(&sEnabledID);
89 AutoSafeJSContext cx;
90 sEnabledID = INTERNED_STRING_TO_JSID(cx, JS_InternString(cx, "enabled"));
91 return JS::HandleId::fromMarkedLocation(&sEnabledID);
94 bool
95 nsScriptSecurityManager::SubjectIsPrivileged()
97 JSContext *cx = GetCurrentJSContext();
98 if (cx && xpc::IsUniversalXPConnectEnabled(cx))
99 return true;
100 bool isSystem = false;
101 return NS_SUCCEEDED(SubjectPrincipalIsSystem(&isSystem)) && isSystem;
104 ///////////////////////////
105 // Convenience Functions //
106 ///////////////////////////
107 // Result of this function should not be freed.
108 static inline const PRUnichar *
109 IDToString(JSContext *cx, jsid id_)
111 JS::RootedId id(cx, id_);
112 if (JSID_IS_STRING(id))
113 return JS_GetInternedStringChars(JSID_TO_STRING(id));
115 JS::Rooted<JS::Value> idval(cx);
116 if (!JS_IdToValue(cx, id, idval.address()))
117 return nullptr;
118 JSString *str = JS::ToString(cx, idval);
119 if(!str)
120 return nullptr;
121 return JS_GetStringCharsZ(cx, str);
124 class nsAutoInPrincipalDomainOriginSetter {
125 public:
126 nsAutoInPrincipalDomainOriginSetter() {
127 ++sInPrincipalDomainOrigin;
129 ~nsAutoInPrincipalDomainOriginSetter() {
130 --sInPrincipalDomainOrigin;
132 static uint32_t sInPrincipalDomainOrigin;
134 uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin;
136 static
137 nsresult
138 GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin)
140 if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) {
141 // Allow a single recursive call to GetPrincipalDomainOrigin, since that
142 // might be happening on a different principal from the first call. But
143 // after that, cut off the recursion; it just indicates that something
144 // we're doing in this method causes us to reenter a security check here.
145 return NS_ERROR_NOT_AVAILABLE;
148 nsAutoInPrincipalDomainOriginSetter autoSetter;
150 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
151 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
153 nsAutoCString hostPort;
155 nsresult rv = uri->GetHostPort(hostPort);
156 if (NS_SUCCEEDED(rv)) {
157 nsAutoCString scheme;
158 rv = uri->GetScheme(scheme);
159 NS_ENSURE_SUCCESS(rv, rv);
160 aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
162 else {
163 // Some URIs (e.g., nsSimpleURI) don't support host. Just
164 // get the full spec.
165 rv = uri->GetSpec(aOrigin);
166 NS_ENSURE_SUCCESS(rv, rv);
169 return NS_OK;
172 static
173 nsresult
174 GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
175 nsACString& aOrigin)
178 nsCOMPtr<nsIURI> uri;
179 aPrincipal->GetDomain(getter_AddRefs(uri));
180 if (!uri) {
181 aPrincipal->GetURI(getter_AddRefs(uri));
183 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
185 return GetOriginFromURI(uri, aOrigin);
188 inline void SetPendingException(JSContext *cx, const char *aMsg)
190 JS_ReportError(cx, "%s", aMsg);
193 inline void SetPendingException(JSContext *cx, const PRUnichar *aMsg)
195 JS_ReportError(cx, "%hs", aMsg);
198 // DomainPolicy members
199 uint32_t DomainPolicy::sGeneration = 0;
201 // Helper class to get stuff from the ClassInfo and not waste extra time with
202 // virtual method calls for things it has already gotten
203 class ClassInfoData
205 public:
206 ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
207 : mClassInfo(aClassInfo),
208 mName(const_cast<char *>(aName)),
209 mDidGetFlags(false),
210 mMustFreeName(false)
214 ~ClassInfoData()
216 if (mMustFreeName)
217 nsMemory::Free(mName);
220 uint32_t GetFlags()
222 if (!mDidGetFlags) {
223 if (mClassInfo) {
224 nsresult rv = mClassInfo->GetFlags(&mFlags);
225 if (NS_FAILED(rv)) {
226 mFlags = 0;
228 } else {
229 mFlags = 0;
232 mDidGetFlags = true;
235 return mFlags;
238 bool IsDOMClass()
240 return !!(GetFlags() & nsIClassInfo::DOM_OBJECT);
243 const char* GetName()
245 if (!mName) {
246 if (mClassInfo) {
247 mClassInfo->GetClassDescription(&mName);
250 if (mName) {
251 mMustFreeName = true;
252 } else {
253 mName = const_cast<char *>("UnnamedClass");
257 return mName;
260 private:
261 nsIClassInfo *mClassInfo; // WEAK
262 uint32_t mFlags;
263 char *mName;
264 bool mDidGetFlags;
265 bool mMustFreeName;
268 JSContext *
269 nsScriptSecurityManager::GetCurrentJSContext()
271 // Get JSContext from stack.
272 return nsXPConnect::XPConnect()->GetCurrentJSContext();
275 JSContext *
276 nsScriptSecurityManager::GetSafeJSContext()
278 // Get JSContext from stack.
279 return nsXPConnect::XPConnect()->GetSafeJSContext();
282 /* static */
283 bool
284 nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
285 nsIURI* aTargetURI)
287 return NS_SecurityCompareURIs(aSourceURI, aTargetURI, sStrictFileOriginPolicy);
290 // SecurityHashURI is consistent with SecurityCompareURIs because NS_SecurityHashURI
291 // is consistent with NS_SecurityCompareURIs. See nsNetUtil.h.
292 uint32_t
293 nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI)
295 return NS_SecurityHashURI(aURI);
298 NS_IMETHODIMP
299 nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
300 nsIPrincipal** aPrincipal)
302 NS_PRECONDITION(aChannel, "Must have channel!");
303 nsCOMPtr<nsISupports> owner;
304 aChannel->GetOwner(getter_AddRefs(owner));
305 if (owner) {
306 CallQueryInterface(owner, aPrincipal);
307 if (*aPrincipal) {
308 return NS_OK;
312 // OK, get the principal from the URI. Make sure this does the same thing
313 // as nsDocument::Reset and XULDocument::StartDocumentLoad.
314 nsCOMPtr<nsIURI> uri;
315 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
316 NS_ENSURE_SUCCESS(rv, rv);
318 nsCOMPtr<nsIDocShell> docShell;
319 NS_QueryNotificationCallbacks(aChannel, docShell);
321 if (docShell) {
322 return GetDocShellCodebasePrincipal(uri, docShell, aPrincipal);
325 return GetCodebasePrincipalInternal(uri, UNKNOWN_APP_ID,
326 /* isInBrowserElement */ false, aPrincipal);
329 NS_IMETHODIMP
330 nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
331 bool* aIsSystem)
333 *aIsSystem = (aPrincipal == mSystemPrincipal);
334 return NS_OK;
337 NS_IMETHODIMP_(nsIPrincipal *)
338 nsScriptSecurityManager::GetCxSubjectPrincipal(JSContext *cx)
340 NS_ASSERTION(cx == GetCurrentJSContext(),
341 "Uh, cx is not the current JS context!");
343 nsresult rv = NS_ERROR_FAILURE;
344 nsIPrincipal *principal = GetSubjectPrincipal(cx, &rv);
345 if (NS_FAILED(rv))
346 return nullptr;
348 return principal;
351 ////////////////////
352 // Policy Storage //
353 ////////////////////
355 // Table of security levels
356 static bool
357 DeleteCapability(nsHashKey *aKey, void *aData, void* closure)
359 NS_Free(aData);
360 return true;
363 //-- Per-Domain Policy - applies to one or more protocols or hosts
364 struct DomainEntry
366 DomainEntry(const char* aOrigin,
367 DomainPolicy* aDomainPolicy) : mOrigin(aOrigin),
368 mDomainPolicy(aDomainPolicy),
369 mNext(nullptr)
371 mDomainPolicy->Hold();
374 ~DomainEntry()
376 mDomainPolicy->Drop();
379 bool Matches(const char *anOrigin)
381 int len = strlen(anOrigin);
382 int thisLen = mOrigin.Length();
383 if (len < thisLen)
384 return false;
385 if (mOrigin.RFindChar(':', thisLen-1, 1) != -1)
386 //-- Policy applies to all URLs of this scheme, compare scheme only
387 return mOrigin.EqualsIgnoreCase(anOrigin, thisLen);
389 //-- Policy applies to a particular host; compare domains
390 if (!mOrigin.Equals(anOrigin + (len - thisLen)))
391 return false;
392 if (len == thisLen)
393 return true;
394 char charBefore = anOrigin[len-thisLen-1];
395 return (charBefore == '.' || charBefore == ':' || charBefore == '/');
398 nsCString mOrigin;
399 DomainPolicy* mDomainPolicy;
400 DomainEntry* mNext;
403 static bool
404 DeleteDomainEntry(nsHashKey *aKey, void *aData, void* closure)
406 DomainEntry *entry = (DomainEntry*) aData;
409 DomainEntry *next = entry->mNext;
410 delete entry;
411 entry = next;
412 } while (entry);
413 return true;
416 /////////////////////////////
417 // nsScriptSecurityManager //
418 /////////////////////////////
420 ////////////////////////////////////
421 // Methods implementing ISupports //
422 ////////////////////////////////////
423 NS_IMPL_ISUPPORTS4(nsScriptSecurityManager,
424 nsIScriptSecurityManager,
425 nsIXPCSecurityManager,
426 nsIChannelEventSink,
427 nsIObserver)
429 ///////////////////////////////////////////////////
430 // Methods implementing nsIScriptSecurityManager //
431 ///////////////////////////////////////////////////
433 ///////////////// Security Checks /////////////////
435 bool
436 nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
438 // Get the security manager
439 nsScriptSecurityManager *ssm =
440 nsScriptSecurityManager::GetScriptSecurityManager();
442 NS_ASSERTION(ssm, "Failed to get security manager service");
443 if (!ssm)
444 return false;
446 nsresult rv;
447 nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
449 NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
450 if (NS_FAILED(rv))
451 return false; // Not just absence of principal, but failure.
453 if (!subjectPrincipal)
454 return true;
456 nsCOMPtr<nsIContentSecurityPolicy> csp;
457 rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
458 NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
460 // don't do anything unless there's a CSP
461 if (!csp)
462 return true;
464 bool evalOK = true;
465 bool reportViolation = false;
466 rv = csp->GetAllowsEval(&reportViolation, &evalOK);
468 if (NS_FAILED(rv))
470 NS_WARNING("CSP: failed to get allowsEval");
471 return true; // fail open to not break sites.
474 if (reportViolation) {
475 nsAutoString fileName;
476 unsigned lineNum = 0;
477 NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
479 JS::RootedScript script(cx);
480 if (JS_DescribeScriptedCaller(cx, &script, &lineNum)) {
481 if (const char *file = JS_GetScriptFilename(cx, script)) {
482 CopyUTF8toUTF16(nsDependentCString(file), fileName);
485 csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
486 fileName,
487 scriptSample,
488 lineNum,
489 EmptyString());
492 return evalOK;
496 bool
497 nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JS::Handle<JSObject*> obj,
498 JS::Handle<jsid> id, JSAccessMode mode,
499 JS::MutableHandle<JS::Value> vp)
501 // Get the security manager
502 nsScriptSecurityManager *ssm =
503 nsScriptSecurityManager::GetScriptSecurityManager();
505 NS_WARN_IF_FALSE(ssm, "Failed to get security manager service");
506 if (!ssm)
507 return false;
509 // Get the object being accessed. We protect these cases:
510 // 1. The Function.prototype.caller property's value, which might lead
511 // an attacker up a call-stack to a function or another object from
512 // a different trust domain.
513 // 2. A user-defined getter or setter function accessible on another
514 // trust domain's window or document object.
515 // vp can be a primitive, in that case, we use obj as the target
516 // object.
517 JSObject* target = JSVAL_IS_PRIMITIVE(vp) ? obj : JSVAL_TO_OBJECT(vp);
519 // Do the same-origin check -- this sets a JS exception if the check fails.
520 // Pass the parent object's class name, as we have no class-info for it.
521 nsresult rv =
522 ssm->CheckPropertyAccess(cx, target, js::GetObjectClass(obj)->name, id,
523 (mode & JSACC_WRITE) ?
524 (int32_t)nsIXPCSecurityManager::ACCESS_SET_PROPERTY :
525 (int32_t)nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
527 if (NS_FAILED(rv))
528 return false; // Security check failed (XXX was an error reported?)
530 return true;
533 NS_IMETHODIMP
534 nsScriptSecurityManager::CheckPropertyAccess(JSContext* cx,
535 JSObject* aJSObject,
536 const char* aClassName,
537 jsid aProperty,
538 uint32_t aAction)
540 return CheckPropertyAccessImpl(aAction, nullptr, cx, aJSObject,
541 nullptr, nullptr,
542 aClassName, aProperty, nullptr);
545 NS_IMETHODIMP
546 nsScriptSecurityManager::CheckSameOrigin(JSContext* cx,
547 nsIURI* aTargetURI)
549 nsresult rv;
551 // Get a context if necessary
552 if (!cx)
554 cx = GetCurrentJSContext();
555 if (!cx)
556 return NS_OK; // No JS context, so allow access
559 // Get a principal from the context
560 nsIPrincipal* sourcePrincipal = GetSubjectPrincipal(cx, &rv);
561 if (NS_FAILED(rv))
562 return rv;
564 if (!sourcePrincipal)
566 NS_WARNING("CheckSameOrigin called on script w/o principals; should this happen?");
567 return NS_OK;
570 if (sourcePrincipal == mSystemPrincipal)
572 // This is a system (chrome) script, so allow access
573 return NS_OK;
576 // Get the original URI from the source principal.
577 // This has the effect of ignoring any change to document.domain
578 // which must be done to avoid DNS spoofing (bug 154930)
579 nsCOMPtr<nsIURI> sourceURI;
580 sourcePrincipal->GetDomain(getter_AddRefs(sourceURI));
581 if (!sourceURI) {
582 sourcePrincipal->GetURI(getter_AddRefs(sourceURI));
583 NS_ENSURE_TRUE(sourceURI, NS_ERROR_FAILURE);
586 // Compare origins
587 if (!SecurityCompareURIs(sourceURI, aTargetURI))
589 ReportError(cx, NS_LITERAL_STRING("CheckSameOriginError"), sourceURI, aTargetURI);
590 return NS_ERROR_DOM_BAD_URI;
592 return NS_OK;
595 NS_IMETHODIMP
596 nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
597 nsIURI* aTargetURI,
598 bool reportError)
600 if (!SecurityCompareURIs(aSourceURI, aTargetURI))
602 if (reportError) {
603 ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"),
604 aSourceURI, aTargetURI);
606 return NS_ERROR_DOM_BAD_URI;
608 return NS_OK;
611 nsresult
612 nsScriptSecurityManager::CheckPropertyAccessImpl(uint32_t aAction,
613 nsAXPCNativeCallContext* aCallContext,
614 JSContext* cx, JSObject* aJSObject,
615 nsISupports* aObj,
616 nsIClassInfo* aClassInfo,
617 const char* aClassName, jsid aProperty,
618 void** aCachedClassPolicy)
620 nsresult rv;
621 JS::RootedObject jsObject(cx, aJSObject);
622 JS::RootedId property(cx, aProperty);
623 nsIPrincipal* subjectPrincipal = GetSubjectPrincipal(cx, &rv);
624 if (NS_FAILED(rv))
625 return rv;
627 if (!subjectPrincipal || subjectPrincipal == mSystemPrincipal)
628 // We have native code or the system principal: just allow access
629 return NS_OK;
631 nsCOMPtr<nsIPrincipal> objectPrincipal;
633 // Hold the class info data here so we don't have to go back to virtual
634 // methods all the time
635 ClassInfoData classInfoData(aClassInfo, aClassName);
637 //-- Look up the security policy for this class and subject domain
638 SecurityLevel securityLevel;
639 rv = LookupPolicy(subjectPrincipal, classInfoData, property, aAction,
640 (ClassPolicy**)aCachedClassPolicy, &securityLevel);
641 if (NS_FAILED(rv))
642 return rv;
644 if (securityLevel.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
646 // No policy found for this property so use the default of last resort.
647 // If we were called from somewhere other than XPConnect
648 // (no XPC call context), assume this is a DOM class. Otherwise,
649 // ask the ClassInfo.
650 if (!aCallContext || classInfoData.IsDOMClass())
651 securityLevel.level = SCRIPT_SECURITY_SAME_ORIGIN_ACCESS;
652 else
653 securityLevel.level = SCRIPT_SECURITY_NO_ACCESS;
656 if (SECURITY_ACCESS_LEVEL_FLAG(securityLevel))
657 // This flag means securityLevel is allAccess, noAccess, or sameOrigin
659 switch (securityLevel.level)
661 case SCRIPT_SECURITY_NO_ACCESS:
662 rv = NS_ERROR_DOM_PROP_ACCESS_DENIED;
663 break;
665 case SCRIPT_SECURITY_ALL_ACCESS:
666 rv = NS_OK;
667 break;
669 case SCRIPT_SECURITY_SAME_ORIGIN_ACCESS:
671 nsCOMPtr<nsIPrincipal> principalHolder;
672 if (jsObject)
674 objectPrincipal = doGetObjectPrincipal(jsObject);
675 if (!objectPrincipal)
676 rv = NS_ERROR_DOM_SECURITY_ERR;
678 else
680 NS_ERROR("CheckPropertyAccessImpl called without a target object or URL");
681 return NS_ERROR_FAILURE;
683 if(NS_SUCCEEDED(rv))
684 rv = CheckSameOriginDOMProp(subjectPrincipal, objectPrincipal,
685 aAction);
686 break;
688 default:
689 NS_ERROR("Bad Security Level Value");
690 return NS_ERROR_FAILURE;
693 else // if SECURITY_ACCESS_LEVEL_FLAG is false, securityLevel is a capability
695 rv = SubjectIsPrivileged() ? NS_OK : NS_ERROR_DOM_SECURITY_ERR;
698 if (NS_SUCCEEDED(rv))
700 return rv;
703 //--See if the object advertises a non-default level of access
704 // using nsISecurityCheckedComponent
705 nsCOMPtr<nsISecurityCheckedComponent> checkedComponent =
706 do_QueryInterface(aObj);
708 nsXPIDLCString objectSecurityLevel;
709 if (checkedComponent)
711 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
712 nsCOMPtr<nsIInterfaceInfo> interfaceInfo;
713 const nsIID* objIID = nullptr;
714 rv = aCallContext->GetCalleeWrapper(getter_AddRefs(wrapper));
715 if (NS_SUCCEEDED(rv) && wrapper)
716 rv = wrapper->FindInterfaceWithMember(property, getter_AddRefs(interfaceInfo));
717 if (NS_SUCCEEDED(rv) && interfaceInfo)
718 rv = interfaceInfo->GetIIDShared(&objIID);
719 if (NS_SUCCEEDED(rv) && objIID)
721 switch (aAction)
723 case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
724 checkedComponent->CanGetProperty(objIID,
725 IDToString(cx, property),
726 getter_Copies(objectSecurityLevel));
727 break;
728 case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
729 checkedComponent->CanSetProperty(objIID,
730 IDToString(cx, property),
731 getter_Copies(objectSecurityLevel));
732 break;
733 case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
734 checkedComponent->CanCallMethod(objIID,
735 IDToString(cx, property),
736 getter_Copies(objectSecurityLevel));
740 rv = CheckXPCPermissions(cx, aObj, jsObject, subjectPrincipal,
741 objectSecurityLevel);
743 if (NS_FAILED(rv)) //-- Security tests failed, access is denied, report error
745 nsAutoString stringName;
746 switch(aAction)
748 case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
749 stringName.AssignLiteral("GetPropertyDeniedOrigins");
750 break;
751 case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
752 stringName.AssignLiteral("SetPropertyDeniedOrigins");
753 break;
754 case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
755 stringName.AssignLiteral("CallMethodDeniedOrigins");
758 // Null out objectPrincipal for now, so we don't leak information about
759 // it. Whenever we can report different error strings to content and
760 // the UI we can take this out again.
761 objectPrincipal = nullptr;
763 NS_ConvertUTF8toUTF16 className(classInfoData.GetName());
764 nsAutoCString subjectOrigin;
765 nsAutoCString subjectDomain;
766 if (!nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin) {
767 nsCOMPtr<nsIURI> uri, domain;
768 subjectPrincipal->GetURI(getter_AddRefs(uri));
769 if (uri) { // Object principal might be expanded
770 GetOriginFromURI(uri, subjectOrigin);
772 subjectPrincipal->GetDomain(getter_AddRefs(domain));
773 if (domain) {
774 GetOriginFromURI(domain, subjectDomain);
776 } else {
777 subjectOrigin.AssignLiteral("the security manager");
779 NS_ConvertUTF8toUTF16 subjectOriginUnicode(subjectOrigin);
780 NS_ConvertUTF8toUTF16 subjectDomainUnicode(subjectDomain);
782 nsAutoCString objectOrigin;
783 nsAutoCString objectDomain;
784 if (!nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin &&
785 objectPrincipal) {
786 nsCOMPtr<nsIURI> uri, domain;
787 objectPrincipal->GetURI(getter_AddRefs(uri));
788 if (uri) { // Object principal might be system
789 GetOriginFromURI(uri, objectOrigin);
791 objectPrincipal->GetDomain(getter_AddRefs(domain));
792 if (domain) {
793 GetOriginFromURI(domain, objectDomain);
796 NS_ConvertUTF8toUTF16 objectOriginUnicode(objectOrigin);
797 NS_ConvertUTF8toUTF16 objectDomainUnicode(objectDomain);
799 nsXPIDLString errorMsg;
800 const PRUnichar *formatStrings[] =
802 subjectOriginUnicode.get(),
803 className.get(),
804 IDToString(cx, property),
805 objectOriginUnicode.get(),
806 subjectDomainUnicode.get(),
807 objectDomainUnicode.get()
810 uint32_t length = ArrayLength(formatStrings);
812 // XXXbz Our localization system is stupid and can't handle not showing
813 // some strings that get passed in. Which means that we have to get
814 // our length precisely right: it has to be exactly the number of
815 // strings our format string wants. This means we'll have to move
816 // strings in the array as needed, sadly...
817 if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin ||
818 !objectPrincipal) {
819 stringName.AppendLiteral("OnlySubject");
820 length -= 3;
821 } else {
822 // default to a length that doesn't include the domains, then
823 // increase it as needed.
824 length -= 2;
825 if (!subjectDomainUnicode.IsEmpty()) {
826 stringName.AppendLiteral("SubjectDomain");
827 length += 1;
829 if (!objectDomainUnicode.IsEmpty()) {
830 stringName.AppendLiteral("ObjectDomain");
831 length += 1;
832 if (length != ArrayLength(formatStrings)) {
833 // We have an object domain but not a subject domain.
834 // Scoot our string over one slot. See the XXX comment
835 // above for why we need to do this.
836 formatStrings[length-1] = formatStrings[length];
841 // We need to keep our existing failure rv and not override it
842 // with a likely success code from the following string bundle
843 // call in order to throw the correct security exception later.
844 nsresult rv2 = sStrBundle->FormatStringFromName(stringName.get(),
845 formatStrings,
846 length,
847 getter_Copies(errorMsg));
848 if (NS_FAILED(rv2)) {
849 // Might just be missing the string... Do our best
850 errorMsg = stringName;
853 SetPendingException(cx, errorMsg.get());
856 return rv;
859 /* static */
860 nsresult
861 nsScriptSecurityManager::CheckSameOriginPrincipal(nsIPrincipal* aSubject,
862 nsIPrincipal* aObject)
865 ** Get origin of subject and object and compare.
867 if (aSubject == aObject)
868 return NS_OK;
870 if (!AppAttributesEqual(aSubject, aObject)) {
871 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
874 // Default to false, and change if that turns out wrong.
875 bool subjectSetDomain = false;
876 bool objectSetDomain = false;
878 nsCOMPtr<nsIURI> subjectURI;
879 nsCOMPtr<nsIURI> objectURI;
881 aSubject->GetDomain(getter_AddRefs(subjectURI));
882 if (!subjectURI) {
883 aSubject->GetURI(getter_AddRefs(subjectURI));
884 } else {
885 subjectSetDomain = true;
888 aObject->GetDomain(getter_AddRefs(objectURI));
889 if (!objectURI) {
890 aObject->GetURI(getter_AddRefs(objectURI));
891 } else {
892 objectSetDomain = true;
895 if (SecurityCompareURIs(subjectURI, objectURI))
896 { // If either the subject or the object has changed its principal by
897 // explicitly setting document.domain then the other must also have
898 // done so in order to be considered the same origin. This prevents
899 // DNS spoofing based on document.domain (154930)
901 // If both or neither explicitly set their domain, allow the access
902 if (subjectSetDomain == objectSetDomain)
903 return NS_OK;
907 ** Access tests failed, so now report error.
909 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
912 // It's important that
914 // CheckSameOriginPrincipal(A, B) == NS_OK
916 // imply
918 // HashPrincipalByOrigin(A) == HashPrincipalByOrigin(B)
920 // if principals A and B could ever be used as keys in a hashtable.
921 // Violation of this invariant leads to spurious failures of hashtable
922 // lookups. See bug 454850.
924 /*static*/ uint32_t
925 nsScriptSecurityManager::HashPrincipalByOrigin(nsIPrincipal* aPrincipal)
927 nsCOMPtr<nsIURI> uri;
928 aPrincipal->GetDomain(getter_AddRefs(uri));
929 if (!uri)
930 aPrincipal->GetURI(getter_AddRefs(uri));
931 return SecurityHashURI(uri);
934 /* static */ bool
935 nsScriptSecurityManager::AppAttributesEqual(nsIPrincipal* aFirst,
936 nsIPrincipal* aSecond)
938 MOZ_ASSERT(aFirst && aSecond, "Don't pass null pointers!");
940 uint32_t firstAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
941 if (!aFirst->GetUnknownAppId()) {
942 firstAppId = aFirst->GetAppId();
945 uint32_t secondAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
946 if (!aSecond->GetUnknownAppId()) {
947 secondAppId = aSecond->GetAppId();
950 return ((firstAppId == secondAppId) &&
951 (aFirst->GetIsInBrowserElement() == aSecond->GetIsInBrowserElement()));
954 nsresult
955 nsScriptSecurityManager::CheckSameOriginDOMProp(nsIPrincipal* aSubject,
956 nsIPrincipal* aObject,
957 uint32_t aAction)
959 nsresult rv;
960 bool subsumes;
961 rv = aSubject->Subsumes(aObject, &subsumes);
962 if (NS_SUCCEEDED(rv) && !subsumes) {
963 rv = NS_ERROR_DOM_PROP_ACCESS_DENIED;
966 if (NS_SUCCEEDED(rv))
967 return NS_OK;
970 * Content can't ever touch chrome (we check for UniversalXPConnect later)
972 if (aObject == mSystemPrincipal)
973 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
976 ** Access tests failed, so now report error.
978 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
981 nsresult
982 nsScriptSecurityManager::LookupPolicy(nsIPrincipal* aPrincipal,
983 ClassInfoData& aClassData,
984 JS::Handle<jsid> aProperty,
985 uint32_t aAction,
986 ClassPolicy** aCachedClassPolicy,
987 SecurityLevel* result)
989 AutoJSContext cx;
990 nsresult rv;
991 JS::RootedId property(cx, aProperty);
992 result->level = SCRIPT_SECURITY_UNDEFINED_ACCESS;
994 DomainPolicy* dpolicy = nullptr;
995 //-- Initialize policies if necessary
996 if (mPolicyPrefsChanged)
998 if (!mPrefInitialized) {
999 rv = InitPrefs();
1000 NS_ENSURE_SUCCESS(rv, rv);
1002 rv = InitPolicies();
1003 if (NS_FAILED(rv))
1004 return rv;
1006 else
1008 aPrincipal->GetSecurityPolicy((void**)&dpolicy);
1011 if (!dpolicy && mOriginToPolicyMap)
1013 //-- Look up the relevant domain policy, if any
1014 if (nsCOMPtr<nsIExpandedPrincipal> exp = do_QueryInterface(aPrincipal))
1016 // For expanded principals domain origin is not defined so let's just
1017 // use the default policy
1018 dpolicy = mDefaultPolicy;
1020 else
1022 nsAutoCString origin;
1023 rv = GetPrincipalDomainOrigin(aPrincipal, origin);
1024 NS_ENSURE_SUCCESS(rv, rv);
1026 char *start = origin.BeginWriting();
1027 const char *nextToLastDot = nullptr;
1028 const char *lastDot = nullptr;
1029 const char *colon = nullptr;
1030 char *p = start;
1032 //-- search domain (stop at the end of the string or at the 3rd slash)
1033 for (uint32_t slashes=0; *p; p++)
1035 if (*p == '/' && ++slashes == 3)
1037 *p = '\0'; // truncate at 3rd slash
1038 break;
1040 if (*p == '.')
1042 nextToLastDot = lastDot;
1043 lastDot = p;
1045 else if (!colon && *p == ':')
1046 colon = p;
1049 nsCStringKey key(nextToLastDot ? nextToLastDot+1 : start);
1050 DomainEntry *de = (DomainEntry*) mOriginToPolicyMap->Get(&key);
1051 if (!de)
1053 nsAutoCString scheme(start, colon-start+1);
1054 nsCStringKey schemeKey(scheme);
1055 de = (DomainEntry*) mOriginToPolicyMap->Get(&schemeKey);
1058 while (de)
1060 if (de->Matches(start))
1062 dpolicy = de->mDomainPolicy;
1063 break;
1065 de = de->mNext;
1068 if (!dpolicy)
1069 dpolicy = mDefaultPolicy;
1072 aPrincipal->SetSecurityPolicy((void*)dpolicy);
1075 ClassPolicy* cpolicy = nullptr;
1077 if ((dpolicy == mDefaultPolicy) && aCachedClassPolicy)
1079 // No per-domain policy for this principal (the more common case)
1080 // so look for a cached class policy from the object wrapper
1081 cpolicy = *aCachedClassPolicy;
1084 if (!cpolicy)
1085 { //-- No cached policy for this class, need to look it up
1086 cpolicy = static_cast<ClassPolicy*>
1087 (PL_DHashTableOperate(dpolicy,
1088 aClassData.GetName(),
1089 PL_DHASH_LOOKUP));
1091 if (PL_DHASH_ENTRY_IS_FREE(cpolicy))
1092 cpolicy = NO_POLICY_FOR_CLASS;
1094 if ((dpolicy == mDefaultPolicy) && aCachedClassPolicy)
1095 *aCachedClassPolicy = cpolicy;
1098 NS_ASSERTION(JSID_IS_INT(property) || JSID_IS_OBJECT(property) ||
1099 JSID_IS_STRING(property), "Property must be a valid id");
1101 // Only atomized strings are stored in the policies' hash tables.
1102 if (!JSID_IS_STRING(property))
1103 return NS_OK;
1105 JS::RootedString propertyKey(cx, JSID_TO_STRING(property));
1107 // We look for a PropertyPolicy in the following places:
1108 // 1) The ClassPolicy for our class we got from our DomainPolicy
1109 // 2) The mWildcardPolicy of our DomainPolicy
1110 // 3) The ClassPolicy for our class we got from mDefaultPolicy
1111 // 4) The mWildcardPolicy of our mDefaultPolicy
1112 PropertyPolicy* ppolicy = nullptr;
1113 if (cpolicy != NO_POLICY_FOR_CLASS)
1115 ppolicy = static_cast<PropertyPolicy*>
1116 (PL_DHashTableOperate(cpolicy->mPolicy,
1117 propertyKey,
1118 PL_DHASH_LOOKUP));
1121 // If there is no class policy for this property, and we have a wildcard
1122 // policy, try that.
1123 if (dpolicy->mWildcardPolicy &&
1124 (!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy)))
1126 ppolicy =
1127 static_cast<PropertyPolicy*>
1128 (PL_DHashTableOperate(dpolicy->mWildcardPolicy->mPolicy,
1129 propertyKey,
1130 PL_DHASH_LOOKUP));
1133 // If dpolicy is not the defauly policy and there's no class or wildcard
1134 // policy for this property, check the default policy for this class and
1135 // the default wildcard policy
1136 if (dpolicy != mDefaultPolicy &&
1137 (!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy)))
1139 cpolicy = static_cast<ClassPolicy*>
1140 (PL_DHashTableOperate(mDefaultPolicy,
1141 aClassData.GetName(),
1142 PL_DHASH_LOOKUP));
1144 if (PL_DHASH_ENTRY_IS_BUSY(cpolicy))
1146 ppolicy =
1147 static_cast<PropertyPolicy*>
1148 (PL_DHashTableOperate(cpolicy->mPolicy,
1149 propertyKey,
1150 PL_DHASH_LOOKUP));
1153 if ((!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy)) &&
1154 mDefaultPolicy->mWildcardPolicy)
1156 ppolicy =
1157 static_cast<PropertyPolicy*>
1158 (PL_DHashTableOperate(mDefaultPolicy->mWildcardPolicy->mPolicy,
1159 propertyKey,
1160 PL_DHASH_LOOKUP));
1164 if (!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy))
1165 return NS_OK;
1167 // Get the correct security level from the property policy
1168 if (aAction == nsIXPCSecurityManager::ACCESS_SET_PROPERTY)
1169 *result = ppolicy->mSet;
1170 else
1171 *result = ppolicy->mGet;
1173 return NS_OK;
1177 NS_IMETHODIMP
1178 nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
1180 // Get principal of currently executing script.
1181 nsresult rv;
1182 nsIPrincipal* principal = GetSubjectPrincipal(cx, &rv);
1183 if (NS_FAILED(rv))
1184 return rv;
1186 // Native code can load all URIs.
1187 if (!principal)
1188 return NS_OK;
1190 rv = CheckLoadURIWithPrincipal(principal, aURI,
1191 nsIScriptSecurityManager::STANDARD);
1192 if (NS_SUCCEEDED(rv)) {
1193 // OK to load
1194 return NS_OK;
1197 // See if we're attempting to load a file: URI. If so, let a
1198 // UniversalXPConnect capability trump the above check.
1199 bool isFile = false;
1200 bool isRes = false;
1201 if (NS_FAILED(aURI->SchemeIs("file", &isFile)) ||
1202 NS_FAILED(aURI->SchemeIs("resource", &isRes)))
1203 return NS_ERROR_FAILURE;
1204 if (isFile || isRes)
1206 if (SubjectIsPrivileged())
1207 return NS_OK;
1210 // Report error.
1211 nsAutoCString spec;
1212 if (NS_FAILED(aURI->GetAsciiSpec(spec)))
1213 return NS_ERROR_FAILURE;
1214 nsAutoCString msg("Access to '");
1215 msg.Append(spec);
1216 msg.AppendLiteral("' from script denied");
1217 SetPendingException(cx, msg.get());
1218 return NS_ERROR_DOM_BAD_URI;
1222 * Helper method to handle cases where a flag passed to
1223 * CheckLoadURIWithPrincipal means denying loading if the given URI has certain
1224 * nsIProtocolHandler flags set.
1225 * @return if success, access is allowed. Otherwise, deny access
1227 static nsresult
1228 DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags)
1230 NS_PRECONDITION(aURI, "Must have URI!");
1232 bool uriHasFlags;
1233 nsresult rv =
1234 NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags);
1235 NS_ENSURE_SUCCESS(rv, rv);
1237 if (uriHasFlags) {
1238 return NS_ERROR_DOM_BAD_URI;
1241 return NS_OK;
1244 NS_IMETHODIMP
1245 nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
1246 nsIURI *aTargetURI,
1247 uint32_t aFlags)
1249 NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
1250 // If someone passes a flag that we don't understand, we should
1251 // fail, because they may need a security check that we don't
1252 // provide.
1253 NS_ENSURE_FALSE(aFlags & ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
1254 nsIScriptSecurityManager::ALLOW_CHROME |
1255 nsIScriptSecurityManager::DISALLOW_SCRIPT |
1256 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
1257 nsIScriptSecurityManager::DONT_REPORT_ERRORS),
1258 NS_ERROR_UNEXPECTED);
1259 NS_ENSURE_ARG_POINTER(aPrincipal);
1260 NS_ENSURE_ARG_POINTER(aTargetURI);
1262 // If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which
1263 // would do such inheriting. That would be URIs that do not have their own
1264 // security context. We do this even for the system principal.
1265 if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) {
1266 nsresult rv =
1267 DenyAccessIfURIHasFlags(aTargetURI,
1268 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT);
1269 NS_ENSURE_SUCCESS(rv, rv);
1272 if (aPrincipal == mSystemPrincipal) {
1273 // Allow access
1274 return NS_OK;
1277 nsCOMPtr<nsIURI> sourceURI;
1278 aPrincipal->GetURI(getter_AddRefs(sourceURI));
1279 if (!sourceURI) {
1280 nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aPrincipal);
1281 if (expanded) {
1282 nsTArray< nsCOMPtr<nsIPrincipal> > *whiteList;
1283 expanded->GetWhiteList(&whiteList);
1284 for (uint32_t i = 0; i < whiteList->Length(); ++i) {
1285 nsresult rv = CheckLoadURIWithPrincipal((*whiteList)[i],
1286 aTargetURI,
1287 aFlags);
1288 if (NS_SUCCEEDED(rv)) {
1289 // Allow access if it succeeded with one of the white listed principals
1290 return NS_OK;
1293 // None of our whitelisted principals worked.
1294 return NS_ERROR_DOM_BAD_URI;
1296 NS_ERROR("Non-system principals or expanded principal passed to CheckLoadURIWithPrincipal "
1297 "must have a URI!");
1298 return NS_ERROR_UNEXPECTED;
1301 // Automatic loads are not allowed from certain protocols.
1302 if (aFlags & nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) {
1303 nsresult rv =
1304 DenyAccessIfURIHasFlags(sourceURI,
1305 nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT);
1306 NS_ENSURE_SUCCESS(rv, rv);
1309 // If either URI is a nested URI, get the base URI
1310 nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI);
1311 nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
1313 //-- get the target scheme
1314 nsAutoCString targetScheme;
1315 nsresult rv = targetBaseURI->GetScheme(targetScheme);
1316 if (NS_FAILED(rv)) return rv;
1318 //-- Some callers do not allow loading javascript:
1319 if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) &&
1320 targetScheme.EqualsLiteral("javascript"))
1322 return NS_ERROR_DOM_BAD_URI;
1325 NS_NAMED_LITERAL_STRING(errorTag, "CheckLoadURIError");
1326 bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS);
1328 // Check for uris that are only loadable by principals that subsume them
1329 bool hasFlags;
1330 rv = NS_URIChainHasFlags(targetBaseURI,
1331 nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
1332 &hasFlags);
1333 NS_ENSURE_SUCCESS(rv, rv);
1335 if (hasFlags) {
1336 return aPrincipal->CheckMayLoad(targetBaseURI, true, false);
1339 //-- get the source scheme
1340 nsAutoCString sourceScheme;
1341 rv = sourceBaseURI->GetScheme(sourceScheme);
1342 if (NS_FAILED(rv)) return rv;
1344 if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
1345 // A null principal can target its own URI.
1346 if (sourceURI == aTargetURI) {
1347 return NS_OK;
1350 else if (targetScheme.Equals(sourceScheme,
1351 nsCaseInsensitiveCStringComparator()))
1353 // every scheme can access another URI from the same scheme,
1354 // as long as they don't represent null principals...
1355 // Or they don't require an special permission to do so
1356 // See bug#773886
1358 bool hasFlags;
1359 rv = NS_URIChainHasFlags(targetBaseURI,
1360 nsIProtocolHandler::URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM,
1361 &hasFlags);
1362 NS_ENSURE_SUCCESS(rv, rv);
1364 if (hasFlags) {
1365 // In this case, we allow opening only if the source and target URIS
1366 // are on the same domain, or the opening URI has the webapps
1367 // permision granted
1368 if (!SecurityCompareURIs(sourceBaseURI,targetBaseURI) &&
1369 !nsContentUtils::IsExactSitePermAllow(aPrincipal,WEBAPPS_PERM_NAME)){
1370 return NS_ERROR_DOM_BAD_URI;
1373 return NS_OK;
1376 // If the schemes don't match, the policy is specified by the protocol
1377 // flags on the target URI. Note that the order of policy checks here is
1378 // very important! We start from most restrictive and work our way down.
1379 // Note that since we're working with the innermost URI, we can just use
1380 // the methods that work on chains of nested URIs and they will only look
1381 // at the flags for our one URI.
1383 // Check for system target URI
1384 rv = DenyAccessIfURIHasFlags(targetBaseURI,
1385 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
1386 if (NS_FAILED(rv)) {
1387 // Deny access, since the origin principal is not system
1388 if (reportErrors) {
1389 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
1391 return rv;
1394 // Check for chrome target URI
1395 rv = NS_URIChainHasFlags(targetBaseURI,
1396 nsIProtocolHandler::URI_IS_UI_RESOURCE,
1397 &hasFlags);
1398 NS_ENSURE_SUCCESS(rv, rv);
1399 if (hasFlags) {
1400 if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
1401 if (!targetScheme.EqualsLiteral("chrome")) {
1402 // for now don't change behavior for resource: or moz-icon:
1403 return NS_OK;
1406 // allow load only if chrome package is whitelisted
1407 nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(
1408 NS_CHROMEREGISTRY_CONTRACTID));
1409 if (reg) {
1410 bool accessAllowed = false;
1411 reg->AllowContentToAccess(targetBaseURI, &accessAllowed);
1412 if (accessAllowed) {
1413 return NS_OK;
1418 // resource: and chrome: are equivalent, securitywise
1419 // That's bogus!! Fix this. But watch out for
1420 // the view-source stylesheet?
1421 bool sourceIsChrome;
1422 rv = NS_URIChainHasFlags(sourceBaseURI,
1423 nsIProtocolHandler::URI_IS_UI_RESOURCE,
1424 &sourceIsChrome);
1425 NS_ENSURE_SUCCESS(rv, rv);
1426 if (sourceIsChrome) {
1427 return NS_OK;
1429 if (reportErrors) {
1430 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
1432 return NS_ERROR_DOM_BAD_URI;
1435 // Check for target URI pointing to a file
1436 rv = NS_URIChainHasFlags(targetBaseURI,
1437 nsIProtocolHandler::URI_IS_LOCAL_FILE,
1438 &hasFlags);
1439 NS_ENSURE_SUCCESS(rv, rv);
1440 if (hasFlags) {
1441 // resource: and chrome: are equivalent, securitywise
1442 // That's bogus!! Fix this. But watch out for
1443 // the view-source stylesheet?
1444 bool sourceIsChrome;
1445 rv = NS_URIChainHasFlags(sourceURI,
1446 nsIProtocolHandler::URI_IS_UI_RESOURCE,
1447 &sourceIsChrome);
1448 NS_ENSURE_SUCCESS(rv, rv);
1449 if (sourceIsChrome) {
1450 return NS_OK;
1453 // Now check capability policies
1454 static const char loadURIPrefGroup[] = "checkloaduri";
1455 ClassInfoData nameData(nullptr, loadURIPrefGroup);
1457 SecurityLevel secLevel;
1458 rv = LookupPolicy(aPrincipal, nameData, EnabledID(),
1459 nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
1460 nullptr, &secLevel);
1461 if (NS_SUCCEEDED(rv) && secLevel.level == SCRIPT_SECURITY_ALL_ACCESS)
1463 // OK for this site!
1464 return NS_OK;
1467 if (reportErrors) {
1468 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
1470 return NS_ERROR_DOM_BAD_URI;
1473 // OK, everyone is allowed to load this, since unflagged handlers are
1474 // deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
1475 // need to warn. At some point we'll want to make this warning into an
1476 // error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
1477 rv = NS_URIChainHasFlags(targetBaseURI,
1478 nsIProtocolHandler::URI_LOADABLE_BY_ANYONE,
1479 &hasFlags);
1480 NS_ENSURE_SUCCESS(rv, rv);
1481 if (!hasFlags) {
1482 nsXPIDLString message;
1483 NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
1484 const PRUnichar* formatStrings[] = { ucsTargetScheme.get() };
1485 rv = sStrBundle->
1486 FormatStringFromName(NS_LITERAL_STRING("ProtocolFlagError").get(),
1487 formatStrings,
1488 ArrayLength(formatStrings),
1489 getter_Copies(message));
1490 if (NS_SUCCEEDED(rv)) {
1491 nsCOMPtr<nsIConsoleService> console(
1492 do_GetService("@mozilla.org/consoleservice;1"));
1493 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
1495 console->LogStringMessage(message.get());
1499 return NS_OK;
1502 nsresult
1503 nsScriptSecurityManager::ReportError(JSContext* cx, const nsAString& messageTag,
1504 nsIURI* aSource, nsIURI* aTarget)
1506 nsresult rv;
1507 NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
1509 // Get the source URL spec
1510 nsAutoCString sourceSpec;
1511 rv = aSource->GetAsciiSpec(sourceSpec);
1512 NS_ENSURE_SUCCESS(rv, rv);
1514 // Get the target URL spec
1515 nsAutoCString targetSpec;
1516 rv = aTarget->GetAsciiSpec(targetSpec);
1517 NS_ENSURE_SUCCESS(rv, rv);
1519 // Localize the error message
1520 nsXPIDLString message;
1521 NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec);
1522 NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
1523 const PRUnichar *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() };
1524 rv = sStrBundle->FormatStringFromName(PromiseFlatString(messageTag).get(),
1525 formatStrings,
1526 ArrayLength(formatStrings),
1527 getter_Copies(message));
1528 NS_ENSURE_SUCCESS(rv, rv);
1530 // If a JS context was passed in, set a JS exception.
1531 // Otherwise, print the error message directly to the JS console
1532 // and to standard output
1533 if (cx)
1535 SetPendingException(cx, message.get());
1537 else // Print directly to the console
1539 nsCOMPtr<nsIConsoleService> console(
1540 do_GetService("@mozilla.org/consoleservice;1"));
1541 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
1543 console->LogStringMessage(message.get());
1545 return NS_OK;
1548 NS_IMETHODIMP
1549 nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
1550 const nsACString& aTargetURIStr,
1551 uint32_t aFlags)
1553 nsresult rv;
1554 nsCOMPtr<nsIURI> target;
1555 rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr,
1556 nullptr, nullptr, sIOService);
1557 NS_ENSURE_SUCCESS(rv, rv);
1559 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
1560 if (rv == NS_ERROR_DOM_BAD_URI) {
1561 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
1562 // return values.
1563 return rv;
1565 NS_ENSURE_SUCCESS(rv, rv);
1567 // Now start testing fixup -- since aTargetURIStr is a string, not
1568 // an nsIURI, we may well end up fixing it up before loading.
1569 // Note: This needs to stay in sync with the nsIURIFixup api.
1570 nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
1571 if (!fixup) {
1572 return rv;
1575 uint32_t flags[] = {
1576 nsIURIFixup::FIXUP_FLAG_NONE,
1577 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
1578 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
1579 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
1580 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI
1583 for (uint32_t i = 0; i < ArrayLength(flags); ++i) {
1584 rv = fixup->CreateFixupURI(aTargetURIStr, flags[i], nullptr,
1585 getter_AddRefs(target));
1586 NS_ENSURE_SUCCESS(rv, rv);
1588 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
1589 if (rv == NS_ERROR_DOM_BAD_URI) {
1590 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
1591 // return values.
1592 return rv;
1594 NS_ENSURE_SUCCESS(rv, rv);
1597 return rv;
1600 bool
1601 nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
1603 MOZ_ASSERT(aGlobal);
1604 MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
1605 AutoJSContext cx;
1606 JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
1608 // Check the bits on the compartment private.
1609 xpc::Scriptability& scriptability = xpc::Scriptability::Get(aGlobal);
1610 if (!scriptability.Allowed()) {
1611 return false;
1614 // If the compartment is immune to script policy, we're done.
1615 if (scriptability.IsImmuneToScriptPolicy()) {
1616 return true;
1619 // Check for a per-site policy.
1620 static const char jsPrefGroupName[] = "javascript";
1621 ClassInfoData nameData(nullptr, jsPrefGroupName);
1622 SecurityLevel secLevel;
1623 nsresult rv = LookupPolicy(doGetObjectPrincipal(global), nameData,
1624 EnabledID(),
1625 nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
1626 nullptr, &secLevel);
1627 if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS) {
1628 return false;
1631 return true;
1634 ///////////////// Principals ///////////////////////
1635 NS_IMETHODIMP
1636 nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **aSubjectPrincipal)
1638 nsresult rv;
1639 *aSubjectPrincipal = doGetSubjectPrincipal(&rv);
1640 if (NS_SUCCEEDED(rv))
1641 NS_IF_ADDREF(*aSubjectPrincipal);
1642 return rv;
1645 nsIPrincipal*
1646 nsScriptSecurityManager::doGetSubjectPrincipal(nsresult* rv)
1648 NS_PRECONDITION(rv, "Null out param");
1649 JSContext *cx = GetCurrentJSContext();
1650 if (!cx)
1652 *rv = NS_OK;
1653 return nullptr;
1655 return GetSubjectPrincipal(cx, rv);
1658 NS_IMETHODIMP
1659 nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
1661 NS_ADDREF(*result = mSystemPrincipal);
1663 return NS_OK;
1666 NS_IMETHODIMP
1667 nsScriptSecurityManager::SubjectPrincipalIsSystem(bool* aIsSystem)
1669 NS_ENSURE_ARG_POINTER(aIsSystem);
1670 *aIsSystem = false;
1672 if (!mSystemPrincipal)
1673 return NS_OK;
1675 nsCOMPtr<nsIPrincipal> subject;
1676 nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject));
1677 if (NS_FAILED(rv))
1678 return rv;
1680 if(!subject)
1682 // No subject principal means no JS is running;
1683 // this is the equivalent of system principal code
1684 *aIsSystem = true;
1685 return NS_OK;
1688 return mSystemPrincipal->Equals(subject, aIsSystem);
1691 nsresult
1692 nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId,
1693 bool aInMozBrowser,
1694 nsIPrincipal **result)
1696 // I _think_ it's safe to not create null principals here based on aURI.
1697 // At least all the callers would do the right thing in those cases, as far
1698 // as I can tell. --bz
1700 nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
1701 if (uriPrinc) {
1702 nsCOMPtr<nsIPrincipal> principal;
1703 uriPrinc->GetPrincipal(getter_AddRefs(principal));
1704 if (!principal || principal == mSystemPrincipal) {
1705 return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
1708 principal.forget(result);
1710 return NS_OK;
1713 nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
1714 if (!codebase)
1715 return NS_ERROR_OUT_OF_MEMORY;
1717 nsresult rv = codebase->Init(aURI, aAppId, aInMozBrowser);
1718 if (NS_FAILED(rv))
1719 return rv;
1721 NS_ADDREF(*result = codebase);
1723 return NS_OK;
1726 NS_IMETHODIMP
1727 nsScriptSecurityManager::GetSimpleCodebasePrincipal(nsIURI* aURI,
1728 nsIPrincipal** aPrincipal)
1730 return GetCodebasePrincipalInternal(aURI,
1731 nsIScriptSecurityManager::UNKNOWN_APP_ID,
1732 false, aPrincipal);
1735 NS_IMETHODIMP
1736 nsScriptSecurityManager::GetNoAppCodebasePrincipal(nsIURI* aURI,
1737 nsIPrincipal** aPrincipal)
1739 return GetCodebasePrincipalInternal(aURI, nsIScriptSecurityManager::NO_APP_ID,
1740 false, aPrincipal);
1743 NS_IMETHODIMP
1744 nsScriptSecurityManager::GetCodebasePrincipal(nsIURI* aURI,
1745 nsIPrincipal** aPrincipal)
1747 return GetNoAppCodebasePrincipal(aURI, aPrincipal);
1750 NS_IMETHODIMP
1751 nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI,
1752 uint32_t aAppId,
1753 bool aInMozBrowser,
1754 nsIPrincipal** aPrincipal)
1756 NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
1757 NS_ERROR_INVALID_ARG);
1759 return GetCodebasePrincipalInternal(aURI, aAppId, aInMozBrowser, aPrincipal);
1762 NS_IMETHODIMP
1763 nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
1764 nsIDocShell* aDocShell,
1765 nsIPrincipal** aPrincipal)
1767 return GetCodebasePrincipalInternal(aURI,
1768 aDocShell->GetAppId(),
1769 aDocShell->GetIsInBrowserElement(),
1770 aPrincipal);
1773 nsresult
1774 nsScriptSecurityManager::GetCodebasePrincipalInternal(nsIURI *aURI,
1775 uint32_t aAppId,
1776 bool aInMozBrowser,
1777 nsIPrincipal **result)
1779 NS_ENSURE_ARG(aURI);
1781 bool inheritsPrincipal;
1782 nsresult rv =
1783 NS_URIChainHasFlags(aURI,
1784 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
1785 &inheritsPrincipal);
1786 if (NS_FAILED(rv) || inheritsPrincipal) {
1787 return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
1790 nsCOMPtr<nsIPrincipal> principal;
1791 rv = CreateCodebasePrincipal(aURI, aAppId, aInMozBrowser,
1792 getter_AddRefs(principal));
1793 NS_ENSURE_SUCCESS(rv, rv);
1794 NS_IF_ADDREF(*result = principal);
1796 return NS_OK;
1799 nsIPrincipal*
1800 nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
1801 nsresult* rv)
1803 *rv = NS_OK;
1804 JSCompartment *compartment = js::GetContextCompartment(cx);
1806 // The context should always be in a compartment, either one it has entered
1807 // or the one associated with its global.
1808 MOZ_ASSERT(!!compartment);
1810 JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
1811 return nsJSPrincipals::get(principals);
1814 NS_IMETHODIMP
1815 nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
1816 nsIPrincipal **result)
1818 JS::Rooted<JSObject*> obj(aCx, aObj);
1819 *result = doGetObjectPrincipal(obj);
1820 if (!*result)
1821 return NS_ERROR_FAILURE;
1822 NS_ADDREF(*result);
1823 return NS_OK;
1826 // static
1827 nsIPrincipal*
1828 nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
1830 JSCompartment *compartment = js::GetObjectCompartment(aObj);
1831 JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
1832 return nsJSPrincipals::get(principals);
1835 ////////////////////////////////////////////////
1836 // Methods implementing nsIXPCSecurityManager //
1837 ////////////////////////////////////////////////
1839 NS_IMETHODIMP
1840 nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
1841 const nsIID &aIID,
1842 nsISupports *aObj,
1843 nsIClassInfo *aClassInfo,
1844 void **aPolicy)
1846 // XXX Special case for nsIXPCException ?
1847 ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nullptr);
1848 if (objClassInfo.IsDOMClass())
1850 return NS_OK;
1853 //--See if the object advertises a non-default level of access
1854 // using nsISecurityCheckedComponent
1855 nsCOMPtr<nsISecurityCheckedComponent> checkedComponent =
1856 do_QueryInterface(aObj);
1858 nsXPIDLCString objectSecurityLevel;
1859 if (checkedComponent)
1860 checkedComponent->CanCreateWrapper((nsIID *)&aIID, getter_Copies(objectSecurityLevel));
1862 nsresult rv = CheckXPCPermissions(cx, aObj, nullptr, nullptr, objectSecurityLevel);
1863 if (NS_FAILED(rv))
1865 //-- Access denied, report an error
1866 NS_ConvertUTF8toUTF16 strName("CreateWrapperDenied");
1867 nsAutoCString origin;
1868 nsresult rv2;
1869 nsIPrincipal* subjectPrincipal = doGetSubjectPrincipal(&rv2);
1870 if (NS_SUCCEEDED(rv2) && subjectPrincipal) {
1871 GetPrincipalDomainOrigin(subjectPrincipal, origin);
1873 NS_ConvertUTF8toUTF16 originUnicode(origin);
1874 NS_ConvertUTF8toUTF16 className(objClassInfo.GetName());
1875 const PRUnichar* formatStrings[] = {
1876 className.get(),
1877 originUnicode.get()
1879 uint32_t length = ArrayLength(formatStrings);
1880 if (originUnicode.IsEmpty()) {
1881 --length;
1882 } else {
1883 strName.AppendLiteral("ForOrigin");
1885 nsXPIDLString errorMsg;
1886 // We need to keep our existing failure rv and not override it
1887 // with a likely success code from the following string bundle
1888 // call in order to throw the correct security exception later.
1889 rv2 = sStrBundle->FormatStringFromName(strName.get(),
1890 formatStrings,
1891 length,
1892 getter_Copies(errorMsg));
1893 NS_ENSURE_SUCCESS(rv2, rv2);
1895 SetPendingException(cx, errorMsg.get());
1898 return rv;
1901 NS_IMETHODIMP
1902 nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
1903 const nsCID &aCID)
1905 nsresult rv = CheckXPCPermissions(cx, nullptr, nullptr, nullptr, nullptr);
1906 if (NS_FAILED(rv))
1908 //-- Access denied, report an error
1909 nsAutoCString errorMsg("Permission denied to create instance of class. CID=");
1910 char cidStr[NSID_LENGTH];
1911 aCID.ToProvidedString(cidStr);
1912 errorMsg.Append(cidStr);
1913 SetPendingException(cx, errorMsg.get());
1915 return rv;
1918 NS_IMETHODIMP
1919 nsScriptSecurityManager::CanGetService(JSContext *cx,
1920 const nsCID &aCID)
1922 nsresult rv = CheckXPCPermissions(cx, nullptr, nullptr, nullptr, nullptr);
1923 if (NS_FAILED(rv))
1925 //-- Access denied, report an error
1926 nsAutoCString errorMsg("Permission denied to get service. CID=");
1927 char cidStr[NSID_LENGTH];
1928 aCID.ToProvidedString(cidStr);
1929 errorMsg.Append(cidStr);
1930 SetPendingException(cx, errorMsg.get());
1933 return rv;
1937 NS_IMETHODIMP
1938 nsScriptSecurityManager::CanAccess(uint32_t aAction,
1939 nsAXPCNativeCallContext* aCallContext,
1940 JSContext* cx,
1941 JSObject* aJSObject,
1942 nsISupports* aObj,
1943 nsIClassInfo* aClassInfo,
1944 jsid aPropertyName,
1945 void** aPolicy)
1947 return CheckPropertyAccessImpl(aAction, aCallContext, cx,
1948 aJSObject, aObj, aClassInfo,
1949 nullptr, aPropertyName, aPolicy);
1952 nsresult
1953 nsScriptSecurityManager::CheckXPCPermissions(JSContext* cx,
1954 nsISupports* aObj, JSObject* aJSObject,
1955 nsIPrincipal* aSubjectPrincipal,
1956 const char* aObjectSecurityLevel)
1958 MOZ_ASSERT(cx);
1959 JS::RootedObject jsObject(cx, aJSObject);
1960 // Check if the subject is privileged.
1961 if (SubjectIsPrivileged())
1962 return NS_OK;
1964 //-- If the object implements nsISecurityCheckedComponent, it has a non-default policy.
1965 if (aObjectSecurityLevel)
1967 if (PL_strcasecmp(aObjectSecurityLevel, "allAccess") == 0)
1968 return NS_OK;
1969 if (cx && PL_strcasecmp(aObjectSecurityLevel, "sameOrigin") == 0)
1971 nsresult rv;
1972 if (!jsObject)
1974 nsCOMPtr<nsIXPConnectWrappedJS> xpcwrappedjs =
1975 do_QueryInterface(aObj);
1976 if (xpcwrappedjs)
1978 jsObject = xpcwrappedjs->GetJSObject();
1979 NS_ENSURE_STATE(jsObject);
1983 if (!aSubjectPrincipal)
1985 // No subject principal passed in. Compute it.
1986 aSubjectPrincipal = GetSubjectPrincipal(cx, &rv);
1987 NS_ENSURE_SUCCESS(rv, rv);
1989 if (aSubjectPrincipal && jsObject)
1991 nsIPrincipal* objectPrincipal = doGetObjectPrincipal(jsObject);
1993 // Only do anything if we have both a subject and object
1994 // principal.
1995 if (objectPrincipal)
1997 bool subsumes;
1998 rv = aSubjectPrincipal->Subsumes(objectPrincipal, &subsumes);
1999 NS_ENSURE_SUCCESS(rv, rv);
2000 if (subsumes)
2001 return NS_OK;
2005 else if (PL_strcasecmp(aObjectSecurityLevel, "noAccess") != 0)
2007 if (SubjectIsPrivileged())
2008 return NS_OK;
2012 //-- Access tests failed
2013 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
2016 /////////////////////////////////////////////
2017 // Method implementing nsIChannelEventSink //
2018 /////////////////////////////////////////////
2019 NS_IMETHODIMP
2020 nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel,
2021 nsIChannel* newChannel,
2022 uint32_t redirFlags,
2023 nsIAsyncVerifyRedirectCallback *cb)
2025 nsCOMPtr<nsIPrincipal> oldPrincipal;
2026 GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
2028 nsCOMPtr<nsIURI> newURI;
2029 newChannel->GetURI(getter_AddRefs(newURI));
2030 nsCOMPtr<nsIURI> newOriginalURI;
2031 newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
2033 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
2035 const uint32_t flags =
2036 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
2037 nsIScriptSecurityManager::DISALLOW_SCRIPT;
2038 nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
2039 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
2040 rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
2043 if (NS_FAILED(rv))
2044 return rv;
2046 cb->OnRedirectVerifyCallback(NS_OK);
2047 return NS_OK;
2051 /////////////////////////////////////
2052 // Method implementing nsIObserver //
2053 /////////////////////////////////////
2054 const char sJSEnabledPrefName[] = "javascript.enabled";
2055 const char sFileOriginPolicyPrefName[] =
2056 "security.fileuri.strict_origin_policy";
2057 static const char sPolicyPrefix[] = "capability.policy.";
2059 static const char* kObservedPrefs[] = {
2060 sJSEnabledPrefName,
2061 sFileOriginPolicyPrefName,
2062 sPolicyPrefix,
2063 nullptr
2067 NS_IMETHODIMP
2068 nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
2069 const PRUnichar* aMessage)
2071 nsresult rv = NS_OK;
2072 NS_ConvertUTF16toUTF8 messageStr(aMessage);
2073 const char *message = messageStr.get();
2075 static const char jsPrefix[] = "javascript.";
2076 static const char securityPrefix[] = "security.";
2077 if ((PL_strncmp(message, jsPrefix, sizeof(jsPrefix)-1) == 0) ||
2078 (PL_strncmp(message, securityPrefix, sizeof(securityPrefix)-1) == 0) )
2080 ScriptSecurityPrefChanged();
2082 else if (PL_strncmp(message, sPolicyPrefix, sizeof(sPolicyPrefix)-1) == 0)
2084 // This will force re-initialization of the pref table
2085 mPolicyPrefsChanged = true;
2087 return rv;
2090 /////////////////////////////////////////////
2091 // Constructor, Destructor, Initialization //
2092 /////////////////////////////////////////////
2093 nsScriptSecurityManager::nsScriptSecurityManager(void)
2094 : mOriginToPolicyMap(nullptr),
2095 mDefaultPolicy(nullptr),
2096 mCapabilities(nullptr),
2097 mPrefInitialized(false),
2098 mIsJavaScriptEnabled(false),
2099 mPolicyPrefsChanged(true)
2101 static_assert(sizeof(intptr_t) == sizeof(void*),
2102 "intptr_t and void* have different lengths on this platform. "
2103 "This may cause a security failure with the SecurityLevel union.");
2106 nsresult nsScriptSecurityManager::Init()
2108 InitPrefs();
2110 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
2111 NS_ENSURE_SUCCESS(rv, rv);
2113 nsCOMPtr<nsIStringBundleService> bundleService =
2114 mozilla::services::GetStringBundleService();
2115 if (!bundleService)
2116 return NS_ERROR_FAILURE;
2118 rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
2119 NS_ENSURE_SUCCESS(rv, rv);
2121 // Create our system principal singleton
2122 nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
2123 NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
2125 mSystemPrincipal = system;
2127 //-- Register security check callback in the JS engine
2128 // Currently this is used to control access to function.caller
2129 rv = nsXPConnect::XPConnect()->GetRuntime(&sRuntime);
2130 NS_ENSURE_SUCCESS(rv, rv);
2132 static const JSSecurityCallbacks securityCallbacks = {
2133 CheckObjectAccess,
2134 ContentSecurityPolicyPermitsJSAction
2137 MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
2138 JS_SetSecurityCallbacks(sRuntime, &securityCallbacks);
2139 JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy);
2141 JS_SetTrustedPrincipals(sRuntime, system);
2143 return NS_OK;
2146 static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
2148 nsScriptSecurityManager::~nsScriptSecurityManager(void)
2150 Preferences::RemoveObservers(this, kObservedPrefs);
2151 delete mOriginToPolicyMap;
2152 if(mDefaultPolicy)
2153 mDefaultPolicy->Drop();
2154 delete mCapabilities;
2155 if (mDomainPolicy)
2156 mDomainPolicy->Deactivate();
2157 MOZ_ASSERT(!mDomainPolicy);
2160 void
2161 nsScriptSecurityManager::Shutdown()
2163 if (sRuntime) {
2164 JS_SetSecurityCallbacks(sRuntime, nullptr);
2165 JS_SetTrustedPrincipals(sRuntime, nullptr);
2166 sRuntime = nullptr;
2168 sEnabledID = JSID_VOID;
2170 NS_IF_RELEASE(sIOService);
2171 NS_IF_RELEASE(sStrBundle);
2174 nsScriptSecurityManager *
2175 nsScriptSecurityManager::GetScriptSecurityManager()
2177 if (!gScriptSecMan && nsXPConnect::XPConnect())
2179 nsRefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
2181 nsresult rv;
2182 rv = ssManager->Init();
2183 if (NS_FAILED(rv)) {
2184 return nullptr;
2187 rv = nsXPConnect::XPConnect()->
2188 SetDefaultSecurityManager(ssManager);
2189 if (NS_FAILED(rv)) {
2190 NS_WARNING("Failed to install xpconnect security manager!");
2191 return nullptr;
2194 ClearOnShutdown(&gScriptSecMan);
2195 gScriptSecMan = ssManager;
2197 return gScriptSecMan;
2200 // Currently this nsGenericFactory constructor is used only from FastLoad
2201 // (XPCOM object deserialization) code, when "creating" the system principal
2202 // singleton.
2203 nsSystemPrincipal *
2204 nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
2206 nsIPrincipal *sysprin = nullptr;
2207 if (gScriptSecMan)
2208 NS_ADDREF(sysprin = gScriptSecMan->mSystemPrincipal);
2209 return static_cast<nsSystemPrincipal*>(sysprin);
2212 nsresult
2213 nsScriptSecurityManager::InitPolicies()
2215 // Clear any policies cached on XPConnect wrappers
2216 nsresult rv =
2217 nsXPConnect::XPConnect()->ClearAllWrappedNativeSecurityPolicies();
2218 if (NS_FAILED(rv)) return rv;
2220 //-- Clear mOriginToPolicyMap: delete mapped DomainEntry items,
2221 //-- whose dtor decrements refcount of stored DomainPolicy object
2222 delete mOriginToPolicyMap;
2224 //-- Marks all the survivor DomainPolicy objects (those cached
2225 //-- by nsPrincipal objects) as invalid: they will be released
2226 //-- on first nsPrincipal::GetSecurityPolicy() attempt.
2227 DomainPolicy::InvalidateAll();
2229 //-- Release old default policy
2230 if(mDefaultPolicy) {
2231 mDefaultPolicy->Drop();
2232 mDefaultPolicy = nullptr;
2235 //-- Initialize a new mOriginToPolicyMap
2236 mOriginToPolicyMap =
2237 new nsObjectHashtable(nullptr, nullptr, DeleteDomainEntry, nullptr);
2238 if (!mOriginToPolicyMap)
2239 return NS_ERROR_OUT_OF_MEMORY;
2241 //-- Create, refcount and initialize a new default policy
2242 mDefaultPolicy = new DomainPolicy();
2243 if (!mDefaultPolicy)
2244 return NS_ERROR_OUT_OF_MEMORY;
2246 mDefaultPolicy->Hold();
2247 if (!mDefaultPolicy->Init())
2248 return NS_ERROR_UNEXPECTED;
2250 //-- Initialize the table of security levels
2251 if (!mCapabilities)
2253 mCapabilities =
2254 new nsObjectHashtable(nullptr, nullptr, DeleteCapability, nullptr);
2255 if (!mCapabilities)
2256 return NS_ERROR_OUT_OF_MEMORY;
2259 // Get a JS context - we need it to create internalized strings later.
2260 AutoSafeJSContext cx;
2261 rv = InitDomainPolicy(cx, "default", mDefaultPolicy);
2262 NS_ENSURE_SUCCESS(rv, rv);
2264 nsAdoptingCString policyNames =
2265 Preferences::GetCString("capability.policy.policynames");
2267 nsAdoptingCString defaultPolicyNames =
2268 Preferences::GetCString("capability.policy.default_policynames");
2269 policyNames += NS_LITERAL_CSTRING(" ") + defaultPolicyNames;
2271 //-- Initialize domain policies
2272 char* policyCurrent = policyNames.BeginWriting();
2273 bool morePolicies = true;
2274 while (morePolicies)
2276 while(*policyCurrent == ' ' || *policyCurrent == ',')
2277 policyCurrent++;
2278 if (*policyCurrent == '\0')
2279 break;
2280 char* nameBegin = policyCurrent;
2282 while(*policyCurrent != '\0' && *policyCurrent != ' ' && *policyCurrent != ',')
2283 policyCurrent++;
2285 morePolicies = (*policyCurrent != '\0');
2286 *policyCurrent = '\0';
2287 policyCurrent++;
2289 nsAutoCString sitesPrefName(
2290 NS_LITERAL_CSTRING(sPolicyPrefix) +
2291 nsDependentCString(nameBegin) +
2292 NS_LITERAL_CSTRING(".sites"));
2293 nsAdoptingCString domainList =
2294 Preferences::GetCString(sitesPrefName.get());
2295 if (!domainList) {
2296 continue;
2299 DomainPolicy* domainPolicy = new DomainPolicy();
2300 if (!domainPolicy)
2301 return NS_ERROR_OUT_OF_MEMORY;
2303 if (!domainPolicy->Init())
2305 delete domainPolicy;
2306 return NS_ERROR_UNEXPECTED;
2308 domainPolicy->Hold();
2309 //-- Parse list of sites and create an entry in mOriginToPolicyMap for each
2310 char* domainStart = domainList.BeginWriting();
2311 char* domainCurrent = domainStart;
2312 char* lastDot = nullptr;
2313 char* nextToLastDot = nullptr;
2314 bool moreDomains = true;
2315 while (moreDomains)
2317 if (*domainCurrent == ' ' || *domainCurrent == '\0')
2319 moreDomains = (*domainCurrent != '\0');
2320 *domainCurrent = '\0';
2321 nsCStringKey key(nextToLastDot ? nextToLastDot+1 : domainStart);
2322 DomainEntry *newEntry = new DomainEntry(domainStart, domainPolicy);
2323 if (!newEntry)
2325 domainPolicy->Drop();
2326 return NS_ERROR_OUT_OF_MEMORY;
2328 DomainEntry *existingEntry = (DomainEntry *)
2329 mOriginToPolicyMap->Get(&key);
2330 if (!existingEntry)
2331 mOriginToPolicyMap->Put(&key, newEntry);
2332 else
2334 if (existingEntry->Matches(domainStart))
2336 newEntry->mNext = existingEntry;
2337 mOriginToPolicyMap->Put(&key, newEntry);
2339 else
2341 while (existingEntry->mNext)
2343 if (existingEntry->mNext->Matches(domainStart))
2345 newEntry->mNext = existingEntry->mNext;
2346 existingEntry->mNext = newEntry;
2347 break;
2349 existingEntry = existingEntry->mNext;
2351 if (!existingEntry->mNext)
2352 existingEntry->mNext = newEntry;
2355 domainStart = domainCurrent + 1;
2356 lastDot = nextToLastDot = nullptr;
2358 else if (*domainCurrent == '.')
2360 nextToLastDot = lastDot;
2361 lastDot = domainCurrent;
2363 domainCurrent++;
2366 rv = InitDomainPolicy(cx, nameBegin, domainPolicy);
2367 domainPolicy->Drop();
2368 if (NS_FAILED(rv))
2369 return rv;
2372 // Reset the "dirty" flag
2373 mPolicyPrefsChanged = false;
2375 return NS_OK;
2379 nsresult
2380 nsScriptSecurityManager::InitDomainPolicy(JSContext* cx,
2381 const char* aPolicyName,
2382 DomainPolicy* aDomainPolicy)
2384 nsresult rv;
2385 nsAutoCString policyPrefix(NS_LITERAL_CSTRING(sPolicyPrefix) +
2386 nsDependentCString(aPolicyName) +
2387 NS_LITERAL_CSTRING("."));
2388 uint32_t prefixLength = policyPrefix.Length() - 1; // subtract the '.'
2390 uint32_t prefCount;
2391 char** prefNames;
2392 nsIPrefBranch* branch = Preferences::GetRootBranch();
2393 NS_ASSERTION(branch, "failed to get the root pref branch");
2394 rv = branch->GetChildList(policyPrefix.get(), &prefCount, &prefNames);
2395 if (NS_FAILED(rv)) return rv;
2396 if (prefCount == 0)
2397 return NS_OK;
2399 //-- Populate the policy
2400 uint32_t currentPref = 0;
2401 for (; currentPref < prefCount; currentPref++)
2403 // Get the class name
2404 const char* start = prefNames[currentPref] + prefixLength + 1;
2405 char* end = PL_strchr(start, '.');
2406 if (!end) // malformed pref, bail on this one
2407 continue;
2408 static const char sitesStr[] = "sites";
2410 // We dealt with "sites" in InitPolicies(), so no need to do
2411 // that again...
2412 if (PL_strncmp(start, sitesStr, sizeof(sitesStr)-1) == 0)
2413 continue;
2415 // Get the pref value
2416 nsAdoptingCString prefValue =
2417 Preferences::GetCString(prefNames[currentPref]);
2418 if (!prefValue) {
2419 continue;
2422 SecurityLevel secLevel;
2423 if (PL_strcasecmp(prefValue, "noAccess") == 0)
2424 secLevel.level = SCRIPT_SECURITY_NO_ACCESS;
2425 else if (PL_strcasecmp(prefValue, "allAccess") == 0)
2426 secLevel.level = SCRIPT_SECURITY_ALL_ACCESS;
2427 else if (PL_strcasecmp(prefValue, "sameOrigin") == 0)
2428 secLevel.level = SCRIPT_SECURITY_SAME_ORIGIN_ACCESS;
2429 else
2430 { //-- pref value is the name of a capability
2431 nsCStringKey secLevelKey(prefValue);
2432 secLevel.capability =
2433 reinterpret_cast<char*>(mCapabilities->Get(&secLevelKey));
2434 if (!secLevel.capability)
2436 secLevel.capability = NS_strdup(prefValue);
2437 if (!secLevel.capability)
2438 break;
2439 mCapabilities->Put(&secLevelKey,
2440 secLevel.capability);
2444 *end = '\0';
2445 // Find or store this class in the classes table
2446 ClassPolicy* cpolicy =
2447 static_cast<ClassPolicy*>
2448 (PL_DHashTableOperate(aDomainPolicy, start,
2449 PL_DHASH_ADD));
2450 if (!cpolicy)
2451 break;
2453 // If this is the wildcard class (class '*'), save it in mWildcardPolicy
2454 // (we leave it stored in the hashtable too to take care of the cleanup)
2455 if ((*start == '*') && (end == start + 1)) {
2456 aDomainPolicy->mWildcardPolicy = cpolicy;
2458 // Make sure that cpolicy knows about aDomainPolicy so it can reset
2459 // the mWildcardPolicy pointer as needed if it gets moved in the
2460 // hashtable.
2461 cpolicy->mDomainWeAreWildcardFor = aDomainPolicy;
2464 // Get the property name
2465 start = end + 1;
2466 end = PL_strchr(start, '.');
2467 if (end)
2468 *end = '\0';
2470 JSString* propertyKey = ::JS_InternString(cx, start);
2471 if (!propertyKey)
2472 return NS_ERROR_OUT_OF_MEMORY;
2474 // Store this property in the class policy
2475 PropertyPolicy* ppolicy =
2476 static_cast<PropertyPolicy*>
2477 (PL_DHashTableOperate(cpolicy->mPolicy, propertyKey,
2478 PL_DHASH_ADD));
2479 if (!ppolicy)
2480 break;
2482 if (end) // The pref specifies an access mode
2484 start = end + 1;
2485 if (PL_strcasecmp(start, "set") == 0)
2486 ppolicy->mSet = secLevel;
2487 else
2488 ppolicy->mGet = secLevel;
2490 else
2492 if (ppolicy->mGet.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
2493 ppolicy->mGet = secLevel;
2494 if (ppolicy->mSet.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
2495 ppolicy->mSet = secLevel;
2499 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames);
2500 if (currentPref < prefCount) // Loop exited early because of out-of-memory error
2501 return NS_ERROR_OUT_OF_MEMORY;
2502 return NS_OK;
2505 inline void
2506 nsScriptSecurityManager::ScriptSecurityPrefChanged()
2508 // JavaScript defaults to enabled in failure cases.
2509 mIsJavaScriptEnabled = true;
2511 sStrictFileOriginPolicy = true;
2513 nsresult rv;
2514 if (!mPrefInitialized) {
2515 rv = InitPrefs();
2516 if (NS_FAILED(rv))
2517 return;
2520 mIsJavaScriptEnabled =
2521 Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
2523 sStrictFileOriginPolicy =
2524 Preferences::GetBool(sFileOriginPolicyPrefName, false);
2527 nsresult
2528 nsScriptSecurityManager::InitPrefs()
2530 nsIPrefBranch* branch = Preferences::GetRootBranch();
2531 NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
2533 mPrefInitialized = true;
2535 // Set the initial value of the "javascript.enabled" prefs
2536 ScriptSecurityPrefChanged();
2538 // set observer callbacks in case the value of the prefs change
2539 Preferences::AddStrongObservers(this, kObservedPrefs);
2541 return NS_OK;
2544 namespace mozilla {
2546 void
2547 GetJarPrefix(uint32_t aAppId, bool aInMozBrowser, nsACString& aJarPrefix)
2549 MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
2551 if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
2552 aAppId = nsIScriptSecurityManager::NO_APP_ID;
2555 aJarPrefix.Truncate();
2557 // Fallback.
2558 if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInMozBrowser) {
2559 return;
2562 // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
2563 aJarPrefix.AppendInt(aAppId);
2564 aJarPrefix.Append('+');
2565 aJarPrefix.Append(aInMozBrowser ? 't' : 'f');
2566 aJarPrefix.Append('+');
2568 return;
2571 } // namespace mozilla
2573 NS_IMETHODIMP
2574 nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId,
2575 bool aInMozBrowser,
2576 nsACString& aJarPrefix)
2578 MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
2580 mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
2581 return NS_OK;
2584 NS_IMETHODIMP
2585 nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
2587 *aRv = !!mDomainPolicy;
2588 return NS_OK;
2591 NS_IMETHODIMP
2592 nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv)
2594 // We only allow one domain policy at a time. The holder of the previous
2595 // policy must explicitly deactivate it first.
2596 if (mDomainPolicy) {
2597 return NS_ERROR_SERVICE_NOT_AVAILABLE;
2600 mDomainPolicy = new mozilla::hotness::DomainPolicy();
2601 nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy;
2602 ptr.forget(aRv);
2603 return NS_OK;
2606 // Intentionally non-scriptable. Script must have a reference to the
2607 // nsIDomainPolicy to deactivate it.
2608 void
2609 nsScriptSecurityManager::DeactivateDomainPolicy()
2611 mDomainPolicy = nullptr;
2614 NS_IMETHODIMP
2615 nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv)
2617 nsresult rv;
2619 // Compute our rule. If we don't have any domain policy set up that might
2620 // provide exceptions to this rule, we're done.
2621 *aRv = mIsJavaScriptEnabled;
2622 if (!mDomainPolicy) {
2623 return NS_OK;
2626 // We have a domain policy. Grab the appropriate set of exceptions to the
2627 // rule (either the blacklist or the whitelist, depending on whether script
2628 // is enabled or disabled by default).
2629 nsCOMPtr<nsIDomainSet> exceptions;
2630 nsCOMPtr<nsIDomainSet> superExceptions;
2631 if (*aRv) {
2632 mDomainPolicy->GetBlacklist(getter_AddRefs(exceptions));
2633 mDomainPolicy->GetSuperBlacklist(getter_AddRefs(superExceptions));
2634 } else {
2635 mDomainPolicy->GetWhitelist(getter_AddRefs(exceptions));
2636 mDomainPolicy->GetSuperWhitelist(getter_AddRefs(superExceptions));
2639 bool contains;
2640 rv = exceptions->Contains(aURI, &contains);
2641 NS_ENSURE_SUCCESS(rv, rv);
2642 if (contains) {
2643 *aRv = !*aRv;
2644 return NS_OK;
2646 rv = superExceptions->ContainsSuperDomain(aURI, &contains);
2647 NS_ENSURE_SUCCESS(rv, rv);
2648 if (contains) {
2649 *aRv = !*aRv;
2652 return NS_OK;