Bug 483672 - Give regular JS objects that have been reflected into C++ a security policy that follows the same-origin model. Also teach caps about "same origin" for these cases. r=jst sr=bzbarsky

This commit is contained in:
Blake Kaplan 2009-05-13 15:01:01 -07:00
parent 385f2d5ef4
commit 3bab9bf56c
3 changed files with 170 additions and 22 deletions

View File

@ -529,13 +529,46 @@ private:
nsresult
SavePrincipal(nsIPrincipal* aToSave);
/**
* Check capability levels for an |aObj| that implements
* nsISecurityCheckedComponent.
*
* NB: This function also checks to see if aObj is a plugin and the user
* has set the "security.xpconnect.plugin.unrestricted" pref to allow
* anybody to script plugin objects from anywhere.
*
* @param cx The context we're running on.
* NB: If null, "sameOrigin" does not have any effect.
* @param aObj The nsISupports representation of the object in question
* object, possibly null.
* @param aJSObject The JSObject representation of the object in question
* if |cx| is non-null and |aObjectSecurityLevel| is
* "sameOrigin". If null will be calculated from aObj (if
* non-null) if and only if aObj is an XPCWrappedJS. The
* rationale behind this is that if we're creating a JS
* wrapper for an XPCWrappedJS, this object definitely
* expects to be exposed to JS.
* @param aSubjectPrincipal The nominal subject principal used when
* aObjectSecurityLevel is "sameOrigin". If null,
* this is calculated if it's needed.
* @param aObjectSecurityLevel Can be one of three values:
* - allAccess: Allow access no matter what.
* - noAccess: Deny access no matter what.
* - sameOrigin: If |cx| is null, behave like noAccess.
* Otherwise, possibly compute a subject
* and object principal and return true if
* and only if the subject has greater than
* or equal privileges to the object.
*/
nsresult
CheckXPCPermissions(nsISupports* aObj,
CheckXPCPermissions(JSContext* cx,
nsISupports* aObj, JSObject* aJSObject,
nsIPrincipal* aSubjectPrincipal,
const char* aObjectSecurityLevel);
nsresult
Init();
nsresult
InitPrefs();

View File

@ -784,7 +784,8 @@ nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction,
}
}
}
rv = CheckXPCPermissions(aObj, objectSecurityLevel);
rv = CheckXPCPermissions(cx, aObj, aJSObject, subjectPrincipal,
objectSecurityLevel);
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
if(NS_SUCCEEDED(rv))
printf("CheckXPCPerms GRANTED.\n");
@ -2858,7 +2859,7 @@ nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
if (checkedComponent)
checkedComponent->CanCreateWrapper((nsIID *)&aIID, getter_Copies(objectSecurityLevel));
nsresult rv = CheckXPCPermissions(aObj, objectSecurityLevel);
nsresult rv = CheckXPCPermissions(cx, aObj, nsnull, nsnull, objectSecurityLevel);
if (NS_FAILED(rv))
{
//-- Access denied, report an error
@ -2969,7 +2970,7 @@ nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
nsCRT::free(cidStr);
#endif
nsresult rv = CheckXPCPermissions(nsnull, nsnull);
nsresult rv = CheckXPCPermissions(nsnull, nsnull, nsnull, nsnull, nsnull);
if (NS_FAILED(rv))
#ifdef XPC_IDISPATCH_SUPPORT
{
@ -3006,7 +3007,7 @@ nsScriptSecurityManager::CanGetService(JSContext *cx,
nsCRT::free(cidStr);
#endif
nsresult rv = CheckXPCPermissions(nsnull, nsnull);
nsresult rv = CheckXPCPermissions(nsnull, nsnull, nsnull, nsnull, nsnull);
if (NS_FAILED(rv))
{
//-- Access denied, report an error
@ -3045,7 +3046,9 @@ nsScriptSecurityManager::CanAccess(PRUint32 aAction,
}
nsresult
nsScriptSecurityManager::CheckXPCPermissions(nsISupports* aObj,
nsScriptSecurityManager::CheckXPCPermissions(JSContext* cx,
nsISupports* aObj, JSObject* aJSObject,
nsIPrincipal* aSubjectPrincipal,
const char* aObjectSecurityLevel)
{
//-- Check for the all-powerful UniversalXPConnect privilege
@ -3058,6 +3061,42 @@ nsScriptSecurityManager::CheckXPCPermissions(nsISupports* aObj,
{
if (PL_strcasecmp(aObjectSecurityLevel, "allAccess") == 0)
return NS_OK;
if (cx && PL_strcasecmp(aObjectSecurityLevel, "sameOrigin") == 0)
{
nsresult rv;
if (!aJSObject)
{
nsCOMPtr<nsIXPConnectWrappedJS> xpcwrappedjs =
do_QueryInterface(aObj);
if (xpcwrappedjs)
{
rv = xpcwrappedjs->GetJSObject(&aJSObject);
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (!aSubjectPrincipal)
{
// No subject principal passed in. Compute it.
aSubjectPrincipal = GetSubjectPrincipal(cx, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
if (aSubjectPrincipal && aJSObject)
{
nsIPrincipal* objectPrincipal = doGetObjectPrincipal(aJSObject);
// Only do anything if we have both a subject and object
// principal.
if (objectPrincipal)
{
PRBool subsumes;
rv = aSubjectPrincipal->Subsumes(objectPrincipal, &subsumes);
NS_ENSURE_SUCCESS(rv, rv);
if (subsumes)
return NS_OK;
}
}
}
else if (PL_strcasecmp(aObjectSecurityLevel, "noAccess") != 0)
{
PRBool canAccess = PR_FALSE;

View File

@ -554,6 +554,67 @@ GetContextFromObject(JSObject *obj)
return nsnull;
}
#ifndef XPCONNECT_STANDALONE
class SameOriginCheckedComponent : public nsISecurityCheckedComponent
{
public:
SameOriginCheckedComponent(nsXPCWrappedJS* delegate)
: mDelegate(delegate)
{}
NS_DECL_ISUPPORTS
NS_DECL_NSISECURITYCHECKEDCOMPONENT
private:
nsRefPtr<nsXPCWrappedJS> mDelegate;
};
NS_IMPL_ADDREF(SameOriginCheckedComponent)
NS_IMPL_RELEASE(SameOriginCheckedComponent)
NS_INTERFACE_MAP_BEGIN(SameOriginCheckedComponent)
NS_INTERFACE_MAP_ENTRY(nsISecurityCheckedComponent)
NS_INTERFACE_MAP_END_AGGREGATED(mDelegate)
NS_IMETHODIMP
SameOriginCheckedComponent::CanCreateWrapper(const nsIID * iid,
char **_retval NS_OUTPARAM)
{
// XXX This doesn't actually work because nsScriptSecurityManager doesn't
// know what to do with "sameOrigin" for canCreateWrapper.
*_retval = NS_strdup("sameOrigin");
return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
SameOriginCheckedComponent::CanCallMethod(const nsIID * iid,
const PRUnichar *methodName,
char **_retval NS_OUTPARAM)
{
*_retval = NS_strdup("sameOrigin");
return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
SameOriginCheckedComponent::CanGetProperty(const nsIID * iid,
const PRUnichar *propertyName,
char **_retval NS_OUTPARAM)
{
*_retval = NS_strdup("sameOrigin");
return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
SameOriginCheckedComponent::CanSetProperty(const nsIID * iid,
const PRUnichar *propertyName,
char **_retval NS_OUTPARAM)
{
*_retval = NS_strdup("sameOrigin");
return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
#endif
NS_IMETHODIMP
nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
REFNSIID aIID,
@ -659,6 +720,14 @@ nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
// Before calling out, ensure that we're not about to claim to implement
// nsISecurityCheckedComponent for an untrusted object. Doing so causes
// problems. See bug 352882.
// But if this is a content object, then we might be wrapping it for
// content. If our JS object isn't a double-wrapped object (that is, we
// don't have XPCWrappedJS(XPCWrappedNative(some C++ object))), then it
// definitely will not have classinfo (and therefore won't be a DOM
// object). Since content wants to be able to use these objects (directly
// or indirectly, see bug 483672), we implement nsISecurityCheckedComponent
// for them and tell caps that they are also bound by the same origin
// model.
if(aIID.Equals(NS_GET_IID(nsISecurityCheckedComponent)))
{
@ -666,29 +735,36 @@ nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
// known as system) principals. It really wants to do a
// UniversalXPConnect type check.
*aInstancePtr = nsnull;
if(!XPCPerThreadData::IsMainThread(ccx.GetJSContext()))
return NS_NOINTERFACE;
nsXPConnect *xpc = nsXPConnect::GetXPConnect();
nsCOMPtr<nsIScriptSecurityManager> secMan =
do_QueryInterface(xpc->GetDefaultSecurityManager());
if(!secMan)
{
*aInstancePtr = nsnull;
return NS_NOINTERFACE;
}
nsCOMPtr<nsIPrincipal> objPrin;
nsresult rv = secMan->GetObjectPrincipal(ccx, self->GetJSObject(),
getter_AddRefs(objPrin));
if(NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIPrincipal> systemPrin;
rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrin));
if(systemPrin != objPrin)
rv = NS_NOINTERFACE;
}
JSObject *selfObj = self->GetJSObject();
nsCOMPtr<nsIPrincipal> objPrin;
nsresult rv = secMan->GetObjectPrincipal(ccx, selfObj,
getter_AddRefs(objPrin));
if(NS_FAILED(rv))
{
*aInstancePtr = nsnull;
return rv;
PRBool isSystem;
rv = secMan->IsSystemPrincipal(objPrin, &isSystem);
if((NS_FAILED(rv) || !isSystem) &&
!IS_WRAPPER_CLASS(STOBJ_GET_CLASS(selfObj)))
{
// A content object.
nsRefPtr<SameOriginCheckedComponent> checked =
new SameOriginCheckedComponent(self);
if(!checked)
return NS_ERROR_OUT_OF_MEMORY;
*aInstancePtr = checked.forget().get();
return NS_OK;
}
}
#endif