diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 8bebe9768bc8..41a794cba373 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -100,8 +100,35 @@ class JS_FRIEND_API(Wrapper) : public ProxyHandler virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE; - /* Policy enforcement traps. */ - enum Action { GET, SET, CALL }; + /* Policy enforcement traps. + * + * enter() allows the policy to specify whether the caller may perform |act| + * on the underlying object's |id| property. In the case when |act| is CALL, + * |id| is generally JSID_VOID. + * + * leave() allows the policy to undo various scoped state changes taken in + * enter(). If enter() succeeds, leave() must be called upon completion of + * the approved action. + * + * The |act| parameter to enter() specifies the action being performed. GET, + * SET, and CALL are self-explanatory, but PUNCTURE requires more explanation: + * + * GET and SET allow for a very fine-grained security membrane, through + * which access can be granted or denied on a per-property, per-object, and + * per-action basis. Sometimes though, we just want to asks if we can access + * _everything_ behind the wrapper barrier. For example, when the structured + * clone algorithm runs up against a cross-compartment wrapper, it needs to + * know whether it can enter the compartment and keep cloning, or whether it + * should throw. This is the role of PUNCTURE. + * + * PUNCTURE allows the policy to specify whether the wrapper barrier may + * be lifted - that is to say, whether the caller is allowed to access + * anything that the wrapped object could access. This is a very powerful + * permission, and thus should generally be denied for security wrappers + * except under very special circumstances. When |act| is PUNCTURE, |id| + * should be JSID_VOID. + * */ + enum Action { GET, SET, CALL, PUNCTURE }; virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp); virtual void leave(JSContext *cx, JSObject *wrapper); diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index 007b6535b146..bc72afc7b00d 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -306,6 +306,15 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid JSObject *obj = Wrapper::wrappedObject(wrapper); + // LocationPolicy checks PUNCTURE first, so we should never get here for + // Location wrappers. For all other wrappers interested in cross-origin + // semantics, we want to allow puncturing only for the same-origin + // document.domain case. + if (act == Wrapper::PUNCTURE) { + MOZ_ASSERT(!WrapperFactory::IsLocationObject(obj)); + return documentDomainMakesSameOrigin(cx, obj); + } + const char *name; js::Class *clasp = js::GetObjectClass(obj); NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here"); @@ -498,6 +507,10 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: perm = PermitObjectAccess; return true; } + if (act == Wrapper::PUNCTURE) { + perm = DenyAccess; + return false; + } perm = DenyAccess; diff --git a/js/xpconnect/wrappers/AccessCheck.h b/js/xpconnect/wrappers/AccessCheck.h index 065821dd9883..43e8d3f5c7ca 100644 --- a/js/xpconnect/wrappers/AccessCheck.h +++ b/js/xpconnect/wrappers/AccessCheck.h @@ -151,12 +151,18 @@ struct LocationPolicy : public Policy { // We should only be dealing with Location objects here. MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper))); + // Default to deny. + perm = DenyAccess; + + // Location object security is complicated enough. Don't allow punctures. + if (act == js::Wrapper::PUNCTURE) + return false; + if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) || AccessCheck::isLocationObjectSameOrigin(cx, wrapper)) { perm = PermitPropertyAccess; return true; } - perm = DenyAccess; JSAutoEnterCompartment ac; if (!ac.enter(cx, wrapper)) return false;