Bug 109113, second half of fix. r=jst, sr=brendan. Adding new CheckObjectAccess

callback to enforce the same-origin policy on function.caller.
This commit is contained in:
mstoltz%netscape.com 2001-11-16 06:17:24 +00:00
Родитель e191aa0dd4
Коммит 144ebd46ff
1 изменённых файлов: 122 добавлений и 47 удалений

Просмотреть файл

@ -80,6 +80,7 @@
#include "nsIConsoleService.h" #include "nsIConsoleService.h"
#include "nsISecurityCheckedComponent.h" #include "nsISecurityCheckedComponent.h"
#include "nsIPrefBranchInternal.h" #include "nsIPrefBranchInternal.h"
#include "nsIJSRuntimeService.h"
static NS_DEFINE_IID(kIIOServiceIID, NS_IIOSERVICE_IID); static NS_DEFINE_IID(kIIOServiceIID, NS_IIOSERVICE_IID);
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
@ -150,6 +151,41 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(nsScriptSecurityManager,
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
///////////////// Security Checks ///////////////// ///////////////// Security Checks /////////////////
static JSBool JS_DLL_CALLBACK
CheckJSFunctionCallerAccess(JSContext *cx, JSObject *obj, jsval id,
JSAccessMode mode, jsval *vp)
{
// Currently, this function will be called only when function.caller
// is accessed. If that changes, we will need to change this function.
NS_ASSERTION(nsCRT::strcmp(JS_GetStringChars(JSVAL_TO_STRING(id)),
NS_LITERAL_STRING("caller").get()) == 0,
"CheckJSFunctionCallerAccess called for a property other than \'caller\'");
// Get the security manager
//XXX: Any way to avoid this service lookup?
nsresult rv;
nsCOMPtr<nsIScriptSecurityManager> ssm =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv))
{
NS_ERROR("Failed to get security manager service");
return JS_FALSE;
}
// Get the caller function object
NS_ASSERTION(JSVAL_IS_OBJECT(*vp), "*vp is not an object");
JSObject* target = JSVAL_TO_OBJECT(*vp);
// Do the same-origin check - this sets a JS exception if the check fails
rv = ssm->CheckPropertyAccess(cx, target, "Function", "caller",
nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
if (NS_FAILED(rv))
return JS_FALSE; // Security check failed
return JS_TRUE;
}
NS_IMETHODIMP NS_IMETHODIMP
nsScriptSecurityManager::CheckPropertyAccess(JSContext* aJSContext, nsScriptSecurityManager::CheckPropertyAccess(JSContext* aJSContext,
JSObject* aJSObject, JSObject* aJSObject,
@ -276,7 +312,7 @@ nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction,
case SCRIPT_SECURITY_SAME_ORIGIN_ACCESS: case SCRIPT_SECURITY_SAME_ORIGIN_ACCESS:
{ {
#ifdef DEBUG_mstoltz #ifdef DEBUG_mstoltz
printf("Level: SameOrigin "); printf("Level: SameOrigin ");
#endif #endif
nsCOMPtr<nsIPrincipal> objectPrincipal; nsCOMPtr<nsIPrincipal> objectPrincipal;
if(aJSObject) if(aJSObject)
@ -304,7 +340,7 @@ nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction,
case SCRIPT_SECURITY_CAPABILITY_ONLY: case SCRIPT_SECURITY_CAPABILITY_ONLY:
{ {
#ifdef DEBUG_mstoltz #ifdef DEBUG_mstoltz
printf("Level: Capability "); printf("Level: Capability ");
#endif #endif
PRBool capabilityEnabled = PR_FALSE; PRBool capabilityEnabled = PR_FALSE;
rv = IsCapabilityEnabled(capability.get(), &capabilityEnabled); rv = IsCapabilityEnabled(capability.get(), &capabilityEnabled);
@ -750,7 +786,7 @@ nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
nsXPIDLCString spec; nsXPIDLCString spec;
if (NS_FAILED(aURI->GetSpec(getter_Copies(spec)))) if (NS_FAILED(aURI->GetSpec(getter_Copies(spec))))
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
JS_ReportError(cx, "illegal URL method '%s'", (const char *)spec); JS_ReportError(cx, "illegal URL method '%s'", (const char *)spec);
return NS_ERROR_DOM_BAD_URI; return NS_ERROR_DOM_BAD_URI;
} }
@ -1071,9 +1107,29 @@ nsScriptSecurityManager::CanExecuteScripts(JSContext* cx,
return NS_OK; return NS_OK;
} }
//-- Always allow chrome pages to run scripts
// This is for about: URLs, which are chrome but don't
// have the system principal
nsresult rv;
if (!mIsJavaScriptEnabled)
{
nsCOMPtr<nsICodebasePrincipal> codebase(do_QueryInterface(principal));
if (codebase)
{
nsXPIDLCString origin;
rv = codebase->GetOrigin(getter_Copies(origin));
static const char chromePrefix[] = "chrome:";
if (NS_SUCCEEDED(rv) &&
(PL_strncmp(origin, chromePrefix, sizeof(chromePrefix)-1) == 0))
{
*result = PR_TRUE;
return NS_OK;
}
}
}
//-- See if the current window allows JS execution //-- See if the current window allows JS execution
nsCOMPtr<nsIDocShell> docshell; nsCOMPtr<nsIDocShell> docshell;
nsresult rv;
rv = GetRootDocShell(cx, getter_AddRefs(docshell)); rv = GetRootDocShell(cx, getter_AddRefs(docshell));
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
{ {
@ -1190,7 +1246,7 @@ nsScriptSecurityManager::GetCertificatePrincipal(const char* aCertID,
nsresult nsresult
nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, nsIPrincipal **result) nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, nsIPrincipal **result)
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;
nsCodebasePrincipal *codebase = new nsCodebasePrincipal(); nsCodebasePrincipal *codebase = new nsCodebasePrincipal();
if (!codebase) if (!codebase)
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
@ -1209,10 +1265,10 @@ NS_IMETHODIMP
nsScriptSecurityManager::GetCodebasePrincipal(nsIURI *aURI, nsScriptSecurityManager::GetCodebasePrincipal(nsIURI *aURI,
nsIPrincipal **result) nsIPrincipal **result)
{ {
nsresult rv; nsresult rv;
nsCOMPtr<nsIPrincipal> principal; nsCOMPtr<nsIPrincipal> principal;
rv = CreateCodebasePrincipal(aURI, getter_AddRefs(principal)); rv = CreateCodebasePrincipal(aURI, getter_AddRefs(principal));
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
if (mPrincipals) if (mPrincipals)
{ {
@ -1221,23 +1277,23 @@ nsScriptSecurityManager::GetCodebasePrincipal(nsIURI *aURI,
nsCOMPtr<nsIPrincipal> fromTable = (nsIPrincipal *) mPrincipals->Get(&key); nsCOMPtr<nsIPrincipal> fromTable = (nsIPrincipal *) mPrincipals->Get(&key);
if (fromTable) if (fromTable)
principal = fromTable; principal = fromTable;
else //-- Check to see if we have a more general principal else //-- Check to see if we have a more general principal
{ {
nsCOMPtr<nsICodebasePrincipal> codebasePrin(do_QueryInterface(principal)); nsCOMPtr<nsICodebasePrincipal> codebasePrin(do_QueryInterface(principal));
nsXPIDLCString originUrl; nsXPIDLCString originUrl;
rv = codebasePrin->GetOrigin(getter_Copies(originUrl)); rv = codebasePrin->GetOrigin(getter_Copies(originUrl));
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIURI> newURI; nsCOMPtr<nsIURI> newURI;
rv = NS_NewURI(getter_AddRefs(newURI), originUrl, nsnull); rv = NS_NewURI(getter_AddRefs(newURI), originUrl, nsnull);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIPrincipal> principal2; nsCOMPtr<nsIPrincipal> principal2;
rv = CreateCodebasePrincipal(newURI, getter_AddRefs(principal2)); rv = CreateCodebasePrincipal(newURI, getter_AddRefs(principal2));
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
nsIPrincipalKey key2(principal2); nsIPrincipalKey key2(principal2);
fromTable = (nsIPrincipal *) mPrincipals->Get(&key2); fromTable = (nsIPrincipal *) mPrincipals->Get(&key2);
if (fromTable) if (fromTable)
principal = fromTable; principal = fromTable;
} }
} }
//-- Bundle this codebase principal into an aggregate principal //-- Bundle this codebase principal into an aggregate principal
@ -1701,8 +1757,8 @@ nsScriptSecurityManager::EnableCapability(const char *capability)
//-- Error checks for capability string length (200) //-- Error checks for capability string length (200)
if(PL_strlen(capability)>200) if(PL_strlen(capability)>200)
{ {
static const char msg[] = "Capability name too long"; static const char msg[] = "Capability name too long";
JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, msg))); JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, msg)));
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -1723,8 +1779,8 @@ nsScriptSecurityManager::EnableCapability(const char *capability)
if (canEnable != nsIPrincipal::ENABLE_GRANTED) if (canEnable != nsIPrincipal::ENABLE_GRANTED)
{ {
static const char msg[] = "enablePrivilege not granted"; static const char msg[] = "enablePrivilege not granted";
JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, msg))); JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, msg)));
return NS_ERROR_FAILURE; // XXX better error code? return NS_ERROR_FAILURE; // XXX better error code?
} }
if (NS_FAILED(principal->EnableCapability(capability, &annotation))) if (NS_FAILED(principal->EnableCapability(capability, &annotation)))
@ -1817,7 +1873,7 @@ nsScriptSecurityManager::SetCanEnableCapability(const char* certificateID,
{ {
JSContext* cx = GetCurrentContextQuick(); JSContext* cx = GetCurrentContextQuick();
if (!cx) return NS_ERROR_FAILURE; if (!cx) return NS_ERROR_FAILURE;
static const char msg1[] = "Only code signed by the system certificate may call SetCanEnableCapability or Invalidate"; static const char msg1[] = "Only code signed by the system certificate may call SetCanEnableCapability or Invalidate";
static const char msg2[] = "Attempt to call SetCanEnableCapability or Invalidate when no system certificate has been established"; static const char msg2[] = "Attempt to call SetCanEnableCapability or Invalidate when no system certificate has been established";
JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
mSystemCertificate ? msg1 : msg2))); mSystemCertificate ? msg1 : msg2)));
@ -2059,6 +2115,25 @@ nsScriptSecurityManager::nsScriptSecurityManager(void)
NS_INIT_REFCNT(); NS_INIT_REFCNT();
InitPrefs(); InitPrefs();
mThreadJSContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1"); mThreadJSContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
//-- Register security check callback in the JS engine
// Currently this is used to control access to function.caller
nsresult rv;
nsCOMPtr<nsIJSRuntimeService> runtimeService =
do_GetService("@mozilla.org/js/xpc/RuntimeService;1", &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get runtime service");
JSRuntime *rt;
rv = runtimeService->GetRuntime(&rt);
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get current JS runtime");
#ifdef DEBUG
JSCheckAccessOp oldCallback =
JS_SetCheckObjectAccessCallback(rt, CheckJSFunctionCallerAccess);
// For now, assert that no callback was set previously
NS_ASSERTION(!oldCallback, "Someone already set a JS CheckObjectAccess callback");
#endif
} }
nsScriptSecurityManager::~nsScriptSecurityManager(void) nsScriptSecurityManager::~nsScriptSecurityManager(void)