Backout bug 754202 (all patches, rather than just patches 3-7).

This commit is contained in:
L. David Baron 2012-06-10 17:22:31 -07:00
parent 9aae70bdc5
commit c9bf4416ea
15 changed files with 443 additions and 87 deletions

View File

@ -9,7 +9,7 @@
interface nsIURI; interface nsIURI;
interface nsIChannel; interface nsIChannel;
[scriptable, uuid(cdb27711-492b-4973-938b-de81ac124658)] [scriptable, uuid(3708aa92-e2d9-4fd1-9e46-edfa3eb5ebf5)]
interface nsIScriptSecurityManager : nsIXPCSecurityManager interface nsIScriptSecurityManager : nsIXPCSecurityManager
{ {
///////////////// Security Checks ////////////////// ///////////////// Security Checks //////////////////
@ -260,6 +260,29 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
[noscript,notxpcom] nsIPrincipal getCxSubjectPrincipal(in JSContextPtr cx); [noscript,notxpcom] nsIPrincipal getCxSubjectPrincipal(in JSContextPtr cx);
[noscript,notxpcom] nsIPrincipal getCxSubjectPrincipalAndFrame(in JSContextPtr cx, [noscript,notxpcom] nsIPrincipal getCxSubjectPrincipalAndFrame(in JSContextPtr cx,
out JSStackFramePtr fp); out JSStackFramePtr fp);
/**
* If no scripted code is running "above" (or called from) fp, then
* instead of looking at cx->globalObject, we will return |principal|.
* This function only affects |cx|. If someone pushes another context onto
* the context stack, then it supersedes this call.
* NOTE: If |fp| is non-null popContextPrincipal must be called before fp
* has finished executing.
*
* @param cx The context to clamp.
* @param fp The frame pointer to clamp at. May be 'null'.
* @param principal The principal to clamp to.
*/
[noscript] void pushContextPrincipal(in JSContextPtr cx,
in JSStackFramePtr fp,
in nsIPrincipal principal);
/**
* Removes a clamp set by pushContextPrincipal from cx. This must be
* called in a stack-like fashion (e.g., given two contexts |a| and |b|,
* it is not legal to do: push(a) push(b) pop(a)).
*/
[noscript] void popContextPrincipal(in JSContextPtr cx);
}; };
%{C++ %{C++

View File

@ -403,11 +403,12 @@ private:
// Returns null if a principal cannot be found; generally callers // Returns null if a principal cannot be found; generally callers
// should error out at that point. // should error out at that point.
static nsIPrincipal* doGetObjectPrincipal(JSObject *obj);
#ifdef DEBUG
static nsIPrincipal* static nsIPrincipal*
old_doGetObjectPrincipal(JSObject *obj, bool aAllowShortCircuit = true); doGetObjectPrincipal(JSObject *obj
#ifdef DEBUG
, bool aAllowShortCircuit = true
#endif #endif
);
// Returns null if a principal cannot be found. Note that rv can be NS_OK // Returns null if a principal cannot be found. Note that rv can be NS_OK
// when this happens -- this means that there was no JS running. // when this happens -- this means that there was no JS running.
@ -553,6 +554,17 @@ private:
PrintPolicyDB(); PrintPolicyDB();
#endif #endif
struct ContextPrincipal {
ContextPrincipal(ContextPrincipal *next, JSContext *cx,
JSStackFrame *fp, nsIPrincipal *principal)
: mNext(next), mCx(cx), mFp(fp), mPrincipal(principal) {}
ContextPrincipal *mNext;
JSContext *mCx;
JSStackFrame *mFp;
nsCOMPtr<nsIPrincipal> mPrincipal;
};
// JS strings we need to clean up on shutdown // JS strings we need to clean up on shutdown
static jsid sEnabledID; static jsid sEnabledID;
@ -565,6 +577,7 @@ private:
nsCOMPtr<nsIPrincipal> mSystemPrincipal; nsCOMPtr<nsIPrincipal> mSystemPrincipal;
nsCOMPtr<nsIPrincipal> mSystemCertificate; nsCOMPtr<nsIPrincipal> mSystemCertificate;
ContextPrincipal *mContextPrincipals;
nsInterfaceHashtable<PrincipalKey, nsIPrincipal> mPrincipals; nsInterfaceHashtable<PrincipalKey, nsIPrincipal> mPrincipals;
bool mPrefInitialized; bool mPrefInitialized;
bool mIsJavaScriptEnabled; bool mIsJavaScriptEnabled;

View File

@ -162,6 +162,44 @@ GetScriptContext(JSContext *cx)
return GetScriptContextFromJSContext(cx); return GetScriptContextFromJSContext(cx);
} }
// Callbacks for the JS engine to use to push/pop context principals.
static JSBool
PushPrincipalCallback(JSContext *cx, JSPrincipals *principals)
{
// We should already be in the compartment of the given principal.
MOZ_ASSERT(principals ==
JS_GetCompartmentPrincipals((js::GetContextCompartment(cx))));
// Get the security manager.
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (!ssm) {
return true;
}
// Push the principal.
JSStackFrame *fp = NULL;
nsresult rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp),
nsJSPrincipals::get(principals));
if (NS_FAILED(rv)) {
JS_ReportOutOfMemory(cx);
return false;
}
return true;
}
static JSBool
PopPrincipalCallback(JSContext *cx)
{
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (ssm) {
ssm->PopContextPrincipal(cx);
}
return true;
}
inline void SetPendingException(JSContext *cx, const char *aMsg) inline void SetPendingException(JSContext *cx, const char *aMsg)
{ {
JSAutoRequest ar(cx); JSAutoRequest ar(cx);
@ -366,6 +404,34 @@ nsScriptSecurityManager::GetCxSubjectPrincipalAndFrame(JSContext *cx, JSStackFra
return principal; return principal;
} }
NS_IMETHODIMP
nsScriptSecurityManager::PushContextPrincipal(JSContext *cx,
JSStackFrame *fp,
nsIPrincipal *principal)
{
NS_ASSERTION(principal, "Must pass a non-null principal");
ContextPrincipal *cp = new ContextPrincipal(mContextPrincipals, cx, fp,
principal);
if (!cp)
return NS_ERROR_OUT_OF_MEMORY;
mContextPrincipals = cp;
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::PopContextPrincipal(JSContext *cx)
{
NS_ASSERTION(mContextPrincipals->mCx == cx, "Mismatched push/pop");
ContextPrincipal *next = mContextPrincipals->mNext;
delete mContextPrincipals;
mContextPrincipals = next;
return NS_OK;
}
//////////////////// ////////////////////
// Policy Storage // // Policy Storage //
//////////////////// ////////////////////
@ -2237,10 +2303,24 @@ nsScriptSecurityManager::GetPrincipalAndFrame(JSContext *cx,
if (cx) if (cx)
{ {
JSStackFrame *target = nsnull;
nsIPrincipal *targetPrincipal = nsnull;
for (ContextPrincipal *cp = mContextPrincipals; cp; cp = cp->mNext)
{
if (cp->mCx == cx)
{
target = cp->mFp;
targetPrincipal = cp->mPrincipal;
break;
}
}
// Get principals from innermost JavaScript frame. // Get principals from innermost JavaScript frame.
JSStackFrame *fp = nsnull; // tell JS_FrameIterator to start at innermost JSStackFrame *fp = nsnull; // tell JS_FrameIterator to start at innermost
for (fp = JS_FrameIterator(cx, &fp); fp; fp = JS_FrameIterator(cx, &fp)) for (fp = JS_FrameIterator(cx, &fp); fp; fp = JS_FrameIterator(cx, &fp))
{ {
if (fp == target)
break;
nsIPrincipal* result = GetFramePrincipal(cx, fp, rv); nsIPrincipal* result = GetFramePrincipal(cx, fp, rv);
if (result) if (result)
{ {
@ -2250,6 +2330,25 @@ nsScriptSecurityManager::GetPrincipalAndFrame(JSContext *cx,
} }
} }
// If targetPrincipal is non-null, then it means that someone wants to
// clamp the principals on this context to this principal. Note that
// fp might not equal target here (fp might be null) because someone
// could have set aside the frame chain in the meantime.
if (targetPrincipal)
{
if (fp && fp == target)
{
*frameResult = fp;
}
else
{
JSStackFrame *inner = nsnull;
*frameResult = JS_FrameIterator(cx, &inner);
}
return targetPrincipal;
}
nsIScriptContextPrincipal* scp = nsIScriptContextPrincipal* scp =
GetScriptContextPrincipalFromJSContext(cx); GetScriptContextPrincipalFromJSContext(cx);
if (scp) if (scp)
@ -2280,15 +2379,9 @@ nsIPrincipal*
nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx, nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
nsresult* rv) nsresult* rv)
{ {
*rv = NS_OK; NS_PRECONDITION(rv, "Null out param");
JSCompartment *compartment = js::GetContextCompartment(cx); JSStackFrame *fp;
return GetPrincipalAndFrame(cx, &fp, rv);
// The context should always be in a compartment, either one it has entered
// or the one associated with its global.
MOZ_ASSERT(!!compartment);
JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
return nsJSPrincipals::get(principals);
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -2304,33 +2397,19 @@ nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
// static // static
nsIPrincipal* nsIPrincipal*
nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj) nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
{
JSCompartment *compartment = js::GetObjectCompartment(aObj);
JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
nsIPrincipal *principal = nsJSPrincipals::get(principals);
// We leave the old code in for a little while to make sure that pulling
// object principals directly off the compartment always gives an equivalent
// result (from a security perspective).
#ifdef DEBUG #ifdef DEBUG
nsIPrincipal *old = old_doGetObjectPrincipal(aObj); , bool aAllowShortCircuit
MOZ_ASSERT(NS_SUCCEEDED(CheckSameOriginPrincipal(principal, old)));
#endif #endif
)
return principal;
}
#ifdef DEBUG
// static
nsIPrincipal*
nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
bool aAllowShortCircuit)
{ {
NS_ASSERTION(aObj, "Bad call to doGetObjectPrincipal()!"); NS_ASSERTION(aObj, "Bad call to doGetObjectPrincipal()!");
nsIPrincipal* result = nsnull; nsIPrincipal* result = nsnull;
#ifdef DEBUG
JSObject* origObj = aObj; JSObject* origObj = aObj;
#endif
js::Class *jsClass = js::GetObjectClass(aObj); js::Class *jsClass = js::GetObjectClass(aObj);
// A common case seen in this code is that we enter this function // A common case seen in this code is that we enter this function
@ -2364,7 +2443,12 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
if (IS_WRAPPER_CLASS(jsClass)) { if (IS_WRAPPER_CLASS(jsClass)) {
result = sXPConnect->GetPrincipal(aObj, result = sXPConnect->GetPrincipal(aObj,
aAllowShortCircuit); #ifdef DEBUG
aAllowShortCircuit
#else
true
#endif
);
if (result) { if (result) {
break; break;
} }
@ -2380,6 +2464,7 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
priv = nsnull; priv = nsnull;
} }
#ifdef DEBUG
if (aAllowShortCircuit) { if (aAllowShortCircuit) {
nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper = nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper =
do_QueryInterface(priv); do_QueryInterface(priv);
@ -2389,6 +2474,7 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
"Uh, an nsIXPConnectWrappedNative with the " "Uh, an nsIXPConnectWrappedNative with the "
"wrong JSClass or getObjectOps hooks!"); "wrong JSClass or getObjectOps hooks!");
} }
#endif
nsCOMPtr<nsIScriptObjectPrincipal> objPrin = nsCOMPtr<nsIScriptObjectPrincipal> objPrin =
do_QueryInterface(priv); do_QueryInterface(priv);
@ -2410,8 +2496,9 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
jsClass = js::GetObjectClass(aObj); jsClass = js::GetObjectClass(aObj);
} while (1); } while (1);
#ifdef DEBUG
if (aAllowShortCircuit) { if (aAllowShortCircuit) {
nsIPrincipal *principal = old_doGetObjectPrincipal(origObj, false); nsIPrincipal *principal = doGetObjectPrincipal(origObj, false);
// Because of inner window reuse, we can have objects with one principal // Because of inner window reuse, we can have objects with one principal
// living in a scope with a different (but same-origin) principal. So // living in a scope with a different (but same-origin) principal. So
@ -2419,10 +2506,10 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
NS_ASSERTION(NS_SUCCEEDED(CheckSameOriginPrincipal(result, principal)), NS_ASSERTION(NS_SUCCEEDED(CheckSameOriginPrincipal(result, principal)),
"Principal mismatch. Not good"); "Principal mismatch. Not good");
} }
#endif
return result; return result;
} }
#endif /* DEBUG */
///////////////// Capabilities API ///////////////////// ///////////////// Capabilities API /////////////////////
NS_IMETHODIMP NS_IMETHODIMP
@ -2434,11 +2521,27 @@ nsScriptSecurityManager::IsCapabilityEnabled(const char *capability,
JSContext *cx = GetCurrentJSContext(); JSContext *cx = GetCurrentJSContext();
fp = cx ? JS_FrameIterator(cx, &fp) : nsnull; fp = cx ? JS_FrameIterator(cx, &fp) : nsnull;
JSStackFrame *target = nsnull;
nsIPrincipal *targetPrincipal = nsnull;
for (ContextPrincipal *cp = mContextPrincipals; cp; cp = cp->mNext)
{
if (cp->mCx == cx)
{
target = cp->mFp;
targetPrincipal = cp->mPrincipal;
break;
}
}
if (!fp) if (!fp)
{ {
// No script code on stack. We don't have enough information and have // No script code on stack. If we had a principal pushed for this
// to allow execution. // context and fp is null, then we use that principal. Otherwise, we
*result = true; // don't have enough information and have to allow execution.
*result = (targetPrincipal && !target)
? (targetPrincipal == mSystemPrincipal)
: true;
return NS_OK; return NS_OK;
} }
@ -2482,7 +2585,7 @@ nsScriptSecurityManager::IsCapabilityEnabled(const char *capability,
// the JS engine via JS_EvaluateScript or similar APIs. // the JS engine via JS_EvaluateScript or similar APIs.
if (JS_IsGlobalFrame(cx, fp)) if (JS_IsGlobalFrame(cx, fp))
break; break;
} while ((fp = JS_FrameIterator(cx, &fp)) != nsnull); } while (fp != target && (fp = JS_FrameIterator(cx, &fp)) != nsnull);
if (!previousPrincipal) if (!previousPrincipal)
{ {
@ -2966,6 +3069,7 @@ nsScriptSecurityManager::nsScriptSecurityManager(void)
: mOriginToPolicyMap(nsnull), : mOriginToPolicyMap(nsnull),
mDefaultPolicy(nsnull), mDefaultPolicy(nsnull),
mCapabilities(nsnull), mCapabilities(nsnull),
mContextPrincipals(nsnull),
mPrefInitialized(false), mPrefInitialized(false),
mIsJavaScriptEnabled(false), mIsJavaScriptEnabled(false),
mIsWritingPrefs(false), mIsWritingPrefs(false),
@ -3026,7 +3130,9 @@ nsresult nsScriptSecurityManager::Init()
CheckObjectAccess, CheckObjectAccess,
nsJSPrincipals::Subsume, nsJSPrincipals::Subsume,
ObjectPrincipalFinder, ObjectPrincipalFinder,
ContentSecurityPolicyPermitsJSAction ContentSecurityPolicyPermitsJSAction,
PushPrincipalCallback,
PopPrincipalCallback
}; };
MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime)); MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
@ -3045,6 +3151,7 @@ jsid nsScriptSecurityManager::sEnabledID = JSID_VOID;
nsScriptSecurityManager::~nsScriptSecurityManager(void) nsScriptSecurityManager::~nsScriptSecurityManager(void)
{ {
Preferences::RemoveObservers(this, kObservedPrefs); Preferences::RemoveObservers(this, kObservedPrefs);
NS_ASSERTION(!mContextPrincipals, "Leaking mContextPrincipals");
delete mOriginToPolicyMap; delete mOriginToPolicyMap;
if(mDefaultPolicy) if(mDefaultPolicy)
mDefaultPolicy->Drop(); mDefaultPolicy->Drop();

View File

@ -1198,29 +1198,27 @@ nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
xpc_UnmarkGrayObject(aScopeObject); xpc_UnmarkGrayObject(aScopeObject);
nsAutoMicroTask mt; nsAutoMicroTask mt;
// Ignore the principal that was passed in, and just assert that it matches // Safety first: get an object representing the script's principals, i.e.,
// the one we pull off the global. // the entities who signed this script, or the fully-qualified-domain-name
nsCOMPtr<nsIPrincipal> principal; // or "codebase" from which it was loaded.
nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal = do_QueryInterface(GetGlobalObject()); nsCOMPtr<nsIPrincipal> principal = aPrincipal;
if (!objPrincipal) nsresult rv;
return NS_ERROR_FAILURE; if (!aPrincipal) {
principal = objPrincipal->GetPrincipal(); nsIScriptGlobalObject *global = GetGlobalObject();
if (!principal) if (!global)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
#ifdef DEBUG nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
bool equal = false; do_QueryInterface(global, &rv);
principal->Equals(aPrincipal, &equal); if (NS_FAILED(rv))
MOZ_ASSERT(equal); return NS_ERROR_FAILURE;
nsIPrincipal *scopeObjectPrincipal = principal = objPrincipal->GetPrincipal();
nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject))); if (!principal)
equal = false; return NS_ERROR_FAILURE;
principal->Equals(scopeObjectPrincipal, &equal); }
MOZ_ASSERT(equal);
#endif
bool ok = false; bool ok = false;
nsresult rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok); rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -1237,6 +1235,9 @@ nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
jsval val; jsval val;
rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
NS_ENSURE_SUCCESS(rv, rv);
nsJSContext::TerminationFuncHolder holder(this); nsJSContext::TerminationFuncHolder holder(this);
// SecurityManager said "ok", but don't compile if aVersion is unknown. // SecurityManager said "ok", but don't compile if aVersion is unknown.
@ -1292,6 +1293,8 @@ nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
} }
} }
sSecurityManager->PopContextPrincipal(mContext);
// Pop here, after JS_ValueToString and any other possible evaluation. // Pop here, after JS_ValueToString and any other possible evaluation.
if (NS_FAILED(stack->Pop(nsnull))) if (NS_FAILED(stack->Pop(nsnull)))
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
@ -1397,25 +1400,19 @@ nsJSContext::EvaluateString(const nsAString& aScript,
xpc_UnmarkGrayObject(aScopeObject); xpc_UnmarkGrayObject(aScopeObject);
// Ignore the principal that was passed in, and just assert that it matches // Safety first: get an object representing the script's principals, i.e.,
// the one we pull off the global. // the entities who signed this script, or the fully-qualified-domain-name
nsCOMPtr<nsIPrincipal> principal; // or "codebase" from which it was loaded.
nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal = do_QueryInterface(GetGlobalObject()); nsCOMPtr<nsIPrincipal> principal = aPrincipal;
if (!objPrincipal) if (!aPrincipal) {
return NS_ERROR_FAILURE; nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
principal = objPrincipal->GetPrincipal(); do_QueryInterface(GetGlobalObject());
if (!principal) if (!objPrincipal)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
#ifdef DEBUG principal = objPrincipal->GetPrincipal();
bool equal = false; if (!principal)
principal->Equals(aPrincipal, &equal); return NS_ERROR_FAILURE;
MOZ_ASSERT(equal); }
nsIPrincipal *scopeObjectPrincipal =
nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject)));
equal = false;
principal->Equals(scopeObjectPrincipal, &equal);
MOZ_ASSERT(equal);
#endif
bool ok = false; bool ok = false;
@ -1440,6 +1437,9 @@ nsJSContext::EvaluateString(const nsAString& aScript,
jsval val = JSVAL_VOID; jsval val = JSVAL_VOID;
jsval* vp = aRetValue ? &val : NULL; jsval* vp = aRetValue ? &val : NULL;
rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
NS_ENSURE_SUCCESS(rv, rv);
nsJSContext::TerminationFuncHolder holder(this); nsJSContext::TerminationFuncHolder holder(this);
++mExecuteDepth; ++mExecuteDepth;
@ -1491,6 +1491,8 @@ nsJSContext::EvaluateString(const nsAString& aScript,
--mExecuteDepth; --mExecuteDepth;
sSecurityManager->PopContextPrincipal(mContext);
// Pop here, after JS_ValueToString and any other possible evaluation. // Pop here, after JS_ValueToString and any other possible evaluation.
if (NS_FAILED(stack->Pop(nsnull))) if (NS_FAILED(stack->Pop(nsnull)))
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
@ -1588,6 +1590,15 @@ nsJSContext::ExecuteScript(JSScript* aScriptObject,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsCOMPtr<nsIPrincipal> principal;
rv = sSecurityManager->GetObjectPrincipal(mContext,
JS_GetGlobalFromScript(aScriptObject),
getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
NS_ENSURE_SUCCESS(rv, rv);
nsJSContext::TerminationFuncHolder holder(this); nsJSContext::TerminationFuncHolder holder(this);
XPCAutoRequest ar(mContext); XPCAutoRequest ar(mContext);
++mExecuteDepth; ++mExecuteDepth;
@ -1614,6 +1625,8 @@ nsJSContext::ExecuteScript(JSScript* aScriptObject,
--mExecuteDepth; --mExecuteDepth;
sSecurityManager->PopContextPrincipal(mContext);
// Pop here, after JS_ValueToString and any other possible evaluation. // Pop here, after JS_ValueToString and any other possible evaluation.
if (NS_FAILED(stack->Pop(nsnull))) if (NS_FAILED(stack->Pop(nsnull)))
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
@ -1854,12 +1867,24 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, JSObject* aScope,
jsval *argv = nsnull; jsval *argv = nsnull;
JSObject *funobj = aHandler; JSObject *funobj = aHandler;
nsCOMPtr<nsIPrincipal> principal;
rv = sSecurityManager->GetObjectPrincipal(mContext, funobj,
getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
JSStackFrame *currentfp = nsnull;
rv = sSecurityManager->PushContextPrincipal(mContext,
JS_FrameIterator(mContext, &currentfp),
principal);
NS_ENSURE_SUCCESS(rv, rv);
jsval funval = OBJECT_TO_JSVAL(funobj); jsval funval = OBJECT_TO_JSVAL(funobj);
JSAutoEnterCompartment ac; JSAutoEnterCompartment ac;
js::ForceFrame ff(mContext, funobj); js::ForceFrame ff(mContext, funobj);
if (!ac.enter(mContext, funobj) || !ff.enter() || if (!ac.enter(mContext, funobj) || !ff.enter() ||
!JS_WrapObject(mContext, &target)) { !JS_WrapObject(mContext, &target)) {
ReportPendingException(); ReportPendingException();
sSecurityManager->PopContextPrincipal(mContext);
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -1902,6 +1927,8 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, JSObject* aScope,
// nested calls through XPConnect. // nested calls through XPConnect.
if (NS_FAILED(rv)) if (NS_FAILED(rv))
ReportPendingException(); ReportPendingException();
sSecurityManager->PopContextPrincipal(mContext);
} }
pusher.Pop(); pusher.Pop();

View File

@ -177,15 +177,32 @@ nsJSON::EncodeFromJSVal(JS::Value *value, JSContext *cx, nsAString &result)
JSAutoRequest ar(cx); JSAutoRequest ar(cx);
JSAutoEnterCompartment ac; JSAutoEnterCompartment ac;
nsIScriptSecurityManager *ssm = nsnull;
if (value->isObject()) { if (value->isObject()) {
JSObject *obj = &value->toObject(); JSObject *obj = &value->toObject();
if (!ac.enter(cx, obj)) { if (!ac.enter(cx, obj)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsCOMPtr<nsIPrincipal> principal;
ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->GetObjectPrincipal(cx, obj, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
JSStackFrame *fp = nsnull;
rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), principal);
NS_ENSURE_SUCCESS(rv, rv);
} }
nsJSONWriter writer; nsJSONWriter writer;
if (!JS_Stringify(cx, value, NULL, JSVAL_NULL, WriteCallback, &writer)) { JSBool ok = JS_Stringify(cx, value, NULL, JSVAL_NULL,
WriteCallback, &writer);
if (ssm) {
ssm->PopContextPrincipal(cx);
}
if (!ok) {
return NS_ERROR_XPC_BAD_CONVERT_JS; return NS_ERROR_XPC_BAD_CONVERT_JS;
} }

View File

@ -37,7 +37,9 @@
function respond(msg) function respond(msg)
{ {
SpecialPowers.wrap(window).parent.postMessage(msg, "*"); // ...so get privileges and test that this works with privileges
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
window.parent.postMessage(msg, "*");
} }
window.addEventListener("message", receiveMessage, false); window.addEventListener("message", receiveMessage, false);

View File

@ -93,6 +93,10 @@ function chromePathIsSet(evt)
function receiveContent(evt) function receiveContent(evt)
{ {
is(evt.isTrusted, true, "should have sent a trusted event"); is(evt.isTrusted, true, "should have sent a trusted event");
is(evt.origin, "http://example.org", "content response event has wrong URI");
is(evt.source, window.frames.contentDomain,
"wrong source for same-domain message!");
finish(); finish();
} }

View File

@ -880,6 +880,20 @@ FullTrustSecMan::GetCxSubjectPrincipalAndFrame(JSContext *cx,
return mSystemPrincipal; return mSystemPrincipal;
} }
NS_IMETHODIMP
FullTrustSecMan::PushContextPrincipal(JSContext *cx,
JSStackFrame *fp,
nsIPrincipal *principal)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::PopContextPrincipal(JSContext *cx)
{
return NS_OK;
}
NS_IMETHODIMP_(nsrefcnt) NS_IMETHODIMP_(nsrefcnt)
XPCShellDirProvider::AddRef() XPCShellDirProvider::AddRef()
{ {

View File

@ -1671,6 +1671,16 @@ typedef JSPrincipals *
typedef JSBool typedef JSBool
(* JSCSPEvalChecker)(JSContext *cx); (* JSCSPEvalChecker)(JSContext *cx);
/*
* Security callbacks for pushing and popping context principals. These are only
* temporarily necessary and will hopefully be gone again in a matter of weeks.
*/
typedef JSBool
(* JSPushContextPrincipalOp)(JSContext *cx, JSPrincipals *principals);
typedef JSBool
(* JSPopContextPrincipalOp)(JSContext *cx);
/* /*
* Callback used to ask the embedding for the cross compartment wrapper handler * Callback used to ask the embedding for the cross compartment wrapper handler
* that implements the desired prolicy for this kind of object in the * that implements the desired prolicy for this kind of object in the
@ -4339,6 +4349,8 @@ struct JSSecurityCallbacks {
JSSubsumePrincipalsOp subsumePrincipals; JSSubsumePrincipalsOp subsumePrincipals;
JSObjectPrincipalsFinder findObjectPrincipals; JSObjectPrincipalsFinder findObjectPrincipals;
JSCSPEvalChecker contentSecurityPolicyAllows; JSCSPEvalChecker contentSecurityPolicyAllows;
JSPushContextPrincipalOp pushContextPrincipal;
JSPopContextPrincipalOp popContextPrincipal;
}; };
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)

View File

@ -472,6 +472,37 @@ JSStructuredCloneWriter::startObject(JSObject *obj)
return out.writePair(obj->isArray() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0); return out.writePair(obj->isArray() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
} }
class AutoEnterCompartmentAndPushPrincipal : public JSAutoEnterCompartment
{
public:
bool enter(JSContext *cx, JSObject *target) {
// First, enter the compartment.
if (!JSAutoEnterCompartment::enter(cx, target))
return false;
// We only need to push a principal if we changed compartments.
if (state != STATE_OTHER_COMPARTMENT)
return true;
// Push.
const JSSecurityCallbacks *cb = cx->runtime->securityCallbacks;
if (cb->pushContextPrincipal)
return cb->pushContextPrincipal(cx, target->principals(cx));
return true;
};
~AutoEnterCompartmentAndPushPrincipal() {
// Pop the principal if necessary.
if (state == STATE_OTHER_COMPARTMENT) {
AutoCompartment *ac = getAutoCompartment();
const JSSecurityCallbacks *cb = ac->context->runtime->securityCallbacks;
if (cb->popContextPrincipal)
cb->popContextPrincipal(ac->context);
}
};
};
bool bool
JSStructuredCloneWriter::startWrite(const Value &v) JSStructuredCloneWriter::startWrite(const Value &v)
{ {
@ -498,7 +529,7 @@ JSStructuredCloneWriter::startWrite(const Value &v)
// If we unwrapped above, we'll need to enter the underlying compartment. // If we unwrapped above, we'll need to enter the underlying compartment.
// Let the AutoEnterCompartment do the right thing for us. // Let the AutoEnterCompartment do the right thing for us.
JSAutoEnterCompartment ac; AutoEnterCompartmentAndPushPrincipal ac;
if (!ac.enter(context(), obj)) if (!ac.enter(context(), obj))
return false; return false;
@ -543,7 +574,7 @@ JSStructuredCloneWriter::write(const Value &v)
RootedObject obj(context(), &objs.back().toObject()); RootedObject obj(context(), &objs.back().toObject());
// The objects in |obj| can live in other compartments. // The objects in |obj| can live in other compartments.
JSAutoEnterCompartment ac; AutoEnterCompartmentAndPushPrincipal ac;
if (!ac.enter(context(), obj)) if (!ac.enter(context(), obj))
return false; return false;

View File

@ -1395,6 +1395,20 @@ FullTrustSecMan::GetSubjectPrincipal(nsIPrincipal **_retval)
return *_retval ? NS_OK : NS_ERROR_FAILURE; return *_retval ? NS_OK : NS_ERROR_FAILURE;
} }
/* [noscript] void pushContextPrincipal (in JSContextPtr cx, in JSStackFramePtr fp, in nsIPrincipal principal); */
NS_IMETHODIMP
FullTrustSecMan::PushContextPrincipal(JSContext * cx, JSStackFrame * fp, nsIPrincipal *principal)
{
return NS_OK;
}
/* [noscript] void popContextPrincipal (in JSContextPtr cx); */
NS_IMETHODIMP
FullTrustSecMan::PopContextPrincipal(JSContext * cx)
{
return NS_OK;
}
/* [noscript] nsIPrincipal getSystemPrincipal (); */ /* [noscript] nsIPrincipal getSystemPrincipal (); */
NS_IMETHODIMP NS_IMETHODIMP
FullTrustSecMan::GetSystemPrincipal(nsIPrincipal **_retval) FullTrustSecMan::GetSystemPrincipal(nsIPrincipal **_retval)

View File

@ -1116,6 +1116,17 @@ nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
class ContextPrincipalGuard
{
nsIScriptSecurityManager *ssm;
XPCCallContext &ccx;
public:
ContextPrincipalGuard(XPCCallContext &ccx)
: ssm(nsnull), ccx(ccx) {}
void principalPushed(nsIScriptSecurityManager *ssm) { this->ssm = ssm; }
~ContextPrincipalGuard() { if (ssm) ssm->PopContextPrincipal(ccx); }
};
NS_IMETHODIMP NS_IMETHODIMP
nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
const XPTMethodDescriptor* info, const XPTMethodDescriptor* info,
@ -1159,6 +1170,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
JS::AutoValueVector args(cx); JS::AutoValueVector args(cx);
AutoScriptEvaluate scriptEval(cx); AutoScriptEvaluate scriptEval(cx);
ContextPrincipalGuard principalGuard(ccx);
// XXX ASSUMES that retval is last arg. The xpidl compiler ensures this. // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
uint8_t paramCount = info->num_args; uint8_t paramCount = info->num_args;
@ -1172,6 +1184,28 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
xpcc->SetException(nsnull); xpcc->SetException(nsnull);
ccx.GetThreadData()->SetException(nsnull); ccx.GetThreadData()->SetException(nsnull);
if (XPCPerThreadData::IsMainThread(ccx)) {
// TODO Remove me in favor of security wrappers.
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (ssm) {
nsIPrincipal *objPrincipal =
xpc::AccessCheck::getPrincipal(js::GetObjectCompartment(obj));
if (objPrincipal) {
JSStackFrame* fp = nsnull;
nsresult rv =
ssm->PushContextPrincipal(ccx, JS_FrameIterator(ccx, &fp),
objPrincipal);
if (NS_FAILED(rv)) {
JS_ReportOutOfMemory(ccx);
retval = NS_ERROR_OUT_OF_MEMORY;
goto pre_call_clean_up;
}
principalGuard.principalPushed(ssm);
}
}
}
// We use js_Invoke so that the gcthings we use as args will be rooted by // We use js_Invoke so that the gcthings we use as args will be rooted by
// the engine as we do conversions and prepare to do the function call. // the engine as we do conversions and prepare to do the function call.

View File

@ -15,7 +15,15 @@
namespace xpc { namespace xpc {
CrossOriginWrapper::CrossOriginWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags) NoWaiverWrapper::NoWaiverWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags)
{
}
NoWaiverWrapper::~NoWaiverWrapper()
{
}
CrossOriginWrapper::CrossOriginWrapper(unsigned flags) : NoWaiverWrapper(flags)
{ {
} }
@ -62,4 +70,36 @@ CrossOriginWrapper::construct(JSContext *cx, JSObject *wrapper,
WrapperFactory::WaiveXrayAndWrap(cx, rval); WrapperFactory::WaiveXrayAndWrap(cx, rval);
} }
bool
NoWaiverWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp)
{
*bp = true; // always allowed
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (!ssm) {
return true;
}
// Note: By the time enter is called here, CrossCompartmentWrapper has
// already pushed the fake stack frame onto cx. Because of this, the frame
// that we're clamping is the one that we want (the one in our compartment).
JSStackFrame *fp = NULL;
nsIPrincipal *principal = GetCompartmentPrincipal(js::GetObjectCompartment(wrappedObject(wrapper)));
nsresult rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), principal);
if (NS_FAILED(rv)) {
NS_WARNING("Not allowing call because we're out of memory");
JS_ReportOutOfMemory(cx);
return false;
}
return true;
}
void
NoWaiverWrapper::leave(JSContext *cx, JSObject *wrapper)
{
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (ssm) {
ssm->PopContextPrincipal(cx);
}
}
} }

View File

@ -15,7 +15,18 @@
namespace xpc { namespace xpc {
class CrossOriginWrapper : public js::CrossCompartmentWrapper { class NoWaiverWrapper : public js::CrossCompartmentWrapper {
public:
NoWaiverWrapper(unsigned flags);
virtual ~NoWaiverWrapper();
virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp) MOZ_OVERRIDE;
virtual void leave(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
static NoWaiverWrapper singleton;
};
class CrossOriginWrapper : public NoWaiverWrapper {
public: public:
CrossOriginWrapper(unsigned flags); CrossOriginWrapper(unsigned flags);
virtual ~CrossOriginWrapper(); virtual ~CrossOriginWrapper();

View File

@ -31,6 +31,13 @@ namespace xpc {
// we know to not apply an X-ray wrapper. // we know to not apply an X-ray wrapper.
Wrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); Wrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
// Objects that haven't been explicitly waived, but have been exposed
// to chrome don't want a CrossOriginWrapper, since that deeply-waives
// but need the transparent behavior of a CrossOriginWrapper. The
// NoWaiverWrapper is like a CrossOriginWrapper that can also hand out
// XrayWrappers as return values.
NoWaiverWrapper NoWaiverWrapper::singleton(0);
// When objects for which we waived the X-ray wrapper cross into // When objects for which we waived the X-ray wrapper cross into
// chrome, we wrap them into a special cross-compartment wrapper // chrome, we wrap them into a special cross-compartment wrapper
// that transitively extends the waiver to all properties we get // that transitively extends the waiver to all properties we get
@ -334,7 +341,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
usingXray = true; usingXray = true;
wrapper = &Xray::singleton; wrapper = &Xray::singleton;
} else { } else {
wrapper = &CrossCompartmentWrapper::singleton; wrapper = &NoWaiverWrapper::singleton;
} }
} }
} }