Bug 951948 - Add a way for automation to force the creation of a privileged Components object for an unprivileged scope. r=ted,r=mrbkap
authorBobby Holley <bobbyholley@gmail.com>
Tue, 14 Jan 2014 18:49:29 -0800
changeset 163436 b61deb1bee05550378eb468c0bf4b2d9b7163e80
parent 163435 4c687bba563b762fdf999dea21b79c3f1a8469ce
child 163437 707abbb92a8ba920caae74fdaf9dc62e6364bc66
push id25996
push useremorley@mozilla.com
push dateWed, 15 Jan 2014 15:54:39 +0000
treeherdermozilla-central@dd2cf81c56b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted, mrbkap
bugs951948
milestone29.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 951948 - Add a way for automation to force the creation of a privileged Components object for an unprivileged scope. r=ted,r=mrbkap
js/xpconnect/idl/xpccomponents.idl
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/xpcprivate.h
testing/specialpowers/content/specialpowersAPI.js
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -115,17 +115,17 @@ interface nsIXPCComponents_utils_Sandbox
 interface ScheduledGCCallback : nsISupports
 {
     void callback();
 };
 
 /**
 * interface of Components.utils
 */
-[scriptable, uuid(ae292d08-cfb0-4862-a2b6-4779e5ef7e72)]
+[scriptable, uuid(1b981569-2b21-4702-a01a-81ffdb5d3d4c)]
 interface nsIXPCComponents_Utils : nsISupports
 {
 
     /* reportError is designed to be called from JavaScript only.
      *
      * It will report a JS Error object to the JS console, and return. It
      * is meant for use in exception handler blocks which want to "eat"
      * an exception, but still want to report it to the console.
@@ -408,16 +408,23 @@ interface nsIXPCComponents_Utils : nsISu
      *
      * Enables Xray vision for same-compartment access for the compartment
      * indicated by |vscope|. All outgoing wrappers are recomputed.
      */
     [implicit_jscontext]
     void setWantXrays(in jsval vscope);
 
     /*
+     * Forces the usage of a privileged |Components| object for a potentially-
+     * unprivileged scope. This will crash if used outside of automation.
+     */
+    [implicit_jscontext]
+    void forcePrivilegedComponentsForScope(in jsval vscope);
+
+    /*
      * This seemingly-paradoxical API allows privileged code to explicitly give
      * unprivileged code a reference to its own Components object (whereas it's
      * normally hidden away on a scope chain visible only to XBL methods). See
      * also SpecialPowers.getComponents.
      */
     [implicit_jscontext]
     jsval getComponentsForScope(in jsval vscope);
 
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3193,16 +3193,29 @@ nsXPCComponents_Utils::SetWantXrays(cons
     JSCompartment *compartment = js::GetObjectCompartment(scopeObj);
     EnsureCompartmentPrivate(scopeObj)->wantXrays = true;
     bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
                                     js::AllCompartments());
     NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
     return NS_OK;
 }
 
+/* jsval forcePrivilegedComponentsForScope(jsval vscope); */
+NS_IMETHODIMP
+nsXPCComponents_Utils::ForcePrivilegedComponentsForScope(const jsval &vscope,
+                                                         JSContext *cx)
+{
+    if (!vscope.isObject())
+        return NS_ERROR_INVALID_ARG;
+    JSObject *scopeObj = js::UncheckedUnwrap(&vscope.toObject());
+    XPCWrappedNativeScope *scope = GetObjectScope(scopeObj);
+    scope->ForcePrivilegedComponents();
+    return NS_OK;
+}
+
 /* jsval getComponentsForScope(jsval vscope); */
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetComponentsForScope(const jsval &vscope, JSContext *cx,
                                              jsval *rval)
 {
     if (!vscope.isObject())
         return NS_ERROR_INVALID_ARG;
     JSObject *scopeObj = js::UncheckedUnwrap(&vscope.toObject());
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -556,16 +556,17 @@ EnableUniversalXPConnect(JSContext *cx)
     NS_ENSURE_TRUE(ok, false);
 
     // The Components object normally isn't defined for unprivileged web content,
     // but we define it when UniversalXPConnect is enabled to support legacy
     // tests.
     XPCWrappedNativeScope *scope = priv->scope;
     if (!scope)
         return true;
+    scope->ForcePrivilegedComponents();
     return scope->AttachComponentsObject(cx);
 }
 
 JSObject *
 GetJunkScope()
 {
     XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
     NS_ENSURE_TRUE(self, nullptr);
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -1,10 +1,11 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Class used to manage the wrapped native objects within a JS scope. */
 
 #include "xpcprivate.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionNoteRootCallback.h"
@@ -148,16 +149,29 @@ XPCWrappedNativeScope::GetComponentsJSOb
     // The call to wrap() here is necessary even though the object is same-
     // compartment, because it applies our security wrapper.
     JS::RootedObject obj(cx, &val.toObject());
     if (NS_WARN_IF(!JS_WrapObject(cx, &obj)))
         return nullptr;
     return obj;
 }
 
+void
+XPCWrappedNativeScope::ForcePrivilegedComponents()
+{
+    // This may only be called on unprivileged scopes during automation where
+    // we allow insecure things.
+    MOZ_RELEASE_ASSERT(Preferences::GetBool("security.turn_off_all_security_so_"
+                                            "that_viruses_can_take_over_this_"
+                                            "computer"));
+    nsCOMPtr<nsIXPCComponents> c = do_QueryInterface(mComponents);
+    if (!c)
+        mComponents = new nsXPCComponents(this);
+}
+
 bool
 XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx)
 {
     RootedObject components(aCx, GetComponentsJSObject());
     if (!components)
         return false;
 
     RootedObject global(aCx, GetGlobalJSObject());
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1070,16 +1070,21 @@ public:
     GetWrappedNativeMap() const {return mWrappedNativeMap;}
 
     ClassInfo2WrappedNativeProtoMap*
     GetWrappedNativeProtoMap() const {return mWrappedNativeProtoMap;}
 
     nsXPCComponentsBase*
     GetComponents() const {return mComponents;}
 
+    // Forces the creation of a privileged |Components| object, even in
+    // content scopes. This will crash if used outside of automation.
+    void
+    ForcePrivilegedComponents();
+
     bool AttachComponentsObject(JSContext *aCx);
 
     // Returns the JS object reflection of the Components object.
     JSObject*
     GetComponentsJSObject();
 
     JSObject*
     GetGlobalJSObject() const {
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -37,16 +37,23 @@ function bindDOMWindowUtils(aWindow) {
     return
 
    var util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils);
    return wrapPrivileged(util);
 }
 
 function getRawComponents(aWindow) {
+  // If we're running in automation that supports enablePrivilege, then we also
+  // provided access to the privileged Components.
+  try {
+    let win = Cu.waiveXrays(aWindow);
+    if (typeof win.netscape.security.PrivilegeManager == 'object')
+      Cu.forcePrivilegedComponentsForScope(aWindow);
+  } catch (e) {}
   return Cu.getComponentsForScope(aWindow);
 }
 
 function isWrappable(x) {
   if (typeof x === "object")
     return x !== null;
   return typeof x === "function";
 };
@@ -544,30 +551,52 @@ SpecialPowersAPI.prototype = {
     return this.wrap(chromeScript);
   },
 
   get Services() {
     return wrapPrivileged(Services);
   },
 
   /*
+   * In general, any Components object created for unprivileged scopes is
+   * neutered (it implements nsIXPCComponentsBase, but not nsIXPCComponents).
+   * We override this in certain legacy automation configurations (see the
+   * implementation of getRawComponents() above), but don't want to support
+   * it in cases where it isn't already required.
+   *
+   * In scopes with neutered Components, we don't have a natural referent for
+   * things like SpecialPowers.Cc. So in those cases, we fall back to the
+   * Components object from the SpecialPowers scope. This doesn't quite behave
+   * the same way (in particular, SpecialPowers.Cc[foo].createInstance() will
+   * create an instance in the SpecialPowers scope), but SpecialPowers wrapping
+   * is already a YMMV / Whatever-It-Takes-To-Get-TBPL-Green sort of thing.
+   *
+   * It probably wouldn't be too much work to just make SpecialPowers.Components
+   * unconditionally point to the Components object in the SpecialPowers scope.
+   * Try will tell what needs to be fixed up.
+   */
+  getFullComponents: function() {
+    return typeof this.Components.classes == 'object' ? this.Components
+                                                      : Components;
+  },
+
+  /*
    * Convenient shortcuts to the standard Components abbreviations. Note that
    * we don't SpecialPowers-wrap Components.interfaces, because it's available
    * to untrusted content, and wrapping it confuses QI and identity checks.
    */
-  get Cc() { return wrapPrivileged(this.Components).classes; },
+  get Cc() { return wrapPrivileged(this.getFullComponents()).classes; },
   get Ci() { return this.Components.interfaces; },
-  get Cu() { return wrapPrivileged(this.Components).utils; },
+  get Cu() { return wrapPrivileged(this.getFullComponents()).utils; },
   get Cr() { return wrapPrivileged(this.Components).results; },
 
   /*
-   * SpecialPowers.getRawComponents() allows content to get a reference to the
-   * naked (non-SpecialPowers-wrapped) Components object for its scope. This
-   * object is normally hidden away on a scope chain available only to XBL
-   * functions.
+   * SpecialPowers.getRawComponents() allows content to get a reference to a
+   * naked (and, in certain automation configurations, privileged) Components
+   * object for its scope.
    *
    * SpecialPowers.getRawComponents(window) is defined as the global property
    * window.SpecialPowers.Components for convenience.
    */
   getRawComponents: getRawComponents,
 
   getDOMWindowUtils: function(aWindow) {
     if (aWindow == this.window.get() && this.DOMWindowUtils != null)