From 0fd528e2e23ecd589f08e108546f5376c5fb56d0 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Sat, 9 Jun 2012 15:19:27 -0700 Subject: [PATCH] Bug 734891 - part 4: Using ExpandedPrincipal --- js/xpconnect/src/XPCComponents.cpp | 71 ++++++++++++++++++- .../tests/unit/test_allowedDomains.js | 42 +++++++++++ js/xpconnect/tests/unit/xpcshell.ini | 1 + 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 js/xpconnect/tests/unit/test_allowedDomains.js diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index f772285da1b..0b2d873d1cb 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -3351,6 +3351,68 @@ GetPrincipalOrSOP(JSContext *cx, JSObject &from, nsISupports **out) return NS_OK; } +// the first parameter of the sandbox constructor might be an array of principals, either in string +// format or actual objects (see GetPrincipalOrSOP) +nsresult +GetExpandedPrincipal(JSContext *cx, JSObject &arrayObj, nsIExpandedPrincipal **out) +{ + MOZ_ASSERT(out); + uint32_t length; + + if (!JS_IsArrayObject(cx, &arrayObj) || + !JS_GetArrayLength(cx, &arrayObj, &length) || + !length) + { + // we need a white list of principals or uri strings to create an + // expanded principal, if we got an empty array or something else + // report error + return NS_ERROR_INVALID_ARG; + } + + nsTArray< nsCOMPtr > allowedDomains(length); + allowedDomains.SetLength(length); + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED); + + for (uint32_t i = 0; i < length; ++i) { + jsval allowed; + + if (!JS_GetElement(cx, &arrayObj, i, &allowed)) + return NS_ERROR_INVALID_ARG; + + nsresult rv; + nsCOMPtr principal; + if (allowed.isString()) { + // in case of string let's try to fetch a codebase principal from it + rv = GetPrincipalFromString(cx, allowed.toString(), getter_AddRefs(principal)); + NS_ENSURE_SUCCESS(rv, rv); + } else if (allowed.isObject()) { + // in case of object let's see if it's a Principal or a ScriptObjectPrincipal + nsCOMPtr prinOrSop; + rv = GetPrincipalOrSOP(cx, allowed.toObject(), getter_AddRefs(prinOrSop)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr sop(do_QueryInterface(prinOrSop)); + principal = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } + } + NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG); + + // We do not allow ExpandedPrincipals to contain any system principals + bool isSystem; + rv = ssm->IsSystemPrincipal(principal, &isSystem); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG); + allowedDomains[i] = principal; + } + + nsCOMPtr result = new nsExpandedPrincipal(allowedDomains); + result.forget(out); + return NS_OK; +} + // helper that tries to get a property form the options object nsresult GetPropFromOptions(JSContext *cx, JSObject &from, const char *name, jsval *prop, JSBool *found) @@ -3393,7 +3455,7 @@ GetObjPropFromOptions(JSContext *cx, JSObject &from, const char *name, JSObject JSBool found; if (NS_FAILED(GetPropFromOptions(cx, from, name, &propVal, &found))) - return NS_ERROR_INVALID_ARG; + return NS_ERROR_INVALID_ARG; if (!found) { *prop = NULL; @@ -3495,7 +3557,12 @@ nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrappe rv = GetPrincipalFromString(cx, argv[0].toString(), getter_AddRefs(principal)); prinOrSop = principal; } else if (argv[0].isObject()) { - rv = GetPrincipalOrSOP(cx, argv[0].toObject(), getter_AddRefs(prinOrSop)); + if (JS_IsArrayObject(cx, &argv[0].toObject())) { + rv = GetExpandedPrincipal(cx, argv[0].toObject(), getter_AddRefs(expanded)); + prinOrSop = expanded; + } else { + rv = GetPrincipalOrSOP(cx, argv[0].toObject(), getter_AddRefs(prinOrSop)); + } } else { return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); } diff --git a/js/xpconnect/tests/unit/test_allowedDomains.js b/js/xpconnect/tests/unit/test_allowedDomains.js new file mode 100644 index 00000000000..21ebee4e292 --- /dev/null +++ b/js/xpconnect/tests/unit/test_allowedDomains.js @@ -0,0 +1,42 @@ +function run_test() { + var cu = Components.utils; + var sbMaster = cu.Sandbox(["http://www.a.com", + "http://www.b.com", + "http://www.d.com"]); + var sbSubset = cu.Sandbox(["http://www.d.com", + "http://www.a.com"]); + + var sbA = cu.Sandbox("http://www.a.com"); + var sbB = cu.Sandbox("http://www.b.com"); + var sbC = cu.Sandbox("http://www.c.com"); + + sbMaster.objA = cu.evalInSandbox("var obj = {prop1:200}; obj", sbA); + sbMaster.objB = cu.evalInSandbox("var obj = {prop1:200}; obj", sbB); + sbMaster.objC = cu.evalInSandbox("var obj = {prop1:200}; obj", sbC); + sbMaster.objOwn = cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster); + + sbMaster.objSubset = cu.evalInSandbox("var obj = {prop1:200}; obj", sbSubset); + sbA.objMaster = cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster); + sbSubset.objMaster = cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster); + + var ret; + ret = cu.evalInSandbox("objA.prop1", sbMaster); + do_check_eq(ret, 200); + ret = cu.evalInSandbox("objB.prop1", sbMaster); + do_check_eq(ret, 200); + ret = cu.evalInSandbox("objSubset.prop1", sbMaster); + do_check_eq(ret, 200); + + function evalAndCatch(str, sb) { + try { + ret = cu.evalInSandbox(str, sb); + do_check_true(false, "unexpected pass") + } catch (e) { + do_check_true(e.message && e.message.indexOf("Permission denied to access property") != -1); + } + } + + evalAndCatch("objC.prop1", sbMaster); + evalAndCatch("objMaster.prop1", sbA); + evalAndCatch("objMaster.prop1", sbSubset); +} diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini index 9b83477ff98..52e5db0de1d 100644 --- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -27,3 +27,4 @@ fail-if = os == "android" [test_params.js] [test_want_components.js] [test_components.js] +[test_allowedDomains.js]