diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index a990e10042e9..f8e6c8250010 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -2271,7 +2271,6 @@ nsJSContext::GetGlobalObject() if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { - NS_WARNING("Global is not an nsISupports."); return nsnull; } diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp index ddaec31dda56..9702716ce06a 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp @@ -155,7 +155,9 @@ ShouldBypassNativeWrapper(JSContext *cx, JSObject *obj) #define XPC_NW_BYPASS_BASE(cx, obj, code) \ JS_BEGIN_MACRO \ if (ShouldBypassNativeWrapper(cx, obj)) { \ - XPCWrappedNative *wn_ = XPCNativeWrapper::GetWrappedNative(obj); \ + /* Use SafeGetWrappedNative since obj can't be an explicit native \ + wrapper. */ \ + XPCWrappedNative *wn_ = XPCNativeWrapper::SafeGetWrappedNative(obj); \ if (!wn_) { \ return JS_TRUE; \ } \ @@ -193,6 +195,45 @@ static inline JSBool EnsureLegalActivity(JSContext *cx, JSObject *obj) { + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if (!ssm) { + // If there's no security manager, then we're not running in a browser + // context: allow access. + return JS_TRUE; + } + + JSStackFrame *fp; + nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp); + if (!subjectPrincipal || !fp) { + // We must allow the access if there is no code running. + return JS_TRUE; + } + + // This might be chrome code or content code with UniversalXPConnect. + void *annotation = JS_GetFrameAnnotation(cx, fp); + PRBool isPrivileged = PR_FALSE; + nsresult rv = subjectPrincipal->IsCapabilityEnabled("UniversalXPConnect", + annotation, + &isPrivileged); + if (NS_SUCCEEDED(rv) && isPrivileged) { + return JS_TRUE; + } + + // We're in unprivileged code, ensure that we're allowed to access the + // underlying object. + XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); + if (wn) { + nsIPrincipal *objectPrincipal = wn->GetScope()->GetPrincipal(); + PRBool subsumes; + if (NS_FAILED(subjectPrincipal->Subsumes(objectPrincipal, &subsumes)) || + !subsumes) { + return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); + } + } + + // The underlying object is accessible, but this might be the wrong + // type of wrapper to access it through. + // TODO This should just be an assertion now. jsval flags; ::JS_GetReservedSlot(cx, obj, 0, &flags); @@ -201,35 +242,65 @@ EnsureLegalActivity(JSContext *cx, JSObject *obj) return JS_TRUE; } - JSStackFrame *frame = nsnull; - uint32 fileFlags = JS_GetTopScriptFilenameFlags(cx, NULL); - if (!JS_FrameIterator(cx, &frame) || - fileFlags == JSFILENAME_NULL || - (fileFlags & JSFILENAME_SYSTEM)) { + JSScript *script = JS_GetFrameScript(cx, fp); + uint32 fileFlags = JS_GetScriptFilenameFlags(script); + if (fileFlags == JSFILENAME_NULL || (fileFlags & JSFILENAME_SYSTEM)) { // We expect implicit native wrappers in system files. return JS_TRUE; } - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (!ssm) { - // If there's no security manager, then we're not running in a browser - // context: allow access. - return JS_TRUE; - } - - // A last ditch effort to allow access: if the currently-running code - // has UniversalXPConnect privileges, then allow access. - PRBool isPrivileged; - nsresult rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &isPrivileged); - if (NS_SUCCEEDED(rv) && isPrivileged) { - return JS_TRUE; - } - // Otherwise, we're looking at a non-system file with a handle on an // implicit wrapper. This is a bug! Deny access. return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); } +// static +JSBool +XPCNativeWrapper::GetWrappedNative(JSContext *cx, JSObject *obj, + XPCWrappedNative **aWrappedNative) +{ + XPCWrappedNative *wn = static_cast(xpc_GetJSPrivate(obj)); + *aWrappedNative = wn; + if (!wn) { + return JS_TRUE; + } + + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if (!ssm) { + return JS_TRUE; + } + + JSStackFrame *fp; + nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp); + if (!subjectPrincipal) { + return JS_TRUE; + } + + if (fp) { + void *annotation = JS_GetFrameAnnotation(cx, fp); + + PRBool isPrivileged; + nsresult rv = + subjectPrincipal->IsCapabilityEnabled("UniversalXPConnect", + annotation, + &isPrivileged); + if (NS_SUCCEEDED(rv) && isPrivileged) { + return JS_TRUE; + } + } + + XPCWrappedNativeScope *scope = wn->GetScope(); + nsIPrincipal *objectPrincipal = scope->GetPrincipal(); + + PRBool subsumes; + nsresult rv = subjectPrincipal->Subsumes(objectPrincipal, &subsumes); + if (NS_FAILED(rv) || !subsumes) { + return JS_FALSE; + } + + return JS_TRUE; +} + JSBool XPC_NW_WrapFunction(JSContext* cx, JSObject* funobj, jsval *rval) { @@ -373,7 +444,7 @@ XPC_NW_RewrapIfDeepWrapper(JSContext *cx, JSObject *obj, jsval v, jsval *rval) #ifdef DEBUG_XPCNativeWrapper printf("Rewrapping for deep explicit wrapper\n"); #endif - if (wrappedNative == XPCNativeWrapper::GetWrappedNative(obj)) { + if (wrappedNative == XPCNativeWrapper::SafeGetWrappedNative(obj)) { // Already wrapped, return the wrapper. *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; @@ -427,9 +498,11 @@ XPC_NW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, // The real method we're going to call is the parent of this // function's JSObject. JSObject *methodToCallObj = STOBJ_GET_PARENT(funObj); - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + XPCWrappedNative *wrappedNative; - if (!::JS_ObjectIsFunction(cx, methodToCallObj) || !wrappedNative) { + if (!XPCNativeWrapper::GetWrappedNative(cx, obj, &wrappedNative) || + !::JS_ObjectIsFunction(cx, methodToCallObj) || + !wrappedNative) { return ThrowException(NS_ERROR_UNEXPECTED, cx); } @@ -469,7 +542,8 @@ XPC_NW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, return JS_FALSE; } - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + // Protected by EnsureLegalActivity. + XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); if (!wrappedNative) { return ThrowException(NS_ERROR_INVALID_ARG, cx); @@ -548,7 +622,8 @@ XPC_NW_Enumerate(JSContext *cx, JSObject *obj) return JS_FALSE; } - XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(obj); + // Protected by EnsureLegalActivity. + XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); if (!wn) { return JS_TRUE; } @@ -585,7 +660,8 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, // the wrapped native's object. if (ShouldBypassNativeWrapper(cx, obj)) { - XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(obj); + // Protected by EnsureLegalActivity. + XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); if (!wn) { return JS_TRUE; } @@ -622,7 +698,8 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, } } - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + // Protected by EnsureLegalActivity. + XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); if (!wrappedNative) { // No wrapped native, no properties. @@ -677,7 +754,8 @@ XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsval id, return JS_FALSE; } - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + // This function does its own security checks. + XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); if (!wrappedNative) { return JS_TRUE; } @@ -722,7 +800,12 @@ XPC_NW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, XPC_NW_BYPASS_TEST(cx, obj, construct, (cx, obj, argc, argv, rval)); - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + if (!EnsureLegalActivity(cx, obj)) { + return JS_FALSE; + } + + // Protected by EnsureLegalActivity. + XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); if (!wrappedNative) { return JS_TRUE; } @@ -802,11 +885,23 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSObject *nativeObj = JSVAL_TO_OBJECT(native); - // Not allowed to go from more restrictive wrappers to less restrictive - // wrappers. - if (STOBJ_GET_CLASS(nativeObj) == &sXPC_XOW_JSClass.base || - STOBJ_GET_CLASS(nativeObj) == &sXPC_SJOW_JSClass.base) { - return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); + // Unwrap a cross origin wrapper, since we're more restrictive than it is. + if (STOBJ_GET_CLASS(nativeObj) == &sXPC_XOW_JSClass.base) { + jsval v; + if (!::JS_GetReservedSlot(cx, nativeObj, XPCWrapper::sWrappedObjSlot, &v)) { + return JS_FALSE; + } + // If v is primitive, allow nativeObj to remain a cross origin wrapper, + // which will fail below (since it isn't a wrapped native). + if (!JSVAL_IS_PRIMITIVE(v)) { + nativeObj = JSVAL_TO_OBJECT(v); + } + } else if (STOBJ_GET_CLASS(nativeObj) == &sXPC_SJOW_JSClass.base) { + // Also unwrap SJOWs. + nativeObj = JS_GetParent(cx, nativeObj); + if (!nativeObj) { + return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); + } } XPCWrappedNative *wrappedNative; @@ -819,7 +914,8 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, printf("Wrapping already wrapped object\n"); #endif - wrappedNative = XPCNativeWrapper::GetWrappedNative(nativeObj); + // It's always safe to re-wrap an object. + wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(nativeObj); if (!wrappedNative) { return ThrowException(NS_ERROR_INVALID_ARG, cx); @@ -963,7 +1059,8 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static void XPC_NW_Trace(JSTracer *trc, JSObject *obj) { - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + // Untrusted code can't trigger this. + XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); if (wrappedNative && wrappedNative->IsValid()) { JS_CALL_OBJECT_TRACER(trc, wrappedNative->GetFlatJSObject(), @@ -978,13 +1075,18 @@ XPC_NW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) "Uh, we should only ever be called for XPCNativeWrapper " "objects!"); + if (!EnsureLegalActivity(cx, obj)) { + return JS_FALSE; + } + if (JSVAL_IS_PRIMITIVE(v)) { *bp = JS_FALSE; return JS_TRUE; } - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + // Protected by EnsureLegalActivity. + XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); if (wrappedNative && wrappedNative->IsValid() && NATIVE_HAS_FLAG(wrappedNative, WantEquality)) { @@ -1020,7 +1122,8 @@ XPC_NW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; } - XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj); + // Protected by EnsureLegalActivity. + XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); if (!wrappedNative) { // toString() called on XPCNativeWrapper.prototype diff --git a/js/src/xpconnect/src/XPCNativeWrapper.h b/js/src/xpconnect/src/XPCNativeWrapper.h index fac8092225d7..79ecdabac077 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.h +++ b/js/src/xpconnect/src/XPCNativeWrapper.h @@ -61,11 +61,16 @@ public: return STOBJ_GET_CLASS(obj) == &sXPC_NW_JSClass.base; } - static XPCWrappedNative *GetWrappedNative(JSObject *obj) + static JSBool GetWrappedNative(JSContext *cx, JSObject *obj, + XPCWrappedNative **aWrappedNative); + + // NB: Use the following carefully. + static XPCWrappedNative *SafeGetWrappedNative(JSObject *obj) { - return (XPCWrappedNative *)xpc_GetJSPrivate(obj); + return static_cast(xpc_GetJSPrivate(obj)); } + static JSClass *GetJSClass() { return &sXPC_NW_JSClass.base; diff --git a/js/src/xpconnect/src/xpcquickstubs.cpp b/js/src/xpconnect/src/xpcquickstubs.cpp index 79895272e7a3..394bd28f4633 100644 --- a/js/src/xpconnect/src/xpcquickstubs.cpp +++ b/js/src/xpconnect/src/xpcquickstubs.cpp @@ -535,8 +535,8 @@ xpc_qsUnwrapThisImpl(JSContext *cx, } else if(XPCNativeWrapper::IsNativeWrapperClass(clazz)) { - wrapper = XPCNativeWrapper::GetWrappedNative(cur); - if(!wrapper) + if(!XPCNativeWrapper::GetWrappedNative(cx, cur, &wrapper) || + !wrapper) goto next; } else if(IsXPCSafeJSObjectWrapperClass(clazz)) diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index cb748e4847ac..dcdecd7e6ae5 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -1444,8 +1444,8 @@ return_tearoff: if(XPCNativeWrapper::IsNativeWrapperClass(clazz)) { - XPCWrappedNative* wrapper = XPCNativeWrapper::GetWrappedNative(cur); - if(wrapper) + XPCWrappedNative* wrapper; + if(XPCNativeWrapper::GetWrappedNative(cx, cur, &wrapper) && wrapper) return GetWrappedNativeOfJSObject(cx, wrapper->GetFlatJSObject(), funobj, pobj2, pTearOff); } diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index 4edc4238f457..b5c9e2a01441 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -736,7 +736,9 @@ XPC_GetIdentityObject(JSContext *cx, JSObject *obj) XPCWrappedNative *wrapper; if(XPCNativeWrapper::IsNativeWrapper(obj)) - wrapper = XPCNativeWrapper::GetWrappedNative(obj); + // Note: It's okay to use SafeGetWrappedNative here since we only do + // identity checking on the returned object. + wrapper = XPCNativeWrapper::SafeGetWrappedNative(obj); else wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); diff --git a/js/src/xpconnect/tests/mochitest/test_wrappers.html b/js/src/xpconnect/tests/mochitest/test_wrappers.html index c64cada9327e..1ab6c808bbd9 100644 --- a/js/src/xpconnect/tests/mochitest/test_wrappers.html +++ b/js/src/xpconnect/tests/mochitest/test_wrappers.html @@ -52,6 +52,8 @@ 'SJOWs equality hook returns true correctly'); } + ok(new XPCSafeJSObjectWrapper(window) == new XPCNativeWrapper(window), + 'SJOWs equality hook returns true correctly against XPCNW'); ok(new XPCSafeJSObjectWrapper(window) == window, 'SJOWs equality hook returns true correctly against XOW'); diff --git a/xpfe/appshell/src/nsContentTreeOwner.cpp b/xpfe/appshell/src/nsContentTreeOwner.cpp index 4523006520ca..81dc2c7fbb49 100644 --- a/xpfe/appshell/src/nsContentTreeOwner.cpp +++ b/xpfe/appshell/src/nsContentTreeOwner.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=2 sw=2 et tw=79: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -66,6 +67,7 @@ #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIWebNavigation.h" +#include "nsIJSContextStack.h" #include "nsIDOMDocument.h" #include "nsIScriptObjectPrincipal.h" @@ -74,6 +76,8 @@ // CIDs static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID); +static const char *sJSStackContractID="@mozilla.org/js/xpc/ContextStack;1"; + //***************************************************************************** //*** nsSiteWindow2 declaration //***************************************************************************** @@ -780,6 +784,34 @@ NS_IMETHODIMP nsContentTreeOwner::SetTitle(const PRUnichar* aTitle) return mXULWindow->SetTitle(title.get()); } +NS_STACK_CLASS class NullJSContextPusher { +public: + NullJSContextPusher() { + mService = do_GetService(sJSStackContractID); + if (mService) { +#ifdef DEBUG + nsresult rv = +#endif + mService->Push(nsnull); + NS_ASSERTION(NS_SUCCEEDED(rv), "Mismatched push/pop"); + } + } + + ~NullJSContextPusher() { + if (mService) { +#ifdef DEBUG + JSContext *cx; + nsresult rv = +#endif + mService->Pop(&cx); + NS_ASSERTION(NS_SUCCEEDED(rv) && !cx, "Bad pop!"); + } + } + +private: + nsCOMPtr mService; +}; + //***************************************************************************** // nsContentTreeOwner: nsIWindowProvider //***************************************************************************** @@ -878,10 +910,14 @@ nsContentTreeOwner::ProvideWindow(nsIDOMWindow* aParent, *aWindowIsNew = (containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW); - // Get a new rendering area from the browserDOMWin. We don't want - // to be starting any loads here, so get it with a null URI. - return browserDOMWin->OpenURI(nsnull, aParent, containerPref, - nsIBrowserDOMWindow::OPEN_NEW, aReturn); + { + NullJSContextPusher pusher; + + // Get a new rendering area from the browserDOMWin. We don't want + // to be starting any loads here, so get it with a null URI. + return browserDOMWin->OpenURI(nsnull, aParent, containerPref, + nsIBrowserDOMWindow::OPEN_NEW, aReturn); + } } //*****************************************************************************