mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Find active native function principals when walking the JS stack, and beef up eval-ish native safeguards (281988, r=shaver/caillon, sr=jst, a=drivers).
This commit is contained in:
parent
c139803221
commit
ea9fd4132c
@ -415,9 +415,13 @@ private:
|
||||
|
||||
// Returns null if a principal cannot be found. Note that rv can be NS_OK
|
||||
// when this happens -- this means that there was no script associated
|
||||
// with the function object. Callers MUST pass in a non-null rv here.
|
||||
// with the function object, and no global object associated with the scope
|
||||
// of obj (the last object on its parent chain). If the caller is walking
|
||||
// the JS stack, fp must point to the current frame in the stack iteration.
|
||||
// Callers MUST pass in a non-null rv here.
|
||||
static nsIPrincipal*
|
||||
GetFunctionObjectPrincipal(JSContext* cx, JSObject* obj, nsresult* rv);
|
||||
GetFunctionObjectPrincipal(JSContext* cx, JSObject* obj, JSStackFrame *fp,
|
||||
nsresult* rv);
|
||||
|
||||
// Returns null if a principal cannot be found. Note that rv can be NS_OK
|
||||
// when this happens -- this means that there was no script
|
||||
|
@ -1420,11 +1420,12 @@ NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
|
||||
void *aTargetObj)
|
||||
{
|
||||
//-- This check is called for event handlers
|
||||
// This check is called for event handlers
|
||||
nsresult rv;
|
||||
nsIPrincipal* subject =
|
||||
GetFunctionObjectPrincipal(aCx, (JSObject *)aFunObj, &rv);
|
||||
//-- If subject is null, get a principal from the function object's scope.
|
||||
GetFunctionObjectPrincipal(aCx, (JSObject *)aFunObj, nsnull, &rv);
|
||||
|
||||
// If subject is null, get a principal from the function object's scope.
|
||||
if (NS_SUCCEEDED(rv) && !subject)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@ -1832,6 +1833,7 @@ nsScriptSecurityManager::GetScriptPrincipal(JSContext *cx,
|
||||
nsIPrincipal*
|
||||
nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
|
||||
JSObject *obj,
|
||||
JSStackFrame *fp,
|
||||
nsresult *rv)
|
||||
{
|
||||
NS_PRECONDITION(rv, "Null out param");
|
||||
@ -1839,24 +1841,62 @@ nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
|
||||
JSScript *script = JS_GetFunctionScript(cx, fun);
|
||||
|
||||
*rv = NS_OK;
|
||||
if (script)
|
||||
if (!script || JS_GetFunctionObject(fun) != obj)
|
||||
{
|
||||
if (JS_GetFunctionObject(fun) != obj)
|
||||
{
|
||||
// Function is a clone, its prototype was precompiled from
|
||||
// brutally shared chrome. For this case only, get the
|
||||
// principals from the clone's scope since there are no
|
||||
// reliable principals compiled into the function.
|
||||
nsIPrincipal* result = doGetObjectPrincipal(cx, obj);
|
||||
if (!result)
|
||||
*rv = NS_ERROR_FAILURE;
|
||||
return result;
|
||||
}
|
||||
// Here, obj is either a native method or a cloned function
|
||||
// object.
|
||||
//
|
||||
// In the native method case, get the object principals of
|
||||
// the particular function object (obj) being called here.
|
||||
// We don't allow the [[Parent]] slot to be set, so instead
|
||||
// of walking up the JS stack to find a scripted caller, it
|
||||
// is necessary and sufficient to get object principals.
|
||||
//
|
||||
// It is necessary because we do allow distinguished chrome
|
||||
// and other privileged trust domains to get and call content
|
||||
// natives. It is sufficient because we do *not* allow a
|
||||
// non-chrome trust domain to access any other domain's
|
||||
// native function object references.
|
||||
//
|
||||
// This bears repeating: it is crucially important that
|
||||
// unprivileged content not be able to access natives from
|
||||
// any trust domain other than its own.
|
||||
//
|
||||
// In the cloned function case, the prototype of the clone
|
||||
// (that is, obj.__proto__) was precompiled from brutally
|
||||
// shared chrome, or else it's a lambda or nested function.
|
||||
// The general case here is a function compiled against a
|
||||
// different scope than the one it is parented by at runtime,
|
||||
// hence the creation of a clone to carry the correct scope
|
||||
// chain linkage.
|
||||
//
|
||||
// Since principals follow scope, we must get the object
|
||||
// principal from the clone's scope chain. There are no
|
||||
// reliable principals compiled into the function itself.
|
||||
|
||||
return GetScriptPrincipal(cx, script, rv);
|
||||
nsIPrincipal *result = doGetObjectPrincipal(cx, obj);
|
||||
if (!result)
|
||||
*rv = NS_ERROR_FAILURE;
|
||||
return result;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
JSScript *frameScript = fp ? JS_GetFrameScript(cx, fp) : nsnull;
|
||||
|
||||
if (frameScript && frameScript != script)
|
||||
{
|
||||
// There is a frame script, and it's different from the
|
||||
// function script. In this case we're dealing with either
|
||||
// an eval or a Script object, and in these cases the
|
||||
// principal we want is in the frame's script, not in the
|
||||
// function's script. The function's script is where the
|
||||
// eval-calling code came from, not where the eval or new
|
||||
// Script object came from, and we want the principal of
|
||||
// the eval function object or new Script object.
|
||||
|
||||
script = frameScript;
|
||||
}
|
||||
|
||||
return GetScriptPrincipal(cx, script, rv);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -1874,7 +1914,7 @@ nsScriptSecurityManager::GetFramePrincipal(JSContext *cx,
|
||||
return GetScriptPrincipal(cx, script, rv);
|
||||
}
|
||||
|
||||
nsIPrincipal* result = GetFunctionObjectPrincipal(cx, obj, rv);
|
||||
nsIPrincipal* result = GetFunctionObjectPrincipal(cx, obj, fp, rv);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (NS_SUCCEEDED(*rv) && !result)
|
||||
|
@ -651,7 +651,8 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
|
||||
|
||||
++sContextCount;
|
||||
|
||||
mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS
|
||||
mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS |
|
||||
JSOPTION_NATIVE_BRANCH_CALLBACK
|
||||
#ifdef DEBUG
|
||||
| JSOPTION_STRICT // lint catching for development
|
||||
#endif
|
||||
@ -2055,6 +2056,30 @@ nsJSEnvironment::Startup()
|
||||
gCollation = nsnull;
|
||||
}
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSPrincipals *)
|
||||
ObjectPrincipalFinder(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsresult rv =
|
||||
sSecurityManager->GetObjectPrincipal(cx, obj,
|
||||
getter_AddRefs(principal));
|
||||
|
||||
if (NS_FAILED(rv) || !principal) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
JSPrincipals *jsPrincipals = nsnull;
|
||||
principal->GetJSPrincipals(cx, &jsPrincipals);
|
||||
|
||||
// nsIPrincipal::GetJSPrincipals() returns a strong reference to the
|
||||
// JS principals, but the caller of this function expects a weak
|
||||
// reference. So we need to release here.
|
||||
|
||||
JSPRINCIPALS_DROP(cx, jsPrincipals);
|
||||
|
||||
return jsPrincipals;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsJSEnvironment::Init()
|
||||
@ -2087,8 +2112,16 @@ nsJSEnvironment::Init()
|
||||
NS_ASSERTION(!gOldJSGCCallback,
|
||||
"nsJSEnvironment initialized more than once");
|
||||
|
||||
// Save the old GC callback to chain to it, for GC-observing generality.
|
||||
gOldJSGCCallback = ::JS_SetGCCallbackRT(sRuntime, DOMGCCallback);
|
||||
|
||||
// No chaining to a pre-existing callback here, we own this problem space.
|
||||
#ifdef NS_DEBUG
|
||||
JSObjectPrincipalsFinder oldfop =
|
||||
#endif
|
||||
::JS_SetObjectPrincipalsFinder(sRuntime, ObjectPrincipalFinder);
|
||||
NS_ASSERTION(!oldfop, " fighting over the findObjectPrincipals callback!");
|
||||
|
||||
// Set these global xpconnect options...
|
||||
nsIXPConnect *xpc = nsContentUtils::XPConnect();
|
||||
xpc->SetCollectGarbageOnMainThreadOnly(PR_TRUE);
|
||||
|
@ -3132,12 +3132,12 @@ JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px)
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObjectPrincipalsFinder)
|
||||
JS_SetObjectPrincipalsFinder(JSContext *cx, JSObjectPrincipalsFinder fop)
|
||||
JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop)
|
||||
{
|
||||
JSObjectPrincipalsFinder oldfop;
|
||||
|
||||
oldfop = cx->findObjectPrincipals;
|
||||
cx->findObjectPrincipals = fop;
|
||||
oldfop = rt->findObjectPrincipals;
|
||||
rt->findObjectPrincipals = fop;
|
||||
return oldfop;
|
||||
}
|
||||
|
||||
|
@ -1292,7 +1292,7 @@ extern JS_PUBLIC_API(JSPrincipalsTranscoder)
|
||||
JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px);
|
||||
|
||||
extern JS_PUBLIC_API(JSObjectPrincipalsFinder)
|
||||
JS_SetObjectPrincipalsFinder(JSContext *cx, JSObjectPrincipalsFinder fop);
|
||||
JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
|
@ -224,6 +224,9 @@ struct JSRuntime {
|
||||
/* Security principals serialization support. */
|
||||
JSPrincipalsTranscoder principalsTranscoder;
|
||||
|
||||
/* Optional hook to find principals for an object in this runtime. */
|
||||
JSObjectPrincipalsFinder findObjectPrincipals;
|
||||
|
||||
/* Shared scope property tree, and allocator for its nodes. */
|
||||
JSDHashTable propertyTreeHash;
|
||||
JSScopeProperty *propertyFreeList;
|
||||
@ -465,9 +468,6 @@ struct JSContext {
|
||||
/* PDL of stack headers describing stack slots not rooted by argv, etc. */
|
||||
JSStackHeader *stackHeaders;
|
||||
|
||||
/* Optional hook to find principals for an object being accessed on cx. */
|
||||
JSObjectPrincipalsFinder findObjectPrincipals;
|
||||
|
||||
/* Optional stack of scoped local GC roots. */
|
||||
JSLocalRootStack *localRootStack;
|
||||
};
|
||||
|
@ -630,6 +630,12 @@ JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
|
||||
return FUN_SCRIPT(fun);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSNative)
|
||||
JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
return FUN_NATIVE(fun);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSPrincipals *)
|
||||
JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
|
||||
{
|
||||
@ -675,12 +681,16 @@ JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
|
||||
JS_PUBLIC_API(JSPrincipals *)
|
||||
JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
if (fp->fun && cx->findObjectPrincipals) {
|
||||
JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]);
|
||||
if (fp->fun) {
|
||||
JSRuntime *rt = cx->runtime;
|
||||
|
||||
if (fp->fun->object != callee)
|
||||
return cx->findObjectPrincipals(cx, callee);
|
||||
/* FALL THROUGH */
|
||||
if (rt->findObjectPrincipals) {
|
||||
JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]);
|
||||
|
||||
if (fp->fun->object != callee)
|
||||
return rt->findObjectPrincipals(cx, callee);
|
||||
/* FALL THROUGH */
|
||||
}
|
||||
}
|
||||
if (fp->script)
|
||||
return fp->script->principals;
|
||||
@ -690,8 +700,11 @@ JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
|
||||
JS_PUBLIC_API(JSPrincipals *)
|
||||
JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
|
||||
{
|
||||
if (cx->findObjectPrincipals)
|
||||
return cx->findObjectPrincipals(cx, JSVAL_TO_OBJECT(fp->argv[-2]));
|
||||
JSRuntime *rt;
|
||||
|
||||
rt = cx->runtime;
|
||||
if (rt->findObjectPrincipals)
|
||||
return rt->findObjectPrincipals(cx, JSVAL_TO_OBJECT(fp->argv[-2]));
|
||||
if (!caller)
|
||||
return NULL;
|
||||
return JS_StackFramePrincipals(cx, caller);
|
||||
|
@ -131,6 +131,9 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno);
|
||||
extern JS_PUBLIC_API(JSScript *)
|
||||
JS_GetFunctionScript(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JS_PUBLIC_API(JSNative)
|
||||
JS_GetFunctionNative(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JS_PUBLIC_API(JSPrincipals *)
|
||||
JS_GetScriptPrincipals(JSContext *cx, JSScript *script);
|
||||
|
||||
@ -164,10 +167,11 @@ extern JS_PUBLIC_API(JSPrincipals *)
|
||||
JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
/*
|
||||
* Like JS_StackFramePrincipals(cx, caller), but if cx->findObjectPrincipals
|
||||
* is non-null, return the object principals for fp's callee function object
|
||||
* (fp->argv[-2]), which is eval, Function, or a similar eval-like method.
|
||||
* The caller parameter should be the result of JS_GetScriptedCaller(cx, fp).
|
||||
* This API is like JS_StackFramePrincipals(cx, caller), except that if
|
||||
* cx->runtime->findObjectPrincipals is non-null, it returns the object
|
||||
* principals for fp's callee function object (fp->argv[-2]), which is eval,
|
||||
* Function, or a similar eval-like method. The caller parameter should be
|
||||
* the result of JS_GetScriptedCaller(cx, fp).
|
||||
*
|
||||
* All eval-like methods must use JS_EvalFramePrincipals to acquire a weak
|
||||
* reference to the correct principals for the eval call to be secure, given
|
||||
|
@ -1008,6 +1008,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
JSPrincipals *principals, *scopePrincipals;
|
||||
JSScript *script;
|
||||
JSBool ok;
|
||||
JSRuntime *rt;
|
||||
#if JS_HAS_EVAL_THIS_SCOPE
|
||||
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
|
||||
JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
|
||||
@ -1125,10 +1126,14 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
#endif
|
||||
|
||||
/* Belt-and-braces: check that this eval callee has access to scopeobj. */
|
||||
if (cx->findObjectPrincipals) {
|
||||
scopePrincipals = cx->findObjectPrincipals(cx, scopeobj);
|
||||
if (scopePrincipals != principals)
|
||||
scopeobj = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
|
||||
rt = cx->runtime;
|
||||
if (rt->findObjectPrincipals) {
|
||||
scopePrincipals = rt->findObjectPrincipals(cx, scopeobj);
|
||||
if (scopePrincipals != principals) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_INDIRECT_CALL, js_eval_str);
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
|
||||
|
@ -238,6 +238,7 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
JSScript *script;
|
||||
JSObject *scopeobj, *parent;
|
||||
JSStackFrame *fp, *caller;
|
||||
JSRuntime *rt;
|
||||
JSPrincipals *scopePrincipals;
|
||||
|
||||
if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
|
||||
@ -299,10 +300,15 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
}
|
||||
|
||||
/* Belt-and-braces: check that this script object has access to scopeobj. */
|
||||
if (cx->findObjectPrincipals) {
|
||||
scopePrincipals = cx->findObjectPrincipals(cx, scopeobj);
|
||||
if (scopePrincipals != script->principals)
|
||||
scopeobj = OBJ_GET_PARENT(cx, obj);
|
||||
rt = cx->runtime;
|
||||
if (rt->findObjectPrincipals) {
|
||||
scopePrincipals = rt->findObjectPrincipals(cx, scopeobj);
|
||||
if (scopePrincipals != script->principals) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_INDIRECT_CALL,
|
||||
"Script.prototype.exec");
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
|
||||
|
Loading…
Reference in New Issue
Block a user