Bug 1941128 - Turn off network.dns.native_https_query on Mac again
[gecko.git] / js / xpconnect / wrappers / AccessCheck.cpp
bloba3c51737769327914ae78b5681ba8fccc001c42c
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;
26 using namespace JS;
27 using namespace js;
29 namespace xpc {
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,
54 JS::Realm* b) {
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,
90 HandleValue v) {
91 // Primitives are fine.
92 if (!v.isObject()) {
93 return true;
95 RootedObject obj(cx, &v.toObject());
97 // Non-wrappers are fine.
98 if (!js::IsWrapper(obj)) {
99 return true;
102 // Same-origin wrappers are fine.
103 if (AccessCheck::wrapperSubsumes(obj)) {
104 return true;
107 // Badness.
108 JS_ReportErrorASCII(cx,
109 "Permission denied to pass object to privileged code");
110 return false;
113 bool AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper,
114 const CallArgs& args) {
115 if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv())) {
116 return false;
118 for (size_t i = 0; i < args.length(); ++i) {
119 if (!checkPassToPrivilegedCode(cx, wrapper, args[i])) {
120 return false;
123 return true;
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)) {
133 return;
136 nsAutoCString message;
137 if (id.isVoid()) {
138 message = "Permission denied to access object"_ns;
139 } else {
140 // We want to use JS_ValueToSource here, because that most closely
141 // matches what AutoEnterPolicy::reportErrorIfExceptionIsNotPending
142 // does.
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)) {
147 return;
149 message = "Permission denied to "_ns + accessType + " property "_ns +
150 NS_ConvertUTF16toUTF8(propName) + " on cross-origin object"_ns;
152 ErrorResult rv;
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");
168 return false;
171 } // namespace xpc