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:
brendan%mozilla.org 2005-05-04 06:28:36 +00:00
Родитель c139803221
Коммит ea9fd4132c
10 изменённых файлов: 152 добавлений и 47 удалений

Просмотреть файл

@ -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);