From 006c524d3bf663d6824d3df68abc39c0d2ced856 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Fri, 5 Sep 2008 16:24:53 -0700 Subject: [PATCH] Bug 451729 - " Allow runtime's security callbacks to be overridden by a context". r=brendan. --- js/src/jsapi.cpp | 48 ++++++++++++++++++++++++--------------------- js/src/jsapi.h | 24 ++++++++++++++++------- js/src/jscntxt.h | 15 ++++++-------- js/src/jsdbgapi.cpp | 17 ++++++++-------- js/src/jsexn.cpp | 6 +++++- js/src/jsfun.cpp | 12 ++++++++---- js/src/jsinterp.cpp | 9 ++++++--- js/src/jsobj.cpp | 28 +++++++++++++++----------- js/src/jspubtd.h | 1 + js/src/jsscript.cpp | 10 ++++++---- 10 files changed, 101 insertions(+), 69 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index d83e4dcb45f5..d68a39503111 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4136,16 +4136,6 @@ JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp); } -JS_PUBLIC_API(JSCheckAccessOp) -JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb) -{ - JSCheckAccessOp oldacb; - - oldacb = rt->checkObjectAccess; - rt->checkObjectAccess = acb; - return oldacb; -} - static JSBool ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, uint32 index, uint32 limit) @@ -4209,24 +4199,38 @@ JS_DropPrincipals(JSContext *cx, JSPrincipals *principals) } #endif -JS_PUBLIC_API(JSPrincipalsTranscoder) -JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px) +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetRuntimeSecurityCallbacks(JSRuntime *rt, JSSecurityCallbacks *callbacks) { - JSPrincipalsTranscoder oldpx; + JSSecurityCallbacks *oldcallbacks; - oldpx = rt->principalsTranscoder; - rt->principalsTranscoder = px; - return oldpx; + oldcallbacks = rt->securityCallbacks; + rt->securityCallbacks = callbacks; + return oldcallbacks; } -JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop) +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetRuntimeSecurityCallbacks(JSRuntime *rt) { - JSObjectPrincipalsFinder oldfop; + return rt->securityCallbacks; +} - oldfop = rt->findObjectPrincipals; - rt->findObjectPrincipals = fop; - return oldfop; +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetContextSecurityCallbacks(JSContext *cx, JSSecurityCallbacks *callbacks) +{ + JSSecurityCallbacks *oldcallbacks; + + oldcallbacks = cx->securityCallbacks; + cx->securityCallbacks = callbacks; + return oldcallbacks; +} + +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetSecurityCallbacks(JSContext *cx) +{ + return cx->securityCallbacks + ? cx->securityCallbacks + : cx->runtime->securityCallbacks; } JS_PUBLIC_API(JSFunction *) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 402a8975a590..8f207d1442c1 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1794,9 +1794,6 @@ extern JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); -extern JS_PUBLIC_API(JSCheckAccessOp) -JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb); - extern JS_PUBLIC_API(JSBool) JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); @@ -1840,11 +1837,24 @@ JS_DropPrincipals(JSContext *cx, JSPrincipals *principals); : (principals)->refcount) #endif -extern JS_PUBLIC_API(JSPrincipalsTranscoder) -JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px); -extern JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop); +struct JSSecurityCallbacks { + JSCheckAccessOp checkObjectAccess; + JSPrincipalsTranscoder principalsTranscoder; + JSObjectPrincipalsFinder findObjectPrincipals; +}; + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetRuntimeSecurityCallbacks(JSRuntime *rt, JSSecurityCallbacks *callbacks); + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetRuntimeSecurityCallbacks(JSRuntime *rt); + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetContextSecurityCallbacks(JSContext *cx, JSSecurityCallbacks *callbacks); + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetSecurityCallbacks(JSContext *cx); /************************************************************************/ diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 56aa51b09545..cd70a7b33e85 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -391,16 +391,10 @@ struct JSRuntime { uint32 debuggerMutations; /* - * Check property accessibility for objects of arbitrary class. Used at - * present to check f.caller accessibility for any function object f. + * Security callbacks set on the runtime are used by each context unless + * an override is set on the context. */ - JSCheckAccessOp checkObjectAccess; - - /* Security principals serialization support. */ - JSPrincipalsTranscoder principalsTranscoder; - - /* Optional hook to find principals for an object in this runtime. */ - JSObjectPrincipalsFinder findObjectPrincipals; + JSSecurityCallbacks *securityCallbacks; /* * Shared scope property tree, and arena-pool for allocating its nodes. @@ -887,6 +881,9 @@ struct JSContext { /* Debug hooks associated with the current context. */ JSDebugHooks *debugHooks; + + /* Security callbacks that override any defined on the runtime. */ + JSSecurityCallbacks *securityCallbacks; }; #ifdef JS_THREADSAFE diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 33b6a5d34299..2a0d9a6d58b0 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -1007,12 +1007,13 @@ JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) JS_PUBLIC_API(JSPrincipals *) JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) { - if (fp->fun) { - JSRuntime *rt = cx->runtime; + JSSecurityCallbacks *callbacks; - if (rt->findObjectPrincipals) { + if (fp->fun) { + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { if (FUN_OBJECT(fp->fun) != fp->callee) - return rt->findObjectPrincipals(cx, fp->callee); + return callbacks->findObjectPrincipals(cx, fp->callee); /* FALL THROUGH */ } } @@ -1024,12 +1025,12 @@ JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) JS_PUBLIC_API(JSPrincipals *) JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller) { - JSRuntime *rt; JSPrincipals *principals, *callerPrincipals; + JSSecurityCallbacks *callbacks; - rt = cx->runtime; - if (rt->findObjectPrincipals) { - principals = rt->findObjectPrincipals(cx, fp->callee); + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { + principals = callbacks->findObjectPrincipals(cx, fp->callee); } else { principals = NULL; } diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 5136d74c4bf7..1ea23e52b988 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -248,6 +248,7 @@ static JSBool InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, JSString *filename, uintN lineno, JSErrorReport *report) { + JSSecurityCallbacks *callbacks; JSCheckAccessOp checkAccess; JSErrorReporter older; JSExceptionState *state; @@ -268,7 +269,10 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, * so we can suppress any checkAccess failures. Such failures should stop * the backtrace procedure, not result in a failure of this constructor. */ - checkAccess = cx->runtime->checkObjectAccess; + callbacks = JS_GetSecurityCallbacks(cx); + checkAccess = callbacks + ? callbacks->checkObjectAccess + : NULL; older = JS_SetErrorReporter(cx, NULL); state = JS_SaveExceptionState(cx); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 25462cd805b9..7a594bde7dfd 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -966,6 +966,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) jsint slot; JSFunction *fun; JSStackFrame *fp; + JSSecurityCallbacks *callbacks; if (!JSVAL_IS_INT(id)) return JS_TRUE; @@ -1040,10 +1041,13 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) *vp = OBJECT_TO_JSVAL(fp->down->callee); else *vp = JSVAL_NULL; - if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) { - id = ATOM_KEY(cx->runtime->atomState.callerAtom); - if (!cx->runtime->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) - return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(*vp)) { + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->checkObjectAccess) { + id = ATOM_KEY(cx->runtime->atomState.callerAtom); + if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) + return JS_FALSE; + } } break; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index c0ec18df8db8..61c2f481b9fc 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1408,6 +1408,8 @@ JSBool js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) { + JSSecurityCallbacks *callbacks; + /* * js_InternalInvoke could result in another try to get or set the same id * again, see bug 355497. @@ -1430,11 +1432,12 @@ js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, * many embeddings have no security policy at all. */ JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); - if (cx->runtime->checkObjectAccess && + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && + callbacks->checkObjectAccess && VALUE_IS_FUNCTION(cx, fval) && FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) && - !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, - &fval)) { + !callbacks->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) { return JS_FALSE; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index c75b484f4b8b..8ca4dce969c9 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1083,13 +1083,13 @@ JSBool js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, JSPrincipals *principals, JSAtom *caller) { - JSRuntime *rt; + JSSecurityCallbacks *callbacks; JSPrincipals *scopePrincipals; const char *callerstr; - rt = cx->runtime; - if (rt->findObjectPrincipals) { - scopePrincipals = rt->findObjectPrincipals(cx, scopeobj); + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { + scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj); if (!principals || !scopePrincipals || !principals->subsume(principals, scopePrincipals)) { callerstr = js_AtomToPrintableString(cx, caller); @@ -1146,8 +1146,11 @@ js_ComputeFilename(JSContext *cx, JSStackFrame *caller, JSPrincipals *principals, uintN *linenop) { uint32 flags; +#ifdef DEBUG + JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); +#endif - JS_ASSERT(principals || !cx->runtime->findObjectPrincipals); + JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals)); flags = JS_GetScriptFilenameFlags(caller->script); if ((flags & JSFILENAME_PROTECTED) && principals && @@ -1370,7 +1373,7 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, void *closure) { JSObject *callable; - JSRuntime *rt; + JSSecurityCallbacks *callbacks; JSStackFrame *caller; JSPrincipals *subject, *watcher; JSResolvingKey key; @@ -1381,8 +1384,8 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, callable = (JSObject *) closure; - rt = cx->runtime; - if (rt->findObjectPrincipals) { + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { /* Skip over any obj_watch_* frames between us and the real subject. */ caller = JS_GetScriptedCaller(cx, cx->fp); if (caller) { @@ -1390,7 +1393,7 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, * Only call the watch handler if the watcher is allowed to watch * the currently executing script. */ - watcher = rt->findObjectPrincipals(cx, callable); + watcher = callbacks->findObjectPrincipals(cx, callable); subject = JS_StackFramePrincipals(cx, caller); if (watcher && subject && !watcher->subsume(watcher, subject)) { @@ -4465,6 +4468,7 @@ js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, JSProperty *prop; JSClass *clasp; JSScopeProperty *sprop; + JSSecurityCallbacks *callbacks; JSCheckAccessOp check; writing = (mode & JSACC_WRITE) != 0; @@ -4532,8 +4536,10 @@ js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, */ clasp = OBJ_GET_CLASS(cx, pobj); check = clasp->checkAccess; - if (!check) - check = cx->runtime->checkObjectAccess; + if (!check) { + callbacks = JS_GetSecurityCallbacks(cx); + check = callbacks ? callbacks->checkObjectAccess : NULL; + } return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp); } diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 9937c4640314..13283d75c346 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -153,6 +153,7 @@ typedef struct JSString JSString; typedef struct JSXDRState JSXDRState; typedef struct JSExceptionState JSExceptionState; typedef struct JSLocaleCallbacks JSLocaleCallbacks; +typedef struct JSSecurityCallbacks JSSecurityCallbacks; /* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 93fa594c0e63..3546ae89de8e 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -428,6 +428,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) uint32 encodeable; JSBool filenameWasSaved; jssrcnote *notes, *sn; + JSSecurityCallbacks *callbacks; cx = xdr->cx; script = *scriptp; @@ -547,25 +548,26 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) goto error; } + callbacks = JS_GetSecurityCallbacks(cx); if (xdr->mode == JSXDR_ENCODE) { principals = script->principals; - encodeable = (cx->runtime->principalsTranscoder != NULL); + encodeable = callbacks && callbacks->principalsTranscoder; if (!JS_XDRUint32(xdr, &encodeable)) goto error; if (encodeable && - !cx->runtime->principalsTranscoder(xdr, &principals)) { + !callbacks->principalsTranscoder(xdr, &principals)) { goto error; } } else { if (!JS_XDRUint32(xdr, &encodeable)) goto error; if (encodeable) { - if (!cx->runtime->principalsTranscoder) { + if (!(callbacks && callbacks->principalsTranscoder)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DECODE_PRINCIPALS); goto error; } - if (!cx->runtime->principalsTranscoder(xdr, &principals)) + if (!callbacks->principalsTranscoder(xdr, &principals)) goto error; script->principals = principals; }