static JSBool XPC_SJOW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { obj = FindSafeObject(obj); NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); JSObject *unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { // No unsafe object, nothing to resolve here. return JS_TRUE; } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return JS_FALSE; } // Resolve toString specially. if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { *objp = obj; return JS_DefineFunction(cx, obj, "toString", XPC_SJOW_toString, 0, 0) != nsnull; } return XPCWrapper::NewResolve(cx, obj, JS_FALSE, unsafeObj, id, flags, objp); }
static JSBool XPC_SJOW_Enumerate(JSContext *cx, JSObject *obj) { obj = FindSafeObject(obj); NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); // We are being notified of a for-in loop or similar operation on // this XPCSafeJSObjectWrapper. Forward to the correct high-level // object hook, OBJ_ENUMERATE on the unsafe object, called via the // JS_Enumerate API. Then reflect properties named by the // enumerated identifiers from the unsafe object to the safe // wrapper. JSObject *unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { return JS_TRUE; } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return JS_FALSE; } // Since we enumerate using JS_Enumerate() on the unsafe object here // we don't need to do a security check since JS_Enumerate() will // look up unsafeObj.__iterator__ and if we don't have permission to // access that, it'll throw and we'll be safe. return XPCWrapper::Enumerate(cx, obj, unsafeObj); }
static JSBool XPC_SJOW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { // The constructor and toString properties needs to live on the safe // wrapper. if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR) || id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { return JS_TRUE; } obj = FindSafeObject(obj); NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); // Do nothing here if we're in the middle of resolving a property on // this safe wrapper. jsval isResolving; JSBool ok = ::JS_GetReservedSlot(cx, obj, XPC_SJOW_SLOT_IS_RESOLVING, &isResolving); if (!ok || HAS_FLAGS(isResolving, FLAG_RESOLVING)) { return ok; } JSObject *unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { return ThrowException(NS_ERROR_UNEXPECTED, cx); } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return JS_FALSE; } return XPCWrapper::AddProperty(cx, obj, JS_FALSE, unsafeObj, id, vp); }
static JSBool XPC_SJOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, JSBool aIsSet) { // We resolve toString to a function in our resolve hook. if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { return JS_TRUE; } obj = FindSafeObject(obj); NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); JSObject *unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { return ThrowException(NS_ERROR_UNEXPECTED, cx); } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return JS_FALSE; } JSObject *scopeFun = GetScopeFunction(cx, obj); if (!scopeFun) { return JS_FALSE; } { SafeCallGuard guard(cx, FindObjectPrincipals(cx, obj, unsafeObj)); if (!guard.ready()) { return JS_FALSE; } jsid interned_id; if (!JS_ValueToId(cx, id, &interned_id)) { return JS_FALSE; } if (aIsSet) { *vp = UnwrapJSValue(*vp); } JSBool ok = aIsSet ? js_SetPropertyByIdWithFakeFrame(cx, unsafeObj, scopeFun, interned_id, vp) : js_GetPropertyByIdWithFakeFrame(cx, unsafeObj, scopeFun, interned_id, vp); if (!ok) { return JS_FALSE; } } return WrapJSValue(cx, obj, *vp, vp); }
static inline JSObject * GetUnsafeObject(JSObject *obj) { obj = FindSafeObject(obj); if (!obj) { return nsnull; } return STOBJ_GET_PARENT(obj); }
static JSObject * XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) { obj = FindSafeObject(obj); NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); JSObject *unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { ThrowException(NS_ERROR_INVALID_ARG, cx); return nsnull; } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return nsnull; } JSObject *tmp = XPCWrapper::UnwrapGeneric(cx, &sXPC_XOW_JSClass, unsafeObj); if (tmp) { unsafeObj = tmp; // Repeat the CanCallerAccess check because the XOW is parented to our // scope's global object which makes the above CanCallerAccess call lie. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return nsnull; } } // Create our dummy SJOW. JSObject *wrapperIter = ::JS_NewObjectWithGivenProto(cx, &sXPC_SJOW_JSClass.base, nsnull, unsafeObj); if (!wrapperIter) { return nsnull; } if (!::JS_SetReservedSlot(cx, wrapperIter, XPC_SJOW_SLOT_IS_RESOLVING, JSVAL_ZERO)) { return nsnull; } JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter)); // Initialize the wrapper. return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, unsafeObj, keysonly); }
JSObject * GetUnsafeObject(JSContext *cx, JSObject *obj) { obj = FindSafeObject(obj); if (!obj) { return nsnull; } jsval v; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sWrappedObjSlot, &v)) { JS_ClearPendingException(cx); return nsnull; } return JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : nsnull; }
static JSBool XPC_SJOW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { obj = FindSafeObject(obj); if (!obj) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } JSObject *unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { // No unsafe object, nothing to stringify here, return "[object // XPCSafeJSObjectWrapper]" so callers know what they're looking // at. JSString *str = JS_NewStringCopyZ(cx, "[object XPCSafeJSObjectWrapper]"); if (!str) { return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return JS_FALSE; } { SafeCallGuard guard(cx, FindObjectPrincipals(cx, obj, unsafeObj)); if (!guard.ready()) { return JS_FALSE; } JSString *str = JS_ValueToString(cx, OBJECT_TO_JSVAL(unsafeObj)); if (!str) { return JS_FALSE; } *rval = STRING_TO_JSVAL(str); } return JS_TRUE; }
static JSBool XPC_SJOW_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *tmp = FindSafeObject(obj); JSObject *unsafeObj, *callThisObj = nsnull; if (tmp) { // A function wrapped in an XPCSafeJSObjectWrapper is being called // directly (i.e. safeObj.fun()), set obj to be the safe object // wrapper. In this case, the "this" object used when calling the // function will be the unsafe object gotten off of the safe // object. obj = tmp; } else { // A function wrapped in an XPCSafeJSObjectWrapper is being called // indirectly off of an object that's not a safe wrapper // (i.e. foo.bar = safeObj.fun; foo.bar()), set obj to be the safe // wrapper for the function, and use the object passed in as the // "this" object when calling the function. callThisObj = obj; // Check that the caller can access the object we're about to pass // in as "this" for the call we're about to make. if (!CanCallerAccess(cx, callThisObj)) { // CanCallerAccess() already threw for us. return JS_FALSE; } obj = FindSafeObject(JSVAL_TO_OBJECT(argv[-2])); if (!obj) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } } unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { return ThrowException(NS_ERROR_UNEXPECTED, cx); } if (!callThisObj) { callThisObj = unsafeObj; } JSObject *safeObj = JSVAL_TO_OBJECT(argv[-2]); JSObject *funToCall = GetUnsafeObject(safeObj); if (!funToCall) { // Someone has called XPCSafeJSObjectWrapper.prototype() causing // us to find a safe object wrapper without an unsafeObject as // its parent. That call shouldn't do anything, so bail here. return JS_TRUE; } // Check that the caller can access the unsafe object on which the // call is being made, and the actual function we're about to call. if (!CanCallerAccess(cx, unsafeObj) || !CanCallerAccess(cx, funToCall)) { // CanCallerAccess() already threw for us. return JS_FALSE; } JSObject *scopeFun = GetScopeFunction(cx, safeObj); if (!scopeFun) { return JS_FALSE; } { SafeCallGuard guard(cx, FindObjectPrincipals(cx, safeObj, funToCall)); for (uintN i = 0; i < argc; ++i) { argv[i] = UnwrapJSValue(argv[i]); } if (!js_CallFunctionValueWithFakeFrame(cx, callThisObj, scopeFun, OBJECT_TO_JSVAL(funToCall), argc, argv, rval)) { return JS_FALSE; } } return WrapJSValue(cx, obj, *rval, rval); }