gecko-dev/lib/libmocha/lm_taint.c

2246 lines
65 KiB
C

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*
* JS/Mozilla data tainting support.
* XXX replace with lm_sec.c and lm_CheckAccess or similar
*
*/
#include "lm.h"
#include "xp.h"
#include "mkparse.h"
#include "prclist.h"
#include "plhash.h"
#include "prmem.h"
#include "shist.h"
#include "jsapi.h"
#include "jsdbgapi.h"
#include "jscntxt.h" /* XXX - needed for charSetName */
#include "nsZip.h"
#include "zig.h"
#include "nsLoadZig.h"
#include "nsCaps.h"
#include "jri.h"
#ifdef JAVA_OR_OJI
#include "jsjava.h"
#endif
#ifdef JAVA
#include "java.h"
#endif
#include "jsobj.h"
#include "jsatom.h"
#include "jsscope.h"
#ifdef OJI
#include "jvmmgr.h"
#endif
#include "nsCaps.h"
extern JRIEnv * LJ_JSJ_CurrentEnv(JSContext * cx);
extern char *LJ_GetAppletScriptOrigin(JRIEnv *env);
char lm_unknown_origin_str[] = "[unknown origin]";
static char file_url_prefix[] = "file:";
static char access_error_message[] =
"access disallowed from scripts at %s to documents at another domain";
static char container_error_message[] =
"script at '%s' is not signed by sufficient principals to access "
"signed container";
static char enablePrivilegeStr[] = "enablePrivilege";
static char isPrivilegeEnabledStr[] = "isPrivilegeEnabled";
static char disablePrivilegeStr[] = "disablePrivilege";
static char revertPrivilegeStr[] = "revertPrivilege";
#define FILE_URL_PREFIX_LEN (sizeof file_url_prefix - 1)
#define WYSIWYG_TYPE_LEN 10 /* wysiwyg:// */
/* XXX: raman: We should set this when JS console is ready */
PRBool lm_console_is_ready = PR_FALSE;
static void lm_PrintToConsole(const char *data);
static void setupJSCapsCallbacks();
/* XXX what about the PREXTERN? */
typedef PRBool
(*nsCapsFn)(void* context, struct nsTarget *target, PRInt32 callerDepth);
static JSBool
callCapsCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval, nsCapsFn fn, char *name)
{
JSString *str;
char *cstr;
struct nsTarget *target;
if (argc == 0 || !JSVAL_IS_STRING(argv[0])) {
JS_ReportError(cx, "String argument expected for %s.", name);
return JS_FALSE;
}
/*
* We don't want to use JS_ValueToString because we want to be able
* to have an object to represent a target in subsequent versions.
* XXX but then use of an object will cause errors here....
*/
str = JSVAL_TO_STRING(argv[0]);
if (!str)
return JS_FALSE;
cstr = JS_GetStringBytes(str);
if (cstr == NULL)
return JS_FALSE;
target = nsCapsFindTarget(cstr);
if (target == NULL)
return JS_FALSE;
/* stack depth of 1: first frame is for the native function called */
if (!(*fn)(cx, target, 1)) {
/* XXX report error, later, throw exception */
return JS_FALSE;
}
return JS_TRUE;
}
JSBool
lm_netscape_security_isPrivilegeEnabled(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return callCapsCode(cx, obj, argc, argv, rval, nsCapsIsPrivilegeEnabled,
isPrivilegeEnabledStr);
}
JSBool
lm_netscape_security_enablePrivilege(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return callCapsCode(cx, obj, argc, argv, rval, nsCapsEnablePrivilege,
enablePrivilegeStr);
}
JSBool
lm_netscape_security_disablePrivilege(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return callCapsCode(cx, obj, argc, argv, rval, nsCapsDisablePrivilege,
disablePrivilegeStr);
}
JSBool
lm_netscape_security_revertPrivilege(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
return callCapsCode(cx, obj, argc, argv, rval, nsCapsRevertPrivilege,
revertPrivilegeStr);
}
static JSFunctionSpec PrivilegeManager_static_methods[] = {
{ isPrivilegeEnabledStr, lm_netscape_security_isPrivilegeEnabled, 1},
{ enablePrivilegeStr, lm_netscape_security_enablePrivilege, 1},
{ disablePrivilegeStr, lm_netscape_security_disablePrivilege, 1},
{ revertPrivilegeStr, lm_netscape_security_revertPrivilege, 1},
{0}
};
JSBool
lm_InitSecurity(MochaDecoder *decoder)
{
JSContext *cx;
JSObject *obj;
JSObject *proto;
JSClass *objectClass;
jsval v;
JSObject *securityObj;
/*
* "Steal" calls to netscape.security.PrivilegeManager.enablePrivilege,
* et. al. so that code that worked with 4.0 can still work.
*/
/*
* Find Object.prototype's class by walking up the window object's
* prototype chain.
*/
cx = decoder->js_context;
obj = decoder->window_object;
while (proto = JS_GetPrototype(cx, obj))
obj = proto;
objectClass = JS_GetClass(cx, obj);
if (!JS_GetProperty(cx, decoder->window_object, "netscape", &v))
return JS_FALSE;
if (JSVAL_IS_OBJECT(v)) {
/*
* "netscape" property of window object exists; must be LiveConnect
* package. Get the "security" property.
*/
obj = JSVAL_TO_OBJECT(v);
if (!JS_GetProperty(cx, obj, "security", &v) || !JSVAL_IS_OBJECT(v))
return JS_FALSE;
securityObj = JSVAL_TO_OBJECT(v);
} else {
/* define netscape.security object */
obj = JS_DefineObject(cx, decoder->window_object, "netscape",
objectClass, NULL, 0);
if (obj == NULL)
return JS_FALSE;
securityObj = JS_DefineObject(cx, obj, "security", objectClass,
NULL, 0);
if (securityObj == NULL)
return JS_FALSE;
}
/* Define PrivilegeManager object with the necessary "static" methods. */
obj = JS_DefineObject(cx, securityObj, "PrivilegeManager", objectClass,
NULL, 0);
if (obj == NULL)
return JS_FALSE;
return JS_DefineFunctions(cx, obj, PrivilegeManager_static_methods);
}
static void
lm_PrintToConsole(const char *data)
{
if (lm_console_is_ready) {
/* XXX: raman: We should write to JS console when it is ready */
/* JS_PrintToConsole(data); */
} else {
MWContext* someRandomContext = XP_FindSomeContext();
FE_Alert(someRandomContext, data);
}
}
PR_PUBLIC_API(int)
LM_PrintZigError(int status, void *zigPtr, const char *metafile,
char *pathname, char *errortext)
{
ZIG *zig = (ZIG *) zigPtr;
char* data;
char* error_fmt = "# Error: %s (%d)\n#\tjar file: %s\n#\tpath: %s\n";
char* zig_name = NULL;
int len;
XP_ASSERT(errortext);
if (zig) {
zig_name = SOB_get_url(zig);
}
if (!zig_name) {
zig_name = "unknown";
}
if (!pathname) {
pathname = "";
}
/* Add 16 slop bytes */
len = strlen(error_fmt) + strlen(zig_name) + strlen(pathname) +
strlen(errortext) + 32;
if ((data = malloc(len)) == 0) {
return 0;
}
sprintf(data, error_fmt, errortext, status, zig_name, pathname);
lm_PrintToConsole(data);
XP_FREE(data);
return 0;
}
PR_PUBLIC_API(char *)
LM_LoadFromZipFile(void *zip, char *fn)
{
struct stat st;
char* data;
if (!ns_zip_stat((ns_zip_t *)zip, fn, &st)) {
return NULL;
}
if ((data = malloc((size_t)st.st_size + 1)) == 0) {
return NULL;
}
if (!ns_zip_get((ns_zip_t *)zip, fn, data, st.st_size)) {
XP_FREE(data);
return NULL;
}
data[st.st_size] = '\0';
return data;
}
const char *
LM_StripWysiwygURLPrefix(const char *url_string)
{
switch (NET_URL_Type(url_string)) {
case WYSIWYG_TYPE_URL:
return LM_SkipWysiwygURLPrefix(url_string);
default:
return url_string;
}
}
const char *
LM_SkipWysiwygURLPrefix(const char *url_string)
{
if (XP_STRLEN(url_string) < WYSIWYG_TYPE_LEN)
return NULL;
url_string += WYSIWYG_TYPE_LEN;
url_string = XP_STRCHR(url_string, '/');
if (!url_string)
return NULL;
return url_string + 1;
}
JSPrincipals *
lm_GetCompilationPrincipals(MochaDecoder *decoder,
JSPrincipals *layoutPrincipals)
{
JSContext *cx;
JSPrincipals *principals;
cx = decoder->js_context;
if (decoder->writing_input && lm_writing_context != NULL) {
/*
* Compiling a script added due to a document.write.
* Get principals from stack frame. We can't just use these
* principals since the document.write code will fail signature
* verification. So just grab the codebase and form a new set
* of principals.
*/
principals = lm_GetPrincipalsFromStackFrame(lm_writing_context);
principals = LM_NewJSPrincipals(NULL, NULL, principals
? principals->codebase
: NULL);
if (principals == NULL) {
JS_ReportOutOfMemory(cx);
return NULL;
}
lm_InvalidateCertPrincipals(decoder, principals);
return principals;
}
if (layoutPrincipals) {
return layoutPrincipals;
}
/*
* Just get principals corresponding to the window or layer we're
* currently parsing.
*/
return lm_GetInnermostPrincipals(cx,
lm_GetActiveContainer(decoder),
NULL);
}
static const char *
find_creator_url(MWContext *context)
{
History_entry *he;
const char *address;
JSContext *cx;
MochaDecoder *decoder;
he = SHIST_GetCurrent(&context->hist);
if (he) {
address = he->wysiwyg_url;
if (!address)
address = he->address;
switch (NET_URL_Type(address)) {
case MOCHA_TYPE_URL:
/* This type cannot name the true origin (server) of JS code. */
break;
case WYSIWYG_TYPE_URL:
return LM_SkipWysiwygURLPrefix(address);
case VIEW_SOURCE_TYPE_URL:
XP_ASSERT(0);
default:
return address;
}
}
if (context->grid_parent) {
address = find_creator_url(context->grid_parent);
if (address)
return address;
}
cx = context->mocha_context;
if (cx) {
decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
if (decoder && decoder->opener) {
/* self.opener property is valid, check its MWContext. */
MochaDecoder *opener = JS_GetPrivate(cx, decoder->opener);
if (!opener->visited) {
opener->visited = JS_TRUE;
address = opener->window_context
? find_creator_url(opener->window_context)
: NULL;
opener->visited = JS_FALSE;
if (address)
return address;
}
}
}
return NULL;
}
static const char *
find_origin_url(JSContext *cx, MochaDecoder *decoder)
{
const char *url_string;
MWContext *context;
MochaDecoder *running;
context = decoder->window_context;
url_string = context ? find_creator_url(context) : NULL;
if (url_string == NULL) {
/* Must be a new, empty window? Use running origin. */
running = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
/* Compare running and decoder to avoid infinite recursion. */
if (running == decoder) {
url_string = lm_unknown_origin_str;
} else {
url_string = lm_GetSubjectOriginURL(cx);
if (!url_string)
return NULL;
}
}
return url_string;
}
static char *
strip_file_double_slash(const char *url_string)
{
char *new_url_string;
if (!XP_STRNCASECMP(url_string, file_url_prefix, FILE_URL_PREFIX_LEN) &&
url_string[FILE_URL_PREFIX_LEN + 0] == '/' &&
url_string[FILE_URL_PREFIX_LEN + 1] == '/') {
new_url_string = PR_smprintf("%s%s",
file_url_prefix,
url_string + FILE_URL_PREFIX_LEN + 2);
} else {
new_url_string = XP_STRDUP(url_string);
}
return new_url_string;
}
static char *
getCanonicalizedOrigin(JSContext *cx, const char *url_string)
{
const char *origin;
if (NET_URL_Type(url_string) == WYSIWYG_TYPE_URL)
url_string = LM_SkipWysiwygURLPrefix(url_string);
origin = NET_ParseURL(url_string, GET_PROTOCOL_PART | GET_HOST_PART);
if (!origin) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return (char *) origin;
}
const char *
lm_GetObjectOriginURL(JSContext *cx, JSObject *obj)
{
JSPrincipals *principals;
principals = lm_GetInnermostPrincipals(cx, obj, NULL);
return principals ? principals->codebase : NULL;
}
static JSBool
sameOrigins(JSContext *cx, const char *origin1, const char *origin2)
{
char *cmp1, *cmp2;
JSBool ok;
if (origin1 == NULL || origin2 == NULL)
return JS_FALSE;
#if 0 /* XXX Need to enable this and test throroughly */
/* Shouldn't return true if both origin1 and origin2 are
* lm_unknown_origin_str. */
if (XP_STRCMP(origin1, lm_unknown_origin_str) == 0)
return JS_FALSE;
#endif
if (XP_STRCMP(origin1, origin2) == 0)
return JS_TRUE;
cmp1 = getCanonicalizedOrigin(cx, origin1);
cmp2 = getCanonicalizedOrigin(cx, origin2);
if (cmp1 && cmp2 &&
XP_STRNCASECMP(cmp1, file_url_prefix, FILE_URL_PREFIX_LEN) == 0 &&
XP_STRNCASECMP(cmp2, file_url_prefix, FILE_URL_PREFIX_LEN) == 0) {
ok = JS_TRUE;
goto done;
}
ok = (JSBool)(cmp1 && cmp2 && XP_STRCMP(cmp1, cmp2) == 0);
done:
PR_FREEIF(cmp1);
PR_FREEIF(cmp2);
return ok;
}
JSBool
lm_CheckPermissions(JSContext *cx, JSObject *obj, JSTarget target)
{
const char *subjectOrigin, *objectOrigin;
MochaDecoder *running;
JSPrincipals *principals;
JSBool result;
/* May be in a layer loaded from a different origin.*/
subjectOrigin = lm_GetSubjectOriginURL(cx);
/*
* Hold onto reference to the running decoder's principals
* in case a call to lm_GetInnermostPrincipals ends up
* dropping a reference due to an origin changing
* underneath us.
*/
running = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
principals = running ? running->principals : NULL;
if (principals)
JSPRINCIPALS_HOLD(cx, principals);
objectOrigin = lm_GetObjectOriginURL(cx, obj);
if (subjectOrigin == NULL || objectOrigin == NULL) {
result = JS_FALSE;
goto out;
}
/* Now see whether the origin methods and servers match. */
if (sameOrigins(cx, subjectOrigin, objectOrigin)) {
result = JS_TRUE;
goto out;
}
/*
* If we failed the origin tests it still might be the case that we
* are a signed script and have permissions to do this operation.
* Check for that here
*/
if (target != JSTARGET_MAX && lm_CanAccessTarget(cx, target)) {
result = JS_TRUE;
goto out;
}
JS_ReportError(cx, access_error_message, subjectOrigin);
result = JS_FALSE;
out:
if (principals)
JSPRINCIPALS_DROP(cx, principals);
return result;
}
static JSBool
isExternalCaptureEnabled(JSContext *cx, JSPrincipals *principals);
JSBool
lm_CanCaptureEvent(JSContext *cx, JSFunction *fun, JSEvent *event)
{
JSScript *script;
JSPrincipals *principals;
const char *origin;
script = JS_GetFunctionScript(cx, fun);
if (script == NULL)
return JS_FALSE;
principals = JS_GetScriptPrincipals(cx, script);
if (principals == NULL)
return JS_FALSE;
origin = lm_GetObjectOriginURL(cx, event->object);
if (origin == NULL)
return JS_FALSE;
return (JSBool)(sameOrigins(cx, origin, principals->codebase) ||
isExternalCaptureEnabled(cx, principals));
}
JSPrincipals *
lm_GetPrincipalsFromStackFrame(JSContext *cx)
{
/*
* Get principals from script of innermost interpreted frame.
*/
JSStackFrame *fp;
JSScript *script;
JSStackFrame *pFrameToStartLooking = JVM_GetStartJSFrameFromParallelStack();
JSStackFrame *pFrameToEndLooking = JVM_GetEndJSFrameFromParallelStack(pFrameToStartLooking);
fp = pFrameToStartLooking;
while ((fp = JS_FrameIterator(cx, &fp)) != pFrameToEndLooking) {
script = JS_GetFrameScript(cx, fp);
if (script) {
return JS_GetScriptPrincipals(cx, script);
}
}
#ifdef OJI
return JVM_GetJavaPrincipalsFromStack(pFrameToStartLooking);
#endif
return NULL;
}
const char *
lm_GetSubjectOriginURL(JSContext *cx)
{
/*
* Get origin from script of innermost interpreted frame.
*/
JSPrincipals *principals;
JSStackFrame *fp;
JSScript *script;
MochaDecoder *running;
#ifdef JAVA
JRIEnv *env;
char *str;
#endif
fp = NULL;
while ((fp = JS_FrameIterator(cx, &fp)) != NULL) {
script = JS_GetFrameScript(cx, fp);
if (script) {
principals = JS_GetScriptPrincipals(cx, script);
return principals
? principals->codebase
: JS_GetScriptFilename(cx, script);
}
}
#ifdef JAVA
/* fell off the js stack, look to see if there's a java
* classloader above us that has MAYSCRIPT set on it */
if (JSJ_IsCalledFromJava(cx)) {
env = LJ_JSJ_CurrentEnv(cx);
if (!env) {
return NULL;
}
str = LJ_GetAppletScriptOrigin(env);
if (!str)
return NULL;
return str;
}
#endif
/*
* Not called from either JS or Java. We must be called
* from the interpreter. Get the origin from the decoder.
*/
running = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
return lm_GetObjectOriginURL(cx, running->window_object);
}
/*
* Reference count ZIGs to increase sharing since creating
* them is so expensive.
*/
typedef struct SharedZig {
ZIG *zig;
int32 refCount;
} SharedZig;
static SharedZig *
newSharedZig(ns_zip_t *zip)
{
ZIG *zig;
SharedZig *result;
zig = nsInitializeZig(zip,
(int (*) (int, ZIG *, const char *,
char *, char *)) LM_PrintZigError);
if (zig == NULL)
return NULL;
result = (SharedZig *) XP_ALLOC(sizeof(SharedZig));
if (result == NULL) {
SOB_destroy(zig);
return NULL;
}
result->zig = zig;
result->refCount = 0;
return result;
}
static void
destroySharedZig(SharedZig *sharedZig)
{
SOB_destroy(sharedZig->zig);
XP_FREE(sharedZig);
}
static SharedZig *
holdZig(SharedZig *sharedZig)
{
if (sharedZig) {
XP_ASSERT(sharedZig->refCount >= 0);
/* XXX: Why are you checking this again */
if (sharedZig)
sharedZig->refCount++;
}
return sharedZig;
}
static void
dropZig(SharedZig *sharedZig)
{
if (sharedZig) {
XP_ASSERT(sharedZig->refCount > 0);
if (--sharedZig->refCount == 0) {
destroySharedZig(sharedZig);
}
}
}
struct JSPrincipalsList {
JSPrincipals *principals;
struct JSPrincipalsList *next;
};
void
lm_DestroyPrincipalsList(JSContext *cx, JSPrincipalsList *p)
{
while (p) {
JSPrincipalsList *next = p->next;
if (p->principals)
JSPRINCIPALS_DROP(cx, p->principals);
XP_FREE(p);
p = next;
}
}
enum Signedness {
HAS_NO_SCRIPTS,
HAS_UNSIGNED_SCRIPTS,
HAS_SIGNED_SCRIPTS
};
#ifdef DEBUG_norris
static int serial;
#endif
typedef struct JSPrincipalsData {
JSPrincipals principals;
SharedZig *sharedZig;
JRIGlobalRef principalsArrayRef;
URL_Struct *url_struct;
char *name;
ns_zip_t *zip;
uint32 externalCapturePrincipalsCount;
char *untransformed;
char *transformed;
JSBool needUnlock;
char *codebaseBeforeSettingDomain;
#ifdef DEBUG_norris
int serial;
#endif
enum Signedness signedness;
void *pNSISecurityContext;
} JSPrincipalsData;
PR_STATIC_CALLBACK(void)
destroyJSPrincipals(JSContext *cx, JSPrincipals *principals);
static JSBool
principalsCanAccessTarget(JSContext *cx, JSTarget target);
PR_STATIC_CALLBACK(void *)
getPrincipalArray(JSContext *cx, struct JSPrincipals *principals);
PR_STATIC_CALLBACK(JSBool)
globalPrivilegesEnabled(JSContext *cx, JSPrincipals *principals);
static JSPrincipalsData unknownPrincipals = {
{
lm_unknown_origin_str,
getPrincipalArray,
globalPrivilegesEnabled,
0,
destroyJSPrincipals
},
NULL
};
static char *
getOriginFromSourceURL(const char *sourceURL)
{
char *s;
char *result;
int urlType;
if (*sourceURL == '\0' || XP_STRCMP(sourceURL, lm_unknown_origin_str) == 0) {
return XP_STRDUP(lm_unknown_origin_str);
}
urlType = NET_URL_Type(sourceURL);
if (urlType == WYSIWYG_TYPE_URL) {
sourceURL = LM_SkipWysiwygURLPrefix(sourceURL);
} else if (urlType == MOCHA_TYPE_URL) {
XP_ASSERT(JS_FALSE); /* this shouldn't occur */
return XP_STRDUP(lm_unknown_origin_str);
}
s = strip_file_double_slash(sourceURL);
if (s == NULL)
return NULL;
result = NET_ParseURL(s, GET_PROTOCOL_PART|GET_HOST_PART|GET_PATH_PART);
PR_FREEIF(s);
return result;
}
static char *
getJavaCodebaseFromOrigin(const char *origin)
{
/* Remove filename part. */
char *result = XP_STRDUP(origin);
if (result) {
char *slash = XP_STRRCHR(result, '/');
if (slash && slash > result && slash[-1] != '/')
slash[1] = '\0';
}
return result;
}
extern JSPrincipals *
LM_NewJSPrincipals(URL_Struct *archive, char *id, const char *codebase)
{
JSPrincipalsData *result;
JSBool needUnlock = JS_FALSE;
ns_zip_t *zip = NULL;
setupJSCapsCallbacks();
if (archive) {
char *fn = NULL;
if (NET_IsLocalFileURL(archive->address)) {
char* pathPart = NET_ParseURL(archive->address, GET_PATH_PART);
NET_UnEscape(pathPart); /* Handle "file:D%7C/dir/file.zip" */
fn = WH_FileName(pathPart, xpURL);
XP_FREE(pathPart);
} else if (archive->cache_file && NET_ChangeCacheFileLock(archive, TRUE)) {
fn = WH_FileName(archive->cache_file, xpCache);
needUnlock = JS_TRUE;
}
if (fn) {
#ifdef XP_MAC
/*
* Unfortunately, ns_zip_open wants a Unix-style name. Convert
* Mac path to a Unix-style path. This code is copied from
* appletStubs.c.
*/
OSErr ConvertMacPathToUnixPath(const char *macPath, char **unixPath);
char *unixPath = NULL;
if (ConvertMacPathToUnixPath(fn, &unixPath) == 0) {
zip = ns_zip_open(unixPath);
}
XP_FREEIF(unixPath);
#else
zip = ns_zip_open(fn);
#endif
XP_FREE(fn);
}
}
result = XP_NEW_ZAP(JSPrincipalsData);
if (result == NULL)
return NULL;
result->principals.codebase = codebase
? getOriginFromSourceURL(codebase)
: NULL;
if (result->principals.codebase == NULL) {
result->principals.codebase = XP_STRDUP(lm_unknown_origin_str);
if (result->principals.codebase == NULL) {
XP_FREE(result);
return NULL;
}
}
if (id) {
result->name = XP_STRDUP(id);
if (result->name == NULL) {
XP_FREE(result);
return NULL;
}
}
result->principals.destroy = destroyJSPrincipals;
result->principals.getPrincipalArray = getPrincipalArray;
result->principals.globalPrivilegesEnabled = globalPrivilegesEnabled;
result->url_struct = NET_HoldURLStruct(archive);
result->zip = zip;
result->needUnlock = needUnlock;
#ifdef DEBUG_norris
result->serial = ++serial;
XP_TRACE(("JSPrincipals #%.4d allocated\n", serial));
#endif
return (JSPrincipals *) result;
}
PR_STATIC_CALLBACK(void)
destroyJSPrincipals(JSContext *cx, JSPrincipals *principals)
{
if (principals != NULL &&
principals != (JSPrincipals *) &unknownPrincipals)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
#ifdef DEBUG_norris
XP_TRACE(("JSPrincipals #%.4d released\n", data->serial));
#endif
XP_FREEIF(principals->codebase);
if (data->sharedZig) {
dropZig(data->sharedZig);
}
if (data->principalsArrayRef != NULL) {
/* XXX: raman: Should we free up the principals that are in that array also? */
nsCapsFreePrincipalArray(data->principalsArrayRef);
}
XP_FREEIF(data->name);
XP_FREEIF(data->untransformed);
XP_FREEIF(data->transformed);
if (data->zip)
ns_zip_close(data->zip);
if (data->needUnlock)
NET_ChangeCacheFileLock(data->url_struct, FALSE);
if (data->url_struct)
NET_DropURLStruct(data->url_struct);
XP_FREEIF(data->codebaseBeforeSettingDomain);
XP_FREE(data);
}
}
PR_STATIC_CALLBACK(JSBool)
globalPrivilegesEnabled(JSContext *cx, JSPrincipals *principals)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
return (JSBool)(data->principalsArrayRef != NULL ||
XP_STRCMP(principals->codebase, lm_unknown_origin_str) != 0);
}
static void
printPrincipalsToConsole(JSContext *cx, JSPrincipals *principals)
{
void *principalsArray;
struct nsPrincipal *principal;
const char *vendor;
uint32 i, count;
static char emptyStr[] = "<empty>\n";
principalsArray = principals->getPrincipalArray(cx, principals);
if (principalsArray == NULL) {
lm_PrintToConsole(emptyStr);
return;
}
lm_PrintToConsole("[\n");
count = nsCapsGetPrincipalArraySize(principalsArray);
for (i = 0; i < count; i++) {
principal = nsCapsGetPrincipalArrayElement(principalsArray, i);
vendor = nsCapsPrincipalGetVendor(principal);
if (vendor == NULL) {
JS_ReportOutOfMemory(cx);
return;
}
lm_PrintToConsole(vendor);
lm_PrintToConsole(",\n");
}
lm_PrintToConsole("]\n");
}
extern void
lm_InvalidateCertPrincipals(MochaDecoder *decoder, JSPrincipals *principals)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
if (data->principalsArrayRef) {
lm_PrintToConsole("Invalidating certificate principals in ");
printPrincipalsToConsole(decoder->js_context, principals);
nsCapsFreePrincipalArray(data->principalsArrayRef);
data->principalsArrayRef = NULL;
}
data->signedness = HAS_UNSIGNED_SCRIPTS;
}
extern JSBool
lm_SetDocumentDomain(JSContext *cx, JSPrincipals *principals,
const char *codebase)
{
JSPrincipalsData *data;
if (principals->codebase == codebase)
return JS_TRUE;
data = (JSPrincipalsData *) principals;
if (data->codebaseBeforeSettingDomain == NULL)
data->codebaseBeforeSettingDomain = principals->codebase;
else
XP_FREEIF(principals->codebase);
principals->codebase = getOriginFromSourceURL(codebase);
if (principals->codebase == NULL) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
if (data->principalsArrayRef != NULL) {
nsCapsFreePrincipalArray(data->principalsArrayRef);
data->principalsArrayRef = NULL;
}
return JS_TRUE;
}
JSPrincipals *
lm_GetInnermostPrincipals(JSContext *cx, JSObject *container,
JSObject **foundIn)
{
/* Get innermost non-null principals */
while (container) {
if (foundIn)
*foundIn = container;
if (JS_InstanceOf(cx, container, &lm_layer_class, 0)) {
JSPrincipals *principals = lm_GetContainerPrincipals(cx, container);
if (principals)
return principals;
} else if (JS_InstanceOf(cx, container, &lm_window_class, 0)) {
MochaDecoder *decoder = JS_GetInstancePrivate(cx, container,
&lm_window_class,
NULL);
const char *origin_url;
/*
* We need to check that the origin hasn't changed underneath
* us as a result of user navigation.
*/
origin_url = find_origin_url(cx, decoder);
if (!origin_url)
return NULL;
if (decoder->principals) {
JSPrincipalsData *data;
if (sameOrigins(cx, origin_url, decoder->principals->codebase))
return decoder->principals;
data = (JSPrincipalsData *) decoder->principals;
if (data->codebaseBeforeSettingDomain &&
sameOrigins(cx, origin_url,
data->codebaseBeforeSettingDomain))
{
/* document.domain was set, so principals are okay */
return decoder->principals;
}
/* Principals have changed underneath us. Remove them. */
JSPRINCIPALS_DROP(cx, decoder->principals);
decoder->principals = NULL;
}
/* Create new principals and return them. */
decoder->principals = LM_NewJSPrincipals(NULL, NULL, origin_url);
if (decoder->principals == NULL) {
JS_ReportOutOfMemory(cx);
return NULL;
}
JSPRINCIPALS_HOLD(cx, decoder->principals);
return decoder->principals;
}
container = JS_GetParent(cx, container);
}
if (foundIn)
*foundIn = NULL;
return (JSPrincipals *) &unknownPrincipals;
}
JSBool lm_CheckSetParentSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSObject *newParent;
if (!JSVAL_IS_OBJECT(*vp))
return JS_TRUE;
newParent = JSVAL_TO_OBJECT(*vp);
if (newParent) {
const char *oldOrigin = lm_GetObjectOriginURL(cx, obj);
const char *newOrigin = lm_GetObjectOriginURL(cx, newParent);
if (!sameOrigins(cx, oldOrigin, newOrigin))
return JS_TRUE;
} else {
if (!JS_InstanceOf(cx, obj, &lm_layer_class, 0) &&
!JS_InstanceOf(cx, obj, &lm_window_class, 0))
{
return JS_TRUE;
}
if (lm_GetContainerPrincipals(cx, obj) == NULL) {
JSPrincipals *principals;
principals = lm_GetInnermostPrincipals(cx, obj, NULL);
if (principals == NULL)
return JS_FALSE;
lm_SetContainerPrincipals(cx, obj, principals);
}
}
return JS_TRUE;
}
static JSBool
canExtendTrust(JSContext *cx, void *from, void *to)
{
if (from == NULL || to == NULL) {
return JS_FALSE;
}
return (from == to) || (JSBool)nsCapsCanExtendTrust(from, to);
}
static JSPrincipals *
newJSPrincipalsFromArray(JSContext *cx, void *principalsArray, void *pNSISecurityContext);
extern JSBool
lm_CheckContainerAccess(JSContext *cx, JSObject *obj, MochaDecoder *decoder,
JSTarget target)
{
JSPrincipals *principals;
JSPrincipalsData *data;
JSStackFrame *fp;
JSScript *script;
JSPrincipals *subjPrincipals;
JSPrincipalsList *list;
const char *fn;
if(decoder->principals) {
/* The decoder's js_context isn't in a request, so we should put it
* in one during this call. */
JS_BeginRequest(decoder->js_context);
principals = lm_GetInnermostPrincipals(decoder->js_context, obj, NULL);
JS_EndRequest(decoder->js_context);
} else {
principals = NULL;
}
if (principals == NULL) {
/*
* Attempt to access container before container has any scripts.
* Most of these accesses come from natives when initializing a
* window. Check for that by seeing if we have an executing script.
* If we do, remember the principals of the script that performed
* the access so we can report an error later if need be.
*/
fp = NULL;
subjPrincipals = lm_GetPrincipalsFromStackFrame(cx);
if (subjPrincipals == NULL) {
return JS_TRUE;
}
/* See if subjPrincipals are already on list */
list = (JSPrincipalsList *) decoder->early_access_list;
while (list && list->principals != subjPrincipals) {
list = list->next;
}
if (list == NULL) {
list = XP_ALLOC(sizeof(*list));
if (list == NULL) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
list->principals = subjPrincipals;
JSPRINCIPALS_HOLD(cx, list->principals);
list->next = (JSPrincipalsList *) decoder->early_access_list;
decoder->early_access_list = list;
}
/*
* XXX - Still possible to modify contents of another page
* even if cross-origin access is disabled by setting to
* about:blank, modifying, and then loading the attackee.
* Similarly with window.open("").
*/
return JS_TRUE;
}
/*
* If object doesn't have signed scripts and cross-origin access
* is enabled, return true.
*/
data = (JSPrincipalsData *) principals;
if (data->signedness != HAS_SIGNED_SCRIPTS && lm_GetCrossOriginEnabled())
return JS_TRUE;
/* Check if user requested lower privileges */
if (data->signedness == HAS_SIGNED_SCRIPTS &&
!lm_GetPrincipalsCompromise(cx, obj))
{
/*
* We have signed scripts. Must check that the object principals are
* a subset of the the subject principals.
*/
fp = NULL;
fp = JS_FrameIterator(cx, &fp);
if (fp == NULL || (script = JS_GetFrameScript(cx, fp)) == NULL) {
/* haven't begun execution yet; allow the parser to create functions */
return JS_TRUE;
}
subjPrincipals = JS_GetScriptPrincipals(cx, script);
if (subjPrincipals &&
canExtendTrust(cx,
principals->getPrincipalArray(cx, principals),
subjPrincipals->getPrincipalArray(cx, subjPrincipals)))
{
return JS_TRUE;
}
fn = lm_GetSubjectOriginURL(cx);
if (!fn)
return JS_FALSE;
if (subjPrincipals && principals) {
lm_PrintToConsole("Principals of script: ");
printPrincipalsToConsole(cx, subjPrincipals);
lm_PrintToConsole("Principals of signed container: ");
printPrincipalsToConsole(cx, principals);
}
JS_ReportError(cx, container_error_message, fn);
return JS_FALSE;
}
/* The signed script has called compromisePrincipals(), so
* we do the weaker origin check.
*/
return lm_CheckPermissions(cx, obj, target);
}
static JSBool
checkEarlyAccess(MochaDecoder *decoder, JSPrincipals *principals)
{
JSContext *cx;
JSPrincipalsData *data;
JSPrincipalsList *p;
JSBool ok;
cx = decoder->js_context;
data = (JSPrincipalsData *) principals;
ok = JS_TRUE;
for (p = (JSPrincipalsList *) decoder->early_access_list; p; p = p->next) {
if (data->signedness == HAS_SIGNED_SCRIPTS) {
if (!canExtendTrust(cx,
principals->getPrincipalArray(cx, principals),
p->principals->getPrincipalArray(cx,
p->principals)))
{
JS_ReportError(cx, container_error_message,
p->principals->codebase);
ok = JS_FALSE;
break;
}
} else {
if (!sameOrigins(cx, p->principals->codebase,
principals->codebase))
{
/*
* Check to see if early access violated the cross-origin
* container check.
*/
JS_ReportError(cx, access_error_message,
p->principals->codebase);
ok = JS_FALSE;
break;
}
}
}
lm_DestroyPrincipalsList(cx, decoder->early_access_list);
decoder->early_access_list = NULL;
return ok;
}
/*
* Compute the intersection of "principals" and "other", saving in
* "principals". Return true iff the intersection is nonnull.
*/
static JSBool
intersectPrincipals(MochaDecoder *decoder, JSPrincipals *principals,
JSPrincipals *newPrincipals)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
JSPrincipalsData *newData = (JSPrincipalsData *) newPrincipals;
JSContext *cx;
void *principalArray;
void *newPrincipalArray;
XP_ASSERT(data->signedness != HAS_NO_SCRIPTS);
XP_ASSERT(newData->signedness != HAS_NO_SCRIPTS);
cx = decoder->js_context;
if (!sameOrigins(cx, principals->codebase, newPrincipals->codebase)) {
XP_FREEIF(principals->codebase);
principals->codebase = JS_strdup(cx, lm_unknown_origin_str);
if (principals->codebase == NULL) {
return JS_FALSE;
}
}
if (data->signedness == HAS_UNSIGNED_SCRIPTS ||
newData->signedness == HAS_UNSIGNED_SCRIPTS)
{
/*
* No cert principals. Nonempty only if there is a codebase
* principal.
*/
lm_InvalidateCertPrincipals(decoder, principals);
return JS_TRUE;
}
/* Compute the intersection. */
principalArray = getPrincipalArray(cx, principals);
newPrincipalArray = getPrincipalArray(cx, newPrincipals);
if (principalArray == NULL
|| newPrincipalArray == NULL)
{
lm_InvalidateCertPrincipals(decoder, principals);
return JS_TRUE;
}
principalArray = nsCapsIntersectPrincipalArray(
principalArray, newPrincipalArray);
if (principalArray == NULL) {
lm_InvalidateCertPrincipals(decoder, principals);
return JS_TRUE;
}
data->principalsArrayRef = principalArray;
return JS_TRUE;
}
static uint32
getPrincipalsCount(JSContext *cx, JSPrincipals *principals)
{
void *principalArray;
/* Get array of principals */
principalArray = getPrincipalArray(cx, principals);
return principalArray ? nsCapsGetPrincipalArraySize(principalArray) : 0;
}
static JSBool
principalsEqual(JSContext *cx, JSPrincipals *a, JSPrincipals *b)
{
JSPrincipalsData *dataA, *dataB;
void *arrayA;
void *arrayB;
if (a == b)
return JS_TRUE;
dataA = (JSPrincipalsData *) a;
dataB = (JSPrincipalsData *) b;
if (dataA->signedness != dataB->signedness)
return JS_FALSE;
arrayA = getPrincipalArray(cx, a);
arrayB = getPrincipalArray(cx, b);
return (JSBool)(nsCapsComparePrincipalArray(arrayA, arrayB)
== nsSetComparisonType_Equal);
}
/*
* createPrincipalsArray takes ZIG file information and returns a
* pointer to an array of nsPrincipal objects.
* It also registers the principals with the nsPrivilegeManager.
*/
static jref
createPrincipalsArray(JSPrincipals *principals)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
JSBool hasCodebase;
SOBITEM *item;
int i;
ZIG *zig;
unsigned count;
void *result;
struct nsPrincipal *principal;
ZIG_Context * zig_cx = NULL;
setupJSCapsCallbacks();
if (principals == (JSPrincipals *) &unknownPrincipals)
return NULL;
hasCodebase = (JSBool)(principals->codebase &&
XP_STRCMP(principals->codebase, lm_unknown_origin_str) != 0);
/* First count the number of principals */
count = hasCodebase ? 1 : 0;
zig = data->signedness == HAS_UNSIGNED_SCRIPTS
? NULL
: (data->sharedZig ? data->sharedZig->zig : NULL);
if (zig && data->name) {
/* Make sure file is signed */
if ((zig_cx = SOB_find(zig, data->name, ZIG_SIGN)) != NULL) {
int zig_count=0;
/* count the number of signers */
while (SOB_find_next(zig_cx, &item) >= 0) {
zig_count++;
}
SOB_find_end(zig_cx);
count += zig_count;
} else {
zig = NULL;
}
}
if (count == 0) {
return NULL;
}
/* Now allocate the array */
result = nsCapsNewPrincipalArray(count);
if (result == NULL) {
return NULL;
}
if (zig && ((zig_cx = SOB_find(zig, data->name, ZIG_SIGN)) == NULL)) {
return NULL;
}
i = 0;
while (zig && SOB_find_next(zig_cx, &item) >= 0) {
FINGERZIG *fingPrint;
fingPrint = (FINGERZIG *) item->data;
/* create a new nsPrincipal(CERT_KEY, fingPrint->key) */
principal = nsCapsNewPrincipal(nsPrincipalType_CertKey,
fingPrint->key,
fingPrint->length,
zig);
nsCapsRegisterPrincipal(principal);
nsCapsSetPrincipalArrayElement(result, i++, principal);
}
if (zig) {
SOB_find_end(zig_cx);
}
if (hasCodebase) {
/* Add a codebase principal. */
char *javaCodebase;
javaCodebase = getJavaCodebaseFromOrigin(principals->codebase);
if (javaCodebase == NULL)
return NULL;
principal = nsCapsNewPrincipal(nsPrincipalType_CodebaseExact,
javaCodebase,
XP_STRLEN(javaCodebase),
NULL);
nsCapsRegisterPrincipal(principal);
nsCapsSetPrincipalArrayElement(result, i++, principal);
XP_FREE(javaCodebase);
}
data->principalsArrayRef = result;
return result;
}
static JSBool
isExternalCaptureEnabled(JSContext *cx, JSPrincipals *principals)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
if (data->externalCapturePrincipalsCount == 0) {
return JS_FALSE;
} else {
uint32 count = getPrincipalsCount(cx, principals);
return (JSBool)(data->externalCapturePrincipalsCount == count);
}
}
void
lm_SetExternalCapture(JSContext *cx, JSPrincipals *principals,
JSBool b)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
if (b) {
uint32 count = getPrincipalsCount(cx, principals);
data->externalCapturePrincipalsCount = count;
} else {
data->externalCapturePrincipalsCount = 0;
}
}
JSBool
lm_CanAccessTarget(JSContext *cx, JSTarget target)
{
JSPrincipals *principals;
principals = lm_GetPrincipalsFromStackFrame(cx);
if ((nsCapsGetRegistrationModeFlag()) && principals &&
(NET_URL_Type(principals->codebase) == FILE_TYPE_URL))
return JS_TRUE;
if (principals && !globalPrivilegesEnabled(cx, principals)) {
return JS_FALSE;
}
if (!principalsCanAccessTarget(cx, target)) {
return JS_FALSE;
}
return JS_TRUE;
}
/* This array must be kept in sync with the JSTarget enum in jsapi.h */
static char *targetStrings[] = {
"UniversalBrowserRead",
"UniversalBrowserWrite",
"UniversalSendMail",
"UniversalFileRead",
"UniversalFileWrite",
"UniversalPreferencesRead",
"UniversalPreferencesWrite",
"AccountSetup",
/* See Target.java for more targets */
};
int
findTarget(const char *target)
{
int i=0;
for(i=0; i<JSTARGET_MAX; i++)
{
if (XP_STRCMP(target, targetStrings[i]) == 0)
{
return i;
}
}
return -1;
}
/*
** Exported entry point to support nsISecurityContext::Implies method.
*/
JSBool
LM_CanAccessTargetStr(JSContext *cx, const char *target)
{
int intTarget = findTarget(target);
JSTarget jsTarget;
if(intTarget < 0)
{
return PR_FALSE;
}
jsTarget = (JSTarget)intTarget;
return lm_CanAccessTarget(cx, jsTarget);
}
/*
* If given principals can access the given target, return true. Otherwise
* return false. The script must already have explicitly requested access
* to the given target.
*/
static JSBool
principalsCanAccessTarget(JSContext *cx, JSTarget target)
{
struct nsPrivilegeTable *annotation;
struct nsPrivilege *privilege;
struct nsTarget *capsTarget;
nsPermissionState perm;
JSStackFrame *fp;
void *annotationRef;
void *principalArray = NULL;
JSStackFrame *pFrameToStartLooking = JVM_GetStartJSFrameFromParallelStack();
JSStackFrame *pFrameToEndLooking = JVM_GetEndJSFrameFromParallelStack(pFrameToStartLooking);
setupJSCapsCallbacks();
/* Map JSTarget to nsTarget */
XP_ASSERT(target >= 0);
XP_ASSERT(target < sizeof(targetStrings)/sizeof(targetStrings[0]));
capsTarget = nsCapsFindTarget(targetStrings[target]);
/* Find annotation */
annotationRef = NULL;
principalArray = NULL;
fp = pFrameToStartLooking;
while ((fp = JS_FrameIterator(cx, &fp)) != pFrameToEndLooking) {
void *current;
if (JS_GetFrameScript(cx, fp) == NULL)
continue;
current = JS_GetFramePrincipalArray(cx, fp);
if (current == NULL) {
return JS_FALSE;
}
annotationRef = (void *) JS_GetFrameAnnotation(cx, fp);
if (annotationRef) {
if (principalArray &&
!nsCapsCanExtendTrust(current, principalArray))
{
return JS_FALSE;
}
break;
}
principalArray = principalArray
? nsCapsIntersectPrincipalArray(principalArray, current)
: current;
}
if (annotationRef) {
annotation = (struct nsPrivilegeTable *)annotationRef;
} else {
#ifdef OJI
/*
* Call from Java into JS. Just call the Java routine for checking
* privileges.
*/
if (principalArray) {
/*
* Must check that the principals that signed the Java applet are
* a subset of the principals that signed this script.
*/
void *javaPrincipals = JVM_GetJavaPrincipalsFromStack(pFrameToStartLooking);
if (!canExtendTrust(cx, javaPrincipals, principalArray)) {
return JS_FALSE;
}
}
/*
* XXX sudu: TODO: Setup the parameters representing a target.
*/
return JVM_NSISecurityContextImplies(pFrameToStartLooking, targetStrings[target], NULL);
#endif /* JAVA */
/* No annotation in stack */
return JS_FALSE;
}
/* Now find permission for (annotation, target) pair. */
privilege = nsCapsGetPrivilege(annotation, capsTarget);
if (privilege == NULL) {
return JS_FALSE;
}
XP_ASSERT(privilege);
perm = nsCapsGetPermission(privilege);
return (JSBool)(perm == nsPermissionState_Allowed);
}
PR_STATIC_CALLBACK(void *)
getPrincipalArray(JSContext *cx, struct JSPrincipals *principals)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
/* Get array of principals */
if (data->principalsArrayRef == NULL) {
if (createPrincipalsArray(principals) == NULL)
return NULL;
}
return data->principalsArrayRef;
}
extern char *
LM_ExtractFromPrincipalsArchive(JSPrincipals *principals, char *name,
uint *length)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
char *result = NULL;
result = LM_LoadFromZipFile(data->zip, name);
*length = result ? XP_STRLEN(result) : 0;
return result;
}
extern JSBool
LM_SetUntransformedSource(JSPrincipals *principals, char *original,
char *transformed)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
XP_ASSERT(data->untransformed == NULL);
data->untransformed = XP_STRDUP(original);
if (data->untransformed == NULL)
return JS_FALSE;
data->transformed = XP_STRDUP(transformed);
if (data->transformed == NULL)
return JS_FALSE;
return JS_TRUE;
}
JSPrincipals * PR_CALLBACK
LM_GetJSPrincipalsFromJavaCaller(JSContext *cx, void *principalsArray, void *pNSISecurityContext)
{
setupJSCapsCallbacks();
if (principalsArray == NULL)
return NULL;
return newJSPrincipalsFromArray(cx, principalsArray, pNSISecurityContext);
}
static JSPrincipals *
newJSPrincipalsFromArray(JSContext *cx, void *principalsArray, void *pNSISecurityContext)
{
JSPrincipals *result;
struct nsPrincipal *principal;
const char *codebase;
JSPrincipalsData *data;
uint32 i, count;
setupJSCapsCallbacks();
count = nsCapsGetPrincipalArraySize(principalsArray);
if (count == 0) {
JS_ReportError(cx, "No principals found for Java caller");
return NULL;
}
codebase = NULL;
for (i = count; i > 0; i--) {
principal = nsCapsGetPrincipalArrayElement(principalsArray, i-1);
if (nsCapsIsCodebaseExact(principal)) {
codebase = nsCapsPrincipalToString(principal);
break;
}
}
result = LM_NewJSPrincipals(NULL, NULL, (char *) codebase);
if (result == NULL) {
JS_ReportOutOfMemory(cx);
return NULL;
}
data = (JSPrincipalsData *) result;
data->principalsArrayRef = principalsArray;
data->pNSISecurityContext = pNSISecurityContext;
data->signedness = count == 1 && codebase
? HAS_UNSIGNED_SCRIPTS
: HAS_SIGNED_SCRIPTS;
return result;
}
static JSBool
verifyPrincipals(MochaDecoder *decoder, JSPrincipals *containerPrincipals,
JSPrincipals *principals, char *name, char *src,
uint srcSize, JSBool implicitName)
{
JSPrincipalsData *data = (JSPrincipalsData *) principals;
ZIG *zig;
DIGESTS *dig = NULL;
JSBool sameName = JS_FALSE;
int ret;
JSPrincipalsData *containerData;
ns_zip_t *containerZip;
JSBool verified;
SOBITEM *item;
ZIG_Context * zig_cx;
if (data->signedness == HAS_UNSIGNED_SCRIPTS)
return JS_FALSE;
containerData = (JSPrincipalsData *) containerPrincipals;
containerZip =
(containerData && containerData->signedness != HAS_UNSIGNED_SCRIPTS)
? containerData->zip
: NULL;
if (data->zip == NULL && containerZip == NULL)
return JS_FALSE;
if (data->name && data->signedness == HAS_NO_SCRIPTS) {
if (XP_STRCMP(name, data->name) == 0) {
sameName = JS_TRUE;
} else {
return JS_FALSE;
}
}
/*
* Set to the value we want if verification fails, and then
* reset below.
*/
verified = JS_FALSE;
if (containerData == NULL) {
/* First script seen; initialize zig. */
data->sharedZig = holdZig(newSharedZig(data->zip));
} else if (data == containerData) {
/* Already have a zig if there is one; nothing more to do. */
} else if (data->zip == NULL) {
/* "Inherit" data->sharedZig from container data. */
data->sharedZig = holdZig(containerData->sharedZig);
} else if (containerData->url_struct &&
XP_STRCMP(data->url_struct->address,
containerData->url_struct->address) == 0)
{
/* Two identical zips. Share the zigs. */
data->sharedZig = holdZig(containerData->sharedZig);
} else {
/* Different zips. Must create a new zig. */
data->sharedZig = holdZig(newSharedZig(data->zip));
}
if (data->sharedZig == NULL)
return JS_FALSE;
zig = data->sharedZig->zig;
dig = SOB_calculate_digest(src, srcSize);
if (dig == NULL)
return JS_FALSE;
zig_cx = NULL;
ret = SOB_verify_digest(zig, name, dig);
XP_FREE(dig);
if ((ret >= 0) &&
((zig_cx = SOB_find(zig, name, ZIG_SIGN)) != NULL) &&
(SOB_find_next(zig_cx, &item) >= 0))
{
verified = JS_TRUE;
if (!sameName) {
data->name = JS_strdup(decoder->js_context, name);
if (data->name == NULL)
return JS_FALSE;
}
} else if (!implicitName || ret != ZIG_ERR_PNF) {
LM_PrintZigError(ret, zig, "", name, SOB_get_error(ret));
}
if (zig_cx) {
SOB_find_end(zig_cx);
}
return verified;
}
extern JSPrincipals *
LM_RegisterPrincipals(MochaDecoder *decoder, JSPrincipals *principals,
char *name, char *src)
{
JSContext *cx = decoder->js_context;
JSBool verified;
JSPrincipalsData *data;
JSObject *inner, *container;
JSPrincipals *containerPrincipals;
JSPrincipalsData *containerData;
char *untransformed, *implicitName;
data = (JSPrincipalsData *) principals;
inner = lm_GetActiveContainer(decoder);
if (inner == NULL)
return NULL;
containerPrincipals = lm_GetInnermostPrincipals(decoder->js_context,
inner, &container);
if (containerPrincipals == NULL) {
/* Out of memory */
return NULL;
}
containerData = (JSPrincipalsData *) containerPrincipals;
if (name == NULL && principals != containerPrincipals && principals) {
/*
* "name" argument omitted since it was specified when "principals"
* was created. Get it from "principals".
*/
name = data->name;
}
implicitName = NULL;
if (name == NULL && data && data->signedness == HAS_SIGNED_SCRIPTS) {
/*
* Name is unspecified. Use the implicit name formed from the
* origin URL and the ordinal within the page. For example, the
* third implicit name on http://www.co.com/dir/mypage.html
* would be "_mypage2".
*/
const char *url;
char *path;
url = LM_GetSourceURL(decoder);
if (url == NULL) {
return NULL;
}
path = *url? NET_ParseURL(url, GET_PATH_PART) : NULL;
if (path && *path) {
char *s;
s = XP_STRRCHR(path, '.');
if (s)
*s = '\0';
s = XP_STRRCHR(path, '/');
implicitName = PR_sprintf_append(NULL, "_%s%d", s ? s+1 : path,
decoder->signature_ordinal++);
name = implicitName;
}
XP_FREEIF(path);
}
untransformed = NULL;
if (data && data->untransformed && !XP_STRCMP(data->transformed, src)) {
/* Perform verification on original source. */
src = untransformed = data->untransformed;
data->untransformed = NULL;
XP_FREE(data->transformed);
data->transformed = NULL;
}
/* Verify cert principals */
verified = (JSBool)(principals && name && src &&
verifyPrincipals(decoder, containerPrincipals, principals, name,
src, XP_STRLEN(src), (JSBool)(implicitName != NULL)));
XP_FREEIF(untransformed);
src = NULL;
XP_FREEIF(implicitName);
name = NULL;
/*
* Now that we've attempted verification, we need to set the appropriate
* level of signedness based on whether verification succeeded.
* We avoid setting signedness if principals is the same as container
* principals (i.e., we "inherited" the principals from a script earlier
* in the page) and we are not in a subcontainer of the container where
* the principals were found. In that case we will create a new set of
* principals for the inner container.
*/
if (data && !(principals == containerPrincipals && container != inner)) {
data->signedness = verified ? HAS_SIGNED_SCRIPTS : HAS_UNSIGNED_SCRIPTS;
}
if (verified && decoder->early_access_list &&
!checkEarlyAccess(decoder, principals))
{
return NULL;
}
if (!verified) {
if (!lm_GetUnsignedExecutionEnabled()) {
/* Execution of unsigned scripts disabled. Return now. */
return NULL;
}
/* No cert principals; try codebase principal */
if (principals == NULL || principals == containerPrincipals) {
if (container == inner ||
containerData->signedness == HAS_UNSIGNED_SCRIPTS)
{
principals = containerPrincipals;
data = (JSPrincipalsData *) principals;
} else {
/* Just put restricted principals in inner */
principals = LM_NewJSPrincipals(NULL, NULL,
containerPrincipals->codebase);
if (principals == NULL) {
JS_ReportOutOfMemory(cx);
return NULL;
}
data = (JSPrincipalsData *) principals;
}
}
lm_InvalidateCertPrincipals(decoder, principals);
if (decoder->early_access_list && !lm_GetCrossOriginEnabled() &&
!checkEarlyAccess(decoder, principals))
{
return NULL;
}
if (container == inner) {
lm_InvalidateCertPrincipals(decoder, containerPrincipals);
/* compare codebase principals */
if (!sameOrigins(cx, containerPrincipals->codebase,
principals->codebase))
{
/* Codebases don't match; evaluate under different
principals than container */
return principals;
}
/* Codebases match */
return containerPrincipals;
}
/* Just put restricted principals in inner */
lm_SetContainerPrincipals(cx, inner, principals);
return principals;
}
if (!principalsEqual(cx, principals, containerPrincipals)) {
/* We have two unequal sets of principals. */
if (containerData->signedness == HAS_NO_SCRIPTS &&
sameOrigins(cx, principals->codebase,
containerPrincipals->codebase))
{
/*
* Principals are unequal because we have container principals
* carrying only a codebase, and the principals of this script
* that carry cert principals as well.
*/
lm_SetContainerPrincipals(cx, container, principals);
return principals;
}
if (inner == container) {
if (containerData->signedness == HAS_NO_SCRIPTS) {
lm_SetContainerPrincipals(cx, container, principals);
return principals;
}
/*
* Intersect principals and container principals,
* modifying the container principals.
*/
lm_PrintToConsole("Intersecting principals ");
printPrincipalsToConsole(cx, containerPrincipals);
lm_PrintToConsole("with ");
printPrincipalsToConsole(cx, principals);
if (!intersectPrincipals(decoder, containerPrincipals,
principals))
{
return NULL;
}
lm_PrintToConsole("yielding ");
printPrincipalsToConsole(cx, containerPrincipals);
} else {
/*
* Store the disjoint set of principals in the
* innermost container
*/
lm_SetContainerPrincipals(cx, inner, principals);
return principals;
}
}
return containerPrincipals;
}
/*******************************************************************************
* Glue code for JS stack crawling callbacks
******************************************************************************/
typedef struct JSFrameIterator {
JSStackFrame *fp;
JSContext *cx;
JRIEnv *env;
void *intersect;
PRBool sawEmptyPrincipals;
} JSFrameIterator;
static JSFrameIterator *
lm_NewJSFrameIterator(void *context)
{
JSContext *cx = (JSContext *)context;
JSFrameIterator *result;
void *array;
JRIEnv *env = NULL;
result = XP_ALLOC(sizeof(JSFrameIterator));
if (result == NULL) {
return NULL;
}
if (cx == NULL) {
return NULL;
}
result->env = env;
result->fp = NULL;
result->cx = cx;
result->fp = JS_FrameIterator(cx, &result->fp);
array = result->fp
? JS_GetFramePrincipalArray(cx, result->fp)
: NULL;
result->intersect = array;
result->sawEmptyPrincipals =
(result->intersect == NULL && result->fp &&
JS_GetFrameScript(cx, result->fp))
? PR_TRUE : PR_FALSE;
return result;
}
static PRBool
lm_NextJSJavaFrame(struct JSFrameIterator *iterator)
{
void *current;
void *previous;
if (iterator->fp == 0) {
return PR_FALSE;
}
current = JS_GetFramePrincipalArray(iterator->cx, iterator->fp);
if (current == NULL) {
if (JS_GetFrameScript(iterator->cx, iterator->fp))
iterator->sawEmptyPrincipals = PR_TRUE;
} else {
if (iterator->intersect) {
previous = iterator->intersect;
current = nsCapsIntersectPrincipalArray(current, previous);
/* XXX: raman: should we do a free the previous principal Array */
nsCapsFreePrincipalArray(iterator->intersect);
}
iterator->intersect = current;
}
iterator->fp = JS_FrameIterator(iterator->cx, &iterator->fp);
return iterator->fp != NULL;
}
static PRBool
nextJSFrame(struct JSFrameIterator **iteratorp)
{
JSFrameIterator *iterator = *iteratorp;
PRBool result = lm_NextJSJavaFrame(iterator);
if (!result) {
if (iterator->intersect)
nsCapsFreePrincipalArray(iterator->intersect);
XP_FREE(iterator);
*iteratorp = NULL;
}
return result;
}
/*
*
* CALLBACKS to walk the stack
*
*/
typedef struct NSJSJavaFrameWrapper {
struct JSFrameIterator *iterator;
} NSJSJavaFrameWrapper;
struct NSJSJavaFrameWrapper *
lm_NewNSJSJavaFrameWrapperCB(void *context)
{
struct NSJSJavaFrameWrapper *result;
result = (struct NSJSJavaFrameWrapper *)PR_CALLOC(sizeof(struct NSJSJavaFrameWrapper));
if (result == NULL) {
return NULL;
}
result->iterator = lm_NewJSFrameIterator(context);
return result;
}
void lm_FreeNSJSJavaFrameWrapperCB(struct NSJSJavaFrameWrapper *wrapper)
{
PR_FREEIF(wrapper);
}
void lm_GetStartFrameCB(struct NSJSJavaFrameWrapper *wrapper)
{
}
PRBool lm_IsEndOfFrameCB(struct NSJSJavaFrameWrapper *wrapper)
{
if ((wrapper == NULL) || (wrapper->iterator == NULL))
return PR_TRUE;
return PR_FALSE;
}
PRBool lm_IsValidFrameCB(struct NSJSJavaFrameWrapper *wrapper)
{
return (wrapper->iterator != NULL);
}
void *lm_GetNextFrameCB(struct NSJSJavaFrameWrapper *wrapper, int *depth)
{
if ((wrapper->iterator == NULL) ||
(!nextJSFrame(&(wrapper->iterator)))) {
return NULL;
}
(*depth)++;
return wrapper->iterator;
}
void * lm_GetPrincipalArrayCB(struct NSJSJavaFrameWrapper *wrapper)
{
JSFrameIterator *iterator;
if (wrapper->iterator == NULL)
return NULL;
iterator = wrapper->iterator;
return JS_GetFramePrincipalArray(iterator->cx, iterator->fp);
}
void * lm_GetAnnotationCB(struct NSJSJavaFrameWrapper *wrapper)
{
JSFrameIterator *iterator;
void *annotaion;
void *current;
if (wrapper->iterator == NULL) {
return NULL;
}
iterator = wrapper->iterator;
annotaion = JS_GetFrameAnnotation(iterator->cx, iterator->fp);
if (annotaion == NULL)
return NULL;
current = JS_GetFramePrincipalArray(iterator->cx, iterator->fp);
if (iterator->sawEmptyPrincipals || current == NULL ||
(iterator->intersect &&
!canExtendTrust(iterator->cx, current, iterator->intersect)))
return NULL;
return annotaion;
}
void * lm_SetAnnotationCB(struct NSJSJavaFrameWrapper *wrapper, void *privTable)
{
if (wrapper->iterator) {
JSFrameIterator *iterator = wrapper->iterator;
JS_SetFrameAnnotation(iterator->cx, iterator->fp, privTable);
}
return privTable;
}
/* End of Callbacks */
static PRBool privManagerInited = PR_FALSE;
static void
setupJSCapsCallbacks()
{
if (privManagerInited)
return;
privManagerInited = TRUE;
nsCapsInitialize();
setNewNSJSJavaFrameWrapperCallback(lm_NewNSJSJavaFrameWrapperCB);
setFreeNSJSJavaFrameWrapperCallback(lm_FreeNSJSJavaFrameWrapperCB);
setGetStartFrameCallback(lm_GetStartFrameCB);
setIsEndOfFrameCallback(lm_IsEndOfFrameCB);
setIsValidFrameCallback(lm_IsValidFrameCB);
setGetNextFrameCallback(lm_GetNextFrameCB);
setOJIGetPrincipalArrayCallback(lm_GetPrincipalArrayCB);
setOJIGetAnnotationCallback(lm_GetAnnotationCB);
setOJISetAnnotationCallback(lm_SetAnnotationCB);
}