JSObject * WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject wrappedProto, HandleObject parent, unsigned flags) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); MOZ_ASSERT(!js::IsInnerObject(obj)); // We sometimes end up here after nsContentUtils has been shut down but before // XPConnect has been shut down, so check the context stack the roundabout way. MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx); // Compute the information we need to select the right wrapper. JSCompartment *origin = js::GetObjectCompartment(obj); JSCompartment *target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG; Wrapper *wrapper; CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target); // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { wrapper = &CrossCompartmentWrapper::singleton; // If this is a chrome object being exposed to content without Xrays, use // a COW. } else if (originIsChrome && !targetIsChrome && xrayType == NotXray) { wrapper = &ChromeObjectWrapper::singleton; // If content is accessing NAC, we need a special filter, even if the // object is same origin. Note that we allow access to NAC for remote-XUL // whitelisted domains, since they don't have XBL scopes. } else if (AccessCheck::needsSystemOnlyWrapper(obj) && xpc::AllowXBLScope(target) && !(targetIsChrome || (targetSubsumesOrigin && nsContentUtils::IsCallerXBL()))) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } // Normally, a non-xrayable non-waived content object that finds itself in // a privileged scope is wrapped with a CrossCompartmentWrapper, even though // the lack of a waiver _really_ should give it an opaque wrapper. This is // a bit too entrenched to change for content-chrome, but we can at least fix // it for XBL scopes. // // See bug 843829. else if (targetSubsumesOrigin && !originSubsumesTarget && !waiveXrayFlag && xrayType == NotXray && IsXBLScope(target)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton; } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool wantXrays = !(sameOrigin && !targetdata->wantXrays); // If Xrays are warranted, the caller may waive them for non-security // wrappers. bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag; wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays); } if (!targetSubsumesOrigin) { // Do a belt-and-suspenders check against exposing eval()/Function() to // non-subsuming content. JSFunction *fun = JS_GetObjectFunction(obj); if (fun) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Permission denied to expose eval or Function to non-subsuming content"); return nullptr; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, parent, wrapper); }
JSObject * WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject parent) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); MOZ_ASSERT(!js::IsInnerObject(obj)); // We sometimes end up here after nsContentUtils has been shut down but before // XPConnect has been shut down, so check the context stack the roundabout way. MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx); // Compute the information we need to select the right wrapper. JSCompartment *origin = js::GetObjectCompartment(obj); JSCompartment *target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); const Wrapper *wrapper; // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // Let the SpecialPowers scope make its stuff easily accessible to content. else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // If this is a chrome object being exposed to content without Xrays, use // a COW. // // We make an exception for Object instances, because we still rely on COWs // for those in a lot of places in the tree. else if (originIsChrome && !targetIsChrome && (xrayType == NotXray || ForceCOWBehavior(obj))) { wrapper = &ChromeObjectWrapper::singleton; } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays || CompartmentPrivate::Get(target)->wantXrays; bool wantXrays = !sameOrigin || sameOriginXrays; // If Xrays are warranted, the caller may waive them for non-security // wrappers. bool waiveXrays = wantXrays && !securityWrapper && HasWaiveXrayFlag(obj); // We have slightly different behavior for the case when the object // being wrapped is in an XBL scope. bool originIsContentXBLScope = IsContentXBLScope(origin); wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays, originIsContentXBLScope); // If we want to apply add-on interposition in the target compartment, // then we try to "upgrade" the wrapper to an interposing one. if (CompartmentPrivate::Get(target)->scope->HasInterposition()) wrapper = SelectAddonWrapper(cx, obj, wrapper); } if (!targetSubsumesOrigin) { // Do a belt-and-suspenders check against exposing eval()/Function() to // non-subsuming content. if (JSFunction *fun = JS_GetObjectFunction(obj)) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { NS_WARNING("Trying to expose eval or Function to non-subsuming content!"); wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, parent, wrapper); }
JSObject * WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject wrappedProto, HandleObject parent, unsigned flags) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); MOZ_ASSERT(JS_GetClass(obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); MOZ_ASSERT(!js::IsInnerObject(obj)); // We sometimes end up here after nsContentUtils has been shut down but before // XPConnect has been shut down, so check the context stack the roundabout way. MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx); // Compute the information we need to select the right wrapper. JSCompartment *origin = js::GetObjectCompartment(obj); JSCompartment *target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumes(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumes(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG; // By default we use the wrapped proto of the underlying object as the // prototype for our wrapper, but we may select something different below. RootedObject proxyProto(cx, wrappedProto); Wrapper *wrapper; CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target); // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { wrapper = &CrossCompartmentWrapper::singleton; // If this is a chrome object being exposed to content without Xrays, use // a COW. } else if (originIsChrome && !targetIsChrome && xrayType == NotXray) { wrapper = &ChromeObjectWrapper::singleton; // If content is accessing a Components object or NAC, we need a special filter, // even if the object is same origin. } else if (IsComponentsObject(obj) && !AccessCheck::isChrome(target)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, ComponentsObjectPolicy>::singleton; } else if (AccessCheck::needsSystemOnlyWrapper(obj) && !(targetIsChrome || (targetSubsumesOrigin && nsContentUtils::IsCallerXBL()))) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OnlyIfSubjectIsSystem>::singleton; } // Normally, a non-xrayable non-waived content object that finds itself in // a privileged scope is wrapped with a CrossCompartmentWrapper, even though // the lack of a waiver _really_ should give it an opaque wrapper. This is // a bit too entrenched to change for content-chrome, but we can at least fix // it for XBL scopes. // // See bug 843829. else if (targetSubsumesOrigin && !originSubsumesTarget && !waiveXrayFlag && xrayType == NotXray && IsXBLScope(target)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton; } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool wantXrays = !(sameOrigin && !targetdata->wantXrays); // If Xrays are warranted, the caller may waive them for non-security // wrappers. bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag; wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays); } // If the prototype of a chrome object being wrapped in content is a prototype // for a standard class, use the one from the content compartment so // that we can safely take advantage of things like .forEach(). // // If the prototype chain of chrome object |obj| looks like this: // // obj => foo => bar => chromeWin.StandardClass.prototype // // The prototype chain of COW(obj) looks lke this: // // COW(obj) => COW(foo) => COW(bar) => contentWin.StandardClass.prototype if (wrapper == &ChromeObjectWrapper::singleton) { JSProtoKey key = JSProto_Null; { JSAutoCompartment ac(cx, obj); RootedObject unwrappedProto(cx); if (!js::GetObjectProto(cx, obj, unwrappedProto.address())) return NULL; if (unwrappedProto && IsCrossCompartmentWrapper(unwrappedProto)) unwrappedProto = Wrapper::wrappedObject(unwrappedProto); if (unwrappedProto) { JSAutoCompartment ac2(cx, unwrappedProto); key = JS_IdentifyClassPrototype(cx, unwrappedProto); } } if (key != JSProto_Null) { RootedObject homeProto(cx); if (!JS_GetClassPrototype(cx, key, homeProto.address())) return NULL; MOZ_ASSERT(homeProto); proxyProto = homeProto; } // This shouldn't happen, but do a quick check to make some dumb addon // doesn't expose chrome eval or Function(). JSFunction *fun = JS_GetObjectFunction(obj); if (fun) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Not allowed to access chrome eval or Function from content"); return nullptr; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing && proxyProto == wrappedProto) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, proxyProto, parent, wrapper); }
JSObject* WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::IsWindowProxy(obj), "wrapped object passed to rewrap"); MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); MOZ_ASSERT(!js::IsWindow(obj)); MOZ_ASSERT(dom::IsJSAPIActive()); // Compute the information we need to select the right wrapper. JSCompartment* origin = js::GetObjectCompartment(obj); JSCompartment* target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); const Wrapper* wrapper; // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // Let the SpecialPowers scope make its stuff easily accessible to content. else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // Special handling for chrome objects being exposed to content. else if (originIsChrome && !targetIsChrome) { // If this is a chrome function being exposed to content, we need to allow // call (but nothing else). We allow CPOWs that purport to be function's // here, but only in the content process. if ((IdentifyStandardInstance(obj) == JSProto_Function || (jsipc::IsCPOW(obj) && JS::IsCallable(obj) && XRE_IsContentProcess()))) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton; } // For Vanilla JSObjects exposed from chrome to content, we use a wrapper // that supports __exposedProps__. We'd like to get rid of these eventually, // but in their current form they don't cause much trouble. else if (IdentifyStandardInstance(obj) == JSProto_Object) { wrapper = &ChromeObjectWrapper::singleton; } // Otherwise we get an opaque wrapper. else { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays || CompartmentPrivate::Get(target)->wantXrays; bool wantXrays = !sameOrigin || sameOriginXrays; // If Xrays are warranted, the caller may waive them for non-security // wrappers (unless explicitly forbidden from doing so). bool waiveXrays = wantXrays && !securityWrapper && CompartmentPrivate::Get(target)->allowWaivers && HasWaiveXrayFlag(obj); // We have slightly different behavior for the case when the object // being wrapped is in an XBL scope. bool originIsContentXBLScope = IsContentXBLScope(origin); wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays, originIsContentXBLScope, obj); // If we want to apply add-on interposition in the target compartment, // then we try to "upgrade" the wrapper to an interposing one. if (CompartmentPrivate::Get(target)->scope->HasInterposition()) wrapper = SelectAddonWrapper(cx, obj, wrapper); } if (!targetSubsumesOrigin) { // Do a belt-and-suspenders check against exposing eval()/Function() to // non-subsuming content. if (JSFunction* fun = JS_GetObjectFunction(obj)) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { NS_WARNING("Trying to expose eval or Function to non-subsuming content!"); wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, wrapper); }