2018-11-30 22:52:05 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2018-11-30 18:39:55 +03:00
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2014-04-03 15:58:00 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2012-05-21 15:12:37 +04:00
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2010-06-26 02:58:09 +04:00
|
|
|
|
2010-07-03 00:54:53 +04:00
|
|
|
#include "AccessCheck.h"
|
2010-06-26 02:58:09 +04:00
|
|
|
|
|
|
|
#include "nsJSPrincipals.h"
|
2013-11-05 12:47:59 +04:00
|
|
|
#include "nsGlobalWindow.h"
|
2010-06-26 02:58:09 +04:00
|
|
|
|
2010-07-03 00:54:53 +04:00
|
|
|
#include "XPCWrapper.h"
|
2010-09-18 01:54:40 +04:00
|
|
|
#include "XrayWrapper.h"
|
2016-12-02 23:24:20 +03:00
|
|
|
#include "FilteringWrapper.h"
|
2010-06-26 02:58:09 +04:00
|
|
|
|
2011-04-13 20:27:37 +04:00
|
|
|
#include "jsfriendapi.h"
|
2019-01-30 22:16:12 +03:00
|
|
|
#include "mozilla/BasePrincipal.h"
|
2017-02-02 20:48:49 +03:00
|
|
|
#include "mozilla/ErrorResult.h"
|
2012-09-13 00:29:30 +04:00
|
|
|
#include "mozilla/dom/BindingUtils.h"
|
2014-07-12 03:34:44 +04:00
|
|
|
#include "mozilla/dom/LocationBinding.h"
|
2013-12-09 19:34:03 +04:00
|
|
|
#include "mozilla/dom/WindowBinding.h"
|
2015-01-27 00:32:18 +03:00
|
|
|
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
2013-11-05 12:47:59 +04:00
|
|
|
#include "nsJSUtils.h"
|
2016-08-04 21:14:35 +03:00
|
|
|
#include "xpcprivate.h"
|
2010-10-28 19:15:53 +04:00
|
|
|
|
2011-10-11 09:50:08 +04:00
|
|
|
using namespace mozilla;
|
2013-09-02 08:51:02 +04:00
|
|
|
using namespace JS;
|
2011-09-09 07:29:15 +04:00
|
|
|
using namespace js;
|
|
|
|
|
2010-06-26 02:58:09 +04:00
|
|
|
namespace xpc {
|
|
|
|
|
2019-01-30 22:16:12 +03:00
|
|
|
BasePrincipal* GetRealmPrincipal(JS::Realm* realm) {
|
|
|
|
return BasePrincipal::Cast(
|
|
|
|
nsJSPrincipals::get(JS::GetRealmPrincipals(realm)));
|
2018-06-02 12:58:29 +03:00
|
|
|
}
|
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
nsIPrincipal* GetObjectPrincipal(JSObject* obj) {
|
2018-08-10 12:57:40 +03:00
|
|
|
return GetRealmPrincipal(js::GetNonCCWObjectRealm(obj));
|
2013-05-07 03:53:10 +04:00
|
|
|
}
|
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
bool AccessCheck::subsumes(JSObject* a, JSObject* b) {
|
2018-09-19 18:25:51 +03:00
|
|
|
return CompartmentOriginInfo::Subsumes(js::GetObjectCompartment(a),
|
|
|
|
js::GetObjectCompartment(b));
|
2012-12-08 02:49:11 +04:00
|
|
|
}
|
|
|
|
|
2014-02-14 06:57:34 +04:00
|
|
|
// Same as above, but considering document.domain.
|
2019-01-30 22:16:12 +03:00
|
|
|
bool AccessCheck::subsumesConsideringDomain(JS::Realm* a, JS::Realm* b) {
|
2017-01-18 15:34:31 +03:00
|
|
|
MOZ_ASSERT(OriginAttributes::IsRestrictOpenerAccessForFPI());
|
2019-01-30 22:16:12 +03:00
|
|
|
BasePrincipal* aprin = GetRealmPrincipal(a);
|
|
|
|
BasePrincipal* bprin = GetRealmPrincipal(b);
|
|
|
|
return aprin->FastSubsumesConsideringDomain(bprin);
|
2012-09-11 21:23:20 +04:00
|
|
|
}
|
|
|
|
|
2019-01-30 22:16:12 +03:00
|
|
|
bool AccessCheck::subsumesConsideringDomainIgnoringFPD(JS::Realm* a,
|
|
|
|
JS::Realm* b) {
|
2017-01-18 15:34:31 +03:00
|
|
|
MOZ_ASSERT(!OriginAttributes::IsRestrictOpenerAccessForFPI());
|
2019-01-30 22:16:12 +03:00
|
|
|
BasePrincipal* aprin = GetRealmPrincipal(a);
|
|
|
|
BasePrincipal* bprin = GetRealmPrincipal(b);
|
|
|
|
return aprin->FastSubsumesConsideringDomainIgnoringFPD(bprin);
|
2017-01-18 15:34:31 +03:00
|
|
|
}
|
|
|
|
|
2012-09-11 12:05:10 +04:00
|
|
|
// Does the compartment of the wrapper subsumes the compartment of the wrappee?
|
2015-03-29 01:22:11 +03:00
|
|
|
bool AccessCheck::wrapperSubsumes(JSObject* wrapper) {
|
2012-09-11 12:05:10 +04:00
|
|
|
MOZ_ASSERT(js::IsWrapper(wrapper));
|
2015-03-29 01:22:11 +03:00
|
|
|
JSObject* wrapped = js::UncheckedUnwrap(wrapper);
|
2018-09-19 18:25:51 +03:00
|
|
|
return CompartmentOriginInfo::Subsumes(js::GetObjectCompartment(wrapper),
|
|
|
|
js::GetObjectCompartment(wrapped));
|
2012-09-11 12:05:10 +04:00
|
|
|
}
|
|
|
|
|
2018-06-07 17:44:40 +03:00
|
|
|
bool AccessCheck::isChrome(JS::Compartment* compartment) {
|
2018-08-11 14:12:49 +03:00
|
|
|
return js::IsSystemCompartment(compartment);
|
2010-07-03 00:54:53 +04:00
|
|
|
}
|
|
|
|
|
2019-01-30 22:16:12 +03:00
|
|
|
bool AccessCheck::isChrome(JS::Realm* realm) {
|
|
|
|
return isChrome(JS::GetCompartmentForRealm(realm));
|
|
|
|
}
|
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
bool AccessCheck::isChrome(JSObject* obj) {
|
2012-09-11 12:05:10 +04:00
|
|
|
return isChrome(js::GetObjectCompartment(obj));
|
|
|
|
}
|
|
|
|
|
Bug 1471496 part 2. Change the way we do cross-compartment wrappers for Window and Location so they don't ever need to be recomputed. r=bholley
The end result we want is that on the web cross-compartment wrappers for
WindowProxy and Location are always CrossOriginObjectWrapper. That needs to be true
for both cases that are different-origin (as now) and cases that are
same-origin, since they might become different-origin due to document.domain
changes but we don't want that to affect the wrappers involved.
On the web, all security checks are symmetric, so in WrapperFactory::Rewrap we
would have originSubsumesTarget == targetSubsumesOrigin in all web cases.
I claim that
originSubsumesTarget == targetSubsumesOrigin &&
(!targetSubsumesOrigin ||
(!originCompartmentPrivate->wantXrays &&
!targetCompartmentPrivate->wantXrays)) &&
"object is a WindowProxy or Location"
is a necessary and sufficient condition for using CrossOriginObjectWrapper.
Comparing to our current code, if originSubsumesTarget and targetSubsumesOrigin
are both false, then for the WindowProxy and Location cases we currently end up
with the following arguments to SelectWrapper:
securityWrapper: true
xrayType: XrayForDOMObject
waiveXrays: false
So SelectWrapper ends up returning CrossOriginObjectWrapper, which the new
condition keeps doing.
If originSubsumesTarget and targetSubsumesOrigin are both true, then there are
two cases. If both compartments have wantXrays false (which is always the case
on the web), then we end up with the following arguments to SelectWrapper:
securityWrapper: false
xrayType: NotXray
waiveXrays: false
and SelectWrapper returns CrossCompartmentWrapper. We want to do
CrossOriginObjectWrapper instead, as explained above.
Finally, if originSubsumesTarget and targetSubsumesOrigin are both true but one
of the compartments has wantXrays set, then we get:
securityWrapper: false
xrayType: XrayForDOMObject
waiveXrays: might be true or false
and then SelectWrapper might return a WaiveXrayWrapper or a PermissiveXrayDOM.
In this case we do _not_ want to start returning CrossOriginObjectWrapper, and
this is a non-web case anyway, since web compartments can't set wantXrays.
Differential Revision: https://phabricator.services.mozilla.com/D18030
--HG--
extra : moz-landing-system : lando
2019-02-06 17:53:48 +03:00
|
|
|
bool IsCrossOriginAccessibleObject(JSObject* obj) {
|
2015-11-06 21:03:52 +03:00
|
|
|
obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
|
2019-08-14 20:15:15 +03:00
|
|
|
const JSClass* clasp = js::GetObjectClass(obj);
|
2014-10-17 18:17:02 +04:00
|
|
|
|
Bug 1471496 part 2. Change the way we do cross-compartment wrappers for Window and Location so they don't ever need to be recomputed. r=bholley
The end result we want is that on the web cross-compartment wrappers for
WindowProxy and Location are always CrossOriginObjectWrapper. That needs to be true
for both cases that are different-origin (as now) and cases that are
same-origin, since they might become different-origin due to document.domain
changes but we don't want that to affect the wrappers involved.
On the web, all security checks are symmetric, so in WrapperFactory::Rewrap we
would have originSubsumesTarget == targetSubsumesOrigin in all web cases.
I claim that
originSubsumesTarget == targetSubsumesOrigin &&
(!targetSubsumesOrigin ||
(!originCompartmentPrivate->wantXrays &&
!targetCompartmentPrivate->wantXrays)) &&
"object is a WindowProxy or Location"
is a necessary and sufficient condition for using CrossOriginObjectWrapper.
Comparing to our current code, if originSubsumesTarget and targetSubsumesOrigin
are both false, then for the WindowProxy and Location cases we currently end up
with the following arguments to SelectWrapper:
securityWrapper: true
xrayType: XrayForDOMObject
waiveXrays: false
So SelectWrapper ends up returning CrossOriginObjectWrapper, which the new
condition keeps doing.
If originSubsumesTarget and targetSubsumesOrigin are both true, then there are
two cases. If both compartments have wantXrays false (which is always the case
on the web), then we end up with the following arguments to SelectWrapper:
securityWrapper: false
xrayType: NotXray
waiveXrays: false
and SelectWrapper returns CrossCompartmentWrapper. We want to do
CrossOriginObjectWrapper instead, as explained above.
Finally, if originSubsumesTarget and targetSubsumesOrigin are both true but one
of the compartments has wantXrays set, then we get:
securityWrapper: false
xrayType: XrayForDOMObject
waiveXrays: might be true or false
and then SelectWrapper might return a WaiveXrayWrapper or a PermissiveXrayDOM.
In this case we do _not_ want to start returning CrossOriginObjectWrapper, and
this is a non-web case anyway, since web compartments can't set wantXrays.
Differential Revision: https://phabricator.services.mozilla.com/D18030
--HG--
extra : moz-landing-system : lando
2019-02-06 17:53:48 +03:00
|
|
|
return (clasp->name[0] == 'L' && !strcmp(clasp->name, "Location")) ||
|
|
|
|
(clasp->name[0] == 'W' && !strcmp(clasp->name, "Window"));
|
2013-09-20 21:32:32 +04:00
|
|
|
}
|
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
bool AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper,
|
|
|
|
HandleValue v) {
|
2014-10-20 17:52:52 +04:00
|
|
|
// Primitives are fine.
|
2018-09-12 19:16:15 +03:00
|
|
|
if (!v.isObject()) {
|
2014-10-20 17:52:52 +04:00
|
|
|
return true;
|
2018-09-12 19:16:15 +03:00
|
|
|
}
|
2014-10-20 17:52:52 +04:00
|
|
|
RootedObject obj(cx, &v.toObject());
|
|
|
|
|
|
|
|
// Non-wrappers are fine.
|
2018-09-12 19:16:15 +03:00
|
|
|
if (!js::IsWrapper(obj)) {
|
2014-10-20 17:52:52 +04:00
|
|
|
return true;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2014-10-20 17:52:52 +04:00
|
|
|
// CPOWs use COWs (in the unprivileged junk scope) for all child->parent
|
|
|
|
// references. Without this test, the child process wouldn't be able to
|
|
|
|
// pass any objects at all to CPOWs.
|
|
|
|
if (mozilla::jsipc::IsWrappedCPOW(obj) &&
|
|
|
|
js::GetObjectCompartment(wrapper) ==
|
|
|
|
js::GetObjectCompartment(xpc::UnprivilegedJunkScope()) &&
|
2015-07-04 04:29:00 +03:00
|
|
|
XRE_IsParentProcess()) {
|
2014-10-20 17:52:52 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same-origin wrappers are fine.
|
2018-09-12 19:16:15 +03:00
|
|
|
if (AccessCheck::wrapperSubsumes(obj)) {
|
2014-10-20 17:52:52 +04:00
|
|
|
return true;
|
2018-09-12 19:16:15 +03:00
|
|
|
}
|
2014-10-20 17:52:52 +04:00
|
|
|
|
|
|
|
// Badness.
|
2016-08-14 14:39:28 +03:00
|
|
|
JS_ReportErrorASCII(cx,
|
|
|
|
"Permission denied to pass object to privileged code");
|
2014-10-20 17:52:52 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
bool AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper,
|
|
|
|
const CallArgs& args) {
|
2018-09-12 19:16:15 +03:00
|
|
|
if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv())) {
|
2014-10-20 17:52:52 +04:00
|
|
|
return false;
|
2018-09-12 19:16:15 +03:00
|
|
|
}
|
2014-10-20 17:52:52 +04:00
|
|
|
for (size_t i = 0; i < args.length(); ++i) {
|
2018-09-12 19:16:15 +03:00
|
|
|
if (!checkPassToPrivilegedCode(cx, wrapper, args[i])) {
|
2014-10-20 17:52:52 +04:00
|
|
|
return false;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2014-10-20 17:52:52 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-02 20:48:49 +03:00
|
|
|
void AccessCheck::reportCrossOriginDenial(JSContext* cx, JS::HandleId id,
|
|
|
|
const nsACString& accessType) {
|
|
|
|
// This function exists because we want to report DOM SecurityErrors, not JS
|
|
|
|
// Errors, when denying access on cross-origin DOM objects. It's
|
|
|
|
// conceptually pretty similar to
|
|
|
|
// AutoEnterPolicy::reportErrorIfExceptionIsNotPending.
|
|
|
|
if (JS_IsExceptionPending(cx)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2017-02-02 20:48:49 +03:00
|
|
|
nsAutoCString message;
|
|
|
|
if (JSID_IS_VOID(id)) {
|
|
|
|
message = NS_LITERAL_CSTRING("Permission denied to access object");
|
|
|
|
} else {
|
|
|
|
// We want to use JS_ValueToSource here, because that most closely
|
|
|
|
// matches what AutoEnterPolicy::reportErrorIfExceptionIsNotPending
|
|
|
|
// does.
|
|
|
|
JS::RootedValue idVal(cx, js::IdToValue(id));
|
|
|
|
nsAutoJSString propName;
|
|
|
|
JS::RootedString idStr(cx, JS_ValueToSource(cx, idVal));
|
|
|
|
if (!idStr || !propName.init(cx, idStr)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
message = NS_LITERAL_CSTRING("Permission denied to ") + accessType +
|
|
|
|
NS_LITERAL_CSTRING(" property ") +
|
|
|
|
NS_ConvertUTF16toUTF8(propName) +
|
|
|
|
NS_LITERAL_CSTRING(" on cross-origin object");
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2017-02-02 20:48:49 +03:00
|
|
|
ErrorResult rv;
|
|
|
|
rv.ThrowDOMException(NS_ERROR_DOM_SECURITY_ERR, message);
|
2017-02-15 03:17:02 +03:00
|
|
|
MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(cx));
|
2017-02-02 20:48:49 +03:00
|
|
|
}
|
|
|
|
|
2017-09-07 21:17:16 +03:00
|
|
|
bool OpaqueWithSilentFailing::deny(JSContext* cx, js::Wrapper::Action act,
|
|
|
|
HandleId id, bool mayThrow) {
|
2014-10-03 12:05:51 +04:00
|
|
|
// Fail silently for GET, ENUMERATE, and GET_PROPERTY_DESCRIPTOR.
|
|
|
|
if (act == js::Wrapper::GET || act == js::Wrapper::ENUMERATE ||
|
|
|
|
act == js::Wrapper::GET_PROPERTY_DESCRIPTOR) {
|
2017-02-02 20:48:49 +03:00
|
|
|
// Note that ReportWrapperDenial doesn't do any _exception_ reporting,
|
|
|
|
// so we want to do this regardless of the value of mayThrow.
|
2014-10-03 12:05:51 +04:00
|
|
|
return ReportWrapperDenial(cx, id, WrapperDenialForCOW,
|
|
|
|
"Access to privileged JS object not permitted");
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2014-10-03 12:05:51 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace xpc
|