mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Stop evals and Script object calls/execs that cross trust domains (289074, r=shaver, sr=jst, a=drivers).
This commit is contained in:
parent
fc3153b467
commit
b02c276f35
@ -367,6 +367,12 @@ private:
|
||||
const char* aClassName, jsval aProperty,
|
||||
void** aCachedClassPolicy);
|
||||
|
||||
void
|
||||
ThrowAccessDeniedException(JSContext *cx,
|
||||
PRUint32 aAction,
|
||||
const char *aClassName,
|
||||
jsval aProperty);
|
||||
|
||||
nsresult
|
||||
CheckSameOriginPrincipalInternal(nsIPrincipal* aSubject,
|
||||
nsIPrincipal* aObject,
|
||||
|
@ -444,6 +444,8 @@ nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JSObject *obj,
|
||||
// a different trust domain.
|
||||
// 2. A user-defined getter or setter function accessible on another
|
||||
// trust domain's window or document object.
|
||||
// 3. An eval or Script.prototype.exec attempt from trusted content where
|
||||
// the eval or exec method comes from untrusted content.
|
||||
// If *vp is not a primitive, some new JS engine call to this hook was
|
||||
// added, but we can handle that case too -- if a primitive value in a
|
||||
// property of obj is being accessed, we should use obj as the target
|
||||
@ -451,6 +453,36 @@ nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JSObject *obj,
|
||||
NS_ASSERTION(!JSVAL_IS_PRIMITIVE(*vp), "unexpected target property value");
|
||||
JSObject* target = JSVAL_IS_PRIMITIVE(*vp) ? obj : JSVAL_TO_OBJECT(*vp);
|
||||
|
||||
NS_ASSERTION(!(mode & JSACC_WRITE), "unexpected write access check");
|
||||
if (mode & JSACC_EXEC)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIPrincipal> subjectPrincipal;
|
||||
rv = ssm->GetSubjectPrincipal(cx, getter_AddRefs(subjectPrincipal));
|
||||
if (NS_FAILED(rv))
|
||||
return JS_FALSE;
|
||||
|
||||
nsCOMPtr<nsIPrincipal> objectPrincipal;
|
||||
rv = ssm->doGetObjectPrincipal(cx, target,
|
||||
getter_AddRefs(objectPrincipal));
|
||||
if (NS_FAILED(rv))
|
||||
return JS_FALSE;
|
||||
|
||||
// If any eval or exec call is crossing a trust boundary, whether
|
||||
// chrome to content, content to chrome, or between two different
|
||||
// origins for content, stop it cold here.
|
||||
if (subjectPrincipal != objectPrincipal)
|
||||
{
|
||||
ssm->ThrowAccessDeniedException(cx,
|
||||
nsIXPCSecurityManager::ACCESS_CALL_METHOD,
|
||||
JS_GetClass(cx, obj)->name,
|
||||
id);
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// Do the same-origin check -- this sets a JS exception if the check fails.
|
||||
// Pass the parent object's class name, as we have no class-info for it.
|
||||
nsresult rv =
|
||||
@ -765,51 +797,59 @@ nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction,
|
||||
printf("CheckXPCPerms DENIED.\n");
|
||||
#endif
|
||||
|
||||
if (NS_FAILED(rv)) //-- Security tests failed, access is denied, report error
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
nsAutoString stringName;
|
||||
switch(aAction)
|
||||
{
|
||||
case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
|
||||
stringName.AssignLiteral("GetPropertyDenied");
|
||||
break;
|
||||
case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
|
||||
stringName.AssignLiteral("SetPropertyDenied");
|
||||
break;
|
||||
case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
|
||||
stringName.AssignLiteral("CallMethodDenied");
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 className(classInfoData.GetName());
|
||||
const PRUnichar *formatStrings[] =
|
||||
{
|
||||
className.get(),
|
||||
JSValIDToString(cx, aProperty)
|
||||
};
|
||||
|
||||
nsXPIDLString errorMsg;
|
||||
// We need to keep our existing failure rv and not override it
|
||||
// with a likely success code from the following string bundle
|
||||
// call in order to throw the correct security exception later.
|
||||
nsresult rv2 = sStrBundle->FormatStringFromName(stringName.get(),
|
||||
formatStrings,
|
||||
NS_ARRAY_LENGTH(formatStrings),
|
||||
getter_Copies(errorMsg));
|
||||
NS_ENSURE_SUCCESS(rv2, rv2);
|
||||
|
||||
SetPendingException(cx, errorMsg.get());
|
||||
|
||||
if (sXPConnect)
|
||||
{
|
||||
nsCOMPtr<nsIXPCNativeCallContext> xpcCallContext;
|
||||
sXPConnect->GetCurrentNativeCallContext(getter_AddRefs(xpcCallContext));
|
||||
if (xpcCallContext)
|
||||
xpcCallContext->SetExceptionWasThrown(PR_TRUE);
|
||||
}
|
||||
//-- Security tests failed, access is denied, report error
|
||||
ThrowAccessDeniedException(cx, aAction, classInfoData.GetName(),
|
||||
aProperty);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
void
|
||||
nsScriptSecurityManager::ThrowAccessDeniedException(JSContext *cx,
|
||||
PRUint32 aAction,
|
||||
const char *aClassName,
|
||||
jsval aProperty)
|
||||
{
|
||||
nsAutoString stringName;
|
||||
switch(aAction)
|
||||
{
|
||||
case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
|
||||
stringName.AssignLiteral("GetPropertyDenied");
|
||||
break;
|
||||
case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
|
||||
stringName.AssignLiteral("SetPropertyDenied");
|
||||
break;
|
||||
case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
|
||||
stringName.AssignLiteral("CallMethodDenied");
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 className(aClassName);
|
||||
const PRUnichar *formatStrings[] =
|
||||
{
|
||||
className.get(),
|
||||
JSValIDToString(cx, aProperty)
|
||||
};
|
||||
|
||||
// If FormatStringFromName fails, we'll still throw an empty string
|
||||
// as an exception, and fail the caller.
|
||||
nsXPIDLString errorMsg;
|
||||
sStrBundle->FormatStringFromName(stringName.get(),
|
||||
formatStrings,
|
||||
NS_ARRAY_LENGTH(formatStrings),
|
||||
getter_Copies(errorMsg));
|
||||
|
||||
SetPendingException(cx, errorMsg.get());
|
||||
|
||||
if (sXPConnect)
|
||||
{
|
||||
nsCOMPtr<nsIXPCNativeCallContext> xpcCallContext;
|
||||
sXPConnect->GetCurrentNativeCallContext(getter_AddRefs(xpcCallContext));
|
||||
if (xpcCallContext)
|
||||
xpcCallContext->SetExceptionWasThrown(PR_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScriptSecurityManager::CheckSameOriginPrincipalInternal(nsIPrincipal* aSubject,
|
||||
@ -2957,8 +2997,8 @@ nsScriptSecurityManager::InitPolicies()
|
||||
|
||||
nsCAutoString sitesPrefName(
|
||||
NS_LITERAL_CSTRING(sPolicyPrefix) +
|
||||
nsDependentCString(nameBegin) +
|
||||
NS_LITERAL_CSTRING(".sites"));
|
||||
nsDependentCString(nameBegin) +
|
||||
NS_LITERAL_CSTRING(".sites"));
|
||||
nsXPIDLCString domainList;
|
||||
rv = mSecurityPref->SecurityGetCharPref(sitesPrefName.get(),
|
||||
getter_Copies(domainList));
|
||||
|
@ -217,7 +217,12 @@ struct JSRuntime {
|
||||
|
||||
/*
|
||||
* Check property accessibility for objects of arbitrary class. Used at
|
||||
* present to check f.caller accessibility for any function object f.
|
||||
* present to check whether:
|
||||
* 1. f.caller should be accessible for any function object f.
|
||||
* 2. a getter or setter on another trust domain's global object should
|
||||
* be accessible to a given script.
|
||||
* 3. eval or eval-like primitives (Script objects) should be accessible
|
||||
* to trusted scripts.
|
||||
*/
|
||||
JSCheckAccessOp checkObjectAccess;
|
||||
|
||||
|
@ -1001,6 +1001,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
JSStackFrame *fp, *caller;
|
||||
JSBool indirectCall;
|
||||
JSRuntime *rt;
|
||||
JSObject *scopeobj;
|
||||
JSString *str;
|
||||
const char *file;
|
||||
@ -1027,6 +1028,20 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the caller (native or scripted) should trust this
|
||||
* eval function object.
|
||||
*/
|
||||
rt = cx->runtime;
|
||||
if (rt->checkObjectAccess) {
|
||||
*rval = argv[-2];
|
||||
if (!rt->checkObjectAccess(cx, obj,
|
||||
ATOM_KEY(rt->atomState.evalAtom),
|
||||
JSACC_EXEC, rval)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!JSVAL_IS_STRING(argv[0])) {
|
||||
*rval = argv[0];
|
||||
return JS_TRUE;
|
||||
|
@ -89,7 +89,10 @@ typedef enum JSType {
|
||||
JSTYPE_LIMIT
|
||||
} JSType;
|
||||
|
||||
/* JSObjectOps.checkAccess mode enumeration. */
|
||||
/*
|
||||
* JSObjectOps.checkAccess mode enumeration.
|
||||
* XXXbe these began as consecutive values but turned into flags -- revisit
|
||||
*/
|
||||
typedef enum JSAccessMode {
|
||||
JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */
|
||||
JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */
|
||||
@ -97,10 +100,11 @@ typedef enum JSAccessMode {
|
||||
JSACC_WATCH = 3, /* a watchpoint on object foo for id 'bar' */
|
||||
JSACC_READ = 4, /* a "get" of foo.bar */
|
||||
JSACC_WRITE = 8, /* a "set" of foo.bar = baz */
|
||||
JSACC_EXEC = 16, /* eval or Script.prototype.exec attempt */
|
||||
JSACC_LIMIT
|
||||
} JSAccessMode;
|
||||
|
||||
#define JSACC_TYPEMASK (JSACC_WRITE - 1)
|
||||
#define JSACC_TYPEMASK (JSACC_EXEC - 1)
|
||||
|
||||
/*
|
||||
* This enum type is used to control the behavior of a JSObject property
|
||||
|
@ -236,6 +236,7 @@ static JSBool
|
||||
script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
JSScript *script;
|
||||
JSRuntime *rt;
|
||||
JSObject *scopeobj, *parent;
|
||||
JSStackFrame *fp, *caller;
|
||||
|
||||
@ -245,6 +246,24 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
if (!script)
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
* Check whether the caller (native or scripted) should trust this
|
||||
* Script.prototype.exec function object.
|
||||
*/
|
||||
rt = cx->runtime;
|
||||
if (rt->checkObjectAccess) {
|
||||
JSString *exec_str;
|
||||
jsval exec_idval;
|
||||
|
||||
exec_str = JS_InternString(cx, "exec");
|
||||
if (!exec_str)
|
||||
return JS_FALSE;
|
||||
exec_idval = STRING_TO_JSVAL(exec_str);
|
||||
*rval = argv[-2];
|
||||
if (!rt->checkObjectAccess(cx, obj, exec_idval, JSACC_EXEC, rval))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
scopeobj = NULL;
|
||||
if (argc) {
|
||||
if (!js_ValueToObject(cx, argv[0], &scopeobj))
|
||||
|
Loading…
Reference in New Issue
Block a user