Bug 840488 - Introduce a mechanism to temporarily or permanently block script for a given scope, and use it for unsafe channels. r=bz

This commit is contained in:
Bobby Holley 2013-11-12 16:43:33 -08:00
parent 589ed842af
commit 260c41dfe4
9 changed files with 116 additions and 9 deletions

View File

@ -1714,6 +1714,11 @@ nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
AutoJSContext cx_;
JS::RootedObject global(cx_, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
// Check the bits on the compartment private.
if (!xpc::Scriptability::Get(global).Allowed()) {
return false;
}
nsCOMPtr<nsIScriptContext> scx = nsJSUtils::GetStaticScriptContext(global);
AutoPushJSContext cx(scx ? scx->GetNativeContext() : GetSafeJSContext());
bool result = false;

View File

@ -2077,12 +2077,6 @@ nsDocShell::GetAllowJavascript(bool * aAllowJavascript)
NS_ENSURE_ARG_POINTER(aAllowJavascript);
*aAllowJavascript = mAllowJavascript;
if (!mAllowJavascript) {
return NS_OK;
}
bool unsafe;
*aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
return NS_OK;
}

View File

@ -139,6 +139,7 @@
#endif
#include "nsIDOMCustomEvent.h"
#include "nsIFrameRequestCallback.h"
#include "nsIJARChannel.h"
#include "xpcprivate.h"
@ -2541,6 +2542,14 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
NS_ENSURE_SUCCESS(rv, rv);
}
// If the document comes from a JAR, check if the channel was determined
// to be unsafe. If so, permanently disable script on the compartment by
// calling Block() and throwing away the key.
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
if (jarChannel && jarChannel->GetIsUnsafe()) {
xpc::Scriptability::Get(newInnerWindow->mJSObject).Block();
}
if (mArguments) {
newInnerWindow->DefineArgumentsProperty(mArguments);
mArguments = nullptr;

View File

@ -120,7 +120,7 @@ interface ScheduledGCCallback : nsISupports
/**
* interface of Components.utils
*/
[scriptable, uuid(90019271-a0d1-439f-911c-967f8e85cd5b)]
[scriptable, uuid(12368474-b448-4ac6-ba2c-db1966a7a697)]
interface nsIXPCComponents_Utils : nsISupports
{
@ -448,6 +448,23 @@ interface nsIXPCComponents_Utils : nsISupports
[implicit_jscontext]
void nukeSandbox(in jsval obj);
/*
* API to dynamically block script for a given global. This takes effect
* immediately, unlike other APIs that only affect newly-created globals.
*
* The machinery here maintains a counter, and allows script only if each
* call to blockScriptForGlobal() has been matched with a call to
* unblockScriptForGlobal(). The caller _must_ make sure never to call
* unblock() more times than it calls block(), since that could potentially
* interfere with another consumer's script blocking.
*/
[implicit_jscontext]
void blockScriptForGlobal(in jsval global);
[implicit_jscontext]
void unblockScriptForGlobal(in jsval global);
/**
* Check whether the given object is an XrayWrapper.
*/

View File

@ -3374,6 +3374,38 @@ nsXPCComponents_Utils::NukeSandbox(const Value &obj, JSContext *cx)
return NS_OK;
}
NS_IMETHODIMP
nsXPCComponents_Utils::BlockScriptForGlobal(const JS::Value &globalArg,
JSContext *cx)
{
NS_ENSURE_TRUE(globalArg.isObject(), NS_ERROR_INVALID_ARG);
JSObject *global = UncheckedUnwrap(&globalArg.toObject(),
/* stopAtOuter = */ false);
NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG);
if (nsContentUtils::IsSystemPrincipal(GetObjectPrincipal(global))) {
JS_ReportError(cx, "Script may not be disabled for system globals");
return NS_ERROR_FAILURE;
}
Scriptability::Get(global).Block();
return NS_OK;
}
NS_IMETHODIMP
nsXPCComponents_Utils::UnblockScriptForGlobal(const JS::Value &globalArg,
JSContext *cx)
{
NS_ENSURE_TRUE(globalArg.isObject(), NS_ERROR_INVALID_ARG);
JSObject *global = UncheckedUnwrap(&globalArg.toObject(),
/* stopAtOuter = */ false);
NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG);
if (nsContentUtils::IsSystemPrincipal(GetObjectPrincipal(global))) {
JS_ReportError(cx, "Script may not be disabled for system globals");
return NS_ERROR_FAILURE;
}
Scriptability::Get(global).Unblock();
return NS_OK;
}
NS_IMETHODIMP
nsXPCComponents_Utils::IsXrayWrapper(const Value &obj, bool* aRetval)
{

View File

@ -410,6 +410,34 @@ EnsureCompartmentPrivate(JSCompartment *c)
return priv;
}
Scriptability::Scriptability() : mScriptBlocks(0) {}
bool
Scriptability::Allowed()
{
return mScriptBlocks == 0;
}
void
Scriptability::Block()
{
++mScriptBlocks;
}
void
Scriptability::Unblock()
{
MOZ_ASSERT(mScriptBlocks > 0);
--mScriptBlocks;
}
/* static */
Scriptability&
Scriptability::Get(JSObject *aScope)
{
return EnsureCompartmentPrivate(aScope)->scriptability;
}
bool
IsXBLScope(JSCompartment *compartment)
{

View File

@ -3782,6 +3782,9 @@ public:
bool adoptedNode;
bool donatedNode;
// The scriptability of this compartment.
Scriptability scriptability;
// Our XPCWrappedNativeScope. This is non-null if and only if this is an
// XPConnect compartment.
XPCWrappedNativeScope *scope;

View File

@ -34,6 +34,25 @@ class nsIMemoryReporterCallback;
#endif
namespace xpc {
class Scriptability {
public:
Scriptability();
bool Allowed();
void Block();
void Unblock();
static Scriptability& Get(JSObject *aScope);
private:
// Whenever a consumer wishes to prevent script from running on a global,
// it increments this value with a call to Block(). When it wishes to
// re-enable it (if ever), it decrements this value with a call to Unblock().
// Script may not run if this value is non-zero.
uint32_t mScriptBlocks;
};
JSObject *
TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target);

View File

@ -5,7 +5,7 @@
#include "nsIChannel.idl"
[scriptable, uuid(6e6cc56d-51eb-4299-a795-dcfd1229ab3d)]
[scriptable, builtinclass, uuid(6e6cc56d-51eb-4299-a795-dcfd1229ab3d)]
interface nsIJARChannel : nsIChannel
{
/**
@ -14,7 +14,7 @@ interface nsIJARChannel : nsIChannel
* redirects, and plugins should be disabled when loading from this
* channel.
*/
readonly attribute boolean isUnsafe;
[infallible] readonly attribute boolean isUnsafe;
/**
* Forces the uri to be a app:// uri.