diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index d0115eb627aa..0dee4966d5d4 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -149,6 +149,22 @@ NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) return true; } +static bool +IdToIteratorValue(JSContext *cx, JSObject *obj, jsid id, uintN flags, jsval *vp) +{ + if (!(flags & JSITER_FOREACH)) { + *vp = ID_TO_VALUE(id); + return true; + } + + /* Do the lookup on the original object instead of the prototype. */ + if (!obj->getProperty(cx, id, vp)) + return false; + if ((flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *vp, vp)) + return false; + return true; +} + static inline bool Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id, bool enumerable, uintN flags, HashSet& ht, @@ -166,17 +182,10 @@ Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id, return false; } if (enumerable || (flags & JSITER_HIDDEN)) { - if (!vec.append(ID_TO_VALUE(id))) + if (!vec.append(JSVAL_VOID)) + return false; + if (!IdToIteratorValue(cx, obj, id, flags, vec.end() - 1)) return false; - if (flags & JSITER_FOREACH) { - jsval *vp = vec.end() - 1; - - /* Do the lookup on the original object instead of the prototype. */ - if (!obj->getProperty(cx, id, vp)) - return false; - if ((flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *vp, vp)) - return false; - } } return true; } @@ -434,6 +443,16 @@ IdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoValueVector &p if (!ni) return false; + /* If this is a for-each iteration, fetch the values or key/value pairs. */ + if (flags & JSITER_FOREACH) { + size_t length = props.length(); + for (size_t n = 0; n < length; ++n) { + jsval *vp = &ni->begin()[n]; + if (!IdToIteratorValue(cx, obj, *vp, flags, vp)) + return false; + } + } + iterobj->setNativeIterator(ni); RegisterEnumerator(cx, iterobj, ni); diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 8543f0d5dd30..248af9dc6f45 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -472,14 +472,14 @@ JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper() { } -#define PIERCE(cx, proxy, pre, op, post) \ - JS_BEGIN_MACRO \ - AutoCompartment call(cx, wrappedObject(proxy)); \ - if (!call.enter() || !(pre) || !enter(cx, proxy, id) || !(op)) \ - return false; \ - leave(cx, proxy); \ - call.leave(); \ - return (post); \ +#define PIERCE(cx, proxy, mode, pre, op, post) \ + JS_BEGIN_MACRO \ + AutoCompartment call(cx, wrappedObject(proxy)); \ + if (!call.enter() || !(pre) || !enter(cx, proxy, id, mode) || !(op)) \ + return false; \ + leave(cx, proxy); \ + call.leave(); \ + return (post); \ JS_END_MACRO #define NOTHING (true) @@ -487,7 +487,7 @@ JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper() bool JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, call.destination->wrapId(cx, &id), JSWrapper::getPropertyDescriptor(cx, proxy, id, desc), call.origin->wrap(cx, desc)); @@ -496,7 +496,7 @@ JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *proxy, bool JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, call.destination->wrapId(cx, &id), JSWrapper::getOwnPropertyDescriptor(cx, proxy, id, desc), call.origin->wrap(cx, desc)); @@ -506,7 +506,7 @@ bool JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) { AutoDescriptor desc2(cx, desc); - PIERCE(cx, proxy, + PIERCE(cx, proxy, SET, call.destination->wrapId(cx, &id) && call.destination->wrap(cx, &desc2), JSWrapper::getOwnPropertyDescriptor(cx, proxy, id, &desc2), NOTHING); @@ -515,16 +515,16 @@ JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *proxy, jsid i bool JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoValueVector &props) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, NOTHING, JSWrapper::getOwnPropertyNames(cx, proxy, props), - filter(cx, proxy, props) && call.origin->wrap(cx, props)); + call.origin->wrap(cx, props)); } bool JSCrossCompartmentWrapper::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, SET, call.destination->wrapId(cx, &id), JSWrapper::delete_(cx, proxy, id, bp), NOTHING); @@ -533,16 +533,16 @@ JSCrossCompartmentWrapper::delete_(JSContext *cx, JSObject *proxy, jsid id, bool bool JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *proxy, AutoValueVector &props) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, NOTHING, JSWrapper::enumerate(cx, proxy, props), - filter(cx, proxy, props) && call.origin->wrap(cx, props)); + call.origin->wrap(cx, props)); } bool JSCrossCompartmentWrapper::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, call.destination->wrapId(cx, &id), JSWrapper::has(cx, proxy, id, bp), NOTHING); @@ -551,7 +551,7 @@ JSCrossCompartmentWrapper::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp bool JSCrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, call.destination->wrapId(cx, &id), JSWrapper::hasOwn(cx, proxy, id, bp), NOTHING); @@ -560,7 +560,7 @@ JSCrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool bool JSCrossCompartmentWrapper::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id), JSWrapper::get(cx, proxy, receiver, id, vp), call.origin->wrap(cx, vp)); @@ -570,7 +570,7 @@ bool JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, jsval *vp) { AutoValueRooter tvr(cx, *vp); - PIERCE(cx, proxy, + PIERCE(cx, proxy, SET, call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id) && call.destination->wrap(cx, tvr.addr()), JSWrapper::set(cx, proxy, receiver, id, tvr.addr()), NOTHING); @@ -579,19 +579,49 @@ JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *proxy, JSObject *receive bool JSCrossCompartmentWrapper::enumerateOwn(JSContext *cx, JSObject *proxy, AutoValueVector &props) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, NOTHING, JSWrapper::enumerateOwn(cx, proxy, props), - filter(cx, proxy, props) && call.origin->wrap(cx, props)); + call.origin->wrap(cx, props)); +} + +/* + * We can reify non-escaping iterator objects instead of having to wrap them. This + * allows fast iteration over objects across a compartment boundary. + */ +static bool +CanReify(jsval *vp) +{ + return !JSVAL_IS_PRIMITIVE(*vp) && + JSVAL_TO_OBJECT(*vp)->getClass() == &js_IteratorClass.base && + !!(JSVAL_TO_OBJECT(*vp)->getNativeIterator()->flags & JSITER_ENUMERATE); +} + +static bool +Reify(JSContext *cx, JSCompartment *origin, jsval *vp) +{ + JSObject *iterObj = JSVAL_TO_OBJECT(*vp); + NativeIterator *ni = iterObj->getNativeIterator(); + AutoValueVector props(cx); + size_t length = ni->length(); + if (length > 0) { + props.resize(length); + for (size_t n = 0; n < length; ++n) + props[n] = origin->wrap(cx, &ni->begin()[n]); + } + + JSObject *obj = ni->obj; + return origin->wrap(cx, &obj) && + IdVectorToIterator(cx, obj, ni->flags, props, vp); } bool JSCrossCompartmentWrapper::iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp) { - PIERCE(cx, proxy, + PIERCE(cx, proxy, GET, NOTHING, JSWrapper::iterate(cx, proxy, flags, vp), - filter(cx, proxy, vp) && call.origin->wrap(cx, vp)); + CanReify(vp) ? Reify(cx, call.origin, vp) : call.origin->wrap(cx, vp)); } bool @@ -612,7 +642,7 @@ JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *proxy, uintN argc, jsva jsval *fakevp = call.getvp(); fakevp[0] = vp[0]; fakevp[1] = vp[1]; - if (!enter(cx, proxy, JSVAL_VOID) || !JSWrapper::call(cx, proxy, argc, vp)) + if (!enter(cx, proxy, JSVAL_VOID, GET) || !JSWrapper::call(cx, proxy, argc, vp)) return false; leave(cx, proxy); @@ -635,7 +665,7 @@ JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *proxy, JSObject *r jsval *vp = call.getvp(); vp[0] = OBJECT_TO_JSVAL(call.target); if (!call.destination->wrap(cx, &receiver) || - !enter(cx, proxy, JSVAL_VOID) || + !enter(cx, proxy, JSVAL_VOID, GET) || !JSWrapper::construct(cx, proxy, receiver, argc, argv, rval)) { return false; } @@ -653,7 +683,7 @@ JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *proxy) if (!call.enter()) return NULL; - if (!enter(cx, proxy, JSVAL_VOID)) + if (!enter(cx, proxy, JSVAL_VOID, GET)) return NULL; JSString *str = JSWrapper::obj_toString(cx, proxy); if (!str) @@ -674,7 +704,7 @@ JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *proxy, uintN in if (!call.enter()) return NULL; - if (!enter(cx, proxy, JSVAL_VOID)) + if (!enter(cx, proxy, JSVAL_VOID, GET)) return NULL; JSString *str = JSWrapper::fun_toString(cx, proxy, indent); if (!str) @@ -689,7 +719,7 @@ JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *proxy, uintN in } bool -JSCrossCompartmentWrapper::enter(JSContext *cx, JSObject *proxy, jsval id) +JSCrossCompartmentWrapper::enter(JSContext *cx, JSObject *proxy, jsval id, Mode mode) { return true; } @@ -699,16 +729,4 @@ JSCrossCompartmentWrapper::leave(JSContext *cx, JSObject *proxy) { } -bool -JSCrossCompartmentWrapper::filter(JSContext *cx, JSObject *proxy, AutoValueVector &props) -{ - return true; -} - -bool -JSCrossCompartmentWrapper::filter(JSContext *cx, JSObject *proxy, jsval *vp) -{ - return true; -} - JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton; diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 36583acf6f34..f6facf9d455f 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -101,6 +101,8 @@ class JSCrossCompartmentWrapper : public JSWrapper { JS_FRIEND_API(JSCrossCompartmentWrapper()); public: + typedef enum { GET, SET } Mode; + virtual JS_FRIEND_API(~JSCrossCompartmentWrapper()); /* ES5 Harmony fundamental proxy traps. */ @@ -130,10 +132,8 @@ class JSCrossCompartmentWrapper : public JSWrapper { virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent); /* Policy enforcement traps. */ - virtual bool enter(JSContext *cx, JSObject *proxy, jsid id); + virtual bool enter(JSContext *cx, JSObject *proxy, jsid id, Mode mode); virtual void leave(JSContext *cx, JSObject *proxy); - virtual bool filter(JSContext *cx, JSObject *proxy, js::AutoValueVector &props); - virtual bool filter(JSContext *cx, JSObject *proxy, jsval *vp); static JSCrossCompartmentWrapper singleton; diff --git a/js/src/xpconnect/src/wrappers/AccessCheck.cpp b/js/src/xpconnect/src/wrappers/AccessCheck.cpp new file mode 100644 index 000000000000..4c9019a43421 --- /dev/null +++ b/js/src/xpconnect/src/wrappers/AccessCheck.cpp @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code, released + * June 24, 2010. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jswrapper.h" + +#include "XPCWrapper.h" + +#include "nsJSPrincipals.h" + +#include "AccessCheck.h" + +namespace xpc { + +nsIPrincipal * +GetCompartmentPrincipal(JSCompartment *compartment) +{ + return static_cast(compartment->principals)->nsIPrincipalPtr; +} + +bool +AccessCheck::isPrivileged(JSCompartment *compartment) +{ + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if(!ssm) { + return true; + } + + PRBool privileged; + if(NS_SUCCEEDED(ssm->IsSystemPrincipal(GetCompartmentPrincipal(compartment), &privileged)) + && privileged) { + return true; + } + if(NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) && privileged) { + return true; + } + return false; +} + +void +AccessCheck::deny(JSContext *cx, jsid id) +{ + if(id == JSVAL_VOID) { + JS_ReportError(cx, "Permission denied to access object"); + } else { + JSString *str = JS_ValueToString(cx, id); + JS_ReportError(cx, "Permission denied to access property '%hs'", str); + } +} + +bool +AccessCheck::enter(JSContext *cx, JSObject *wrapper, JSObject *wrappedObject, jsid id, + JSCrossCompartmentWrapper::Mode mode) +{ + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if(!ssm) { + return true; + } + JSStackFrame *fp = NULL; + nsresult rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), + GetCompartmentPrincipal(wrappedObject->getCompartment(cx))); + if(NS_FAILED(rv)) { + NS_WARNING("Not allowing call because we're out of memory"); + JS_ReportOutOfMemory(cx); + return false; + } + return true; +} + +void +AccessCheck::leave(JSContext *cx, JSObject *wrapper, JSObject *wrappedObject) +{ + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if(ssm) { + ssm->PopContextPrincipal(cx); + } +} + +} diff --git a/js/src/xpconnect/src/wrappers/AccessCheck.h b/js/src/xpconnect/src/wrappers/AccessCheck.h new file mode 100644 index 000000000000..014a2924d250 --- /dev/null +++ b/js/src/xpconnect/src/wrappers/AccessCheck.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code, released + * June 24, 2010. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jswrapper.h" + +namespace xpc { + +class AccessCheck { + public: + static bool subsumes(JSCompartment *subject, JSCompartment *object, bool *yesno); + static bool isPrivileged(JSCompartment *compartment); + + static void deny(JSContext *cx, jsid id); + + static bool enter(JSContext *cx, JSObject *wrapper, JSObject *wrappedObject, jsid id, + JSCrossCompartmentWrapper::Mode mode); + static void leave(JSContext *cx, JSObject *wrapper, JSObject *wrappedObject); +}; + +} diff --git a/js/src/xpconnect/src/wrappers/ChromeWrapper.cpp b/js/src/xpconnect/src/wrappers/ChromeWrapper.cpp new file mode 100644 index 000000000000..56df424ca3c1 --- /dev/null +++ b/js/src/xpconnect/src/wrappers/ChromeWrapper.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code, released + * June 24, 2010. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ChromeWrapper.h" +#include "AccessCheck.h" + +#include "XPCWrapper.h" + +using namespace js; + +namespace xpc { + +ChromeWrapper ChromeWrapper::singleton; + +ChromeWrapper::ChromeWrapper() : JSCrossCompartmentWrapper() +{ +} + +ChromeWrapper::~ChromeWrapper() +{ +} + +typedef enum { READ = (1<<0), WRITE = (1<<1), DENIED=0 } Permission; + +static bool +Allow(JSContext *cx, bool *yesno) +{ + if(yesno) { + *yesno = true; + } + return true; +} + +static bool +Deny(JSContext *cx, bool *yesno, const char *error) +{ + if(yesno) { + *yesno = false; + return true; + } + JS_ReportError(cx, error); + return false; +} + +static bool +CheckAccess(JSContext *cx, JSObject *hallpass, jsid id, JSCrossCompartmentWrapper::Mode mode, + bool *yesno = NULL) +{ + if(!hallpass) { + return Allow(cx, yesno); + } + + Permission perm = DENIED; + + jsval v; + if(!JS_LookupPropertyById(cx, hallpass, id, &v)) { + return false; + } + + if(!JSVAL_IS_STRING(v)) { + return Deny(cx, yesno, "property permission must be a string"); + } + + JSString *str = JSVAL_TO_STRING(v); + const jschar *chars = JS_GetStringChars(str); + size_t length = JS_GetStringLength(str); + for (size_t i = 0; i < length; ++i) { + switch (chars[i]) { + case 'r': + if(perm & READ) { + return Deny(cx, yesno, "duplicate 'readable' property flag"); + } + perm = Permission(perm | READ); + break; + + case 'w': + if(perm & WRITE) { + return Deny(cx, yesno, "duplicate 'writable' property flag"); + } + perm = Permission(perm | WRITE); + break; + + default: + return Deny(cx, yesno, "property permission can only be readable or read and writable"); + } + } + + if(perm == DENIED) { + return Deny(cx, yesno, "invalid property permission"); + } + + if((mode == JSCrossCompartmentWrapper::GET && !(perm & READ)) || + (mode == JSCrossCompartmentWrapper::SET && !(perm & WRITE))) { + if(yesno) { + *yesno = false; + return true; + } + AccessCheck::deny(cx, id); + return false; + } + + return Allow(cx, yesno); +} + +static bool +GetHallPass(JSContext *cx, JSObject *wrappedObject, jsid id, JSObject **hallpassp) +{ + jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS); + + JSBool found = JS_FALSE; + if(!JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found)) + return false; + if(!found) { + *hallpassp = NULL; + return true; + } + + jsval exposedProps; + if(!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps)) + return false; + + if(JSVAL_IS_VOID(exposedProps) || JSVAL_IS_NULL(exposedProps)) { + AccessCheck::deny(cx, id); + return false; + } + + if(!JSVAL_IS_OBJECT(exposedProps)) { + JS_ReportError(cx, + "__exposedProps__ must be undefined, null, or an object"); + return false; + } + + *hallpassp = JSVAL_TO_OBJECT(exposedProps); + return true; +} + +static bool +Filter(JSContext *cx, JSObject *wrappedObject, AutoValueVector &props) +{ + JSObject *hallpass; + if(!GetHallPass(cx, wrappedObject, JSVAL_VOID, &hallpass)) + return false; + if(!hallpass) + return true; + size_t w = 0; + for (size_t n = 0; n < props.length(); ++n) { + bool yes; + jsid id = props[n]; + if(!CheckAccess(cx, hallpass, id, JSCrossCompartmentWrapper::GET, &yes)) + return false; + if(yes) { + props[w++] = id; + } + } + props.resize(w); + return true; +} + +bool +ChromeWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoValueVector &props) +{ + return JSCrossCompartmentWrapper::getOwnPropertyNames(cx, wrapper, props) && + Filter(cx, wrappedObject(wrapper), props); +} + +bool +ChromeWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoValueVector &props) +{ + return JSCrossCompartmentWrapper::enumerate(cx, wrapper, props) && + Filter(cx, wrappedObject(wrapper), props); +} + +bool +ChromeWrapper::enumerateOwn(JSContext *cx, JSObject *wrapper, AutoValueVector &props) +{ + return JSCrossCompartmentWrapper::enumerateOwn(cx, wrapper, props) && + Filter(cx, wrappedObject(wrapper), props); +} + +bool +ChromeWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, jsval *vp) +{ + // We refuse to trigger the iterator hook across chrome wrappers because + // we don't know how to censor custom iterator objects. Instead we trigger + // the default proxy iterate trap, which will ask ChromeWrapper::enumerate() + // for the list of (consored) ids. + return JSProxyHandler::iterate(cx, wrapper, flags, vp); +} + +bool +ChromeWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Mode mode) +{ + JSObject *hallpass; + return GetHallPass(cx, wrappedObject(wrapper), id, &hallpass) && + CheckAccess(cx, hallpass, id, mode); +} + +JSString * +ChromeWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) +{ + // Censor Function.prototype.toString.call(wrapper) and decompile Function instead. + JSObject *ctor; + if(!JS_GetClassObject(cx, wrapper->getGlobal(), JSProto_Function, &ctor)) + return false; + return JS_DecompileFunction(cx, JS_ValueToConstructor(cx, OBJECT_TO_JSVAL(ctor)), indent); +} + +} diff --git a/js/src/xpconnect/src/wrappers/ChromeWrapper.h b/js/src/xpconnect/src/wrappers/ChromeWrapper.h new file mode 100644 index 000000000000..a63c1ebf479e --- /dev/null +++ b/js/src/xpconnect/src/wrappers/ChromeWrapper.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code, released + * June 24, 2010. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jswrapper.h" + +namespace xpc { + +class ChromeWrapper : public JSCrossCompartmentWrapper { + public: + ChromeWrapper(); + virtual ~ChromeWrapper(); + + virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, js::AutoValueVector &props); + virtual bool enumerate(JSContext *cx, JSObject *wrapper, js::AutoValueVector &props); + virtual bool enumerateOwn(JSContext *cx, JSObject *wrapper, js::AutoValueVector &props); + virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, jsval *vp); + + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Mode mode); + + virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent); + + static ChromeWrapper singleton; +}; + +} diff --git a/js/src/xpconnect/src/wrappers/ContentWrapper.cpp b/js/src/xpconnect/src/wrappers/ContentWrapper.cpp index 0e2aca64fea0..614f2482c76b 100644 --- a/js/src/xpconnect/src/wrappers/ContentWrapper.cpp +++ b/js/src/xpconnect/src/wrappers/ContentWrapper.cpp @@ -14,8 +14,8 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9.1 code, released - * June 30, 2009. + * The Original Code is mozilla.org code, released + * June 24, 2010. * * The Initial Developer of the Original Code is * The Mozilla Foundation @@ -37,25 +37,12 @@ * * ***** END LICENSE BLOCK ***** */ -#include "jsapi.h" -#include "jswrapper.h" - -#include "XPCWrapper.h" - -#include "nsJSPrincipals.h" +#include "ContentWrapper.h" +#include "AccessCheck.h" namespace xpc { -class ContentWrapper : public JSCrossCompartmentWrapper { - public: - ContentWrapper(); - virtual ~ContentWrapper(); - - virtual bool enter(JSContext *cx, JSObject *proxy, jsid id); - virtual void leave(JSContext *cx, JSObject *proxy); - - static ContentWrapper singleton; -}; +ContentWrapper ContentWrapper::singleton; ContentWrapper::ContentWrapper() : JSCrossCompartmentWrapper() { @@ -66,41 +53,15 @@ ContentWrapper::~ContentWrapper() } bool -ContentWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id) +ContentWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Mode mode) { - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if(!ssm) { - return true; - } - JSPrincipals *subjectPrincipals = wrapper->getCompartment(cx)->principals; - JSPrincipals *objectPrincipals = wrappedObject(wrapper)->getCompartment(cx)->principals; - if(!subjectPrincipals->subsume(subjectPrincipals, objectPrincipals)) { - if(id == JSVAL_VOID) { - JS_ReportError(cx, "Permission denied to access object"); - } else { - JSString *str = JS_ValueToString(cx, id); - JS_ReportError(cx, "Permission denied to access property '%hs'", str); - } - return false; - } - JSStackFrame *fp = nsnull; - nsIPrincipal *principal = static_cast(objectPrincipals)->nsIPrincipalPtr; - nsresult rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), principal); - if (NS_FAILED(rv)) { - NS_WARNING("Not allowing call because we're out of memory"); - JS_ReportOutOfMemory(cx); - return false; - } - return true; + return AccessCheck::enter(cx, wrapper, wrappedObject(wrapper), id, mode); } void -ContentWrapper::leave(JSContext *cx, JSObject *proxy) +ContentWrapper::leave(JSContext *cx, JSObject *wrapper) { - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (ssm) { - ssm->PopContextPrincipal(cx); - } + return AccessCheck::leave(cx, wrapper, wrappedObject(wrapper)); } } diff --git a/js/src/xpconnect/src/wrappers/ContentWrapper.h b/js/src/xpconnect/src/wrappers/ContentWrapper.h new file mode 100644 index 000000000000..fee053e7fb9c --- /dev/null +++ b/js/src/xpconnect/src/wrappers/ContentWrapper.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code, released + * June 24, 2010. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jswrapper.h" + +// Content wrappers allow unmitigated access and are only used if the +// origin (subject) compartment's principals subsume the target (object) +// compartment's principals. +// +// The main responsibility of the content wrapper is to push and pop the +// target (object) compartment's principals when entering and leaving +// that compartment. + +namespace xpc { + +class ContentWrapper : public JSCrossCompartmentWrapper { + public: + ContentWrapper(); + virtual ~ContentWrapper(); + + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Mode mode); + virtual void leave(JSContext *cx, JSObject *wrapper); + + static ContentWrapper singleton; +}; + +} diff --git a/js/src/xpconnect/src/wrappers/Makefile.in b/js/src/xpconnect/src/wrappers/Makefile.in index 74d1f263c743..3c4e44528de4 100644 --- a/js/src/xpconnect/src/wrappers/Makefile.in +++ b/js/src/xpconnect/src/wrappers/Makefile.in @@ -47,7 +47,10 @@ LIBRARY_NAME = xpcwrappers_s FORCE_STATIC_LIB = 1 LIBXUL_LIBRARY = 1 -CPPSRCS = ContentWrapper.cpp +CPPSRCS = ContentWrapper.cpp \ + ChromeWrapper.cpp \ + AccessCheck.cpp \ + WrapperFactory.cpp LOCAL_INCLUDES = \ -I$(srcdir)/.. \ diff --git a/js/src/xpconnect/src/wrappers/WrapperFactory.cpp b/js/src/xpconnect/src/wrappers/WrapperFactory.cpp new file mode 100644 index 000000000000..98f8612a5eb1 --- /dev/null +++ b/js/src/xpconnect/src/wrappers/WrapperFactory.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code, released + * June 24, 2010. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "WrapperFactory.h" +#include "ContentWrapper.h" +#include "ChromeWrapper.h" +#include "AccessCheck.h" + +namespace xpc { + +JSCrossCompartmentWrapper * +WrapperFactory::select(JSContext *cx, JSCompartment *subject, JSCompartment *object) +{ + if(AccessCheck::isPrivileged(object)) { + return &ChromeWrapper::singleton; + } + return &ContentWrapper::singleton; +} + +} diff --git a/js/src/xpconnect/src/wrappers/WrapperFactory.h b/js/src/xpconnect/src/wrappers/WrapperFactory.h new file mode 100644 index 000000000000..fc8d381f2916 --- /dev/null +++ b/js/src/xpconnect/src/wrappers/WrapperFactory.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code, released + * June 24, 2010. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jswrapper.h" + +namespace xpc { + +class WrapperFactory { + // Return the wrapper handler to use, or NULL in case of error. + static JSCrossCompartmentWrapper *select(JSContext *cx, + JSCompartment *subject, + JSCompartment *object); +}; + +}