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 9409d47933
commit 91859c5a28
15 changed files with 443 additions and 87 deletions

View File

@ -9,7 +9,7 @@
interface nsIURI;
interface nsIChannel;
[scriptable, uuid(cdb27711-492b-4973-938b-de81ac124658)]
[scriptable, uuid(3708aa92-e2d9-4fd1-9e46-edfa3eb5ebf5)]
interface nsIScriptSecurityManager : nsIXPCSecurityManager
{
///////////////// Security Checks //////////////////
@ -260,6 +260,29 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
[noscript,notxpcom] nsIPrincipal getCxSubjectPrincipal(in JSContextPtr cx);
[noscript,notxpcom] nsIPrincipal getCxSubjectPrincipalAndFrame(in JSContextPtr cx,
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++

View File

@ -403,11 +403,12 @@ private:
// Returns null if a principal cannot be found; generally callers
// should error out at that point.
static nsIPrincipal* doGetObjectPrincipal(JSObject *obj);
#ifdef DEBUG
static nsIPrincipal*
old_doGetObjectPrincipal(JSObject *obj, bool aAllowShortCircuit = true);
doGetObjectPrincipal(JSObject *obj
#ifdef DEBUG
, bool aAllowShortCircuit = true
#endif
);
// 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.
@ -553,6 +554,17 @@ private:
PrintPolicyDB();
#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
static jsid sEnabledID;
@ -565,6 +577,7 @@ private:
nsCOMPtr<nsIPrincipal> mSystemPrincipal;
nsCOMPtr<nsIPrincipal> mSystemCertificate;
ContextPrincipal *mContextPrincipals;
nsInterfaceHashtable<PrincipalKey, nsIPrincipal> mPrincipals;
bool mPrefInitialized;
bool mIsJavaScriptEnabled;

View File

@ -162,6 +162,44 @@ GetScriptContext(JSContext *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)
{
JSAutoRequest ar(cx);
@ -366,6 +404,34 @@ nsScriptSecurityManager::GetCxSubjectPrincipalAndFrame(JSContext *cx, JSStackFra
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 //
////////////////////
@ -2237,10 +2303,24 @@ nsScriptSecurityManager::GetPrincipalAndFrame(JSContext *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.
JSStackFrame *fp = nsnull; // tell JS_FrameIterator to start at innermost
for (fp = JS_FrameIterator(cx, &fp); fp; fp = JS_FrameIterator(cx, &fp))
{
if (fp == target)
break;
nsIPrincipal* result = GetFramePrincipal(cx, fp, rv);
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 =
GetScriptContextPrincipalFromJSContext(cx);
if (scp)
@ -2280,15 +2379,9 @@ nsIPrincipal*
nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
nsresult* rv)
{
*rv = NS_OK;
JSCompartment *compartment = js::GetContextCompartment(cx);
// 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_PRECONDITION(rv, "Null out param");
JSStackFrame *fp;
return GetPrincipalAndFrame(cx, &fp, rv);
}
NS_IMETHODIMP
@ -2304,33 +2397,19 @@ nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
// static
nsIPrincipal*
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).
nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
#ifdef DEBUG
nsIPrincipal *old = old_doGetObjectPrincipal(aObj);
MOZ_ASSERT(NS_SUCCEEDED(CheckSameOriginPrincipal(principal, old)));
, bool aAllowShortCircuit
#endif
return principal;
}
#ifdef DEBUG
// static
nsIPrincipal*
nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
bool aAllowShortCircuit)
)
{
NS_ASSERTION(aObj, "Bad call to doGetObjectPrincipal()!");
nsIPrincipal* result = nsnull;
#ifdef DEBUG
JSObject* origObj = aObj;
#endif
js::Class *jsClass = js::GetObjectClass(aObj);
// 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)) {
result = sXPConnect->GetPrincipal(aObj,
aAllowShortCircuit);
#ifdef DEBUG
aAllowShortCircuit
#else
true
#endif
);
if (result) {
break;
}
@ -2380,6 +2464,7 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
priv = nsnull;
}
#ifdef DEBUG
if (aAllowShortCircuit) {
nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper =
do_QueryInterface(priv);
@ -2389,6 +2474,7 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
"Uh, an nsIXPConnectWrappedNative with the "
"wrong JSClass or getObjectOps hooks!");
}
#endif
nsCOMPtr<nsIScriptObjectPrincipal> objPrin =
do_QueryInterface(priv);
@ -2410,8 +2496,9 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj,
jsClass = js::GetObjectClass(aObj);
} while (1);
#ifdef DEBUG
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
// 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)),
"Principal mismatch. Not good");
}
#endif
return result;
}
#endif /* DEBUG */
///////////////// Capabilities API /////////////////////
NS_IMETHODIMP
@ -2434,11 +2521,27 @@ nsScriptSecurityManager::IsCapabilityEnabled(const char *capability,
JSContext *cx = GetCurrentJSContext();
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)
{
// No script code on stack. We don't have enough information and have
// to allow execution.
*result = true;
// No script code on stack. If we had a principal pushed for this
// context and fp is null, then we use that principal. Otherwise, we
// don't have enough information and have to allow execution.
*result = (targetPrincipal && !target)
? (targetPrincipal == mSystemPrincipal)
: true;
return NS_OK;
}
@ -2482,7 +2585,7 @@ nsScriptSecurityManager::IsCapabilityEnabled(const char *capability,
// the JS engine via JS_EvaluateScript or similar APIs.
if (JS_IsGlobalFrame(cx, fp))
break;
} while ((fp = JS_FrameIterator(cx, &fp)) != nsnull);
} while (fp != target && (fp = JS_FrameIterator(cx, &fp)) != nsnull);
if (!previousPrincipal)
{
@ -2966,6 +3069,7 @@ nsScriptSecurityManager::nsScriptSecurityManager(void)
: mOriginToPolicyMap(nsnull),
mDefaultPolicy(nsnull),
mCapabilities(nsnull),
mContextPrincipals(nsnull),
mPrefInitialized(false),
mIsJavaScriptEnabled(false),
mIsWritingPrefs(false),
@ -3026,7 +3130,9 @@ nsresult nsScriptSecurityManager::Init()
CheckObjectAccess,
nsJSPrincipals::Subsume,
ObjectPrincipalFinder,
ContentSecurityPolicyPermitsJSAction
ContentSecurityPolicyPermitsJSAction,
PushPrincipalCallback,
PopPrincipalCallback
};
MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
@ -3045,6 +3151,7 @@ jsid nsScriptSecurityManager::sEnabledID = JSID_VOID;
nsScriptSecurityManager::~nsScriptSecurityManager(void)
{
Preferences::RemoveObservers(this, kObservedPrefs);
NS_ASSERTION(!mContextPrincipals, "Leaking mContextPrincipals");
delete mOriginToPolicyMap;
if(mDefaultPolicy)
mDefaultPolicy->Drop();

View File

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

View File

@ -177,15 +177,32 @@ nsJSON::EncodeFromJSVal(JS::Value *value, JSContext *cx, nsAString &result)
JSAutoRequest ar(cx);
JSAutoEnterCompartment ac;
nsIScriptSecurityManager *ssm = nsnull;
if (value->isObject()) {
JSObject *obj = &value->toObject();
if (!ac.enter(cx, obj)) {
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;
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;
}

View File

@ -37,7 +37,9 @@
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);

View File

@ -93,6 +93,10 @@ function chromePathIsSet(evt)
function receiveContent(evt)
{
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();
}

View File

@ -880,6 +880,20 @@ FullTrustSecMan::GetCxSubjectPrincipalAndFrame(JSContext *cx,
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)
XPCShellDirProvider::AddRef()
{

View File

@ -1671,6 +1671,16 @@ typedef JSPrincipals *
typedef JSBool
(* 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
* that implements the desired prolicy for this kind of object in the
@ -4339,6 +4349,8 @@ struct JSSecurityCallbacks {
JSSubsumePrincipalsOp subsumePrincipals;
JSObjectPrincipalsFinder findObjectPrincipals;
JSCSPEvalChecker contentSecurityPolicyAllows;
JSPushContextPrincipalOp pushContextPrincipal;
JSPopContextPrincipalOp popContextPrincipal;
};
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);
}
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
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.
// Let the AutoEnterCompartment do the right thing for us.
JSAutoEnterCompartment ac;
AutoEnterCompartmentAndPushPrincipal ac;
if (!ac.enter(context(), obj))
return false;
@ -543,7 +574,7 @@ JSStructuredCloneWriter::write(const Value &v)
RootedObject obj(context(), &objs.back().toObject());
// The objects in |obj| can live in other compartments.
JSAutoEnterCompartment ac;
AutoEnterCompartmentAndPushPrincipal ac;
if (!ac.enter(context(), obj))
return false;

View File

@ -1395,6 +1395,20 @@ FullTrustSecMan::GetSubjectPrincipal(nsIPrincipal **_retval)
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 (); */
NS_IMETHODIMP
FullTrustSecMan::GetSystemPrincipal(nsIPrincipal **_retval)

View File

@ -1116,6 +1116,17 @@ nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
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
nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
const XPTMethodDescriptor* info,
@ -1159,6 +1170,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
JS::AutoValueVector args(cx);
AutoScriptEvaluate scriptEval(cx);
ContextPrincipalGuard principalGuard(ccx);
// XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
uint8_t paramCount = info->num_args;
@ -1172,6 +1184,28 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
xpcc->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
// the engine as we do conversions and prepare to do the function call.

View File

@ -15,7 +15,15 @@
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);
}
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 {
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:
CrossOriginWrapper(unsigned flags);
virtual ~CrossOriginWrapper();

View File

@ -31,6 +31,13 @@ namespace xpc {
// we know to not apply an X-ray wrapper.
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
// chrome, we wrap them into a special cross-compartment wrapper
// 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;
wrapper = &Xray::singleton;
} else {
wrapper = &CrossCompartmentWrapper::singleton;
wrapper = &NoWaiverWrapper::singleton;
}
}
}