netscape-revival
lib/libmocha/lm_taint.c
/*
** Mocha/Mozilla data tainting support.
**
** Brendan Eich, 5/21/96
*/
#include "lm.h"
#include "xp.h"
#include "mkparse.h" /* included via -I../libnet */
#include "mkutils.h" /* for NET_URL_Type() */
#include "prclist.h"
#include "prhash.h"
#include "prmem.h"
#include "prprf.h"
MochaBoolean lm_data_tainting = MOCHA_FALSE;
uint16 lm_branch_taint = MOCHA_TAINT_IDENTITY;
/* Taint origin-to-info cache. */
static PRHashTable *origin_to_taint;
static PRCList taint_freelist = PR_INIT_STATIC_CLIST(&taint_freelist);
static uint16 taint_freelist_len;
static uint16 taint_generator;
/* Cache at least 16 and at most 64 free taint entries on the freelist. */
#define TAINT_FREELIST_MIN 16
#define TAINT_FREELIST_MAX 64
typedef struct TaintEntry {
PRCList links; /* LRU freelist linkage -- must be first */
MochaRefCount nrefs; /* reference count */
MochaRefCount mcrefs; /* references from Mocha contexts */
MochaTaintInfo info; /* Mocha runtime taint info */
const char *origin; /* origin URL prefix (http://server, e.g.) */
const char *index; /* taint code pseudo-URL (taint:code) */
} TaintEntry;
#define APPEND_TO_TAINT_FREELIST(te) { \
XP_ASSERT(te->links.next == &te->links); \
PR_APPEND_LINK(&(te)->links, &taint_freelist); \
taint_freelist_len++; \
}
#define REMOVE_FROM_TAINT_FREELIST(te) { \
XP_ASSERT(te->links.next != &te->links); \
PR_REMOVE_AND_INIT_LINK(&(te)->links); \
taint_freelist_len--; \
}
static char taint_index_format[] = "taint:%u";
static char file_url_prefix[] = "file:";
static char unknown_origin_str[] = "[unknown origin]";
#define TAINT_INDEX_BUFFER_MAX 16
#define FILE_URL_PREFIX_LEN (sizeof file_url_prefix - 1)
static TaintEntry *get_taint_entry(MochaContext *mc, const char *url_string);
static TaintEntry *set_taint_entry(MochaContext *mc, const char *url_string,
uint16 taint);
static TaintEntry *file_taint_entry;
static uint16 mix_taint(MochaContext *mc, uint16 accum, uint16 taint);
static void hold_taint(MochaContext *mc, uint16 taint);
static void drop_taint(MochaContext *mc, uint16 taint);
MochaBoolean
lm_InitTaint(MochaContext *mc)
{
#ifdef DEBUG_brendan
if (!getenv("NS_DISABLE_TAINT"))
#else
if (getenv("NS_ENABLE_TAINT"))
#endif
lm_data_tainting = MOCHA_TRUE;
if (!lm_data_tainting)
return MOCHA_TRUE;
origin_to_taint = PR_NewHashTable(2 * TAINT_FREELIST_MAX,
PR_HashString, PR_CompareStrings,
PR_CompareValues, 0, 0);
if (!origin_to_taint)
return MOCHA_FALSE;
(void) set_taint_entry(mc, "[session history]", MOCHA_TAINT_SHIST);
(void) set_taint_entry(mc, "[Java applets]", MOCHA_TAINT_JAVA);
(void) set_taint_entry(mc, unknown_origin_str, MOCHA_TAINT_MAX);
file_taint_entry = get_taint_entry(mc, file_url_prefix);
MOCHA_SetTaintCallbacks(hold_taint, drop_taint, mix_taint);
return MOCHA_TRUE;
}
static int
is_taint_unique(PRHashEntry *he, int i, void *arg)
{
TaintEntry *te = he->value;
MochaBoolean *donep = arg;
if (te->info.taint == taint_generator) {
*donep = MOCHA_FALSE;
return HT_ENUMERATE_STOP;
}
return HT_ENUMERATE_NEXT;
}
static uint16
generate_taint(void)
{
MochaBoolean done;
do {
do {
taint_generator++;
} while (taint_generator == MOCHA_TAINT_IDENTITY ||
taint_generator == MOCHA_TAINT_SHIST ||
taint_generator == MOCHA_TAINT_JAVA ||
taint_generator == MOCHA_TAINT_MAX);
done = MOCHA_TRUE;
PR_HashTableEnumerateEntries(origin_to_taint, is_taint_unique, &done);
} while (!done);
return taint_generator;
}
static void destroy_taint_entry(TaintEntry *te, MochaBoolean unhash);
static void free_taint_entry(TaintEntry *te, MochaBoolean unhash);
static void put_taint_entry(TaintEntry *te);
static TaintEntry *
new_taint_entry(MochaContext *mc, const char *origin, uint16 taint)
{
TaintEntry *te;
const char *index;
if (taint_freelist_len < TAINT_FREELIST_MIN) {
te = MOCHA_malloc(mc, sizeof *te);
if (!te) {
MOCHA_ReportOutOfMemory(mc);
return 0;
}
} else {
te = (TaintEntry *)taint_freelist.next;
XP_ASSERT(&te->links != &taint_freelist);
REMOVE_FROM_TAINT_FREELIST(te);
free_taint_entry(te, MOCHA_TRUE);
}
if (taint == MOCHA_TAINT_IDENTITY) {
switch (NET_URL_Type(origin)) {
case ABOUT_TYPE_URL:
case MOCHA_TYPE_URL:
case VIEW_SOURCE_TYPE_URL:
break;
default:
taint = generate_taint();
break;
}
}
index = PR_smprintf(taint_index_format, taint);
if (!index) {
MOCHA_ReportOutOfMemory(mc);
MOCHA_free(mc, te);
return 0;
}
PR_INIT_CLIST(&te->links);
te->nrefs = te->mcrefs = 0;
te->info.taint = taint;
te->info.accum = MOCHA_TAINT_IDENTITY;
te->info.data = te;
te->origin = origin;
te->index = index;
if (!PR_HashTableAdd(origin_to_taint, origin, te) ||
!PR_HashTableAdd(origin_to_taint, index, te)) {
MOCHA_ReportOutOfMemory(mc);
destroy_taint_entry(te, MOCHA_TRUE);
return MOCHA_FALSE;
}
return te;
}
static TaintEntry *
context_taint_entry(MochaContext *mc)
{
return MOCHA_GetTaintInfo(mc)->data;
}
static void
switch_context_taint_entry(MochaContext *mc, TaintEntry *te)
{
TaintEntry *cte;
cte = context_taint_entry(mc);
if (cte == te)
return;
if (te) {
MOCHA_SetTaintInfo(mc, &te->info);
te->nrefs++;
te->mcrefs++;
} else {
MOCHA_SetTaintInfo(mc, 0);
}
if (cte) {
XP_ASSERT(cte->nrefs > 0 && cte->mcrefs > 0);
if (--cte->mcrefs == 0)
cte->info.accum = MOCHA_TAINT_IDENTITY;
put_taint_entry(cte);
}
}
static void
destroy_taint_entry(TaintEntry *te, MochaBoolean unhash)
{
MochaContext *iter, *mc;
TaintEntry *cte;
XP_ASSERT(!unhash || te->nrefs == 0);
iter = 0;
while ((mc = MOCHA_ContextIterator(&iter)) != 0) {
cte = context_taint_entry(mc);
XP_ASSERT(cte != te);
if (cte == te)
switch_context_taint_entry(mc, 0);
}
free_taint_entry(te, unhash);
XP_FREE(te);
}
static void
free_taint_entry(TaintEntry *te, MochaBoolean unhash)
{
if (unhash) {
PR_HashTableRemove(origin_to_taint, te->origin);
PR_HashTableRemove(origin_to_taint, te->index);
}
XP_FREE((void *)te->origin);
XP_FREE((void *)te->index);
te->origin = te->index = 0;
}
static TaintEntry *
find_taint_entry(uint16 taint)
{
char buf[TAINT_INDEX_BUFFER_MAX];
PR_snprintf(buf, sizeof buf, taint_index_format, taint);
return PR_HashTableLookup(origin_to_taint, buf);
}
#define HOLD_TAINT_ENTRY(te) { \
XP_ASSERT((te)->nrefs >= 0); \
if ((te)->nrefs == 0) \
REMOVE_FROM_TAINT_FREELIST(te); \
(te)->nrefs++; \
}
static void
hold_taint(MochaContext *mc, uint16 taint)
{
TaintEntry *te = find_taint_entry(taint);
if (te)
HOLD_TAINT_ENTRY(te);
}
static void
drop_taint(MochaContext *mc, uint16 taint)
{
TaintEntry *te = find_taint_entry(taint);
if (te)
put_taint_entry(te);
}
static TaintEntry *
get_taint_entry(MochaContext *mc, const char *url_string)
{
const char *origin;
TaintEntry *te;
if (!XP_STRCHR(url_string, '\n'))
origin = lm_GetOrigin(mc, url_string);
else
origin = MOCHA_strdup(mc, url_string);
if (!origin)
return 0;
te = PR_HashTableLookup(origin_to_taint, origin);
if (te) {
/* An entry already exists; share its info struct. */
MOCHA_free(mc, (void *)origin);
HOLD_TAINT_ENTRY(te);
} else {
/* Create a new entry and add it to the table. */
te = new_taint_entry(mc, origin, MOCHA_TAINT_IDENTITY);
if (!te) {
MOCHA_free(mc, (void *)origin);
return 0;
}
te->nrefs = 1;
}
return te;
}
static void
put_taint_entry(TaintEntry *te)
{
TaintEntry *victim_te;
XP_ASSERT(te->nrefs > 0);
if (--te->nrefs == 0) {
/* Evict the oldest taint entry if the freelist is full. */
if (taint_freelist_len >= TAINT_FREELIST_MAX) {
victim_te = (TaintEntry *)taint_freelist.next;
REMOVE_FROM_TAINT_FREELIST(victim_te);
destroy_taint_entry(victim_te, MOCHA_TRUE);
}
APPEND_TO_TAINT_FREELIST(te);
}
}
static TaintEntry *
set_taint_entry(MochaContext *mc, const char *origin, uint16 taint)
{
TaintEntry *te;
/* Create a new entry and add it to the table. */
te = new_taint_entry(mc, origin, taint);
if (!te) {
XP_FREE((void *)origin);
return 0;
}
te->nrefs = 1;
return te;
}
static MochaBoolean
split_sort_uniq(MochaContext *mc, const char *s, char c, const char ***vp)
{
uint i, j, len;
char **vec, **newvec;
const char *p, *q;
int compare;
char *tmp;
i = 0;
vec = (char **) *vp;
if (vec) {
while (vec[i])
i++;
}
for (p = s; ; p = q + 1) {
q = XP_STRCHR(p, c);
if (!vec) {
vec = MOCHA_malloc(mc, 2 * sizeof *vec);
if (!vec)
return MOCHA_FALSE;
} else {
newvec = XP_REALLOC(vec, (i + 2) * sizeof *vec);
if (!newvec) {
/* XXX add MOCHA_realloc and define its API carefully */
MOCHA_ReportOutOfMemory(mc);
goto bad;
}
vec = newvec;
}
len = q ? q - p : XP_STRLEN(p);
vec[i] = MOCHA_malloc(mc, len + 1);
if (!vec[i])
goto bad;
XP_MEMCPY(vec[i], p, len);
vec[i][len] = '\0';
for (j = 0; j < i; j++) {
compare = XP_STRCMP(vec[i], vec[j]);
if (compare == 0) {
MOCHA_free(mc, vec[i]);
vec[i--] = 0;
break;
}
if (compare < 0) {
tmp = vec[j];
vec[j] = vec[i];
vec[i] = tmp;
}
}
i++;
if (!q)
break;
}
vec[i] = 0;
*vp = (const char **)vec;
return MOCHA_TRUE;
bad:
if (i) {
do {
MOCHA_free(mc, vec[--i]);
} while (i);
}
MOCHA_free(mc, vec);
return MOCHA_FALSE;
}
static const char *
join_vec(MochaContext *mc, const char **vec, char c)
{
char *s;
uint i;
s = 0;
for (i = 0; vec[i]; i++)
s = PR_sprintf_append(s, "%s%c", vec[i], c);
if (i && !s) {
MOCHA_ReportOutOfMemory(mc);
return 0;
}
s[strlen(s)-1] = '\0';
return s;
}
static void
free_vec(MochaContext *mc, const char **vec)
{
uint i;
for (i = 0; vec[i]; i++)
MOCHA_free(mc, (void *)vec[i]);
MOCHA_free(mc, vec);
}
static TaintEntry *
mix_taint_entries(MochaContext *mc, TaintEntry *te, TaintEntry *te2)
{
const char **origin_vec, *mixture;
TaintEntry *mixed_te;
origin_vec = 0;
if (!split_sort_uniq(mc, te->origin, '\n', &origin_vec))
return 0;
if (!split_sort_uniq(mc, te2->origin, '\n', &origin_vec))
return 0;
mixture = join_vec(mc, origin_vec, '\n');
free_vec(mc, origin_vec);
if (!mixture)
return 0;
mixed_te = get_taint_entry(mc, mixture);
XP_FREE((void *)mixture);
return mixed_te;
}
static uint16
mix_taint(MochaContext *mc, uint16 accum, uint16 taint)
{
TaintEntry *accum_te, *taint_te, *mixed_te;
uint16 mixed_taint;
accum_te = find_taint_entry(accum);
taint_te = find_taint_entry(taint);
if (!accum_te || !taint_te)
return MOCHA_TAINT_MAX;
mixed_te = mix_taint_entries(mc, accum_te, taint_te);
if (!mixed_te)
return MOCHA_TAINT_MAX;
mixed_taint = mixed_te->info.taint;
put_taint_entry(mixed_te);
return mixed_taint;
}
#define WYSIWYG_TYPE_LEN 10 /* wysiwyg:// */
const char *
LM_SkipWysiwygURLPrefix(const char *url_string)
{
if (XP_STRLEN(url_string) < WYSIWYG_TYPE_LEN)
return 0;
url_string += WYSIWYG_TYPE_LEN;
url_string = XP_STRCHR(url_string, '/');
if (!url_string)
return 0;
return url_string + 1;
}
static const char *
find_creator_url(MWContext *context)
{
History_entry *he;
const char *address;
MochaContext *mc;
MochaObject *obj;
MochaDecoder *decoder;
he = SHIST_GetCurrent(&context->hist);
if (he && (address = he->address) != 0) {
switch (NET_URL_Type(address)) {
case ABOUT_TYPE_URL:
case MOCHA_TYPE_URL:
/* These types 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;
}
mc = context->mocha_context;
if (mc) {
obj = MOCHA_GetGlobalObject(mc);
decoder = obj->data;
if (decoder->opener) {
/* self.opener property is valid, check its MWContext. */
decoder = decoder->opener->data;
if (!decoder->visited) {
decoder->visited = MOCHA_TRUE;
address = find_creator_url(decoder->window_context);
decoder->visited = MOCHA_FALSE;
if (address)
return address;
}
}
}
return 0;
}
static const char *
find_origin_url(MochaContext *mc, MochaDecoder *decoder)
{
const char *url_string;
MWContext *context;
MochaDecoder *running;
url_string = decoder->origin_url;
if (url_string && decoder->domain_set)
return url_string;
context = decoder->window_context;
if (!context) {
MOCHA_ReportError(mc, "cannot determine origin of %s",
mocha_language_name);
return 0;
}
url_string = find_creator_url(context);
if (!url_string) {
/* Must be a new, empty window? Use running origin. */
running = MOCHA_GetGlobalObject(mc)->data;
if (running->origin_url)
url_string = running->origin_url;
else
url_string = unknown_origin_str;
}
return lm_SetOriginURL(mc, decoder, url_string);
}
MochaBoolean
lm_GetTaint(MochaContext *mc, MochaDecoder *decoder, uint16 *taintp)
{
MochaTaintInfo *info;
uint16 taint;
const char *save, *url_string;
TaintEntry *te;
if (!lm_data_tainting) {
*taintp = MOCHA_TAINT_IDENTITY;
return MOCHA_TRUE;
}
/*
** Compare old and new origin_url pointers to optimize for same taint.
** NB: this requires find_origin_url() to allocate a new origin_url before
** it frees the old string. It means save may point at free space after
** the find_origin_url() call, but we don't refer to free memory, we only
** compare pointers.
*/
info = MOCHA_GetTaintInfo(decoder->mocha_context);
taint = info->taint;
save = decoder->origin_url;
url_string = find_origin_url(mc, decoder);
if (!url_string)
return MOCHA_FALSE;
if (taint != MOCHA_TAINT_IDENTITY && url_string == save) {
*taintp = taint;
return MOCHA_TRUE;
}
te = get_taint_entry(mc, url_string);
if (!te)
return MOCHA_FALSE;
if (&te->info != info) {
MOCHA_MIX_TAINT(mc, te->info.accum, info->accum);
switch_context_taint_entry(decoder->mocha_context, te);
}
*taintp = te->info.taint;
put_taint_entry(te);
return MOCHA_TRUE;
}
MochaBoolean
lm_SetTaint(MochaContext *mc, MochaDecoder *decoder, const char *url_string)
{
TaintEntry *te;
MochaTaintInfo *info;
if (!lm_data_tainting)
return MOCHA_TRUE;
te = get_taint_entry(mc, url_string);
if (!te)
return MOCHA_FALSE;
info = MOCHA_GetTaintInfo(decoder->mocha_context);
if (&te->info != info)
switch_context_taint_entry(decoder->mocha_context, te);
put_taint_entry(te);
return MOCHA_TRUE;
}
MochaBoolean
lm_MergeTaint(MochaContext *mc, MochaContext *mc2)
{
MochaTaintInfo *info, *info2;
TaintEntry *merged_te;
info = MOCHA_GetTaintInfo(mc);
if (!info->data)
return MOCHA_TRUE;
info2 = MOCHA_GetTaintInfo(mc2);
if (info == info2)
return MOCHA_TRUE;
if (!info2->data) {
merged_te = info->data;
HOLD_TAINT_ENTRY(merged_te);
} else {
merged_te = mix_taint_entries(mc, info->data, info2->data);
if (!merged_te)
return MOCHA_FALSE;
}
MOCHA_MIX_TAINT(mc, merged_te->info.accum, info->accum);
MOCHA_MIX_TAINT(mc, merged_te->info.accum, info2->accum);
switch_context_taint_entry(mc, merged_te);
switch_context_taint_entry(mc2, merged_te);
put_taint_entry(merged_te);
return MOCHA_TRUE;
}
void
lm_ClearTaint(MochaContext *mc)
{
switch_context_taint_entry(mc, 0);
}
MochaBoolean
lm_CheckTaint(MochaContext *mc, uint16 taint, const char *url_desc,
const char *url_string)
{
TaintEntry *te;
uint16 accum, script_taint, server_taint;
char *message;
MochaDecoder *decoder;
MochaBoolean ok;
if (!lm_data_tainting)
return MOCHA_TRUE;
te = get_taint_entry(mc, url_string);
if (!te)
return MOCHA_FALSE;
server_taint = te->info.taint;
put_taint_entry(te);
if (server_taint == MOCHA_TAINT_IDENTITY ||
server_taint == file_taint_entry->info.taint) {
return MOCHA_TRUE;
}
script_taint = MOCHA_GetTaintInfo(mc)->accum;
accum = taint;
MOCHA_MIX_TAINT(mc, accum, script_taint);
MOCHA_MIX_TAINT(mc, accum, server_taint);
if (accum == server_taint) {
/* data, script, and server have compatible taint. */
return MOCHA_TRUE;
}
/* XXX refer users to the handbook for more on tainting. */
message = PR_smprintf("%s Confirm:\n"
"The %s%s%s\n"
"may contain sensitive or private data gathered\n",
mocha_language_name,
url_desc,
(XP_STRLEN(url_desc) + XP_STRLEN(url_string) > 40)
? "\n" : " ",
url_string);
te = find_taint_entry((taint != MOCHA_TAINT_IDENTITY &&
taint != MOCHA_TAINT_MAX &&
taint != server_taint)
? taint
: script_taint);
if (te) {
message = PR_sprintf_append(message,
"from the following location(s):\n%s\n",
te->origin);
}
message =
PR_sprintf_append(message,
"and encoded by JavaScript.\n\n"
"You may click OK to continue this operation,\n"
"or Cancel to abort it.\n");
if (!message) {
MOCHA_ReportOutOfMemory(mc);
return MOCHA_FALSE;
}
decoder = MOCHA_GetGlobalObject(mc)->data;
ok = FE_Confirm(decoder->window_context, message);
XP_FREE(message);
return ok;
}
MochaBoolean
LM_CheckGetURL(MWContext *context, URL_Struct *url_struct)
{
MochaDecoder *decoder;
MochaContext *mc;
MochaBoolean ok;
if (url_struct->force_reload != NET_DONT_RELOAD) {
/* We're just reloading the same document: allow it. */
return MOCHA_TRUE;
}
/* Handle document.write("<frame src='cgi/steal?"+encode(history)+"'>") */
while (!context->mocha_context) {
context = context->grid_parent;
if (!context)
return MOCHA_TRUE;
}
decoder = LM_GetMochaDecoder(context);
if (!decoder) {
/* Perhaps JavaScript was disabled after some use of it. */
return MOCHA_TRUE;
}
/*
** Use the executing Mocha context, if Mocha is running. Otherwise use
** decoder's context. We've already checked in lm_GetURL for a tainted
** URL string, and in LM_SendOnSubmit for a tainted form action URL, and
** for tainted form element data.
*/
mc = lm_running_context;
if (!mc)
mc = decoder->mocha_context;
/*
** Mix taint conveyed via document.write() in with accumulated taint.
** Over-conservative, but necessary in lieu of a <TAINT CODE=%u> tag
** tag that we might add around the written strings, and process in
** layout so as to taint its tag structures.
*/
ok = lm_CheckTaint(mc, decoder->write_taint_accum, lm_location_str,
url_struct->address);
LM_PutMochaDecoder(decoder);
return ok;
}
static const char *
strip_file_double_slash(MochaContext *mc, 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);
if (!new_url_string)
MOCHA_ReportOutOfMemory(mc);
} else {
new_url_string = MOCHA_strdup(mc, url_string);
}
return new_url_string;
}
const char *
lm_GetOrigin(MochaContext *mc, const char *url_string)
{
const char *origin, *new_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) {
MOCHA_ReportOutOfMemory(mc);
return 0;
}
new_origin = strip_file_double_slash(mc, origin);
XP_FREE((char *)origin);
return new_origin;
}
const char *
lm_GetOriginURL(MochaContext *mc, MochaDecoder *decoder)
{
return find_origin_url(mc, decoder);
}
const char *
lm_SetOriginURL(MochaContext *mc, MochaDecoder *decoder,
const char *url_string)
{
const char *origin_url, *new_origin_url;
origin_url = decoder->origin_url;
if (origin_url && !XP_STRCMP(origin_url, url_string))
return origin_url;
new_origin_url = strip_file_double_slash(mc, url_string);
if (new_origin_url) {
decoder->origin_url = new_origin_url;
PR_FREEIF((char *)origin_url);
}
return new_origin_url;
}
MochaBoolean
lm_CheckOrigins(MochaContext *mc, MochaDecoder *decoder)
{
MochaDecoder *running;
const char *org1, *org2;
MochaBoolean ok;
if (lm_data_tainting)
return MOCHA_TRUE;
/* Get non-null origin_url pointers for running and decoder. */
running = MOCHA_GetGlobalObject(mc)->data;
if (running == decoder)
return MOCHA_TRUE;
if (!find_origin_url(mc, running) || !find_origin_url(mc, decoder))
return MOCHA_FALSE;
/* Now see whether the origin methods and servers match. */
if (!XP_STRCMP(running->origin_url, decoder->origin_url))
return MOCHA_TRUE;
org1 = lm_GetOrigin(mc, running->origin_url);
org2 = lm_GetOrigin(mc, decoder->origin_url);
if (org1 && org2) {
ok = !XP_STRCMP(org1, org2);
if (!ok) {
MOCHA_ReportError(mc,
"access disallowed from scripts at %s to documents at %s",
LM_GetSourceURL(running), LM_GetSourceURL(decoder));
}
} else {
/* lm_GetOrigin already reported the error. */
ok = MOCHA_FALSE;
}
PR_FREEIF((char *)org1);
PR_FREEIF((char *)org2);
return ok;
}