Bug 733984 - Clarify the security characteristics of Location objects. r=mrbkap
authorBobby Holley <bobbyholley@gmail.com>
Fri, 23 Mar 2012 14:59:07 -0700
changeset 93522 c8464d25e40dea51e098de652fc69184a366997a
parent 93521 2b113540e75a445d08ab66122ee47aeaf676c444
child 93523 ae71e6cdc6c479bca3e8f8ccc7e27424a519192c
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs733984
milestone14.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 733984 - Clarify the security characteristics of Location objects. r=mrbkap I was getting confused by some of the naming and lack of comments here.
js/xpconnect/wrappers/AccessCheck.cpp
js/xpconnect/wrappers/AccessCheck.h
js/xpconnect/wrappers/FilteringWrapper.cpp
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/WrapperFactory.h
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -44,17 +44,16 @@
 #include "nsJSPrincipals.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsContentUtils.h"
 
 #include "XPCWrapper.h"
 #include "XrayWrapper.h"
 #include "FilteringWrapper.h"
-#include "WrapperFactory.h"
 
 #include "jsfriendapi.h"
 
 using namespace mozilla;
 using namespace js;
 
 namespace xpc {
 
@@ -84,16 +83,19 @@ AccessCheck::isSameOrigin(JSCompartment 
     }
 
     return equals;
 }
 
 bool
 AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper)
 {
+    // The caller must ensure that the given wrapper wraps a Location object.
+    MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
+
     // Location objects are parented to the outer window for which they
     // were created. This gives us an easy way to determine whether our
     // object is same origin with the current inner window:
 
     // Grab the outer window...
     JSObject *obj = js::GetObjectParent(js::UnwrapObject(wrapper));
     if (!js::GetObjectClass(obj)->ext.innerObject) {
         // ...which might be wrapped in a security wrapper.
@@ -315,18 +317,23 @@ AccessCheck::isCrossOriginAccessPermitte
     if (JSID_IS_STRING(id)) {
         if (IsPermitted(name, JSID_TO_FLAT_STRING(id), act == Wrapper::SET))
             return true;
     }
 
     if (IsWindow(name) && IsFrameId(cx, obj, id))
         return true;
 
-    // We only reach this point for cross origin location objects (see
-    // SameOriginOrCrossOriginAccessiblePropertiesOnly::check).
+    // Do the dynamic document.domain check.
+    //
+    // Location also needs a dynamic access check, but it's a different one, and
+    // we do it in LocationPolicy::check. Before LocationPolicy::check does that
+    // though, it first calls this function to check whether the property is
+    // accessible to anyone regardless of origin. So make sure not to do the
+    // document.domain check in that case.
     if (!IsLocation(name) && documentDomainMakesSameOrigin(cx, obj))
         return true;
 
     return (act == Wrapper::SET)
            ? nsContentUtils::IsCallerTrustedForWrite()
            : nsContentUtils::IsCallerTrustedForRead();
 }
 
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -34,16 +34,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsapi.h"
 #include "jswrapper.h"
+#include "WrapperFactory.h"
 
 class nsIPrincipal;
 
 namespace xpc {
 
 nsIPrincipal *
 GetCompartmentPrincipal(JSCompartment *compartment);
 
@@ -100,34 +101,61 @@ struct OnlyIfSubjectIsSystem : public Po
     }
 };
 
 // This policy only permits access to properties that are safe to be used
 // across origins.
 struct CrossOriginAccessiblePropertiesOnly : public Policy {
     static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
                       Permission &perm) {
+        // Location objects should always use LocationPolicy.
+        MOZ_ASSERT(!WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
+
         if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act)) {
             perm = PermitPropertyAccess;
             return true;
         }
         perm = DenyAccess;
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, wrapper))
             return false;
         AccessCheck::deny(cx, id);
         return false;
     }
 };
 
-// This policy only permits access to properties that are safe to be used
-// across origins.
-struct SameOriginOrCrossOriginAccessiblePropertiesOnly : public Policy {
+// We need a special security policy for Location objects.
+//
+// Location objects are special because their effective principal is that of
+// the outer window, not the inner window. So while the security characteristics
+// of most objects can be inferred from their compartments, those of the Location
+// object cannot. This has two implications:
+//
+// 1 - Same-compartment access of Location objects is not necessarily allowed.
+//     This means that objects must see a security wrapper around Location objects
+//     in their own compartment.
+// 2 - Cross-origin access of Location objects is not necessarily forbidden.
+//     Since the security decision depends on the current state of the outer window,
+//     we can't make it at wrap time. Instead, we need to make it at the time of
+//     access.
+//
+// So for any Location object access, be it same-compartment or cross-compartment,
+// we need to do a dynamic security check to determine whether the outer window is
+// same-origin with the caller.
+//
+// So this policy first checks whether the access is something that any code,
+// same-origin or not, is allowed to make. If it isn't, it _also_ checks the
+// state of the outer window to determine whether we happen to be same-origin
+// at the moment.
+struct LocationPolicy : public Policy {
     static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
                       Permission &perm) {
+        // We should only be dealing with Location objects here.
+        MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
+
         if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) ||
             AccessCheck::isLocationObjectSameOrigin(cx, wrapper)) {
             perm = PermitPropertyAccess;
             return true;
         }
         perm = DenyAccess;
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, wrapper))
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -141,19 +141,19 @@ FilteringWrapper<Base, Policy>::enter(JS
 #define COW FilteringWrapper<CrossCompartmentSecurityWrapper, ExposedPropertiesOnly>
 #define XOW FilteringWrapper<XrayWrapper<CrossCompartmentSecurityWrapper>, \
                              CrossOriginAccessiblePropertiesOnly>
 #define PXOW   FilteringWrapper<XrayProxy, \
                                 CrossOriginAccessiblePropertiesOnly>
 #define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, \
                                CrossOriginAccessiblePropertiesOnly>
 #define LW    FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>, \
-                               SameOriginOrCrossOriginAccessiblePropertiesOnly>
+                               LocationPolicy>
 #define XLW   FilteringWrapper<XrayWrapper<CrossCompartmentSecurityWrapper>, \
-                               SameOriginOrCrossOriginAccessiblePropertiesOnly>
+                               LocationPolicy>
 
 template<> SOW SOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::SOW_FLAG);
 template<> SCSOW SCSOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                   WrapperFactory::SOW_FLAG);
 template<> COW COW::singleton(0);
 template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::PARTIALLY_TRANSPARENT);
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -32,17 +32,16 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "WrapperFactory.h"
 #include "CrossOriginWrapper.h"
 #include "FilteringWrapper.h"
 #include "XrayWrapper.h"
 #include "AccessCheck.h"
 #include "XPCWrapper.h"
 
 #include "xpcprivate.h"
 #include "dombindings.h"
@@ -385,18 +384,17 @@ WrapperFactory::Rewrap(JSContext *cx, JS
                                             CrossOriginAccessiblePropertiesOnly>::singleton;
             } else {
                 typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
                 usingXray = true;
 
                 // Location objects can become same origin after navigation, so we might
                 // have to grant transparent access later on.
                 if (IsLocationObject(obj)) {
-                    wrapper = &FilteringWrapper<Xray,
-                        SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton;
+                    wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
                 } else {
                     wrapper = &FilteringWrapper<Xray,
                         CrossOriginAccessiblePropertiesOnly>::singleton;
                 }
             }
         }
     }
 
@@ -406,18 +404,17 @@ WrapperFactory::Rewrap(JSContext *cx, JS
 
     JSObject *xrayHolder = XrayUtils::createHolder(cx, obj, parent);
     if (!xrayHolder)
         return nsnull;
     js::SetProxyExtra(wrapperObj, 0, js::ObjectValue(*xrayHolder));
     return wrapperObj;
 }
 
-typedef FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>,
-                         SameOriginOrCrossOriginAccessiblePropertiesOnly> LW;
+typedef FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>, LocationPolicy> LW;
 
 bool
 WrapperFactory::IsLocationObject(JSObject *obj)
 {
     const char *name = js::GetObjectClass(obj)->name;
     return name[0] == 'L' && !strcmp(name, "Location");
 }
 
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -32,16 +32,19 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#ifndef _xpc_WRAPPERFACTORY_H
+#define _xpc_WRAPPERFACTORY_H
+
 #include "jsapi.h"
 #include "jswrapper.h"
 
 namespace xpc {
 
 class WrapperFactory {
   public:
     enum { WAIVE_XRAY_WRAPPER_FLAG = js::Wrapper::LAST_USED_FLAG << 1,
@@ -97,8 +100,10 @@ class WrapperFactory {
 
     // Wrap a (same compartment) object in a SOW.
     static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj);
 };
 
 extern js::Wrapper WaiveXrayWrapperWrapper;
 
 }
+
+#endif /* _xpc_WRAPPERFACTORY_H */