2010-06-25 15:58:09 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
|
|
|
|
*
|
2012-05-21 04:12:37 -07:00
|
|
|
* 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/. */
|
2010-06-25 15:58:09 -07:00
|
|
|
|
2011-10-10 22:50:08 -07:00
|
|
|
#include "mozilla/Util.h"
|
|
|
|
|
2010-07-02 13:54:53 -07:00
|
|
|
#include "AccessCheck.h"
|
2010-06-25 15:58:09 -07:00
|
|
|
|
|
|
|
#include "nsJSPrincipals.h"
|
2010-07-02 13:54:53 -07:00
|
|
|
#include "nsIDOMWindow.h"
|
|
|
|
#include "nsIDOMWindowCollection.h"
|
2010-09-17 14:54:40 -07:00
|
|
|
#include "nsContentUtils.h"
|
2012-05-25 09:42:40 -07:00
|
|
|
#include "nsJSUtils.h"
|
2010-06-25 15:58:09 -07:00
|
|
|
|
2010-07-02 13:54:53 -07:00
|
|
|
#include "XPCWrapper.h"
|
2010-09-17 14:54:40 -07:00
|
|
|
#include "XrayWrapper.h"
|
2010-10-10 15:39:08 -07:00
|
|
|
#include "FilteringWrapper.h"
|
2010-06-25 15:58:09 -07:00
|
|
|
|
2011-04-13 09:27:37 -07:00
|
|
|
#include "jsfriendapi.h"
|
2010-10-28 08:15:53 -07:00
|
|
|
|
2011-10-10 22:50:08 -07:00
|
|
|
using namespace mozilla;
|
2011-09-08 20:29:15 -07:00
|
|
|
using namespace js;
|
|
|
|
|
2010-06-25 15:58:09 -07:00
|
|
|
namespace xpc {
|
|
|
|
|
2011-08-06 14:05:25 -07:00
|
|
|
nsIPrincipal *
|
2010-06-25 15:58:09 -07:00
|
|
|
GetCompartmentPrincipal(JSCompartment *compartment)
|
|
|
|
{
|
2012-03-09 01:48:50 -08:00
|
|
|
return nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
|
2010-06-25 15:58:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-07-02 13:54:53 -07:00
|
|
|
AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b)
|
|
|
|
{
|
2010-10-13 11:37:25 -07:00
|
|
|
nsIPrincipal *aprin = GetCompartmentPrincipal(a);
|
|
|
|
nsIPrincipal *bprin = GetCompartmentPrincipal(b);
|
|
|
|
|
|
|
|
// If either a or b doesn't have principals, we don't have enough
|
|
|
|
// information to tell. Seeing as how this is Gecko, we are default-unsafe
|
|
|
|
// in this case.
|
|
|
|
if (!aprin || !bprin)
|
|
|
|
return true;
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool equals;
|
2011-05-19 04:31:54 -07:00
|
|
|
nsresult rv = aprin->EqualsIgnoringDomain(bprin, &equals);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
NS_ERROR("unable to ask about equality");
|
|
|
|
return false;
|
|
|
|
}
|
2011-02-02 22:05:07 -08:00
|
|
|
|
2011-05-19 04:31:54 -07:00
|
|
|
return equals;
|
2010-07-02 13:54:53 -07:00
|
|
|
}
|
|
|
|
|
2010-09-29 10:00:52 -07:00
|
|
|
bool
|
2010-10-10 15:36:41 -07:00
|
|
|
AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper)
|
2010-09-29 10:00:52 -07:00
|
|
|
{
|
2012-03-23 14:59:07 -07:00
|
|
|
// The caller must ensure that the given wrapper wraps a Location object.
|
|
|
|
MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
|
|
|
|
|
2012-01-26 05:55:27 -08:00
|
|
|
// 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...
|
2011-10-04 07:06:54 -07:00
|
|
|
JSObject *obj = js::GetObjectParent(js::UnwrapObject(wrapper));
|
|
|
|
if (!js::GetObjectClass(obj)->ext.innerObject) {
|
2012-01-26 05:55:27 -08:00
|
|
|
// ...which might be wrapped in a security wrapper.
|
2011-10-04 07:06:54 -07:00
|
|
|
obj = js::UnwrapObject(obj);
|
|
|
|
JS_ASSERT(js::GetObjectClass(obj)->ext.innerObject);
|
2010-09-29 10:00:52 -07:00
|
|
|
}
|
2012-01-26 05:55:27 -08:00
|
|
|
|
|
|
|
// Now innerize it to find the *current* inner window for our outer.
|
2011-10-04 07:06:54 -07:00
|
|
|
obj = JS_ObjectToInnerObject(cx, obj);
|
2012-01-26 05:55:27 -08:00
|
|
|
|
|
|
|
// Which lets us compare the current compartment against the old one.
|
2011-02-23 22:13:17 -08:00
|
|
|
return obj &&
|
2011-10-04 07:06:54 -07:00
|
|
|
(isSameOrigin(js::GetObjectCompartment(wrapper),
|
|
|
|
js::GetObjectCompartment(obj)) ||
|
2011-02-23 22:13:17 -08:00
|
|
|
documentDomainMakesSameOrigin(cx, obj));
|
2010-09-29 10:00:52 -07:00
|
|
|
}
|
|
|
|
|
2010-07-02 13:54:53 -07:00
|
|
|
bool
|
|
|
|
AccessCheck::isChrome(JSCompartment *compartment)
|
2010-06-25 15:58:09 -07:00
|
|
|
{
|
|
|
|
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
2010-07-02 13:54:53 -07:00
|
|
|
if (!ssm) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool privileged;
|
2010-07-02 13:54:53 -07:00
|
|
|
nsIPrincipal *principal = GetCompartmentPrincipal(compartment);
|
|
|
|
return NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) && privileged;
|
|
|
|
}
|
|
|
|
|
2012-06-18 06:47:09 -07:00
|
|
|
bool
|
|
|
|
AccessCheck::callerIsChrome()
|
|
|
|
{
|
|
|
|
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
|
|
|
if (!ssm)
|
|
|
|
return false;
|
|
|
|
bool subjectIsSystem;
|
|
|
|
nsresult rv = ssm->SubjectPrincipalIsSystem(&subjectIsSystem);
|
|
|
|
return NS_SUCCEEDED(rv) && subjectIsSystem;
|
|
|
|
}
|
|
|
|
|
2010-09-24 18:00:58 -07:00
|
|
|
nsIPrincipal *
|
|
|
|
AccessCheck::getPrincipal(JSCompartment *compartment)
|
|
|
|
{
|
|
|
|
return GetCompartmentPrincipal(compartment);
|
|
|
|
}
|
|
|
|
|
2010-10-28 08:15:53 -07:00
|
|
|
#define NAME(ch, str, cases) \
|
|
|
|
case ch: if (!strcmp(name, str)) switch (propChars[0]) { cases }; break;
|
2010-07-02 13:54:53 -07:00
|
|
|
#define PROP(ch, actions) case ch: { actions }; break;
|
2010-12-03 00:24:17 -08:00
|
|
|
#define RW(str) if (JS_FlatStringEqualsAscii(prop, str)) return true;
|
|
|
|
#define R(str) if (!set && JS_FlatStringEqualsAscii(prop, str)) return true;
|
|
|
|
#define W(str) if (set && JS_FlatStringEqualsAscii(prop, str)) return true;
|
2010-07-02 13:54:53 -07:00
|
|
|
|
|
|
|
// Hardcoded policy for cross origin property access. This was culled from the
|
|
|
|
// preferences file (all.js). We don't want users to overwrite highly sensitive
|
|
|
|
// security policies.
|
|
|
|
static bool
|
2010-12-03 00:24:17 -08:00
|
|
|
IsPermitted(const char *name, JSFlatString *prop, bool set)
|
2010-07-02 13:54:53 -07:00
|
|
|
{
|
2010-10-28 08:15:53 -07:00
|
|
|
size_t propLength;
|
2012-01-15 00:13:07 -08:00
|
|
|
const jschar *propChars =
|
|
|
|
JS_GetInternedStringCharsAndLength(JS_FORGET_STRING_FLATNESS(prop), &propLength);
|
2010-10-28 08:15:53 -07:00
|
|
|
if (!propLength)
|
|
|
|
return false;
|
2011-10-14 10:52:48 -07:00
|
|
|
switch (name[0]) {
|
2010-07-02 13:54:53 -07:00
|
|
|
NAME('H', "History",
|
|
|
|
PROP('b', R("back"))
|
|
|
|
PROP('f', R("forward"))
|
|
|
|
PROP('g', R("go")))
|
2010-09-17 14:54:40 -07:00
|
|
|
NAME('L', "Location",
|
|
|
|
PROP('h', W("hash") W("href"))
|
|
|
|
PROP('r', R("replace")))
|
2010-07-02 13:54:53 -07:00
|
|
|
NAME('W', "Window",
|
|
|
|
PROP('b', R("blur"))
|
|
|
|
PROP('c', R("close") R("closed"))
|
|
|
|
PROP('f', R("focus") R("frames"))
|
|
|
|
PROP('h', R("history"))
|
|
|
|
PROP('l', RW("location") R("length"))
|
|
|
|
PROP('o', R("opener"))
|
|
|
|
PROP('p', R("parent") R("postMessage"))
|
|
|
|
PROP('s', R("self"))
|
|
|
|
PROP('t', R("top"))
|
|
|
|
PROP('w', R("window")))
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef NAME
|
|
|
|
#undef RW
|
|
|
|
#undef R
|
|
|
|
#undef W
|
|
|
|
|
|
|
|
static bool
|
2010-09-17 14:54:40 -07:00
|
|
|
IsFrameId(JSContext *cx, JSObject *obj, jsid id)
|
2010-07-02 13:54:53 -07:00
|
|
|
{
|
2010-09-17 14:54:40 -07:00
|
|
|
XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
|
|
|
|
if (!wn) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-07-02 13:54:53 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMWindow> domwin(do_QueryWrappedNative(wn));
|
|
|
|
if (!domwin) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMWindowCollection> col;
|
|
|
|
domwin->GetFrames(getter_AddRefs(col));
|
|
|
|
if (!col) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (JSID_IS_INT(id)) {
|
|
|
|
col->Item(JSID_TO_INT(id), getter_AddRefs(domwin));
|
2012-01-15 00:13:07 -08:00
|
|
|
} else if (JSID_IS_STRING(id)) {
|
2010-12-03 00:24:17 -08:00
|
|
|
nsAutoString str(JS_GetInternedStringChars(JSID_TO_STRING(id)));
|
2010-07-02 13:54:53 -07:00
|
|
|
col->NamedItem(str, getter_AddRefs(domwin));
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return domwin != nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
IsWindow(const char *name)
|
|
|
|
{
|
|
|
|
return name[0] == 'W' && !strcmp(name, "Window");
|
|
|
|
}
|
|
|
|
|
2010-10-15 14:15:53 -07:00
|
|
|
static bool
|
|
|
|
IsLocation(const char *name)
|
|
|
|
{
|
|
|
|
return name[0] == 'L' && !strcmp(name, "Location");
|
|
|
|
}
|
|
|
|
|
2010-10-14 16:57:27 -07:00
|
|
|
static nsIPrincipal *
|
|
|
|
GetPrincipal(JSObject *obj)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(!IS_SLIM_WRAPPER(obj), "global object is a slim wrapper?");
|
2012-03-30 21:42:20 -07:00
|
|
|
NS_ASSERTION(js::GetObjectClass(obj)->flags & JSCLASS_IS_GLOBAL,
|
|
|
|
"Not a global object?");
|
|
|
|
NS_ASSERTION(!(js::GetObjectClass(obj)->flags & JSCLASS_IS_DOMJSCLASS),
|
|
|
|
"Not sure what we should do with these yet!");
|
2010-10-14 16:57:27 -07:00
|
|
|
if (!IS_WN_WRAPPER(obj)) {
|
2011-10-04 07:06:54 -07:00
|
|
|
NS_ASSERTION(!(~js::GetObjectClass(obj)->flags &
|
2010-10-14 16:57:27 -07:00
|
|
|
(JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE)),
|
|
|
|
"bad object");
|
|
|
|
nsCOMPtr<nsIScriptObjectPrincipal> objPrin =
|
|
|
|
do_QueryInterface((nsISupports*)xpc_GetJSPrivate(obj));
|
|
|
|
NS_ASSERTION(objPrin, "global isn't nsIScriptObjectPrincipal?");
|
|
|
|
return objPrin->GetPrincipal();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIXPConnect *xpc = nsXPConnect::GetRuntimeInstance()->GetXPConnect();
|
2011-10-17 07:59:28 -07:00
|
|
|
return xpc->GetPrincipal(obj, true);
|
2010-10-14 16:57:27 -07:00
|
|
|
}
|
|
|
|
|
2010-10-16 15:26:14 -07:00
|
|
|
bool
|
|
|
|
AccessCheck::documentDomainMakesSameOrigin(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
2012-04-16 12:30:04 -07:00
|
|
|
JSObject *scope = JS_GetScriptedGlobal(cx);
|
2011-02-19 20:46:44 -08:00
|
|
|
|
2010-10-16 15:26:14 -07:00
|
|
|
nsIPrincipal *subject;
|
2011-02-19 20:46:44 -08:00
|
|
|
nsIPrincipal *object;
|
|
|
|
|
2010-10-16 15:26:14 -07:00
|
|
|
{
|
|
|
|
JSAutoEnterCompartment ac;
|
|
|
|
|
|
|
|
if (!ac.enter(cx, scope))
|
|
|
|
return false;
|
|
|
|
|
2011-09-28 17:57:27 -07:00
|
|
|
subject = GetPrincipal(scope);
|
2010-10-16 15:26:14 -07:00
|
|
|
}
|
2011-02-19 20:46:44 -08:00
|
|
|
|
2010-11-10 14:08:11 -08:00
|
|
|
if (!subject)
|
|
|
|
return false;
|
|
|
|
|
2010-10-16 15:26:14 -07:00
|
|
|
{
|
|
|
|
JSAutoEnterCompartment ac;
|
|
|
|
|
|
|
|
if (!ac.enter(cx, obj))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
object = GetPrincipal(JS_GetGlobalForObject(cx, obj));
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool subsumes;
|
2010-10-16 15:26:14 -07:00
|
|
|
return NS_SUCCEEDED(subject->Subsumes(object, &subsumes)) && subsumes;
|
|
|
|
}
|
|
|
|
|
2010-07-02 13:54:53 -07:00
|
|
|
bool
|
2010-09-17 14:54:40 -07:00
|
|
|
AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid id,
|
2011-09-08 20:29:15 -07:00
|
|
|
Wrapper::Action act)
|
2010-07-02 13:54:53 -07:00
|
|
|
{
|
2010-09-17 14:54:40 -07:00
|
|
|
if (!XPCWrapper::GetSecurityManager())
|
|
|
|
return true;
|
2010-07-02 13:54:53 -07:00
|
|
|
|
2011-09-08 20:29:15 -07:00
|
|
|
if (act == Wrapper::CALL)
|
2010-09-17 14:54:40 -07:00
|
|
|
return true;
|
|
|
|
|
2011-09-08 20:29:15 -07:00
|
|
|
JSObject *obj = Wrapper::wrappedObject(wrapper);
|
2010-07-02 13:54:53 -07:00
|
|
|
|
2012-03-23 14:59:27 -07:00
|
|
|
// LocationPolicy checks PUNCTURE first, so we should never get here for
|
|
|
|
// Location wrappers. For all other wrappers interested in cross-origin
|
|
|
|
// semantics, we want to allow puncturing only for the same-origin
|
|
|
|
// document.domain case.
|
|
|
|
if (act == Wrapper::PUNCTURE) {
|
|
|
|
MOZ_ASSERT(!WrapperFactory::IsLocationObject(obj));
|
|
|
|
return documentDomainMakesSameOrigin(cx, obj);
|
|
|
|
}
|
|
|
|
|
2010-09-17 14:54:40 -07:00
|
|
|
const char *name;
|
2011-10-04 07:06:54 -07:00
|
|
|
js::Class *clasp = js::GetObjectClass(obj);
|
2010-09-23 15:56:28 -07:00
|
|
|
NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here");
|
2010-09-17 14:54:40 -07:00
|
|
|
if (clasp->ext.innerObject)
|
|
|
|
name = "Window";
|
|
|
|
else
|
|
|
|
name = clasp->name;
|
2010-07-02 13:54:53 -07:00
|
|
|
|
2012-01-15 00:13:07 -08:00
|
|
|
if (JSID_IS_STRING(id)) {
|
2011-09-08 20:29:15 -07:00
|
|
|
if (IsPermitted(name, JSID_TO_FLAT_STRING(id), act == Wrapper::SET))
|
2010-07-02 13:54:53 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-09-17 14:54:40 -07:00
|
|
|
if (IsWindow(name) && IsFrameId(cx, obj, id))
|
2010-07-02 13:54:53 -07:00
|
|
|
return true;
|
|
|
|
|
2012-03-23 14:59:07 -07:00
|
|
|
// 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.
|
2010-10-16 15:26:14 -07:00
|
|
|
if (!IsLocation(name) && documentDomainMakesSameOrigin(cx, obj))
|
|
|
|
return true;
|
2010-10-10 15:39:19 -07:00
|
|
|
|
2011-09-08 20:29:15 -07:00
|
|
|
return (act == Wrapper::SET)
|
2010-09-17 14:54:40 -07:00
|
|
|
? nsContentUtils::IsCallerTrustedForWrite()
|
|
|
|
: nsContentUtils::IsCallerTrustedForRead();
|
2010-07-02 13:54:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
AccessCheck::isSystemOnlyAccessPermitted(JSContext *cx)
|
|
|
|
{
|
|
|
|
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
|
|
|
if (!ssm) {
|
2010-06-25 15:58:09 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-07-02 13:54:53 -07:00
|
|
|
JSStackFrame *fp;
|
|
|
|
nsIPrincipal *principal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp);
|
|
|
|
if (!principal) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-04-16 12:25:28 -07:00
|
|
|
JSScript *script = nsnull;
|
2010-07-02 13:54:53 -07:00
|
|
|
if (!fp) {
|
2012-04-16 12:25:28 -07:00
|
|
|
if (!JS_DescribeScriptedCaller(cx, &script, nsnull)) {
|
2010-07-02 13:54:53 -07:00
|
|
|
// No code at all is running. So we must be arriving here as the result
|
|
|
|
// of C++ code asking us to do something. Allow access.
|
|
|
|
return true;
|
|
|
|
}
|
2012-04-16 12:25:28 -07:00
|
|
|
} else if (JS_IsScriptFrame(cx, fp)) {
|
|
|
|
script = JS_GetFrameScript(cx, fp);
|
2010-07-02 13:54:53 -07:00
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool privileged;
|
2010-07-02 13:54:53 -07:00
|
|
|
if (NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) &&
|
|
|
|
privileged) {
|
2010-06-25 15:58:09 -07:00
|
|
|
return true;
|
|
|
|
}
|
2010-07-02 13:54:53 -07:00
|
|
|
|
|
|
|
// Allow any code loaded from chrome://global/ to touch us, even if it was
|
|
|
|
// cloned into a less privileged context.
|
|
|
|
static const char prefix[] = "chrome://global/";
|
|
|
|
const char *filename;
|
2012-04-16 12:25:28 -07:00
|
|
|
if (script &&
|
|
|
|
(filename = JS_GetScriptFilename(cx, script)) &&
|
2011-10-10 22:50:08 -07:00
|
|
|
!strncmp(filename, prefix, ArrayLength(prefix) - 1)) {
|
2010-06-25 15:58:09 -07:00
|
|
|
return true;
|
|
|
|
}
|
2010-07-02 13:54:53 -07:00
|
|
|
|
|
|
|
return NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) && privileged;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
AccessCheck::needsSystemOnlyWrapper(JSObject *obj)
|
|
|
|
{
|
2010-09-17 14:54:40 -07:00
|
|
|
if (!IS_WN_WRAPPER(obj))
|
|
|
|
return false;
|
|
|
|
|
2011-10-04 07:06:54 -07:00
|
|
|
XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(js::GetObjectPrivate(obj));
|
2010-07-02 13:54:53 -07:00
|
|
|
return wn->NeedsSOW();
|
2010-06-25 15:58:09 -07:00
|
|
|
}
|
|
|
|
|
2010-10-10 15:39:08 -07:00
|
|
|
bool
|
|
|
|
AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper)
|
|
|
|
{
|
2011-10-04 07:06:54 -07:00
|
|
|
JS_ASSERT(js::IsWrapper(wrapper));
|
2010-10-10 15:39:08 -07:00
|
|
|
|
2012-02-28 15:11:11 -08:00
|
|
|
unsigned flags;
|
2012-01-26 05:55:27 -08:00
|
|
|
JSObject *obj = js::UnwrapObject(wrapper, true, &flags);
|
2010-10-10 15:39:08 -07:00
|
|
|
|
|
|
|
// If the wrapper indicates script-only access, we are done.
|
|
|
|
if (flags & WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG) {
|
2010-10-10 15:48:55 -07:00
|
|
|
if (flags & WrapperFactory::SOW_FLAG)
|
|
|
|
return !isSystemOnlyAccessPermitted(cx);
|
|
|
|
|
2010-10-10 15:49:30 -07:00
|
|
|
if (flags & WrapperFactory::PARTIALLY_TRANSPARENT)
|
|
|
|
return !XrayUtils::IsTransparent(cx, wrapper);
|
|
|
|
|
2010-10-10 15:39:08 -07:00
|
|
|
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
|
|
|
if (!ssm)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Bypass script-only status if UniversalXPConnect is enabled.
|
2011-09-28 23:19:26 -07:00
|
|
|
bool privileged;
|
2010-10-10 15:39:08 -07:00
|
|
|
return !NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) ||
|
|
|
|
!privileged;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In addition, chrome objects can explicitly opt-in by setting .scriptOnly to true.
|
2011-10-04 10:50:25 -07:00
|
|
|
if (js::GetProxyHandler(wrapper) ==
|
|
|
|
&FilteringWrapper<CrossCompartmentSecurityWrapper,
|
2011-10-14 10:52:48 -07:00
|
|
|
CrossOriginAccessiblePropertiesOnly>::singleton) {
|
2010-10-10 15:39:08 -07:00
|
|
|
jsid scriptOnlyId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_SCRIPTONLY);
|
|
|
|
jsval scriptOnly;
|
|
|
|
if (JS_LookupPropertyById(cx, obj, scriptOnlyId, &scriptOnly) &&
|
|
|
|
scriptOnly == JSVAL_TRUE)
|
|
|
|
return true; // script-only
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allow non-script access to same-origin location objects and any other
|
|
|
|
// objects.
|
|
|
|
return WrapperFactory::IsLocationObject(obj) && !isLocationObjectSameOrigin(cx, wrapper);
|
|
|
|
}
|
|
|
|
|
2010-06-25 15:58:09 -07:00
|
|
|
void
|
|
|
|
AccessCheck::deny(JSContext *cx, jsid id)
|
|
|
|
{
|
2010-07-14 23:19:36 -07:00
|
|
|
if (id == JSID_VOID) {
|
2010-06-25 15:58:09 -07:00
|
|
|
JS_ReportError(cx, "Permission denied to access object");
|
|
|
|
} else {
|
2010-07-14 23:19:36 -07:00
|
|
|
jsval idval;
|
|
|
|
if (!JS_IdToValue(cx, id, &idval))
|
|
|
|
return;
|
|
|
|
JSString *str = JS_ValueToString(cx, idval);
|
|
|
|
if (!str)
|
|
|
|
return;
|
2010-12-03 00:24:17 -08:00
|
|
|
const jschar *chars = JS_GetStringCharsZ(cx, str);
|
|
|
|
if (chars)
|
|
|
|
JS_ReportError(cx, "Permission denied to access property '%hs'", chars);
|
2010-06-25 15:58:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-29 18:47:17 -08:00
|
|
|
enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
|
|
|
|
|
2011-01-29 18:48:30 -08:00
|
|
|
static bool
|
2011-09-08 20:29:15 -07:00
|
|
|
Deny(JSContext *cx, jsid id, Wrapper::Action act)
|
2011-01-29 18:48:30 -08:00
|
|
|
{
|
|
|
|
// Refuse to perform the action and just return the default value.
|
2011-09-08 20:29:15 -07:00
|
|
|
if (act == Wrapper::GET)
|
2011-01-29 18:48:30 -08:00
|
|
|
return true;
|
|
|
|
// If its a set, deny it and throw an exception.
|
|
|
|
AccessCheck::deny(cx, id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-29 18:47:17 -08:00
|
|
|
bool
|
2011-09-08 20:29:15 -07:00
|
|
|
PermitIfUniversalXPConnect(JSContext *cx, jsid id, Wrapper::Action act,
|
2011-01-29 18:48:30 -08:00
|
|
|
ExposedPropertiesOnly::Permission &perm)
|
2011-01-29 18:47:17 -08:00
|
|
|
{
|
|
|
|
// If UniversalXPConnect is enabled, allow access even if __exposedProps__ doesn't
|
|
|
|
// exists.
|
|
|
|
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
|
|
|
if (!ssm) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-18 06:47:09 -07:00
|
|
|
|
|
|
|
// Double-check that the subject principal according to CAPS is a content
|
|
|
|
// principal rather than the system principal. If it isn't, this check is
|
|
|
|
// meaningless.
|
2012-06-25 06:24:21 -07:00
|
|
|
NS_ASSERTION(!AccessCheck::callerIsChrome(), "About to do a meaningless security check!");
|
2012-06-18 06:47:09 -07:00
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool privileged;
|
2011-01-29 18:47:17 -08:00
|
|
|
if (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) &&
|
|
|
|
privileged) {
|
|
|
|
perm = ExposedPropertiesOnly::PermitPropertyAccess;
|
|
|
|
return true; // Allow
|
|
|
|
}
|
|
|
|
|
2011-01-29 18:48:30 -08:00
|
|
|
// Deny
|
|
|
|
return Deny(cx, id, act);
|
2011-01-29 18:47:17 -08:00
|
|
|
}
|
2010-07-02 13:54:53 -07:00
|
|
|
|
2010-06-25 15:58:09 -07:00
|
|
|
bool
|
2011-09-08 20:29:15 -07:00
|
|
|
ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act,
|
2010-09-17 14:54:40 -07:00
|
|
|
Permission &perm)
|
2010-06-25 15:58:09 -07:00
|
|
|
{
|
2011-09-08 20:29:15 -07:00
|
|
|
JSObject *wrappedObject = Wrapper::wrappedObject(wrapper);
|
2011-01-29 18:47:17 -08:00
|
|
|
|
2011-09-08 20:29:15 -07:00
|
|
|
if (act == Wrapper::CALL) {
|
2011-01-29 18:47:17 -08:00
|
|
|
perm = PermitObjectAccess;
|
|
|
|
return true;
|
|
|
|
}
|
2010-07-02 13:54:53 -07:00
|
|
|
|
|
|
|
perm = DenyAccess;
|
2012-05-04 05:22:55 -07:00
|
|
|
if (act == Wrapper::PUNCTURE)
|
|
|
|
return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
|
2010-07-02 13:54:53 -07:00
|
|
|
|
|
|
|
jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS);
|
|
|
|
|
2012-06-18 06:47:09 -07:00
|
|
|
// We need to enter the wrappee's compartment to look at __exposedProps__,
|
|
|
|
// but we need to be in the wrapper's compartment to check UniversalXPConnect.
|
|
|
|
//
|
|
|
|
// Unfortunately, |cx| can be in either compartment when we call ::check. :-(
|
2010-09-22 17:34:20 -07:00
|
|
|
JSAutoEnterCompartment ac;
|
2012-06-18 06:47:09 -07:00
|
|
|
JSAutoEnterCompartment wrapperAC;
|
|
|
|
if (!ac.enter(cx, wrappedObject))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
JSBool found = false;
|
|
|
|
if (!JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found))
|
2010-07-02 13:54:53 -07:00
|
|
|
return false;
|
2011-01-29 18:47:17 -08:00
|
|
|
|
|
|
|
// Always permit access to "length" and indexed properties of arrays.
|
|
|
|
if (JS_IsArrayObject(cx, wrappedObject) &&
|
|
|
|
((JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) ||
|
2012-01-15 00:13:07 -08:00
|
|
|
(JSID_IS_STRING(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length")))) {
|
2011-01-29 18:47:17 -08:00
|
|
|
perm = PermitPropertyAccess;
|
2010-07-02 13:54:53 -07:00
|
|
|
return true; // Allow
|
|
|
|
}
|
|
|
|
|
2011-01-29 18:47:17 -08:00
|
|
|
// If no __exposedProps__ existed, deny access.
|
|
|
|
if (!found) {
|
2012-06-18 06:47:09 -07:00
|
|
|
// Everything below here needs to be done in the wrapper's compartment.
|
|
|
|
if (!wrapperAC.enter(cx, wrapper))
|
|
|
|
return false;
|
|
|
|
|
2011-01-29 18:47:17 -08:00
|
|
|
// For now, only do this on functions.
|
|
|
|
if (!JS_ObjectIsFunction(cx, wrappedObject)) {
|
2012-05-25 09:42:40 -07:00
|
|
|
|
|
|
|
// This little loop hole will go away soon! See bug 553102.
|
|
|
|
nsCOMPtr<nsPIDOMWindow> win =
|
|
|
|
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(cx, wrapper));
|
|
|
|
if (win) {
|
|
|
|
nsCOMPtr<nsIDocument> doc =
|
|
|
|
do_QueryInterface(win->GetExtantDocument());
|
|
|
|
if (doc) {
|
2012-05-31 07:28:09 -07:00
|
|
|
doc->WarnOnceAbout(nsIDocument::eNoExposedProps,
|
|
|
|
/* asError = */ true);
|
2012-05-25 09:42:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-29 18:47:17 -08:00
|
|
|
perm = PermitPropertyAccess;
|
|
|
|
return true;
|
|
|
|
}
|
2011-01-29 18:48:30 -08:00
|
|
|
return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
|
2011-01-29 18:47:17 -08:00
|
|
|
}
|
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
if (id == JSID_VOID) {
|
2010-07-02 13:54:53 -07:00
|
|
|
// This will force the caller to call us back for individual property accesses.
|
|
|
|
perm = PermitPropertyAccess;
|
2010-06-25 15:58:09 -07:00
|
|
|
return true;
|
|
|
|
}
|
2010-07-02 13:54:53 -07:00
|
|
|
|
2012-05-11 08:46:26 -07:00
|
|
|
JS::Value exposedProps;
|
2011-01-29 18:47:17 -08:00
|
|
|
if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps))
|
2010-07-01 15:45:08 -07:00
|
|
|
return false;
|
2010-07-02 13:54:53 -07:00
|
|
|
|
2012-05-11 08:46:26 -07:00
|
|
|
if (exposedProps.isNullOrUndefined()) {
|
2012-06-18 06:47:09 -07:00
|
|
|
return wrapperAC.enter(cx, wrapper) &&
|
|
|
|
PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
|
2010-07-01 15:45:08 -07:00
|
|
|
}
|
|
|
|
|
2012-05-11 08:46:26 -07:00
|
|
|
if (!exposedProps.isObject()) {
|
2010-07-02 13:54:53 -07:00
|
|
|
JS_ReportError(cx, "__exposedProps__ must be undefined, null, or an Object");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-11 08:46:26 -07:00
|
|
|
JSObject *hallpass = &exposedProps.toObject();
|
2010-07-02 13:54:53 -07:00
|
|
|
|
|
|
|
Access access = NO_ACCESS;
|
|
|
|
|
2010-10-10 15:41:56 -07:00
|
|
|
JSPropertyDescriptor desc;
|
|
|
|
if (!JS_GetPropertyDescriptorById(cx, hallpass, id, JSRESOLVE_QUALIFIED, &desc)) {
|
2010-07-02 13:54:53 -07:00
|
|
|
return false; // Error
|
2010-07-01 15:45:08 -07:00
|
|
|
}
|
2010-10-10 15:41:56 -07:00
|
|
|
if (desc.obj == NULL || !(desc.attrs & JSPROP_ENUMERATE)) {
|
2012-06-18 06:47:09 -07:00
|
|
|
return wrapperAC.enter(cx, wrapper) &&
|
|
|
|
PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
|
2010-10-10 15:41:56 -07:00
|
|
|
}
|
2010-07-02 13:54:53 -07:00
|
|
|
|
2010-10-10 15:41:56 -07:00
|
|
|
if (!JSVAL_IS_STRING(desc.value)) {
|
2010-07-02 13:54:53 -07:00
|
|
|
JS_ReportError(cx, "property must be a string");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-10-10 15:41:56 -07:00
|
|
|
JSString *str = JSVAL_TO_STRING(desc.value);
|
2010-12-03 00:24:17 -08:00
|
|
|
size_t length;
|
|
|
|
const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
|
|
|
|
if (!chars)
|
|
|
|
return false;
|
|
|
|
|
2010-07-02 13:54:53 -07:00
|
|
|
for (size_t i = 0; i < length; ++i) {
|
|
|
|
switch (chars[i]) {
|
|
|
|
case 'r':
|
|
|
|
if (access & READ) {
|
|
|
|
JS_ReportError(cx, "duplicate 'readable' property flag");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
access = Access(access | READ);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'w':
|
|
|
|
if (access & WRITE) {
|
|
|
|
JS_ReportError(cx, "duplicate 'writable' property flag");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
access = Access(access | WRITE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
JS_ReportError(cx, "properties can only be readable or read and writable");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access == NO_ACCESS) {
|
|
|
|
JS_ReportError(cx, "specified properties must have a permission bit set");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-09-08 20:29:15 -07:00
|
|
|
if ((act == Wrapper::SET && !(access & WRITE)) ||
|
|
|
|
(act != Wrapper::SET && !(access & READ))) {
|
2012-06-18 06:47:09 -07:00
|
|
|
return wrapperAC.enter(cx, wrapper) &&
|
|
|
|
PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
|
2010-07-02 13:54:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
perm = PermitPropertyAccess;
|
|
|
|
return true; // Allow
|
2010-06-25 15:58:09 -07:00
|
|
|
}
|
|
|
|
|
2012-04-28 06:12:28 -07:00
|
|
|
bool
|
|
|
|
ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act,
|
|
|
|
Permission &perm)
|
|
|
|
{
|
|
|
|
perm = DenyAccess;
|
|
|
|
JSAutoEnterCompartment ac;
|
|
|
|
if (!ac.enter(cx, wrapper))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (JSID_IS_STRING(id) && act == Wrapper::GET) {
|
|
|
|
JSFlatString *flatId = JSID_TO_FLAT_STRING(id);
|
|
|
|
if (JS_FlatStringEqualsAscii(flatId, "isSuccessCode") ||
|
|
|
|
JS_FlatStringEqualsAscii(flatId, "lookupMethod") ||
|
|
|
|
JS_FlatStringEqualsAscii(flatId, "interfaces") ||
|
|
|
|
JS_FlatStringEqualsAscii(flatId, "interfacesByID") ||
|
|
|
|
JS_FlatStringEqualsAscii(flatId, "results"))
|
|
|
|
{
|
|
|
|
perm = PermitPropertyAccess;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
|
|
|
|
}
|
|
|
|
|
2010-06-25 15:58:09 -07:00
|
|
|
}
|