Bug 840488 - Compute immunity from caps checks exactly once, and kill nsScriptSecurityManager::CanExecuteScripts. r=bz
authorBobby Holley <bobbyholley@gmail.com>
Tue, 12 Nov 2013 16:43:35 -0800
changeset 169380 4beeac0fbc68a4347687d3069e4d5d2615344e0a
parent 169379 73fd5de6e82048c2a9959e38d081a210cf54db1d
child 169381 9f9022aabfe9114c68879fd51660b2899ddf3c7c
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs840488
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 840488 - Compute immunity from caps checks exactly once, and kill nsScriptSecurityManager::CanExecuteScripts. r=bz
caps/idl/nsIScriptSecurityManager.idl
caps/include/nsScriptSecurityManager.h
caps/src/nsScriptSecurityManager.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
--- a/caps/idl/nsIScriptSecurityManager.idl
+++ b/caps/idl/nsIScriptSecurityManager.idl
@@ -196,16 +196,23 @@ interface nsIScriptSecurityManager : nsI
     nsIPrincipal getChannelPrincipal(in nsIChannel aChannel);
 
     /**
      * Check whether a given principal is a system principal.  This allows us
      * to avoid handing back the system principal to script while allowing
      * 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
      * passed null, and it must be the context on the top of the
      * context stack. Does *not* reference count the returned
      * principal.
      */
     [noscript,notxpcom] nsIPrincipal getCxSubjectPrincipal(in JSContextPtr cx);
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -376,17 +376,17 @@ private:
                       JS::MutableHandle<JS::Value> vp);
     
     // Decides, based on CSP, whether or not eval() and stuff can be executed.
     static bool
     ContentSecurityPolicyPermitsJSAction(JSContext *cx);
 
     // 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.
     nsIPrincipal*
     doGetSubjectPrincipal(nsresult* rv);
     
     nsresult
     CheckPropertyAccessImpl(uint32_t aAction,
@@ -458,19 +458,16 @@ private:
      */
     nsresult
     CheckXPCPermissions(JSContext* cx,
                         nsISupports* aObj, JSObject* aJSObject,
                         nsIPrincipal* aSubjectPrincipal,
                         const char* aObjectSecurityLevel);
 
     nsresult
-    CanExecuteScripts(JSContext* cx, nsIPrincipal *aPrincipal, bool *result);
-
-    nsresult
     Init();
 
     nsresult
     InitPrefs();
 
     nsresult
     InitPolicies();
 
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -1591,107 +1591,53 @@ nsScriptSecurityManager::CheckLoadURIStr
             return rv;
         }
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     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 ///////////////////////
 NS_IMETHODIMP
 nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **aSubjectPrincipal)
 {
     nsresult rv;
     *aSubjectPrincipal = doGetSubjectPrincipal(&rv);
@@ -1878,17 +1824,17 @@ nsScriptSecurityManager::GetObjectPrinci
     if (!*result)
         return NS_ERROR_FAILURE;
     NS_ADDREF(*result);
     return NS_OK;
 }
 
 // static
 nsIPrincipal*
-nsScriptSecurityManager::doGetObjectPrincipal(JS::Handle<JSObject*> aObj)
+nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
 {
     JSCompartment *compartment = js::GetObjectCompartment(aObj);
     JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
     return nsJSPrincipals::get(principals);
 }
 
 ////////////////////////////////////////////////
 // Methods implementing nsIXPCSecurityManager //
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -34,16 +34,17 @@
 #include "js/MemoryMetrics.h"
 #include "js/OldDebugAPI.h"
 #include "mozilla/dom/GeneratedAtomList.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Attributes.h"
 #include "AccessCheck.h"
 #include "nsGlobalWindow.h"
+#include "nsAboutProtocolUtils.h"
 
 #include "GeckoProfiler.h"
 #include "nsJSPrincipals.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
@@ -400,31 +401,75 @@ EnsureCompartmentPrivate(JSObject *obj)
 }
 
 CompartmentPrivate*
 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()
 {
     return mDocShellAllowsScript && mScriptBlocks == 0;
 }
 
+bool
+Scriptability::IsImmuneToScriptPolicy()
+{
+    return mImmuneToScriptPolicy;
+}
+
 void
 Scriptability::Block()
 {
     ++mScriptBlocks;
 }
 
 void
 Scriptability::Unblock()
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3753,21 +3753,22 @@ namespace xpc {
 class CompartmentPrivate
 {
 public:
     enum LocationHint {
         LocationHintRegular,
         LocationHintAddon
     };
 
-    CompartmentPrivate()
+    CompartmentPrivate(JSCompartment *c)
         : wantXrays(false)
         , universalXPConnectEnabled(false)
         , adoptedNode(false)
         , donatedNode(false)
+        , scriptability(c)
         , scope(nullptr)
     {
         MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
     }
 
     ~CompartmentPrivate();
 
     bool wantXrays;
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -32,18 +32,19 @@ class nsIMemoryReporterCallback;
 #ifndef BAD_TLS_INDEX
 #define BAD_TLS_INDEX ((uint32_t) -1)
 #endif
 
 namespace xpc {
 
 class Scriptability {
 public:
-    Scriptability();
+    Scriptability(JSCompartment *c);
     bool Allowed();
+    bool IsImmuneToScriptPolicy();
 
     void Block();
     void Unblock();
     void SetDocShellAllowsScript(bool aAllowed);
 
     static Scriptability& Get(JSObject *aScope);
 
 private:
@@ -51,16 +52,20 @@ private:
     // 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;
 
     // 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 *
 TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target);
 
 // Return a raw XBL scope object corresponding to contentScope, which must
 // be an object whose global is a DOM window.
 //