nsIGlobalObject * GetNativeForGlobal(JSObject *obj) { MOZ_ASSERT(JS_IsGlobalObject(obj)); if (!EnsureCompartmentPrivate(obj)->scope) return nullptr; // Every global needs to hold a native as its private. MOZ_ASSERT(GetObjectClass(obj)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE)); nsISupports *native = static_cast<nsISupports *>(js::GetObjectPrivate(obj)); MOZ_ASSERT(native); // In some cases (like for windows) it is a wrapped native, // in other cases (sandboxes, backstage passes) it's just // a direct pointer to the native. If it's a wrapped native // let's unwrap it first. if (nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native)) { native = wn->Native(); } nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(native); MOZ_ASSERT(global, "Native held by global needs to implement nsIGlobalObject!"); return global; }
JSObject *GetXBLScope(JSContext *cx, JSObject *contentScope) { JSAutoCompartment ac(cx, contentScope); JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx); scope = js::UnwrapObject(scope); xpc_UnmarkGrayObject(scope); return scope; }
JSObject *GetXBLScope(JSContext *cx, JSObject *contentScope) { JSAutoCompartment ac(cx, contentScope); JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx); NS_ENSURE_TRUE(scope, nullptr); // See bug 858642. scope = js::UnwrapObject(scope); xpc_UnmarkGrayObject(scope); return scope; }
JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg) { JS::RootedObject contentScope(cx, contentScopeArg); JSAutoCompartment ac(cx, contentScope); JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx); NS_ENSURE_TRUE(scope, nullptr); // See bug 858642. scope = js::UncheckedUnwrap(scope); JS::ExposeObjectToActiveJS(scope); return scope; }
bool AllowXBLScope(JSCompartment *c) { XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope; return scope && scope->AllowXBLScope(); }
void SetLocationForGlobal(JSObject *global, nsIURI *locationURI) { MOZ_ASSERT(global); EnsureCompartmentPrivate(global)->SetLocationURI(locationURI); }
void SetLocationForGlobal(JSObject *global, const nsACString& location) { MOZ_ASSERT(global); EnsureCompartmentPrivate(global)->SetLocation(location); }
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 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); }