Bug 840488 - Compute immunity from caps checks exactly once, and kill nsScriptSecurityManager::CanExecuteScripts. r=bz

This commit is contained in:
Bobby Holley 2013-11-12 16:43:35 -08:00
parent 15add0796f
commit a80315ff5c
6 changed files with 94 additions and 93 deletions

View File

@ -201,6 +201,13 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
* script to check whether a given principal is system.
*/
boolean isSystemPrincipal(in nsIPrincipal aPrincipal);
%{C++
bool IsSystemPrincipal(nsIPrincipal* aPrincipal) {
bool isSystem = false;
IsSystemPrincipal(aPrincipal, &isSystem);
return isSystem;
}
%}
/**
* Same as getSubjectPrincipal(), only faster. cx must *never* be

View File

@ -381,7 +381,7 @@ private:
// Returns null if a principal cannot be found; generally callers
// should error out at that point.
static nsIPrincipal* doGetObjectPrincipal(JS::Handle<JSObject*> obj);
static nsIPrincipal* doGetObjectPrincipal(JSObject* obj);
// 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.
@ -462,9 +462,6 @@ private:
nsIPrincipal* aSubjectPrincipal,
const char* aObjectSecurityLevel);
nsresult
CanExecuteScripts(JSContext* cx, nsIPrincipal *aPrincipal, bool *result);
nsresult
Init();

View File

@ -1596,97 +1596,43 @@ nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
return rv;
}
nsresult
nsScriptSecurityManager::CanExecuteScripts(JSContext* cx,
nsIPrincipal *aPrincipal,
bool *result)
{
*result = false;
if (aPrincipal == mSystemPrincipal)
{
// Even if JavaScript is disabled, we must still execute system scripts
*result = true;
return NS_OK;
}
// Same thing for nsExpandedPrincipal, which is pseudo-privileged.
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
if (ep)
{
*result = true;
return NS_OK;
}
// Check whether our URI is an "about:" URI that allows scripts. If it is,
// we need to allow JS to run. In this case, don't apply the JS enabled
// pref or policies. On failures, just press on and don't do this special
// case.
nsCOMPtr<nsIURI> principalURI;
aPrincipal->GetURI(getter_AddRefs(principalURI));
if (!principalURI) {
// Broken principal of some sort. Disallow.
*result = false;
return NS_ERROR_UNEXPECTED;
}
bool isAbout;
nsresult rv = principalURI->SchemeIs("about", &isAbout);
if (NS_SUCCEEDED(rv) && isAbout) {
nsCOMPtr<nsIAboutModule> module;
rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
if (NS_SUCCEEDED(rv)) {
uint32_t flags;
rv = module->GetURIFlags(principalURI, &flags);
if (NS_SUCCEEDED(rv) &&
(flags & nsIAboutModule::ALLOW_SCRIPT)) {
*result = true;
return NS_OK;
}
}
}
*result = mIsJavaScriptEnabled;
if (!*result)
return NS_OK; // Do not run scripts
//-- Check for a per-site policy
static const char jsPrefGroupName[] = "javascript";
ClassInfoData nameData(nullptr, jsPrefGroupName);
SecurityLevel secLevel;
rv = LookupPolicy(aPrincipal, nameData, EnabledID(),
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
nullptr, &secLevel);
if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS)
{
*result = false;
return rv;
}
//-- Nobody vetoed, so allow the JS to run.
*result = true;
return NS_OK;
}
NS_IMETHODIMP_(bool)
bool
nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
{
MOZ_ASSERT(aGlobal);
MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
AutoJSContext cx_;
JS::RootedObject global(cx_, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
AutoJSContext cx;
JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
// Check the bits on the compartment private.
if (!xpc::Scriptability::Get(global).Allowed()) {
xpc::Scriptability& scriptability = xpc::Scriptability::Get(aGlobal);
if (!scriptability.Allowed()) {
return false;
}
nsCOMPtr<nsIScriptContext> scx = nsJSUtils::GetStaticScriptContext(global);
AutoPushJSContext cx(scx ? scx->GetNativeContext() : GetSafeJSContext());
bool result = false;
nsresult rv = CanExecuteScripts(cx, doGetObjectPrincipal(global), &result);
return NS_SUCCEEDED(rv) && result;
// If the compartment is immune to script policy, we're done.
if (scriptability.IsImmuneToScriptPolicy()) {
return true;
}
// If JS is disabled system-wide, we disallow.
if (!mIsJavaScriptEnabled) {
return false;
}
// Check for a per-site policy.
static const char jsPrefGroupName[] = "javascript";
ClassInfoData nameData(nullptr, jsPrefGroupName);
SecurityLevel secLevel;
nsresult rv = LookupPolicy(doGetObjectPrincipal(global), nameData,
EnabledID(),
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
nullptr, &secLevel);
if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS) {
return false;
}
return true;
}
///////////////// Principals ///////////////////////
@ -1883,7 +1829,7 @@ nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
// static
nsIPrincipal*
nsScriptSecurityManager::doGetObjectPrincipal(JS::Handle<JSObject*> aObj)
nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
{
JSCompartment *compartment = js::GetObjectCompartment(aObj);
JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);

View File

@ -39,6 +39,7 @@
#include "mozilla/Attributes.h"
#include "AccessCheck.h"
#include "nsGlobalWindow.h"
#include "nsAboutProtocolUtils.h"
#include "GeckoProfiler.h"
#include "nsJSPrincipals.h"
@ -405,14 +406,52 @@ EnsureCompartmentPrivate(JSCompartment *c)
CompartmentPrivate *priv = GetCompartmentPrivate(c);
if (priv)
return priv;
priv = new CompartmentPrivate();
priv = new CompartmentPrivate(c);
JS_SetCompartmentPrivate(c, priv);
return priv;
}
Scriptability::Scriptability() : mScriptBlocks(0)
, mDocShellAllowsScript(true)
{}
static bool
PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal)
{
// System principal gets a free pass.
if (XPCWrapper::GetSecurityManager()->IsSystemPrincipal(aPrincipal))
return true;
// nsExpandedPrincipal gets a free pass.
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
if (ep)
return true;
// Check whether our URI is an "about:" URI that allows scripts. If it is,
// we need to allow JS to run.
nsCOMPtr<nsIURI> principalURI;
aPrincipal->GetURI(getter_AddRefs(principalURI));
MOZ_ASSERT(principalURI);
bool isAbout;
nsresult rv = principalURI->SchemeIs("about", &isAbout);
if (NS_SUCCEEDED(rv) && isAbout) {
nsCOMPtr<nsIAboutModule> module;
rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
if (NS_SUCCEEDED(rv)) {
uint32_t flags;
rv = module->GetURIFlags(principalURI, &flags);
if (NS_SUCCEEDED(rv) &&
(flags & nsIAboutModule::ALLOW_SCRIPT)) {
return true;
}
}
}
return false;
}
Scriptability::Scriptability(JSCompartment *c) : mScriptBlocks(0)
, mDocShellAllowsScript(true)
{
nsIPrincipal *prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
}
bool
Scriptability::Allowed()
@ -420,6 +459,12 @@ Scriptability::Allowed()
return mDocShellAllowsScript && mScriptBlocks == 0;
}
bool
Scriptability::IsImmuneToScriptPolicy()
{
return mImmuneToScriptPolicy;
}
void
Scriptability::Block()
{

View File

@ -3758,11 +3758,12 @@ public:
LocationHintAddon
};
CompartmentPrivate()
CompartmentPrivate(JSCompartment *c)
: wantXrays(false)
, universalXPConnectEnabled(false)
, adoptedNode(false)
, donatedNode(false)
, scriptability(c)
, scope(nullptr)
{
MOZ_COUNT_CTOR(xpc::CompartmentPrivate);

View File

@ -37,8 +37,9 @@ namespace xpc {
class Scriptability {
public:
Scriptability();
Scriptability(JSCompartment *c);
bool Allowed();
bool IsImmuneToScriptPolicy();
void Block();
void Unblock();
@ -56,6 +57,10 @@ private:
// Whether the docshell allows javascript in this scope. If this scope
// doesn't have a docshell, this value is always true.
bool mDocShellAllowsScript;
// Whether this scope is immune to user-defined or addon-defined script
// policy.
bool mImmuneToScriptPolicy;
};
JSObject *