1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AccessCheck.h"
9 #include "nsJSPrincipals.h"
11 #include "XPCWrapper.h"
12 #include "XrayWrapper.h"
13 #include "FilteringWrapper.h"
15 #include "jsfriendapi.h"
16 #include "js/Object.h" // JS::GetClass, JS::GetCompartment
17 #include "mozilla/BasePrincipal.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/dom/BindingUtils.h"
20 #include "mozilla/dom/LocationBinding.h"
21 #include "mozilla/dom/WindowBinding.h"
22 #include "nsJSUtils.h"
23 #include "xpcprivate.h"
25 using namespace mozilla
;
31 BasePrincipal
* GetRealmPrincipal(JS::Realm
* realm
) {
32 return BasePrincipal::Cast(
33 nsJSPrincipals::get(JS::GetRealmPrincipals(realm
)));
36 nsIPrincipal
* GetObjectPrincipal(JSObject
* obj
) {
37 return GetRealmPrincipal(js::GetNonCCWObjectRealm(obj
));
40 bool AccessCheck::subsumes(JSObject
* a
, JSObject
* b
) {
41 return CompartmentOriginInfo::Subsumes(JS::GetCompartment(a
),
42 JS::GetCompartment(b
));
45 // Same as above, but considering document.domain.
46 bool AccessCheck::subsumesConsideringDomain(JS::Realm
* a
, JS::Realm
* b
) {
47 MOZ_ASSERT(OriginAttributes::IsRestrictOpenerAccessForFPI());
48 BasePrincipal
* aprin
= GetRealmPrincipal(a
);
49 BasePrincipal
* bprin
= GetRealmPrincipal(b
);
50 return aprin
->FastSubsumesConsideringDomain(bprin
);
53 bool AccessCheck::subsumesConsideringDomainIgnoringFPD(JS::Realm
* a
,
55 MOZ_ASSERT(!OriginAttributes::IsRestrictOpenerAccessForFPI());
56 BasePrincipal
* aprin
= GetRealmPrincipal(a
);
57 BasePrincipal
* bprin
= GetRealmPrincipal(b
);
58 return aprin
->FastSubsumesConsideringDomainIgnoringFPD(bprin
);
61 // Does the compartment of the wrapper subsumes the compartment of the wrappee?
62 bool AccessCheck::wrapperSubsumes(JSObject
* wrapper
) {
63 MOZ_ASSERT(js::IsWrapper(wrapper
));
64 JSObject
* wrapped
= js::UncheckedUnwrap(wrapper
);
65 return CompartmentOriginInfo::Subsumes(JS::GetCompartment(wrapper
),
66 JS::GetCompartment(wrapped
));
69 bool AccessCheck::isChrome(JS::Compartment
* compartment
) {
70 return js::IsSystemCompartment(compartment
);
73 bool AccessCheck::isChrome(JS::Realm
* realm
) {
74 return isChrome(JS::GetCompartmentForRealm(realm
));
77 bool AccessCheck::isChrome(JSObject
* obj
) {
78 return isChrome(JS::GetCompartment(obj
));
81 bool IsCrossOriginAccessibleObject(JSObject
* obj
) {
82 obj
= js::UncheckedUnwrap(obj
, /* stopAtWindowProxy = */ false);
83 const JSClass
* clasp
= JS::GetClass(obj
);
85 return (clasp
->name
[0] == 'L' && !strcmp(clasp
->name
, "Location")) ||
86 (clasp
->name
[0] == 'W' && !strcmp(clasp
->name
, "Window"));
89 bool AccessCheck::checkPassToPrivilegedCode(JSContext
* cx
, HandleObject wrapper
,
91 // Primitives are fine.
95 RootedObject
obj(cx
, &v
.toObject());
97 // Non-wrappers are fine.
98 if (!js::IsWrapper(obj
)) {
102 // Same-origin wrappers are fine.
103 if (AccessCheck::wrapperSubsumes(obj
)) {
108 JS_ReportErrorASCII(cx
,
109 "Permission denied to pass object to privileged code");
113 bool AccessCheck::checkPassToPrivilegedCode(JSContext
* cx
, HandleObject wrapper
,
114 const CallArgs
& args
) {
115 if (!checkPassToPrivilegedCode(cx
, wrapper
, args
.thisv())) {
118 for (size_t i
= 0; i
< args
.length(); ++i
) {
119 if (!checkPassToPrivilegedCode(cx
, wrapper
, args
[i
])) {
126 void AccessCheck::reportCrossOriginDenial(JSContext
* cx
, JS::HandleId id
,
127 const nsACString
& accessType
) {
128 // This function exists because we want to report DOM SecurityErrors, not JS
129 // Errors, when denying access on cross-origin DOM objects. It's
130 // conceptually pretty similar to
131 // AutoEnterPolicy::reportErrorIfExceptionIsNotPending.
132 if (JS_IsExceptionPending(cx
)) {
136 nsAutoCString message
;
138 message
= "Permission denied to access object"_ns
;
140 // We want to use JS_ValueToSource here, because that most closely
141 // matches what AutoEnterPolicy::reportErrorIfExceptionIsNotPending
143 JS::RootedValue
idVal(cx
, js::IdToValue(id
));
144 nsAutoJSString propName
;
145 JS::RootedString
idStr(cx
, JS_ValueToSource(cx
, idVal
));
146 if (!idStr
|| !propName
.init(cx
, idStr
)) {
149 message
= "Permission denied to "_ns
+ accessType
+ " property "_ns
+
150 NS_ConvertUTF16toUTF8(propName
) + " on cross-origin object"_ns
;
153 rv
.ThrowSecurityError(message
);
154 MOZ_ALWAYS_TRUE(rv
.MaybeSetPendingException(cx
));
157 bool OpaqueWithSilentFailing::deny(JSContext
* cx
, js::Wrapper::Action act
,
158 HandleId id
, bool mayThrow
) {
159 // Fail silently for GET, ENUMERATE, and GET_PROPERTY_DESCRIPTOR.
160 if (act
== js::Wrapper::GET
|| act
== js::Wrapper::ENUMERATE
||
161 act
== js::Wrapper::GET_PROPERTY_DESCRIPTOR
) {
162 // Note that ReportWrapperDenial doesn't do any _exception_ reporting,
163 // so we want to do this regardless of the value of mayThrow.
164 return ReportWrapperDenial(cx
, id
, WrapperDenialForCOW
,
165 "Access to privileged JS object not permitted");