Bug 843829 - Wrap unwaived content JS objects in opaque wrappers for XBL scopes. r=mrbkap

This commit is contained in:
Bobby Holley 2013-04-02 18:51:20 -07:00
parent d77249c1d5
commit 6e2393b436
5 changed files with 92 additions and 3 deletions

View File

@ -71,6 +71,20 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=821850
return "method:" + arg;
</body>
</method>
<method name="passMeAJSObject">
<parameter name="arg" />
<body>
is(typeof arg.prop, 'undefined', "No properties");
is(Object.getOwnPropertyNames(arg).length, 0, "Should have no own properties");
try {
arg.foo = 2;
ok(true, "Stuff fails silently");
} catch (e) {
ok(false, "Stuff should fail silently");
}
is(typeof arg.foo, 'undefined', "Shouldn't place props");
</body>
</method>
<property name="prop">
<getter>return this._prop;</getter>
<setter>this._prop = "set:" + val;</setter>
@ -120,11 +134,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=821850
bound.prop = "someOtherVal";
is(bound.prop, "set:someOtherVal", "Can set properties from content");
// Make sure we can't pass JS objects to the XBL scope.
var proto = bound.__proto__;
proto.passMeAJSObject({prop: 2});
//
// Try sticking a bunch of stuff on the prototype object.
//
var proto = bound.__proto__;
proto.someExpando = 201;
is(bound.someExpando, 201, "Can stick non-XBL properties on the proto");

View File

@ -53,6 +53,30 @@ struct Opaque : public Policy {
}
};
// This policy is designed to protect privileged callers from untrusted non-
// Xrayable objects. Nothing is allowed, and nothing throws.
struct GentlyOpaque : public Policy {
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) {
return false;
}
static bool deny(js::Wrapper::Action act) {
return true;
}
static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl)
{
// We allow nativeCall here because the alternative is throwing (which
// happens in SecurityWrapper::nativeCall), which we don't want. There's
// unlikely to be too much harm to letting this through, because this
// wrapper is only used to wrap less-privileged objects in more-privileged
// scopes, so unwrapping here only drops privileges.
return true;
}
static bool isSafeToUnwrap() {
return false;
}
};
// This policy only permits access to the object if the subject can touch
// system objects.
struct OnlyIfSubjectIsSystem : public Policy {

View File

@ -143,6 +143,30 @@ FilteringWrapper<Base, Policy>::nativeCall(JSContext *cx, JS::IsAcceptableThis t
return Base::Restrictive::nativeCall(cx, test, impl, args);
}
template <typename Base, typename Policy>
bool
FilteringWrapper<Base, Policy>::defaultValue(JSContext *cx, JS::Handle<JSObject*> obj,
JSType hint, MutableHandleValue vp)
{
return Base::defaultValue(cx, obj, hint, vp);
}
// With our entirely-opaque wrapper, the DefaultValue algorithm throws,
// causing spurious exceptions. Manually implement something benign.
template<>
bool
FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>
::defaultValue(JSContext *cx, JS::Handle<JSObject*> obj,
JSType hint, MutableHandleValue vp)
{
JSString *str = JS_NewStringCopyZ(cx, "[Opaque]");
if (!str)
return false;
vp.set(JS::StringValue(str));
return true;
}
template <typename Base, typename Policy>
bool
FilteringWrapper<Base, Policy>::enter(JSContext *cx, JS::Handle<JSObject*> wrapper,
@ -179,6 +203,7 @@ FilteringWrapper<Base, Policy>::enter(JSContext *cx, JS::Handle<JSObject*> wrapp
#define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>
#define CW FilteringWrapper<SameCompartmentSecurityWrapper, ComponentsObjectPolicy>
#define XCW FilteringWrapper<CrossCompartmentSecurityWrapper, ComponentsObjectPolicy>
#define GO FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>
template<> SOW SOW::singleton(WrapperFactory::SOW_FLAG);
template<> SCSOW SCSOW::singleton(WrapperFactory::SOW_FLAG);
template<> XOW XOW::singleton(0);
@ -188,9 +213,12 @@ template<> NNXOW NNXOW::singleton(0);
template<> CW CW::singleton(0);
template<> XCW XCW::singleton(0);
template<> GO GO::singleton(0);
template class SOW;
template class XOW;
template class DXOW;
template class NNXOW;
template class ChromeObjectWrapperBase;
template class GO;
}

View File

@ -39,6 +39,8 @@ class FilteringWrapper : public Base {
virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl,
JS::CallArgs args) MOZ_OVERRIDE;
virtual bool defaultValue(JSContext *cx, JS::Handle<JSObject*> obj, JSType hint, JS::MutableHandleValue vp) MOZ_OVERRIDE;
virtual bool enter(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
js::Wrapper::Action act, bool *bp) MOZ_OVERRIDE;

View File

@ -293,6 +293,10 @@ DEBUG_CheckUnwrapSafety(JSObject *obj, js::Wrapper *handler,
} else if (AccessCheck::needsSystemOnlyWrapper(obj)) {
// SOWs have a dynamic unwrap check, so we can't really say anything useful
// about them here :-(
} else if (handler == &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton) {
// We explicitly use a SecurityWrapper to protect privileged callers from
// less-privileged objects that they should never see. Skip the check in
// this case.
} else {
// Otherwise, it should depend on whether the target subsumes the origin.
MOZ_ASSERT(handler->isSafeToUnwrap() == AccessCheck::subsumes(target, origin));
@ -361,6 +365,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj,
bool targetSubsumesOrigin = AccessCheck::subsumes(target, origin);
bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget;
XrayType xrayType = GetXrayType(obj);
bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG;
// By default we use the wrapped proto of the underlying object as the
// prototype for our wrapper, but we may select something different below.
@ -395,6 +400,20 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj,
OnlyIfSubjectIsSystem>::singleton;
}
// Normally, a non-xrayable non-waived content object that finds itself in
// a privileged scope is wrapped with a CrossCompartmentWrapper, even though
// the lack of a waiver _really_ should give it an opaque wrapper. This is
// a bit too entrenched to change for content-chrome, but we can at least fix
// it for XBL scopes.
//
// See bug 843829.
else if (targetSubsumesOrigin && !originSubsumesTarget &&
!waiveXrayFlag && xrayType == NotXray &&
IsXBLScope(target))
{
wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton;
}
//
// Now, handle the regular cases.
//
@ -417,8 +436,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj,
// If Xrays are warranted, the caller may waive them for non-security
// wrappers.
bool waiveXrays = wantXrays && !securityWrapper &&
(flags & WAIVE_XRAY_WRAPPER_FLAG);
bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag;
wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays);
}