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_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_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 JSBool XPC_XOW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { obj = GetWrapper(obj); if (!obj) { return ThrowException(NS_ERROR_UNEXPECTED, cx); } JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { // Someone's calling toString on our prototype. NS_NAMED_LITERAL_CSTRING(protoString, "[object XPCCrossOriginWrapper]"); JSString *str = JS_NewStringCopyN(cx, protoString.get(), protoString.Length()); if (!str) { return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { return ThrowException(NS_ERROR_FAILURE, cx); } nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull); if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); } rv = ssm->CheckPropertyAccess(cx, wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING), nsIXPCSecurityManager::ACCESS_GET_PROPERTY); } if (NS_FAILED(rv)) { return JS_FALSE; } XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); return XPCWrapper::NativeToString(cx, wn, argc, argv, rval, JS_FALSE); }
static JSBool XPC_XOW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { obj = GetWrapper(obj); JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { // No wrappedObj means that this is probably the prototype. *objp = nsnull; return JS_TRUE; } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { return ThrowException(NS_ERROR_FAILURE, cx); } JSBool privilegeEnabled; nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled); if (NS_FAILED(rv)) { if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { return JS_FALSE; } // We're dealing with a cross-origin lookup. Ensure that we're allowed to // resolve this property and resolve it if so. Otherwise, we deny access // and throw a security error. Note that this code does not actually check // to see if the property exists, that's dealt with below. XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); NS_ASSERTION(wn, "How did we wrap a non-WrappedNative?"); if (!IsValFrame(wrappedObj, id, wn)) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); } PRUint32 action = (flags & JSRESOLVE_ASSIGNING) ? XPCWrapper::sSecMgrSetProp : XPCWrapper::sSecMgrGetProp; rv = ssm->CheckPropertyAccess(cx, wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, id, action); if (NS_FAILED(rv)) { // The security manager threw an exception for us. return JS_FALSE; } } // We're out! We're allowed to resolve this property. return XPCWrapper::ResolveNativeProperty(cx, obj, wrappedObj, wn, id, flags, objp, JS_FALSE); } if (privilegeEnabled && !(obj = GetUXPCObject(cx, obj))) { return JS_FALSE; } if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { jsval oldSlotVal; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, &oldSlotVal) || !JS_SetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, INT_TO_JSVAL(JSVAL_TO_INT(oldSlotVal) | FLAG_RESOLVING))) { return JS_FALSE; } JSBool ok = JS_DefineFunction(cx, obj, "toString", XPC_XOW_toString, 0, 0) != nsnull; JS_SetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, oldSlotVal); if (ok) { *objp = obj; } return ok; } return XPCWrapper::NewResolve(cx, obj, JS_TRUE, wrappedObj, id, flags, objp); }
static JSBool XPC_XOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, JSBool isSet) { if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { return JS_TRUE; } // Don't do anything if we already resolved to a wrapped function in // NewResolve. In practice, this means that this is a wrapped eval // function. jsval v = *vp; if (!JSVAL_IS_PRIMITIVE(v) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v)) && JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)) == XPC_XOW_FunctionWrapper) { return JS_TRUE; } JSObject *origObj = obj; obj = GetWrapper(obj); if (!obj) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { return ThrowException(NS_ERROR_FAILURE, cx); } AUTO_MARK_JSVAL(ccx, vp); JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } JSBool privilegeEnabled; nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled); if (NS_FAILED(rv)) { if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { return JS_FALSE; } // This is a request to get a property across origins. We need to // determine if this property is allAccess. If it is, then we need to // actually get the property. If not, we simply need to throw an // exception. XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); NS_ASSERTION(wn, "How did we wrap a non-WrappedNative?"); if (!IsValFrame(wrappedObj, id, wn)) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); } rv = ssm->CheckPropertyAccess(cx, wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, id, isSet ? XPCWrapper::sSecMgrSetProp : XPCWrapper::sSecMgrGetProp); if (NS_FAILED(rv)) { // The security manager threw an exception for us. return JS_FALSE; } } return XPCWrapper::GetOrSetNativeProperty(cx, obj, wn, id, vp, isSet, JS_FALSE); } JSObject *proto = nsnull; // Initialize this to quiet GCC. JSBool checkProto = (isSet && id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_PROTO)); if (checkProto) { proto = STOBJ_GET_PROTO(wrappedObj); } // Same origin, pass this request along as though nothing interesting // happened. jsid asId; if (!JS_ValueToId(cx, id, &asId)) { return JS_FALSE; } JSBool ok = isSet ? JS_SetPropertyById(cx, wrappedObj, asId, vp) : JS_GetPropertyById(cx, wrappedObj, asId, vp); if (!ok) { return JS_FALSE; } if (checkProto) { JSObject *newProto = STOBJ_GET_PROTO(wrappedObj); // If code is trying to set obj.__proto__ and we're on obj's // prototype chain, then the JS_GetPropertyById above will do the // wrong thing if wrappedObj still delegates to Object.prototype. // However, it's hard to figure out if wrappedObj still does // delegate to Object.prototype so check to see if proto changed as a // result of setting __proto__. if (origObj != obj) { // Undo the damage. if (!JS_SetPrototype(cx, wrappedObj, proto) || !JS_SetPrototype(cx, origObj, newProto)) { return JS_FALSE; } } else if (newProto) { // __proto__ setting is a bad hack, people shouldn't do it. In // this case we're setting the direct prototype of a XOW object, // in the interests of sanity only allow it to be set to null in // this case. JS_SetPrototype(cx, wrappedObj, proto); JS_ReportError(cx, "invalid __proto__ value (can only be set to null)"); return JS_FALSE; } } return WrapSameOriginProp(cx, obj, vp); }