gecko/caps/src/nsScriptSecurityManager.cpp

3821 lines
124 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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 the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Norris Boyd
* Mitch Stoltz
* Steve Morse
* Christopher A. Aillon
* Giorgio Maone
* Daniel Veditz
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/Util.h"
#include "xpcprivate.h"
#include "nsScriptSecurityManager.h"
#include "nsIServiceManager.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScriptContext.h"
#include "nsIURL.h"
#include "nsINestedURI.h"
#include "nspr.h"
#include "nsJSPrincipals.h"
#include "nsSystemPrincipal.h"
#include "nsPrincipal.h"
#include "nsNullPrincipal.h"
#include "nsXPIDLString.h"
#include "nsCRT.h"
#include "nsCRTGlue.h"
#include "nsIJSContextStack.h"
#include "nsDOMError.h"
#include "nsDOMCID.h"
#include "jsdbgapi.h"
#include "nsIXPConnect.h"
#include "nsIXPCSecurityManager.h"
#include "nsTextFormatter.h"
#include "nsIStringBundle.h"
#include "nsNetUtil.h"
#include "nsIProperties.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsIFileURL.h"
#include "nsIZipReader.h"
#include "nsIXPConnect.h"
#include "nsIScriptGlobalObject.h"
#include "nsPIDOMWindow.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIPrompt.h"
#include "nsIWindowWatcher.h"
#include "nsIConsoleService.h"
#include "nsISecurityCheckedComponent.h"
#include "nsIJSRuntimeService.h"
#include "nsIObserverService.h"
#include "nsIContent.h"
#include "nsAutoPtr.h"
#include "nsDOMJSUtils.h"
#include "nsAboutProtocolUtils.h"
#include "nsIClassInfo.h"
#include "nsIURIFixup.h"
#include "nsCDefaultURIFixup.h"
#include "nsIChromeRegistry.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "mozilla/Preferences.h"
Bug 742217. Reduce the use of nested namespaces in our binding code. r=peterv,bent In the new setup, all per-interface DOM binding files are exported into mozilla/dom. General files not specific to an interface are also exported into mozilla/dom. In terms of namespaces, most things now live in mozilla::dom. Each interface Foo that has generated code has a mozilla::dom::FooBinding namespace for said generated code (and possibly a mozilla::bindings::FooBinding_workers if there's separate codegen for workers). IDL enums are a bit weird: since the name of the enum and the names of its entries all end up in the same namespace, we still generate a C++ namespace with the name of the IDL enum type with "Values" appended to it, with a ::valuelist inside for the actual C++ enum. We then typedef EnumFooValues::valuelist to EnumFoo. That makes it a bit more difficult to refer to the values, but means that values from different enums don't collide with each other. The enums with the proto and constructor IDs in them now live under the mozilla::dom::prototypes and mozilla::dom::constructors namespaces respectively. Again, this lets us deal sanely with the whole "enum value names are flattened into the namespace the enum is in" deal. The main benefit of this setup (and the reason "Binding" got appended to the per-interface namespaces) is that this way "using mozilla::dom" should Just Work for consumers and still allow C++ code to sanely use the IDL interface names for concrete classes, which is fairly desirable. --HG-- rename : dom/bindings/Utils.cpp => dom/bindings/BindingUtils.cpp rename : dom/bindings/Utils.h => dom/bindings/BindingUtils.h
2012-05-02 21:35:38 -07:00
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/StandardInteger.h"
using namespace mozilla;
using namespace mozilla::dom;
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
nsIIOService *nsScriptSecurityManager::sIOService = nsnull;
nsIXPConnect *nsScriptSecurityManager::sXPConnect = nsnull;
nsIThreadJSContextStack *nsScriptSecurityManager::sJSContextStack = nsnull;
nsIStringBundle *nsScriptSecurityManager::sStrBundle = nsnull;
JSRuntime *nsScriptSecurityManager::sRuntime = 0;
bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
///////////////////////////
// Convenience Functions //
///////////////////////////
// Result of this function should not be freed.
static inline const PRUnichar *
2010-07-14 23:19:36 -07:00
IDToString(JSContext *cx, jsid id)
{
2010-07-14 23:19:36 -07:00
if (JSID_IS_STRING(id))
return JS_GetInternedStringChars(JSID_TO_STRING(id));
2010-07-14 23:19:36 -07:00
JSAutoRequest ar(cx);
2010-07-14 23:19:36 -07:00
jsval idval;
if (!JS_IdToValue(cx, id, &idval))
return nsnull;
JSString *str = JS_ValueToString(cx, idval);
if(!str)
return nsnull;
return JS_GetStringCharsZ(cx, str);
}
class nsAutoInPrincipalDomainOriginSetter {
public:
nsAutoInPrincipalDomainOriginSetter() {
++sInPrincipalDomainOrigin;
}
~nsAutoInPrincipalDomainOriginSetter() {
--sInPrincipalDomainOrigin;
}
static PRUint32 sInPrincipalDomainOrigin;
};
PRUint32 nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin;
2008-07-28 23:37:58 -07:00
static
nsresult
GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin)
2008-07-28 23:37:58 -07:00
{
if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) {
// Allow a single recursive call to GetPrincipalDomainOrigin, since that
// might be happening on a different principal from the first call. But
// after that, cut off the recursion; it just indicates that something
// we're doing in this method causes us to reenter a security check here.
return NS_ERROR_NOT_AVAILABLE;
}
nsAutoInPrincipalDomainOriginSetter autoSetter;
2008-07-28 23:37:58 -07:00
nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
2008-07-28 23:37:58 -07:00
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
nsCAutoString hostPort;
nsresult rv = uri->GetHostPort(hostPort);
if (NS_SUCCEEDED(rv)) {
nsCAutoString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
}
else {
// Some URIs (e.g., nsSimpleURI) don't support host. Just
// get the full spec.
rv = uri->GetSpec(aOrigin);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
static
nsresult
GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
nsACString& aOrigin)
{
nsCOMPtr<nsIURI> uri;
aPrincipal->GetDomain(getter_AddRefs(uri));
if (!uri) {
aPrincipal->GetURI(getter_AddRefs(uri));
}
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
return GetOriginFromURI(uri, aOrigin);
}
static nsIScriptContext *
GetScriptContext(JSContext *cx)
{
return GetScriptContextFromJSContext(cx);
}
// Callbacks for the JS engine to use to push/pop context principals.
static JSBool
PushPrincipalCallback(JSContext *cx, JSPrincipals *principals)
{
// We should already be in the compartment of the given principal.
MOZ_ASSERT(principals ==
JS_GetCompartmentPrincipals((js::GetContextCompartment(cx))));
// Get the security manager.
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (!ssm) {
return true;
}
// Push the principal.
JSStackFrame *fp = NULL;
nsresult rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp),
nsJSPrincipals::get(principals));
if (NS_FAILED(rv)) {
JS_ReportOutOfMemory(cx);
return false;
}
return true;
}
static JSBool
PopPrincipalCallback(JSContext *cx)
{
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
if (ssm) {
ssm->PopContextPrincipal(cx);
}
return true;
}
inline void SetPendingException(JSContext *cx, const char *aMsg)
{
JSAutoRequest ar(cx);
JS_ReportError(cx, "%s", aMsg);
}
inline void SetPendingException(JSContext *cx, const PRUnichar *aMsg)
{
JSAutoRequest ar(cx);
JS_ReportError(cx, "%hs", aMsg);
}
// DomainPolicy members
#ifdef DEBUG_CAPS_DomainPolicyLifeCycle
PRUint32 DomainPolicy::sObjects=0;
void DomainPolicy::_printPopulationInfo()
{
printf("CAPS.DomainPolicy: Gen. %d, %d DomainPolicy objects.\n",
sGeneration, sObjects);
}
#endif
PRUint32 DomainPolicy::sGeneration = 0;
// Helper class to get stuff from the ClassInfo and not waste extra time with
// virtual method calls for things it has already gotten
class ClassInfoData
{
public:
ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
: mClassInfo(aClassInfo),
mName(const_cast<char *>(aName)),
mDidGetFlags(false),
mMustFreeName(false)
{
}
~ClassInfoData()
{
if (mMustFreeName)
nsMemory::Free(mName);
}
PRUint32 GetFlags()
{
if (!mDidGetFlags) {
if (mClassInfo) {
nsresult rv = mClassInfo->GetFlags(&mFlags);
if (NS_FAILED(rv)) {
mFlags = 0;
}
} else {
mFlags = 0;
}
mDidGetFlags = true;
}
return mFlags;
}
bool IsDOMClass()
{
return !!(GetFlags() & nsIClassInfo::DOM_OBJECT);
}
const char* GetName()
{
if (!mName) {
if (mClassInfo) {
mClassInfo->GetClassDescription(&mName);
}
if (mName) {
mMustFreeName = true;
} else {
mName = const_cast<char *>("UnnamedClass");
}
}
return mName;
}
private:
nsIClassInfo *mClassInfo; // WEAK
PRUint32 mFlags;
char *mName;
bool mDidGetFlags;
bool mMustFreeName;
};
class AutoCxPusher {
public:
AutoCxPusher(nsIJSContextStack *aStack, JSContext *cx)
: mStack(aStack), mContext(cx)
{
if (NS_FAILED(mStack->Push(mContext))) {
mStack = nsnull;
}
}
~AutoCxPusher()
{
if (mStack) {
mStack->Pop(nsnull);
}
}
private:
nsCOMPtr<nsIJSContextStack> mStack;
JSContext *mContext;
};
JSContext *
nsScriptSecurityManager::GetCurrentJSContext()
{
// Get JSContext from stack.
JSContext *cx;
if (NS_FAILED(sJSContextStack->Peek(&cx)))
return nsnull;
return cx;
}
JSContext *
nsScriptSecurityManager::GetSafeJSContext()
{
// Get JSContext from stack.
JSContext *cx;
if (NS_FAILED(sJSContextStack->GetSafeJSContext(&cx)))
return nsnull;
return cx;
}
/* static */
bool
nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
nsIURI* aTargetURI)
{
return NS_SecurityCompareURIs(aSourceURI, aTargetURI, sStrictFileOriginPolicy);
}
// SecurityHashURI is consistent with SecurityCompareURIs because NS_SecurityHashURI
// is consistent with NS_SecurityCompareURIs. See nsNetUtil.h.
PRUint32
nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI)
{
return NS_SecurityHashURI(aURI);
}
NS_IMETHODIMP
nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
nsIPrincipal** aPrincipal)
{
NS_PRECONDITION(aChannel, "Must have channel!");
nsCOMPtr<nsISupports> owner;
aChannel->GetOwner(getter_AddRefs(owner));
if (owner) {
CallQueryInterface(owner, aPrincipal);
if (*aPrincipal) {
return NS_OK;
}
}
// OK, get the principal from the URI. Make sure this does the same thing
// as nsDocument::Reset and nsXULDocument::StartDocumentLoad.
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
return GetCodebasePrincipal(uri, aPrincipal);
}
NS_IMETHODIMP
nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
bool* aIsSystem)
{
*aIsSystem = (aPrincipal == mSystemPrincipal);
return NS_OK;
}
NS_IMETHODIMP_(nsIPrincipal *)
nsScriptSecurityManager::GetCxSubjectPrincipal(JSContext *cx)
{
NS_ASSERTION(cx == GetCurrentJSContext(),
"Uh, cx is not the current JS context!");
nsresult rv = NS_ERROR_FAILURE;
nsIPrincipal *principal = GetSubjectPrincipal(cx, &rv);
if (NS_FAILED(rv))
return nsnull;
return principal;
}
NS_IMETHODIMP_(nsIPrincipal *)
nsScriptSecurityManager::GetCxSubjectPrincipalAndFrame(JSContext *cx, JSStackFrame **fp)
{
NS_ASSERTION(cx == GetCurrentJSContext(),
"Uh, cx is not the current JS context!");
nsresult rv = NS_ERROR_FAILURE;
nsIPrincipal *principal = GetPrincipalAndFrame(cx, fp, &rv);
if (NS_FAILED(rv))
return nsnull;
return principal;
}
NS_IMETHODIMP
nsScriptSecurityManager::PushContextPrincipal(JSContext *cx,
JSStackFrame *fp,
nsIPrincipal *principal)
{
NS_ASSERTION(principal, "Must pass a non-null principal");
ContextPrincipal *cp = new ContextPrincipal(mContextPrincipals, cx, fp,
principal);
if (!cp)
return NS_ERROR_OUT_OF_MEMORY;
mContextPrincipals = cp;
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::PopContextPrincipal(JSContext *cx)
{
NS_ASSERTION(mContextPrincipals->mCx == cx, "Mismatched push/pop");
ContextPrincipal *next = mContextPrincipals->mNext;
delete mContextPrincipals;
mContextPrincipals = next;
return NS_OK;
}
////////////////////
// Policy Storage //
////////////////////
// Table of security levels
static bool
DeleteCapability(nsHashKey *aKey, void *aData, void* closure)
{
NS_Free(aData);
return true;
}
//-- Per-Domain Policy - applies to one or more protocols or hosts
struct DomainEntry
{
DomainEntry(const char* aOrigin,
DomainPolicy* aDomainPolicy) : mOrigin(aOrigin),
mDomainPolicy(aDomainPolicy),
mNext(nsnull)
{
mDomainPolicy->Hold();
}
~DomainEntry()
{
mDomainPolicy->Drop();
}
bool Matches(const char *anOrigin)
{
int len = strlen(anOrigin);
int thisLen = mOrigin.Length();
if (len < thisLen)
return false;
if (mOrigin.RFindChar(':', thisLen-1, 1) != -1)
//-- Policy applies to all URLs of this scheme, compare scheme only
return mOrigin.EqualsIgnoreCase(anOrigin, thisLen);
//-- Policy applies to a particular host; compare domains
if (!mOrigin.Equals(anOrigin + (len - thisLen)))
return false;
if (len == thisLen)
return true;
char charBefore = anOrigin[len-thisLen-1];
return (charBefore == '.' || charBefore == ':' || charBefore == '/');
}
nsCString mOrigin;
DomainPolicy* mDomainPolicy;
DomainEntry* mNext;
#if defined(DEBUG) || defined(DEBUG_CAPS_HACKER)
nsCString mPolicyName_DEBUG;
#endif
};
static bool
DeleteDomainEntry(nsHashKey *aKey, void *aData, void* closure)
{
DomainEntry *entry = (DomainEntry*) aData;
do
{
DomainEntry *next = entry->mNext;
delete entry;
entry = next;
} while (entry);
return true;
}
/////////////////////////////
// nsScriptSecurityManager //
/////////////////////////////
////////////////////////////////////
// Methods implementing ISupports //
////////////////////////////////////
NS_IMPL_ISUPPORTS4(nsScriptSecurityManager,
nsIScriptSecurityManager,
nsIXPCSecurityManager,
nsIChannelEventSink,
nsIObserver)
///////////////////////////////////////////////////
// Methods implementing nsIScriptSecurityManager //
///////////////////////////////////////////////////
///////////////// Security Checks /////////////////
/* static */ JSPrincipals *
nsScriptSecurityManager::ObjectPrincipalFinder(JSObject *aObj)
{
return nsJSPrincipals::get(doGetObjectPrincipal(aObj));
}
JSBool
nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
{
// Get the security manager
nsScriptSecurityManager *ssm =
nsScriptSecurityManager::GetScriptSecurityManager();
NS_ASSERTION(ssm, "Failed to get security manager service");
if (!ssm)
return JS_FALSE;
nsresult rv;
nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
if (NS_FAILED(rv))
return JS_FALSE; // Not just absence of principal, but failure.
if (!subjectPrincipal) {
// See bug 553448 for discussion of this case.
NS_ASSERTION(!JS_GetSecurityCallbacks(js::GetRuntime(cx))->findObjectPrincipals,
"CSP: Should have been able to find subject principal. "
"Reluctantly granting access.");
return JS_TRUE;
}
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
// don't do anything unless there's a CSP
if (!csp)
return JS_TRUE;
bool evalOK = true;
rv = csp->GetAllowsEval(&evalOK);
if (NS_FAILED(rv))
{
NS_WARNING("CSP: failed to get allowsEval");
return JS_TRUE; // fail open to not break sites.
}
if (!evalOK) {
// get the script filename, script sample, and line number
// to log with the violation
nsAutoString fileName;
unsigned lineNum = 0;
NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
JSScript *script;
if (JS_DescribeScriptedCaller(cx, &script, &lineNum)) {
if (const char *file = JS_GetScriptFilename(cx, script)) {
CopyUTF8toUTF16(nsDependentCString(file), fileName);
}
}
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
fileName,
scriptSample,
lineNum);
}
return evalOK;
}
JSBool
nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JSObject *obj,
2010-07-14 23:19:36 -07:00
jsid id, JSAccessMode mode,
jsval *vp)
{
// Get the security manager
nsScriptSecurityManager *ssm =
nsScriptSecurityManager::GetScriptSecurityManager();
NS_ASSERTION(ssm, "Failed to get security manager service");
if (!ssm)
return JS_FALSE;
// Get the object being accessed. We protect these cases:
// 1. The Function.prototype.caller property's value, which might lead
// an attacker up a call-stack to a function or another object from
// a different trust domain.
// 2. A user-defined getter or setter function accessible on another
// trust domain's window or document object.
// *vp can be a primitive, in that case, we use obj as the target
// object.
JSObject* target = JSVAL_IS_PRIMITIVE(*vp) ? obj : JSVAL_TO_OBJECT(*vp);
// Do the same-origin check -- this sets a JS exception if the check fails.
// Pass the parent object's class name, as we have no class-info for it.
nsresult rv =
ssm->CheckPropertyAccess(cx, target, js::GetObjectClass(obj)->name, id,
(mode & JSACC_WRITE) ?
(PRInt32)nsIXPCSecurityManager::ACCESS_SET_PROPERTY :
(PRInt32)nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
if (NS_FAILED(rv))
return JS_FALSE; // Security check failed (XXX was an error reported?)
return JS_TRUE;
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckPropertyAccess(JSContext* cx,
JSObject* aJSObject,
const char* aClassName,
2010-07-14 23:19:36 -07:00
jsid aProperty,
PRUint32 aAction)
{
return CheckPropertyAccessImpl(aAction, nsnull, cx, aJSObject,
nsnull, nsnull, nsnull,
aClassName, aProperty, nsnull);
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckSameOrigin(JSContext* cx,
nsIURI* aTargetURI)
{
nsresult rv;
// Get a context if necessary
if (!cx)
{
cx = GetCurrentJSContext();
if (!cx)
return NS_OK; // No JS context, so allow access
}
// Get a principal from the context
nsIPrincipal* sourcePrincipal = GetSubjectPrincipal(cx, &rv);
if (NS_FAILED(rv))
return rv;
if (!sourcePrincipal)
{
NS_WARNING("CheckSameOrigin called on script w/o principals; should this happen?");
return NS_OK;
}
if (sourcePrincipal == mSystemPrincipal)
{
// This is a system (chrome) script, so allow access
return NS_OK;
}
// Get the original URI from the source principal.
// This has the effect of ignoring any change to document.domain
// which must be done to avoid DNS spoofing (bug 154930)
nsCOMPtr<nsIURI> sourceURI;
sourcePrincipal->GetDomain(getter_AddRefs(sourceURI));
if (!sourceURI) {
sourcePrincipal->GetURI(getter_AddRefs(sourceURI));
NS_ENSURE_TRUE(sourceURI, NS_ERROR_FAILURE);
}
// Compare origins
if (!SecurityCompareURIs(sourceURI, aTargetURI))
{
ReportError(cx, NS_LITERAL_STRING("CheckSameOriginError"), sourceURI, aTargetURI);
return NS_ERROR_DOM_BAD_URI;
}
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
nsIURI* aTargetURI,
bool reportError)
{
if (!SecurityCompareURIs(aSourceURI, aTargetURI))
{
if (reportError) {
ReportError(nsnull, NS_LITERAL_STRING("CheckSameOriginError"),
aSourceURI, aTargetURI);
}
return NS_ERROR_DOM_BAD_URI;
}
return NS_OK;
}
nsresult
nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction,
nsAXPCNativeCallContext* aCallContext,
JSContext* cx, JSObject* aJSObject,
nsISupports* aObj, nsIURI* aTargetURI,
nsIClassInfo* aClassInfo,
2010-07-14 23:19:36 -07:00
const char* aClassName, jsid aProperty,
void** aCachedClassPolicy)
{
nsresult rv;
nsIPrincipal* subjectPrincipal = GetSubjectPrincipal(cx, &rv);
if (NS_FAILED(rv))
return rv;
if (!subjectPrincipal || subjectPrincipal == mSystemPrincipal)
// We have native code or the system principal: just allow access
return NS_OK;
nsCOMPtr<nsIPrincipal> objectPrincipal;
// Hold the class info data here so we don't have to go back to virtual
// methods all the time
ClassInfoData classInfoData(aClassInfo, aClassName);
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
nsCAutoString propertyName;
2010-07-14 23:19:36 -07:00
propertyName.AssignWithConversion((PRUnichar*)IDToString(cx, aProperty));
printf("### CanAccess(%s.%s, %i) ", classInfoData.GetName(),
propertyName.get(), aAction);
#endif
//-- Look up the security policy for this class and subject domain
SecurityLevel securityLevel;
rv = LookupPolicy(subjectPrincipal, classInfoData, aProperty, aAction,
(ClassPolicy**)aCachedClassPolicy, &securityLevel);
if (NS_FAILED(rv))
return rv;
if (securityLevel.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
{
// No policy found for this property so use the default of last resort.
// If we were called from somewhere other than XPConnect
// (no XPC call context), assume this is a DOM class. Otherwise,
// ask the ClassInfo.
if (!aCallContext || classInfoData.IsDOMClass())
securityLevel.level = SCRIPT_SECURITY_SAME_ORIGIN_ACCESS;
else
securityLevel.level = SCRIPT_SECURITY_NO_ACCESS;
}
if (SECURITY_ACCESS_LEVEL_FLAG(securityLevel))
// This flag means securityLevel is allAccess, noAccess, or sameOrigin
{
switch (securityLevel.level)
{
case SCRIPT_SECURITY_NO_ACCESS:
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
printf("noAccess ");
#endif
rv = NS_ERROR_DOM_PROP_ACCESS_DENIED;
break;
case SCRIPT_SECURITY_ALL_ACCESS:
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
printf("allAccess ");
#endif
rv = NS_OK;
break;
case SCRIPT_SECURITY_SAME_ORIGIN_ACCESS:
{
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
printf("sameOrigin ");
#endif
nsCOMPtr<nsIPrincipal> principalHolder;
if(aJSObject)
{
objectPrincipal = doGetObjectPrincipal(aJSObject);
if (!objectPrincipal)
rv = NS_ERROR_DOM_SECURITY_ERR;
}
else if(aTargetURI)
{
if (NS_FAILED(GetCodebasePrincipal(
aTargetURI, getter_AddRefs(objectPrincipal))))
return NS_ERROR_FAILURE;
}
else
{
NS_ERROR("CheckPropertyAccessImpl called without a target object or URL");
return NS_ERROR_FAILURE;
}
if(NS_SUCCEEDED(rv))
rv = CheckSameOriginDOMProp(subjectPrincipal, objectPrincipal,
aAction);
break;
}
default:
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
printf("ERROR ");
#endif
NS_ERROR("Bad Security Level Value");
return NS_ERROR_FAILURE;
}
}
else // if SECURITY_ACCESS_LEVEL_FLAG is false, securityLevel is a capability
{
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
printf("Cap:%s ", securityLevel.capability);
#endif
bool capabilityEnabled = false;
rv = IsCapabilityEnabled(securityLevel.capability, &capabilityEnabled);
if (NS_FAILED(rv) || !capabilityEnabled)
rv = NS_ERROR_DOM_SECURITY_ERR;
else
rv = NS_OK;
}
if (NS_SUCCEEDED(rv))
{
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
printf(" GRANTED.\n");
#endif
return rv;
}
//--See if the object advertises a non-default level of access
// using nsISecurityCheckedComponent
nsCOMPtr<nsISecurityCheckedComponent> checkedComponent =
do_QueryInterface(aObj);
nsXPIDLCString objectSecurityLevel;
if (checkedComponent)
{
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
nsCOMPtr<nsIInterfaceInfo> interfaceInfo;
const nsIID* objIID = nsnull;
rv = aCallContext->GetCalleeWrapper(getter_AddRefs(wrapper));
if (NS_SUCCEEDED(rv) && wrapper)
rv = wrapper->FindInterfaceWithMember(aProperty, getter_AddRefs(interfaceInfo));
if (NS_SUCCEEDED(rv) && interfaceInfo)
rv = interfaceInfo->GetIIDShared(&objIID);
if (NS_SUCCEEDED(rv) && objIID)
{
switch (aAction)
{
case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
checkedComponent->CanGetProperty(objIID,
2010-07-14 23:19:36 -07:00
IDToString(cx, aProperty),
getter_Copies(objectSecurityLevel));
break;
case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
checkedComponent->CanSetProperty(objIID,
2010-07-14 23:19:36 -07:00
IDToString(cx, aProperty),
getter_Copies(objectSecurityLevel));
break;
case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
checkedComponent->CanCallMethod(objIID,
2010-07-14 23:19:36 -07:00
IDToString(cx, aProperty),
getter_Copies(objectSecurityLevel));
}
}
}
rv = CheckXPCPermissions(cx, aObj, aJSObject, subjectPrincipal,
objectSecurityLevel);
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
if(NS_SUCCEEDED(rv))
printf("CheckXPCPerms GRANTED.\n");
else
printf("CheckXPCPerms DENIED.\n");
#endif
if (NS_FAILED(rv)) //-- Security tests failed, access is denied, report error
{
nsAutoString stringName;
switch(aAction)
{
case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
stringName.AssignLiteral("GetPropertyDeniedOrigins");
break;
case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
stringName.AssignLiteral("SetPropertyDeniedOrigins");
break;
case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
stringName.AssignLiteral("CallMethodDeniedOrigins");
}
// Null out objectPrincipal for now, so we don't leak information about
// it. Whenever we can report different error strings to content and
// the UI we can take this out again.
objectPrincipal = nsnull;
NS_ConvertUTF8toUTF16 className(classInfoData.GetName());
nsCAutoString subjectOrigin;
nsCAutoString subjectDomain;
if (!nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin) {
nsCOMPtr<nsIURI> uri, domain;
subjectPrincipal->GetURI(getter_AddRefs(uri));
// Subject can't be system if we failed the security
// check, so |uri| is non-null.
NS_ASSERTION(uri, "How did that happen?");
GetOriginFromURI(uri, subjectOrigin);
subjectPrincipal->GetDomain(getter_AddRefs(domain));
if (domain) {
GetOriginFromURI(domain, subjectDomain);
}
} else {
subjectOrigin.AssignLiteral("the security manager");
}
NS_ConvertUTF8toUTF16 subjectOriginUnicode(subjectOrigin);
NS_ConvertUTF8toUTF16 subjectDomainUnicode(subjectDomain);
nsCAutoString objectOrigin;
nsCAutoString objectDomain;
if (!nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin &&
objectPrincipal) {
nsCOMPtr<nsIURI> uri, domain;
objectPrincipal->GetURI(getter_AddRefs(uri));
if (uri) { // Object principal might be system
GetOriginFromURI(uri, objectOrigin);
}
objectPrincipal->GetDomain(getter_AddRefs(domain));
if (domain) {
GetOriginFromURI(domain, objectDomain);
}
}
NS_ConvertUTF8toUTF16 objectOriginUnicode(objectOrigin);
NS_ConvertUTF8toUTF16 objectDomainUnicode(objectDomain);
nsXPIDLString errorMsg;
const PRUnichar *formatStrings[] =
{
subjectOriginUnicode.get(),
className.get(),
2010-07-14 23:19:36 -07:00
IDToString(cx, aProperty),
objectOriginUnicode.get(),
subjectDomainUnicode.get(),
objectDomainUnicode.get()
};
PRUint32 length = ArrayLength(formatStrings);
// XXXbz Our localization system is stupid and can't handle not showing
// some strings that get passed in. Which means that we have to get
// our length precisely right: it has to be exactly the number of
// strings our format string wants. This means we'll have to move
// strings in the array as needed, sadly...
if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin ||
!objectPrincipal) {
stringName.AppendLiteral("OnlySubject");
length -= 3;
} else {
// default to a length that doesn't include the domains, then
// increase it as needed.
length -= 2;
if (!subjectDomainUnicode.IsEmpty()) {
stringName.AppendLiteral("SubjectDomain");
length += 1;
}
if (!objectDomainUnicode.IsEmpty()) {
stringName.AppendLiteral("ObjectDomain");
length += 1;
if (length != ArrayLength(formatStrings)) {
// We have an object domain but not a subject domain.
// Scoot our string over one slot. See the XXX comment
// above for why we need to do this.
formatStrings[length-1] = formatStrings[length];
}
}
}
// We need to keep our existing failure rv and not override it
// with a likely success code from the following string bundle
// call in order to throw the correct security exception later.
nsresult rv2 = sStrBundle->FormatStringFromName(stringName.get(),
formatStrings,
length,
getter_Copies(errorMsg));
if (NS_FAILED(rv2)) {
// Might just be missing the string... Do our best
errorMsg = stringName;
}
SetPendingException(cx, errorMsg.get());
}
return rv;
}
/* static */
nsresult
nsScriptSecurityManager::CheckSameOriginPrincipal(nsIPrincipal* aSubject,
nsIPrincipal* aObject)
{
/*
** Get origin of subject and object and compare.
*/
if (aSubject == aObject)
return NS_OK;
// Default to false, and change if that turns out wrong.
bool subjectSetDomain = false;
bool objectSetDomain = false;
nsCOMPtr<nsIURI> subjectURI;
nsCOMPtr<nsIURI> objectURI;
aSubject->GetDomain(getter_AddRefs(subjectURI));
if (!subjectURI) {
aSubject->GetURI(getter_AddRefs(subjectURI));
} else {
subjectSetDomain = true;
}
aObject->GetDomain(getter_AddRefs(objectURI));
if (!objectURI) {
aObject->GetURI(getter_AddRefs(objectURI));
} else {
objectSetDomain = true;
}
if (SecurityCompareURIs(subjectURI, objectURI))
{ // If either the subject or the object has changed its principal by
// explicitly setting document.domain then the other must also have
// done so in order to be considered the same origin. This prevents
// DNS spoofing based on document.domain (154930)
// If both or neither explicitly set their domain, allow the access
if (subjectSetDomain == objectSetDomain)
return NS_OK;
}
/*
** Access tests failed, so now report error.
*/
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
// It's important that
//
// CheckSameOriginPrincipal(A, B) == NS_OK
//
// imply
//
// HashPrincipalByOrigin(A) == HashPrincipalByOrigin(B)
//
// if principals A and B could ever be used as keys in a hashtable.
// Violation of this invariant leads to spurious failures of hashtable
// lookups. See bug 454850.
/*static*/ PRUint32
nsScriptSecurityManager::HashPrincipalByOrigin(nsIPrincipal* aPrincipal)
{
nsCOMPtr<nsIURI> uri;
aPrincipal->GetDomain(getter_AddRefs(uri));
if (!uri)
aPrincipal->GetURI(getter_AddRefs(uri));
return SecurityHashURI(uri);
}
nsresult
nsScriptSecurityManager::CheckSameOriginDOMProp(nsIPrincipal* aSubject,
nsIPrincipal* aObject,
PRUint32 aAction)
{
nsresult rv;
bool subsumes;
rv = aSubject->Subsumes(aObject, &subsumes);
if (NS_SUCCEEDED(rv) && !subsumes) {
rv = NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
if (NS_SUCCEEDED(rv))
return NS_OK;
/*
* Content can't ever touch chrome (we check for UniversalXPConnect later)
*/
if (aObject == mSystemPrincipal)
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
/*
** Access tests failed, so now report error.
*/
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
nsresult
nsScriptSecurityManager::LookupPolicy(nsIPrincipal* aPrincipal,
ClassInfoData& aClassData,
2010-07-14 23:19:36 -07:00
jsid aProperty,
PRUint32 aAction,
ClassPolicy** aCachedClassPolicy,
SecurityLevel* result)
{
nsresult rv;
result->level = SCRIPT_SECURITY_UNDEFINED_ACCESS;
DomainPolicy* dpolicy = nsnull;
//-- Initialize policies if necessary
if (mPolicyPrefsChanged)
{
if (!mPrefInitialized) {
rv = InitPrefs();
NS_ENSURE_SUCCESS(rv, rv);
}
rv = InitPolicies();
if (NS_FAILED(rv))
return rv;
}
else
{
aPrincipal->GetSecurityPolicy((void**)&dpolicy);
}
if (!dpolicy && mOriginToPolicyMap)
{
//-- Look up the relevant domain policy, if any
#ifdef DEBUG_CAPS_LookupPolicy
printf("DomainLookup ");
#endif
nsCAutoString origin;
rv = GetPrincipalDomainOrigin(aPrincipal, origin);
NS_ENSURE_SUCCESS(rv, rv);
char *start = origin.BeginWriting();
const char *nextToLastDot = nsnull;
const char *lastDot = nsnull;
const char *colon = nsnull;
char *p = start;
//-- search domain (stop at the end of the string or at the 3rd slash)
for (PRUint32 slashes=0; *p; p++)
{
if (*p == '/' && ++slashes == 3)
{
*p = '\0'; // truncate at 3rd slash
break;
}
if (*p == '.')
{
nextToLastDot = lastDot;
lastDot = p;
}
else if (!colon && *p == ':')
colon = p;
}
nsCStringKey key(nextToLastDot ? nextToLastDot+1 : start);
DomainEntry *de = (DomainEntry*) mOriginToPolicyMap->Get(&key);
if (!de)
{
nsCAutoString scheme(start, colon-start+1);
nsCStringKey schemeKey(scheme);
de = (DomainEntry*) mOriginToPolicyMap->Get(&schemeKey);
}
while (de)
{
if (de->Matches(start))
{
dpolicy = de->mDomainPolicy;
break;
}
de = de->mNext;
}
if (!dpolicy)
dpolicy = mDefaultPolicy;
aPrincipal->SetSecurityPolicy((void*)dpolicy);
}
ClassPolicy* cpolicy = nsnull;
if ((dpolicy == mDefaultPolicy) && aCachedClassPolicy)
{
// No per-domain policy for this principal (the more common case)
// so look for a cached class policy from the object wrapper
cpolicy = *aCachedClassPolicy;
}
if (!cpolicy)
{ //-- No cached policy for this class, need to look it up
#ifdef DEBUG_CAPS_LookupPolicy
printf("ClassLookup ");
#endif
cpolicy = static_cast<ClassPolicy*>
(PL_DHashTableOperate(dpolicy,
aClassData.GetName(),
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_FREE(cpolicy))
cpolicy = NO_POLICY_FOR_CLASS;
if ((dpolicy == mDefaultPolicy) && aCachedClassPolicy)
*aCachedClassPolicy = cpolicy;
}
2010-07-14 23:19:36 -07:00
NS_ASSERTION(JSID_IS_INT(aProperty) || JSID_IS_OBJECT(aProperty) ||
JSID_IS_STRING(aProperty), "Property must be a valid id");
// Only atomized strings are stored in the policies' hash tables.
if (!JSID_IS_STRING(aProperty))
return NS_OK;
JSString *propertyKey = JSID_TO_STRING(aProperty);
// We look for a PropertyPolicy in the following places:
// 1) The ClassPolicy for our class we got from our DomainPolicy
// 2) The mWildcardPolicy of our DomainPolicy
// 3) The ClassPolicy for our class we got from mDefaultPolicy
// 4) The mWildcardPolicy of our mDefaultPolicy
PropertyPolicy* ppolicy = nsnull;
if (cpolicy != NO_POLICY_FOR_CLASS)
{
ppolicy = static_cast<PropertyPolicy*>
(PL_DHashTableOperate(cpolicy->mPolicy,
2010-07-14 23:19:36 -07:00
propertyKey,
PL_DHASH_LOOKUP));
}
// If there is no class policy for this property, and we have a wildcard
// policy, try that.
if (dpolicy->mWildcardPolicy &&
(!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy)))
{
ppolicy =
static_cast<PropertyPolicy*>
(PL_DHashTableOperate(dpolicy->mWildcardPolicy->mPolicy,
2010-07-14 23:19:36 -07:00
propertyKey,
PL_DHASH_LOOKUP));
}
// If dpolicy is not the defauly policy and there's no class or wildcard
// policy for this property, check the default policy for this class and
// the default wildcard policy
if (dpolicy != mDefaultPolicy &&
(!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy)))
{
cpolicy = static_cast<ClassPolicy*>
(PL_DHashTableOperate(mDefaultPolicy,
aClassData.GetName(),
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(cpolicy))
{
ppolicy =
static_cast<PropertyPolicy*>
(PL_DHashTableOperate(cpolicy->mPolicy,
2010-07-14 23:19:36 -07:00
propertyKey,
PL_DHASH_LOOKUP));
}
if ((!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy)) &&
mDefaultPolicy->mWildcardPolicy)
{
ppolicy =
static_cast<PropertyPolicy*>
(PL_DHashTableOperate(mDefaultPolicy->mWildcardPolicy->mPolicy,
2010-07-14 23:19:36 -07:00
propertyKey,
PL_DHASH_LOOKUP));
}
}
if (!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy))
return NS_OK;
// Get the correct security level from the property policy
if (aAction == nsIXPCSecurityManager::ACCESS_SET_PROPERTY)
*result = ppolicy->mSet;
else
*result = ppolicy->mGet;
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
{
// Get principal of currently executing script.
nsresult rv;
nsIPrincipal* principal = GetSubjectPrincipal(cx, &rv);
if (NS_FAILED(rv))
return rv;
// Native code can load all URIs.
if (!principal)
return NS_OK;
rv = CheckLoadURIWithPrincipal(principal, aURI,
nsIScriptSecurityManager::STANDARD);
if (NS_SUCCEEDED(rv)) {
// OK to load
return NS_OK;
}
// See if we're attempting to load a file: URI. If so, let a
// UniversalXPConnect capability trump the above check.
bool isFile = false;
bool isRes = false;
if (NS_FAILED(aURI->SchemeIs("file", &isFile)) ||
NS_FAILED(aURI->SchemeIs("resource", &isRes)))
return NS_ERROR_FAILURE;
if (isFile || isRes)
{
bool enabled;
if (NS_FAILED(IsCapabilityEnabled("UniversalXPConnect", &enabled)))
return NS_ERROR_FAILURE;
if (enabled)
return NS_OK;
}
// Report error.
nsCAutoString spec;
if (NS_FAILED(aURI->GetAsciiSpec(spec)))
return NS_ERROR_FAILURE;
nsCAutoString msg("Access to '");
msg.Append(spec);
msg.AppendLiteral("' from script denied");
SetPendingException(cx, msg.get());
return NS_ERROR_DOM_BAD_URI;
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckLoadURI(nsIURI *aSourceURI, nsIURI *aTargetURI,
PRUint32 aFlags)
{
// FIXME: bug 327244 -- this function should really die... Really truly.
NS_PRECONDITION(aSourceURI, "CheckLoadURI called with null source URI");
NS_ENSURE_ARG_POINTER(aSourceURI);
// Note: this is not _quite_ right if aSourceURI has
// NS_NULLPRINCIPAL_SCHEME, but we'll just extract the scheme in
// CheckLoadURIWithPrincipal anyway, so this is good enough. This method
// really needs to go away....
nsCOMPtr<nsIPrincipal> sourcePrincipal;
nsresult rv = CreateCodebasePrincipal(aSourceURI,
getter_AddRefs(sourcePrincipal));
NS_ENSURE_SUCCESS(rv, rv);
return CheckLoadURIWithPrincipal(sourcePrincipal, aTargetURI, aFlags);
}
/**
* Helper method to handle cases where a flag passed to
* CheckLoadURIWithPrincipal means denying loading if the given URI has certain
* nsIProtocolHandler flags set.
* @return if success, access is allowed. Otherwise, deny access
*/
static nsresult
DenyAccessIfURIHasFlags(nsIURI* aURI, PRUint32 aURIFlags)
{
NS_PRECONDITION(aURI, "Must have URI!");
bool uriHasFlags;
nsresult rv =
NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (uriHasFlags) {
return NS_ERROR_DOM_BAD_URI;
}
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
nsIURI *aTargetURI,
PRUint32 aFlags)
{
NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
// If someone passes a flag that we don't understand, we should
// fail, because they may need a security check that we don't
// provide.
NS_ENSURE_FALSE(aFlags & ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
nsIScriptSecurityManager::ALLOW_CHROME |
nsIScriptSecurityManager::DISALLOW_SCRIPT |
nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL),
NS_ERROR_UNEXPECTED);
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_ARG_POINTER(aTargetURI);
// If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which
// would do such inheriting. That would be URIs that do not have their own
// security context. We do this even for the system principal.
if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) {
nsresult rv =
DenyAccessIfURIHasFlags(aTargetURI,
nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT);
NS_ENSURE_SUCCESS(rv, rv);
}
if (aPrincipal == mSystemPrincipal) {
// Allow access
return NS_OK;
}
nsCOMPtr<nsIURI> sourceURI;
aPrincipal->GetURI(getter_AddRefs(sourceURI));
if (!sourceURI) {
NS_ERROR("Non-system principals passed to CheckLoadURIWithPrincipal "
"must have a URI!");
return NS_ERROR_UNEXPECTED;
}
// Automatic loads are not allowed from certain protocols.
if (aFlags & nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) {
nsresult rv =
DenyAccessIfURIHasFlags(sourceURI,
nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT);
NS_ENSURE_SUCCESS(rv, rv);
}
// If either URI is a nested URI, get the base URI
nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI);
nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
//-- get the target scheme
nsCAutoString targetScheme;
nsresult rv = targetBaseURI->GetScheme(targetScheme);
if (NS_FAILED(rv)) return rv;
//-- Some callers do not allow loading javascript:
if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) &&
targetScheme.EqualsLiteral("javascript"))
{
return NS_ERROR_DOM_BAD_URI;
}
NS_NAMED_LITERAL_STRING(errorTag, "CheckLoadURIError");
// Check for uris that are only loadable by principals that subsume them
bool hasFlags;
rv = NS_URIChainHasFlags(targetBaseURI,
nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) {
return aPrincipal->CheckMayLoad(targetBaseURI, true);
}
//-- get the source scheme
nsCAutoString sourceScheme;
rv = sourceBaseURI->GetScheme(sourceScheme);
if (NS_FAILED(rv)) return rv;
2008-03-27 20:46:15 -07:00
if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
// A null principal can target its own URI.
if (sourceURI == aTargetURI) {
return NS_OK;
}
}
else if (targetScheme.Equals(sourceScheme,
nsCaseInsensitiveCStringComparator()))
{
// every scheme can access another URI from the same scheme,
// as long as they don't represent null principals.
return NS_OK;
}
// If the schemes don't match, the policy is specified by the protocol
// flags on the target URI. Note that the order of policy checks here is
// very important! We start from most restrictive and work our way down.
// Note that since we're working with the innermost URI, we can just use
// the methods that work on chains of nested URIs and they will only look
// at the flags for our one URI.
// Check for system target URI
rv = DenyAccessIfURIHasFlags(targetBaseURI,
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
if (NS_FAILED(rv)) {
// Deny access, since the origin principal is not system
ReportError(nsnull, errorTag, sourceURI, aTargetURI);
return rv;
}
// Check for chrome target URI
rv = NS_URIChainHasFlags(targetBaseURI,
nsIProtocolHandler::URI_IS_UI_RESOURCE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) {
if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
if (!targetScheme.EqualsLiteral("chrome")) {
// for now don't change behavior for resource: or moz-icon:
return NS_OK;
}
// allow load only if chrome package is whitelisted
nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(
NS_CHROMEREGISTRY_CONTRACTID));
if (reg) {
bool accessAllowed = false;
reg->AllowContentToAccess(targetBaseURI, &accessAllowed);
if (accessAllowed) {
return NS_OK;
}
}
}
// resource: and chrome: are equivalent, securitywise
// That's bogus!! Fix this. But watch out for
// the view-source stylesheet?
bool sourceIsChrome;
rv = NS_URIChainHasFlags(sourceBaseURI,
nsIProtocolHandler::URI_IS_UI_RESOURCE,
&sourceIsChrome);
NS_ENSURE_SUCCESS(rv, rv);
if (sourceIsChrome) {
return NS_OK;
}
ReportError(nsnull, errorTag, sourceURI, aTargetURI);
return NS_ERROR_DOM_BAD_URI;
}
// Check for target URI pointing to a file
rv = NS_URIChainHasFlags(targetBaseURI,
nsIProtocolHandler::URI_IS_LOCAL_FILE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) {
// resource: and chrome: are equivalent, securitywise
// That's bogus!! Fix this. But watch out for
// the view-source stylesheet?
bool sourceIsChrome;
rv = NS_URIChainHasFlags(sourceURI,
nsIProtocolHandler::URI_IS_UI_RESOURCE,
&sourceIsChrome);
NS_ENSURE_SUCCESS(rv, rv);
if (sourceIsChrome) {
return NS_OK;
}
// Now check capability policies
static const char loadURIPrefGroup[] = "checkloaduri";
ClassInfoData nameData(nsnull, loadURIPrefGroup);
SecurityLevel secLevel;
rv = LookupPolicy(aPrincipal, nameData, sEnabledID,
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
nsnull, &secLevel);
if (NS_SUCCEEDED(rv) && secLevel.level == SCRIPT_SECURITY_ALL_ACCESS)
{
// OK for this site!
return NS_OK;
}
ReportError(nsnull, errorTag, sourceURI, aTargetURI);
return NS_ERROR_DOM_BAD_URI;
}
// OK, everyone is allowed to load this, since unflagged handlers are
// deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
// need to warn. At some point we'll want to make this warning into an
// error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
rv = NS_URIChainHasFlags(targetBaseURI,
nsIProtocolHandler::URI_LOADABLE_BY_ANYONE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (!hasFlags) {
nsXPIDLString message;
NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
const PRUnichar* formatStrings[] = { ucsTargetScheme.get() };
rv = sStrBundle->
FormatStringFromName(NS_LITERAL_STRING("ProtocolFlagError").get(),
formatStrings,
ArrayLength(formatStrings),
getter_Copies(message));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIConsoleService> console(
do_GetService("@mozilla.org/consoleservice;1"));
NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
console->LogStringMessage(message.get());
#ifdef DEBUG
fprintf(stderr, "%s\n", NS_ConvertUTF16toUTF8(message).get());
#endif
}
}
return NS_OK;
}
nsresult
nsScriptSecurityManager::ReportError(JSContext* cx, const nsAString& messageTag,
nsIURI* aSource, nsIURI* aTarget)
{
nsresult rv;
NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
// Get the source URL spec
nsCAutoString sourceSpec;
rv = aSource->GetAsciiSpec(sourceSpec);
NS_ENSURE_SUCCESS(rv, rv);
// Get the target URL spec
nsCAutoString targetSpec;
rv = aTarget->GetAsciiSpec(targetSpec);
NS_ENSURE_SUCCESS(rv, rv);
// Localize the error message
nsXPIDLString message;
NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec);
NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
const PRUnichar *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() };
rv = sStrBundle->FormatStringFromName(PromiseFlatString(messageTag).get(),
formatStrings,
ArrayLength(formatStrings),
getter_Copies(message));
NS_ENSURE_SUCCESS(rv, rv);
// If a JS context was passed in, set a JS exception.
// Otherwise, print the error message directly to the JS console
// and to standard output
if (cx)
{
SetPendingException(cx, message.get());
}
else // Print directly to the console
{
nsCOMPtr<nsIConsoleService> console(
do_GetService("@mozilla.org/consoleservice;1"));
NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
console->LogStringMessage(message.get());
#ifdef DEBUG
fprintf(stderr, "%s\n", NS_LossyConvertUTF16toASCII(message).get());
#endif
}
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckLoadURIStr(const nsACString& aSourceURIStr,
const nsACString& aTargetURIStr,
PRUint32 aFlags)
{
// FIXME: bug 327244 -- this function should really die... Really truly.
nsCOMPtr<nsIURI> source;
nsresult rv = NS_NewURI(getter_AddRefs(source), aSourceURIStr,
nsnull, nsnull, sIOService);
NS_ENSURE_SUCCESS(rv, rv);
// Note: this is not _quite_ right if aSourceURI has
// NS_NULLPRINCIPAL_SCHEME, but we'll just extract the scheme in
// CheckLoadURIWithPrincipal anyway, so this is good enough. This method
// really needs to go away....
nsCOMPtr<nsIPrincipal> sourcePrincipal;
rv = CreateCodebasePrincipal(source,
getter_AddRefs(sourcePrincipal));
NS_ENSURE_SUCCESS(rv, rv);
return CheckLoadURIStrWithPrincipal(sourcePrincipal, aTargetURIStr,
aFlags);
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
const nsACString& aTargetURIStr,
PRUint32 aFlags)
{
nsresult rv;
nsCOMPtr<nsIURI> target;
rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr,
nsnull, nsnull, sIOService);
NS_ENSURE_SUCCESS(rv, rv);
rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
NS_ENSURE_SUCCESS(rv, rv);
// Now start testing fixup -- since aTargetURIStr is a string, not
// an nsIURI, we may well end up fixing it up before loading.
// Note: This needs to stay in sync with the nsIURIFixup api.
nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
if (!fixup) {
return rv;
}
PRUint32 flags[] = {
nsIURIFixup::FIXUP_FLAG_NONE,
nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI
};
for (PRUint32 i = 0; i < ArrayLength(flags); ++i) {
rv = fixup->CreateFixupURI(aTargetURIStr, flags[i],
getter_AddRefs(target));
NS_ENSURE_SUCCESS(rv, rv);
rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
NS_IMETHODIMP
nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
void *aTargetObj)
{
// This check is called for event handlers
nsresult rv;
nsIPrincipal* subject =
GetFunctionObjectPrincipal(aCx, (JSObject *)aFunObj, nsnull, &rv);
// If subject is null, get a principal from the function object's scope.
if (NS_SUCCEEDED(rv) && !subject)
{
#ifdef DEBUG
{
JS_ASSERT(JS_ObjectIsFunction(aCx, (JSObject *)aFunObj));
2011-10-12 08:32:42 -07:00
JSFunction *fun = JS_GetObjectFunction((JSObject *)aFunObj);
JSScript *script = JS_GetFunctionScript(aCx, fun);
NS_ASSERTION(!script, "Null principal for non-native function!");
}
#endif
subject = doGetObjectPrincipal((JSObject*)aFunObj);
}
if (!subject)
return NS_ERROR_FAILURE;
if (subject == mSystemPrincipal)
// This is the system principal: just allow access
return NS_OK;
// Check if the principal the function was compiled under is
// allowed to execute scripts.
bool result;
rv = CanExecuteScripts(aCx, subject, &result);
if (NS_FAILED(rv))
return rv;
if (!result)
return NS_ERROR_DOM_SECURITY_ERR;
/*
** Get origin of subject and object and compare.
*/
JSObject* obj = (JSObject*)aTargetObj;
nsIPrincipal* object = doGetObjectPrincipal(obj);
if (!object)
return NS_ERROR_FAILURE;
bool subsumes;
rv = subject->Subsumes(object, &subsumes);
if (NS_SUCCEEDED(rv) && !subsumes) {
rv = NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
return rv;
}
NS_IMETHODIMP
nsScriptSecurityManager::CanExecuteScripts(JSContext* cx,
nsIPrincipal *aPrincipal,
bool *result)
{
*result = false;
if (aPrincipal == mSystemPrincipal)
{
// Even if JavaScript is disabled, we must still execute system scripts
*result = true;
return NS_OK;
}
//-- See if the current window allows JS execution
nsIScriptContext *scriptContext = GetScriptContext(cx);
if (!scriptContext) return NS_ERROR_FAILURE;
if (!scriptContext->GetScriptsEnabled()) {
// No scripting on this context, folks
*result = false;
return NS_OK;
}
nsIScriptGlobalObject *sgo = scriptContext->GetGlobalObject();
if (!sgo) {
return NS_ERROR_FAILURE;
}
// window can be null here if we're running with a non-DOM window
// as the script global (i.e. a XUL prototype document).
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(sgo);
nsCOMPtr<nsIDocShell> docshell;
nsresult rv;
if (window) {
docshell = window->GetDocShell();
}
if (docshell) {
rv = docshell->GetCanExecuteScripts(result);
if (NS_FAILED(rv)) return rv;
if (!*result) return NS_OK;
}
// OK, the docshell doesn't have script execution explicitly disabled.
// Check whether our URI is an "about:" URI that allows scripts. If it is,
// we need to allow JS to run. In this case, don't apply the JS enabled
// pref or policies. On failures, just press on and don't do this special
// case.
nsCOMPtr<nsIURI> principalURI;
aPrincipal->GetURI(getter_AddRefs(principalURI));
if (!principalURI) {
// Broken principal of some sort. Disallow.
*result = false;
return NS_ERROR_UNEXPECTED;
}
bool isAbout;
rv = principalURI->SchemeIs("about", &isAbout);
if (NS_SUCCEEDED(rv) && isAbout) {
nsCOMPtr<nsIAboutModule> module;
rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
if (NS_SUCCEEDED(rv)) {
PRUint32 flags;
rv = module->GetURIFlags(principalURI, &flags);
if (NS_SUCCEEDED(rv) &&
(flags & nsIAboutModule::ALLOW_SCRIPT)) {
*result = true;
return NS_OK;
}
}
}
*result = mIsJavaScriptEnabled;
if (!*result)
return NS_OK; // Do not run scripts
//-- Check for a per-site policy
static const char jsPrefGroupName[] = "javascript";
ClassInfoData nameData(nsnull, jsPrefGroupName);
SecurityLevel secLevel;
rv = LookupPolicy(aPrincipal, nameData, sEnabledID,
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
nsnull, &secLevel);
if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS)
{
*result = false;
return rv;
}
//-- Nobody vetoed, so allow the JS to run.
*result = true;
return NS_OK;
}
///////////////// Principals ///////////////////////
NS_IMETHODIMP
nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **aSubjectPrincipal)
{
nsresult rv;
*aSubjectPrincipal = doGetSubjectPrincipal(&rv);
if (NS_SUCCEEDED(rv))
NS_IF_ADDREF(*aSubjectPrincipal);
return rv;
}
nsIPrincipal*
nsScriptSecurityManager::doGetSubjectPrincipal(nsresult* rv)
{
NS_PRECONDITION(rv, "Null out param");
JSContext *cx = GetCurrentJSContext();
if (!cx)
{
*rv = NS_OK;
return nsnull;
}
return GetSubjectPrincipal(cx, rv);
}
NS_IMETHODIMP
nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
{
NS_ADDREF(*result = mSystemPrincipal);
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::SubjectPrincipalIsSystem(bool* aIsSystem)
{
NS_ENSURE_ARG_POINTER(aIsSystem);
*aIsSystem = false;
if (!mSystemPrincipal)
return NS_OK;
nsCOMPtr<nsIPrincipal> subject;
nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject));
if (NS_FAILED(rv))
return rv;
if(!subject)
{
// No subject principal means no JS is running;
// this is the equivalent of system principal code
*aIsSystem = true;
return NS_OK;
}
return mSystemPrincipal->Equals(subject, aIsSystem);
}
NS_IMETHODIMP
nsScriptSecurityManager::GetCertificatePrincipal(const nsACString& aCertFingerprint,
const nsACString& aSubjectName,
const nsACString& aPrettyName,
nsISupports* aCertificate,
nsIURI* aURI,
nsIPrincipal **result)
{
*result = nsnull;
NS_ENSURE_ARG(!aCertFingerprint.IsEmpty() &&
!aSubjectName.IsEmpty() &&
aCertificate);
return DoGetCertificatePrincipal(aCertFingerprint, aSubjectName,
aPrettyName, aCertificate, aURI, true,
result);
}
nsresult
nsScriptSecurityManager::DoGetCertificatePrincipal(const nsACString& aCertFingerprint,
const nsACString& aSubjectName,
const nsACString& aPrettyName,
nsISupports* aCertificate,
nsIURI* aURI,
bool aModifyTable,
nsIPrincipal **result)
{
NS_ENSURE_ARG(!aCertFingerprint.IsEmpty());
// Create a certificate principal out of the certificate ID
// and URI given to us. We will use this principal to test
// equality when doing our hashtable lookups below.
nsRefPtr<nsPrincipal> certificate = new nsPrincipal();
if (!certificate)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = certificate->Init(aCertFingerprint, aSubjectName,
aPrettyName, aCertificate, aURI);
NS_ENSURE_SUCCESS(rv, rv);
// Check to see if we already have this principal.
nsCOMPtr<nsIPrincipal> fromTable;
mPrincipals.Get(certificate, getter_AddRefs(fromTable));
if (fromTable) {
// Bingo. We found the certificate in the table, which means
// that it has escalated privileges.
if (aModifyTable) {
// Make sure this principal has names, so if we ever go to save it
// we'll save them. If we get a name mismatch here we'll throw,
// but that's desirable.
rv = static_cast<nsPrincipal*>
(static_cast<nsIPrincipal*>(fromTable))
->EnsureCertData(aSubjectName, aPrettyName, aCertificate);
if (NS_FAILED(rv)) {
// We have a subject name mismatch for the same cert id.
// Hand back the |certificate| object we created and don't give
// it any rights from the table.
NS_ADDREF(*result = certificate);
return NS_OK;
}
}
if (!aURI) {
// We were asked to just get the base certificate, so output
// what we have in the table.
certificate = static_cast<nsPrincipal*>
(static_cast<nsIPrincipal*>
(fromTable));
} else {
// We found a certificate and now need to install a codebase
// on it. We don't want to modify the principal in the hash
// table, so create a new principal and clone the pertinent
// things.
nsXPIDLCString prefName;
nsXPIDLCString id;
nsXPIDLCString subjectName;
nsXPIDLCString granted;
nsXPIDLCString denied;
bool isTrusted;
rv = fromTable->GetPreferences(getter_Copies(prefName),
getter_Copies(id),
getter_Copies(subjectName),
getter_Copies(granted),
getter_Copies(denied),
&isTrusted);
// XXXbz assert something about subjectName and aSubjectName here?
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(!isTrusted, "Shouldn't have isTrusted true here");
certificate = new nsPrincipal();
if (!certificate)
return NS_ERROR_OUT_OF_MEMORY;
rv = certificate->InitFromPersistent(prefName, id,
subjectName, aPrettyName,
granted, denied,
aCertificate,
true, false);
if (NS_FAILED(rv))
return rv;
certificate->SetURI(aURI);
}
}
}
NS_ADDREF(*result = certificate);
return rv;
}
nsresult
nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, nsIPrincipal **result)
{
// I _think_ it's safe to not create null principals here based on aURI.
// At least all the callers would do the right thing in those cases, as far
// as I can tell. --bz
nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
if (uriPrinc) {
nsCOMPtr<nsIPrincipal> principal;
uriPrinc->GetPrincipal(getter_AddRefs(principal));
if (!principal || principal == mSystemPrincipal) {
return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
}
principal.forget(result);
return NS_OK;
}
nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
if (!codebase)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = codebase->Init(EmptyCString(), EmptyCString(),
EmptyCString(), nsnull, aURI);
if (NS_FAILED(rv))
return rv;
NS_ADDREF(*result = codebase);
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::GetCodebasePrincipal(nsIURI *aURI,
nsIPrincipal **result)
{
NS_ENSURE_ARG(aURI);
bool inheritsPrincipal;
nsresult rv =
NS_URIChainHasFlags(aURI,
nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
&inheritsPrincipal);
if (NS_FAILED(rv) || inheritsPrincipal) {
return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
}
nsCOMPtr<nsIPrincipal> principal;
rv = CreateCodebasePrincipal(aURI, getter_AddRefs(principal));
if (NS_FAILED(rv)) return rv;
if (mPrincipals.Count() > 0)
{
//-- Check to see if we already have this principal.
nsCOMPtr<nsIPrincipal> fromTable;
mPrincipals.Get(principal, getter_AddRefs(fromTable));
if (fromTable) {
// We found an existing codebase principal. But it might have a
// generic codebase for this origin on it. Install our particular
// codebase.
// XXXbz this is kinda similar to the code in
// GetCertificatePrincipal, but just ever so slightly different.
// Oh, well.
nsXPIDLCString prefName;
nsXPIDLCString id;
nsXPIDLCString subjectName;
nsXPIDLCString granted;
nsXPIDLCString denied;
bool isTrusted;
rv = fromTable->GetPreferences(getter_Copies(prefName),
getter_Copies(id),
getter_Copies(subjectName),
getter_Copies(granted),
getter_Copies(denied),
&isTrusted);
if (NS_SUCCEEDED(rv)) {
nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
if (!codebase)
return NS_ERROR_OUT_OF_MEMORY;
rv = codebase->InitFromPersistent(prefName, id,
subjectName, EmptyCString(),
granted, denied,
nsnull, false,
isTrusted);
if (NS_FAILED(rv))
return rv;
codebase->SetURI(aURI);
principal = codebase;
}
}
}
NS_IF_ADDREF(*result = principal);
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::GetPrincipalFromContext(JSContext *cx,
nsIPrincipal **result)
{
*result = nsnull;
nsIScriptContextPrincipal* scp =
GetScriptContextPrincipalFromJSContext(cx);
if (!scp)
{
return NS_ERROR_FAILURE;
}
nsIScriptObjectPrincipal* globalData = scp->GetObjectPrincipal();
if (globalData)
NS_IF_ADDREF(*result = globalData->GetPrincipal());
return NS_OK;
}
// static
nsIPrincipal*
nsScriptSecurityManager::GetScriptPrincipal(JSScript *script,
nsresult* rv)
{
NS_PRECONDITION(rv, "Null out param");
*rv = NS_OK;
if (!script)
{
return nsnull;
}
JSPrincipals *jsp = JS_GetScriptPrincipals(script);
if (!jsp) {
*rv = NS_ERROR_FAILURE;
NS_ERROR("Script compiled without principals!");
return nsnull;
}
return nsJSPrincipals::get(jsp);
}
// static
nsIPrincipal*
nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
JSObject *obj,
JSStackFrame *fp,
nsresult *rv)
{
NS_PRECONDITION(rv, "Null out param");
*rv = NS_OK;
if (!JS_ObjectIsFunction(cx, obj))
{
// Protect against pseudo-functions (like SJOWs).
nsIPrincipal *result = doGetObjectPrincipal(obj);
if (!result)
*rv = NS_ERROR_FAILURE;
return result;
}
2011-10-12 08:32:42 -07:00
JSFunction *fun = JS_GetObjectFunction(obj);
JSScript *script = JS_GetFunctionScript(cx, fun);
if (!script)
{
// A native function: skip it in order to find its scripted caller.
return nsnull;
}
JSScript *frameScript = fp ? JS_GetFrameScript(cx, fp) : nsnull;
if (frameScript && frameScript != script)
{
// There is a frame script, and it's different from the
// function script. In this case we're dealing with either
// an eval or a Script object, and in these cases the
// principal we want is in the frame's script, not in the
// function's script. The function's script is where the
// eval-calling code came from, not where the eval or new
// Script object came from, and we want the principal of
// the eval function object or new Script object.
script = frameScript;
}
2011-10-21 14:31:01 -07:00
else if (!js::IsOriginalScriptFunction(fun))
{
// Here, obj is a cloned function object. In this case, the
// clone's prototype may have been precompiled from brutally
// shared chrome, or else it is a lambda or nested function.
// The general case here is a function compiled against a
// different scope than the one it is parented by at runtime,
// hence the creation of a clone to carry the correct scope
// chain linkage.
//
// Since principals follow scope, we must get the object
// principal from the clone's scope chain. There are no
// reliable principals compiled into the function itself.
nsIPrincipal *result = doGetObjectPrincipal(obj);
if (!result)
*rv = NS_ERROR_FAILURE;
return result;
}
return GetScriptPrincipal(script, rv);
}
nsIPrincipal*
nsScriptSecurityManager::GetFramePrincipal(JSContext *cx,
JSStackFrame *fp,
nsresult *rv)
{
NS_PRECONDITION(rv, "Null out param");
JSObject *obj = JS_GetFrameFunctionObject(cx, fp);
if (!obj)
{
// Must be in a top-level script. Get principal from the script.
JSScript *script = JS_GetFrameScript(cx, fp);
return GetScriptPrincipal(script, rv);
}
nsIPrincipal* result = GetFunctionObjectPrincipal(cx, obj, fp, rv);
#ifdef DEBUG
if (NS_SUCCEEDED(*rv) && !result)
{
2011-10-12 08:32:42 -07:00
JSFunction *fun = JS_GetObjectFunction(obj);
JSScript *script = JS_GetFunctionScript(cx, fun);
NS_ASSERTION(!script, "Null principal for non-native function!");
}
#endif
return result;
}
nsIPrincipal*
nsScriptSecurityManager::GetPrincipalAndFrame(JSContext *cx,
JSStackFrame **frameResult,
nsresult* rv)
{
NS_PRECONDITION(rv, "Null out param");
//-- If there's no principal on the stack, look at the global object
// and return the innermost frame for annotations.
*rv = NS_OK;
if (cx)
{
JSStackFrame *target = nsnull;
nsIPrincipal *targetPrincipal = nsnull;
for (ContextPrincipal *cp = mContextPrincipals; cp; cp = cp->mNext)
{
if (cp->mCx == cx)
{
target = cp->mFp;
targetPrincipal = cp->mPrincipal;
break;
}
}
// Get principals from innermost JavaScript frame.
JSStackFrame *fp = nsnull; // tell JS_FrameIterator to start at innermost
for (fp = JS_FrameIterator(cx, &fp); fp; fp = JS_FrameIterator(cx, &fp))
{
if (fp == target)
break;
nsIPrincipal* result = GetFramePrincipal(cx, fp, rv);
if (result)
{
NS_ASSERTION(NS_SUCCEEDED(*rv), "Weird return");
*frameResult = fp;
return result;
}
}
// If targetPrincipal is non-null, then it means that someone wants to
// clamp the principals on this context to this principal. Note that
// fp might not equal target here (fp might be null) because someone
// could have set aside the frame chain in the meantime.
if (targetPrincipal)
{
if (fp && fp == target)
{
*frameResult = fp;
}
else
{
JSStackFrame *inner = nsnull;
*frameResult = JS_FrameIterator(cx, &inner);
}
return targetPrincipal;
}
nsIScriptContextPrincipal* scp =
GetScriptContextPrincipalFromJSContext(cx);
if (scp)
{
nsIScriptObjectPrincipal* globalData = scp->GetObjectPrincipal();
if (!globalData)
{
*rv = NS_ERROR_FAILURE;
return nsnull;
}
// Note that we're not in a loop or anything, and nothing comes
// after this point in the function, so we can just return here.
nsIPrincipal* result = globalData->GetPrincipal();
if (result)
{
JSStackFrame *inner = nsnull;
*frameResult = JS_FrameIterator(cx, &inner);
return result;
}
}
}
return nsnull;
}
nsIPrincipal*
nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
nsresult* rv)
{
NS_PRECONDITION(rv, "Null out param");
JSStackFrame *fp;
return GetPrincipalAndFrame(cx, &fp, rv);
}
NS_IMETHODIMP
nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
nsIPrincipal **result)
{
*result = doGetObjectPrincipal(aObj);
if (!*result)
return NS_ERROR_FAILURE;
NS_ADDREF(*result);
return NS_OK;
}
// static
nsIPrincipal*
nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
#ifdef DEBUG
, bool aAllowShortCircuit
#endif
)
{
2008-01-29 13:11:24 -08:00
NS_ASSERTION(aObj, "Bad call to doGetObjectPrincipal()!");
nsIPrincipal* result = nsnull;
#ifdef DEBUG
JSObject* origObj = aObj;
#endif
js::Class *jsClass = js::GetObjectClass(aObj);
// A common case seen in this code is that we enter this function
// with aObj being a Function object, whose parent is a Call
// object. Neither of those have object principals, so we can skip
// those objects here before we enter the below loop. That way we
// avoid wasting time checking properties of their classes etc in
// the loop.
if (jsClass == &js::FunctionClass) {
aObj = js::GetObjectParent(aObj);
if (!aObj)
return nsnull;
jsClass = js::GetObjectClass(aObj);
if (jsClass == &js::CallClass) {
aObj = js::GetObjectParentMaybeScope(aObj);
if (!aObj)
return nsnull;
jsClass = js::GetObjectClass(aObj);
}
}
do {
// Note: jsClass is set before this loop, and also at the
// *end* of this loop.
if (IS_WRAPPER_CLASS(jsClass)) {
result = sXPConnect->GetPrincipal(aObj,
#ifdef DEBUG
aAllowShortCircuit
#else
true
#endif
);
if (result) {
break;
}
} else {
nsISupports *priv;
if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE |
JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
priv = (nsISupports *) js::GetObjectPrivate(aObj);
} else if ((jsClass->flags & JSCLASS_IS_DOMJSCLASS) &&
Bug 742217. Reduce the use of nested namespaces in our binding code. r=peterv,bent In the new setup, all per-interface DOM binding files are exported into mozilla/dom. General files not specific to an interface are also exported into mozilla/dom. In terms of namespaces, most things now live in mozilla::dom. Each interface Foo that has generated code has a mozilla::dom::FooBinding namespace for said generated code (and possibly a mozilla::bindings::FooBinding_workers if there's separate codegen for workers). IDL enums are a bit weird: since the name of the enum and the names of its entries all end up in the same namespace, we still generate a C++ namespace with the name of the IDL enum type with "Values" appended to it, with a ::valuelist inside for the actual C++ enum. We then typedef EnumFooValues::valuelist to EnumFoo. That makes it a bit more difficult to refer to the values, but means that values from different enums don't collide with each other. The enums with the proto and constructor IDs in them now live under the mozilla::dom::prototypes and mozilla::dom::constructors namespaces respectively. Again, this lets us deal sanely with the whole "enum value names are flattened into the namespace the enum is in" deal. The main benefit of this setup (and the reason "Binding" got appended to the per-interface namespaces) is that this way "using mozilla::dom" should Just Work for consumers and still allow C++ code to sanely use the IDL interface names for concrete classes, which is fairly desirable. --HG-- rename : dom/bindings/Utils.cpp => dom/bindings/BindingUtils.cpp rename : dom/bindings/Utils.h => dom/bindings/BindingUtils.h
2012-05-02 21:35:38 -07:00
DOMJSClass::FromJSClass(jsClass)->mDOMObjectIsISupports) {
priv = UnwrapDOMObject<nsISupports>(aObj, jsClass);
} else {
priv = nsnull;
}
#ifdef DEBUG
if (aAllowShortCircuit) {
nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper =
do_QueryInterface(priv);
NS_ASSERTION(!xpcWrapper ||
!strcmp(jsClass->name, "XPCNativeWrapper"),
"Uh, an nsIXPConnectWrappedNative with the "
"wrong JSClass or getObjectOps hooks!");
}
#endif
nsCOMPtr<nsIScriptObjectPrincipal> objPrin =
do_QueryInterface(priv);
if (objPrin) {
result = objPrin->GetPrincipal();
if (result) {
break;
}
}
}
aObj = js::GetObjectParentMaybeScope(aObj);
if (!aObj)
break;
jsClass = js::GetObjectClass(aObj);
} while (1);
#ifdef DEBUG
if (aAllowShortCircuit) {
nsIPrincipal *principal = doGetObjectPrincipal(origObj, false);
// Because of inner window reuse, we can have objects with one principal
// living in a scope with a different (but same-origin) principal. So
// just check same-origin here.
NS_ASSERTION(NS_SUCCEEDED(CheckSameOriginPrincipal(result, principal)),
"Principal mismatch. Not good");
}
#endif
return result;
}
///////////////// Capabilities API /////////////////////
NS_IMETHODIMP
nsScriptSecurityManager::IsCapabilityEnabled(const char *capability,
bool *result)
{
nsresult rv;
JSStackFrame *fp = nsnull;
JSContext *cx = GetCurrentJSContext();
fp = cx ? JS_FrameIterator(cx, &fp) : nsnull;
JSStackFrame *target = nsnull;
nsIPrincipal *targetPrincipal = nsnull;
for (ContextPrincipal *cp = mContextPrincipals; cp; cp = cp->mNext)
{
if (cp->mCx == cx)
{
target = cp->mFp;
targetPrincipal = cp->mPrincipal;
break;
}
}
if (!fp)
{
// No script code on stack. If we had a principal pushed for this
// context and fp is null, then we use that principal. Otherwise, we
// don't have enough information and have to allow execution.
*result = (targetPrincipal && !target)
? (targetPrincipal == mSystemPrincipal)
: true;
return NS_OK;
}
*result = false;
nsIPrincipal* previousPrincipal = nsnull;
do
{
nsIPrincipal* principal = GetFramePrincipal(cx, fp, &rv);
if (NS_FAILED(rv))
return rv;
if (!principal)
continue;
// If caller has a different principal, stop looking up the stack.
if(previousPrincipal)
{
bool isEqual = false;
if(NS_FAILED(previousPrincipal->Equals(principal, &isEqual)) || !isEqual)
break;
}
else
previousPrincipal = principal;
// First check if the principal is even able to enable the
// given capability. If not, don't look any further.
PRInt16 canEnable;
rv = principal->CanEnableCapability(capability, &canEnable);
if (NS_FAILED(rv)) return rv;
if (canEnable != nsIPrincipal::ENABLE_GRANTED &&
canEnable != nsIPrincipal::ENABLE_WITH_USER_PERMISSION)
return NS_OK;
// Now see if the capability is enabled.
void *annotation = JS_GetFrameAnnotation(cx, fp);
rv = principal->IsCapabilityEnabled(capability, annotation, result);
if (NS_FAILED(rv)) return rv;
if (*result)
return NS_OK;
// Capabilities do not extend to calls into C/C++ and then back into
// the JS engine via JS_EvaluateScript or similar APIs.
if (JS_IsGlobalFrame(cx, fp))
break;
2011-07-27 12:29:26 -07:00
} while (fp != target && (fp = JS_FrameIterator(cx, &fp)) != nsnull);
if (!previousPrincipal)
{
// No principals on the stack, all native code. Allow
// execution if the subject principal is the system principal.
return SubjectPrincipalIsSystem(result);
}
return NS_OK;
}
void
nsScriptSecurityManager::FormatCapabilityString(nsAString& aCapability)
{
nsAutoString newcaps;
nsAutoString rawcap;
NS_NAMED_LITERAL_STRING(capdesc, "capdesc.");
PRInt32 pos;
PRInt32 index = kNotFound;
nsresult rv;
NS_ASSERTION(kNotFound == -1, "Basic constant changed, algorithm broken!");
do {
pos = index+1;
index = aCapability.FindChar(' ', pos);
rawcap = Substring(aCapability, pos,
(index == kNotFound) ? index : index - pos);
nsXPIDLString capstr;
rv = sStrBundle->GetStringFromName(
nsPromiseFlatString(capdesc+rawcap).get(),
getter_Copies(capstr));
if (NS_SUCCEEDED(rv))
newcaps += capstr;
else
{
nsXPIDLString extensionCap;
const PRUnichar* formatArgs[] = { rawcap.get() };
rv = sStrBundle->FormatStringFromName(
NS_LITERAL_STRING("ExtensionCapability").get(),
formatArgs,
ArrayLength(formatArgs),
getter_Copies(extensionCap));
if (NS_SUCCEEDED(rv))
newcaps += extensionCap;
else
newcaps += rawcap;
}
newcaps += NS_LITERAL_STRING("\n");
} while (index != kNotFound);
aCapability = newcaps;
}
NS_IMETHODIMP
nsScriptSecurityManager::RequestCapability(nsIPrincipal* aPrincipal,
const char *capability, PRInt16* canEnable)
{
if (NS_FAILED(aPrincipal->CanEnableCapability(capability, canEnable)))
return NS_ERROR_FAILURE;
// The confirm dialog is no longer supported. All of this stuff is going away
// real soon now anyhow.
if (*canEnable == nsIPrincipal::ENABLE_WITH_USER_PERMISSION)
*canEnable = nsIPrincipal::ENABLE_DENIED;
return NS_OK;
}
NS_IMETHODIMP
nsScriptSecurityManager::EnableCapability(const char *capability)
{
JSContext *cx = GetCurrentJSContext();
JSStackFrame *fp;
//-- Error checks for capability string length (200)
if(PL_strlen(capability)>200)
{
static const char msg[] = "Capability name too long";
SetPendingException(cx, msg);
return NS_ERROR_FAILURE;
}
//-- Check capability string for valid characters
//
// Logically we might have wanted this in nsPrincipal, but performance
// worries dictate it can't go in IsCapabilityEnabled() and we may have
// to show the capability on a dialog before we call the principal's
// EnableCapability().
//
// We don't need to validate the capability string on the other APIs
// available to web content. Without the ability to enable junk then
// isPrivilegeEnabled, disablePrivilege, and revertPrivilege all do
// the right thing (effectively nothing) when passed unallowed chars.
for (const char *ch = capability; *ch; ++ch)
{
if (!NS_IS_ALPHA(*ch) && *ch != ' ' && !NS_IS_DIGIT(*ch)
&& *ch != '_' && *ch != '-' && *ch != '.')
{
static const char msg[] = "Invalid character in capability name";
SetPendingException(cx, msg);
return NS_ERROR_FAILURE;
}
}
nsresult rv;
nsIPrincipal* principal = GetPrincipalAndFrame(cx, &fp, &rv);
if (NS_FAILED(rv))
return rv;
if (!principal)
return NS_ERROR_NOT_AVAILABLE;
void *annotation = JS_GetFrameAnnotation(cx, fp);
bool enabled;
if (NS_FAILED(principal->IsCapabilityEnabled(capability, annotation,
&enabled)))
return NS_ERROR_FAILURE;
if (enabled)
return NS_OK;
PRInt16 canEnable;
if (NS_FAILED(RequestCapability(principal, capability, &canEnable)))
return NS_ERROR_FAILURE;
if (canEnable != nsIPrincipal::ENABLE_GRANTED)
{
nsCAutoString val;
bool hasCert;
nsresult rv;
principal->GetHasCertificate(&hasCert);
if (hasCert)
rv = principal->GetPrettyName(val);
else
rv = GetPrincipalDomainOrigin(principal, val);
if (NS_FAILED(rv))
return rv;
NS_ConvertUTF8toUTF16 location(val);
NS_ConvertUTF8toUTF16 cap(capability);
const PRUnichar *formatStrings[] = { location.get(), cap.get() };
nsXPIDLString message;
rv = sStrBundle->FormatStringFromName(NS_LITERAL_STRING("EnableCapabilityDenied").get(),
formatStrings,
ArrayLength(formatStrings),
getter_Copies(message));
if (NS_FAILED(rv))
return rv;
SetPendingException(cx, message.get());
return NS_ERROR_FAILURE; // XXX better error code?
}
if (NS_FAILED(principal->EnableCapability(capability, &annotation)))
return NS_ERROR_FAILURE;
JS_SetFrameAnnotation(cx, fp, annotation);
return NS_OK;
}
////////////////////////////////////////////////
// Methods implementing nsIXPCSecurityManager //
////////////////////////////////////////////////
NS_IMETHODIMP
nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
const nsIID &aIID,
nsISupports *aObj,
nsIClassInfo *aClassInfo,
void **aPolicy)
{
#ifdef DEBUG_CAPS_CanCreateWrapper
char* iidStr = aIID.ToString();
printf("### CanCreateWrapper(%s) ", iidStr);
NS_Free(iidStr);
#endif
// XXX Special case for nsIXPCException ?
ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nsnull);
if (objClassInfo.IsDOMClass())
{
#ifdef DEBUG_CAPS_CanCreateWrapper
printf("DOM class - GRANTED.\n");
#endif
return NS_OK;
}
//--See if the object advertises a non-default level of access
// using nsISecurityCheckedComponent
nsCOMPtr<nsISecurityCheckedComponent> checkedComponent =
do_QueryInterface(aObj);
nsXPIDLCString objectSecurityLevel;
if (checkedComponent)
checkedComponent->CanCreateWrapper((nsIID *)&aIID, getter_Copies(objectSecurityLevel));
nsresult rv = CheckXPCPermissions(cx, aObj, nsnull, nsnull, objectSecurityLevel);
if (NS_FAILED(rv))
{
//-- Access denied, report an error
NS_ConvertUTF8toUTF16 strName("CreateWrapperDenied");
nsCAutoString origin;
nsresult rv2;
nsIPrincipal* subjectPrincipal = doGetSubjectPrincipal(&rv2);
if (NS_SUCCEEDED(rv2) && subjectPrincipal) {
GetPrincipalDomainOrigin(subjectPrincipal, origin);
}
NS_ConvertUTF8toUTF16 originUnicode(origin);
NS_ConvertUTF8toUTF16 className(objClassInfo.GetName());
const PRUnichar* formatStrings[] = {
className.get(),
originUnicode.get()
};
PRUint32 length = ArrayLength(formatStrings);
if (originUnicode.IsEmpty()) {
--length;
} else {
strName.AppendLiteral("ForOrigin");
}
nsXPIDLString errorMsg;
// We need to keep our existing failure rv and not override it
// with a likely success code from the following string bundle
// call in order to throw the correct security exception later.
rv2 = sStrBundle->FormatStringFromName(strName.get(),
formatStrings,
length,
getter_Copies(errorMsg));
NS_ENSURE_SUCCESS(rv2, rv2);
SetPendingException(cx, errorMsg.get());
#ifdef DEBUG_CAPS_CanCreateWrapper
printf("DENIED.\n");
}
else
{
printf("GRANTED.\n");
#endif
}
return rv;
}
NS_IMETHODIMP
nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
const nsCID &aCID)
{
#ifdef DEBUG_CAPS_CanCreateInstance
char* cidStr = aCID.ToString();
printf("### CanCreateInstance(%s) ", cidStr);
NS_Free(cidStr);
#endif
nsresult rv = CheckXPCPermissions(nsnull, nsnull, nsnull, nsnull, nsnull);
if (NS_FAILED(rv))
{
//-- Access denied, report an error
nsCAutoString errorMsg("Permission denied to create instance of class. CID=");
char cidStr[NSID_LENGTH];
aCID.ToProvidedString(cidStr);
errorMsg.Append(cidStr);
SetPendingException(cx, errorMsg.get());
#ifdef DEBUG_CAPS_CanCreateInstance
printf("DENIED\n");
}
else
{
printf("GRANTED\n");
#endif
}
return rv;
}
NS_IMETHODIMP
nsScriptSecurityManager::CanGetService(JSContext *cx,
const nsCID &aCID)
{
#ifdef DEBUG_CAPS_CanGetService
char* cidStr = aCID.ToString();
printf("### CanGetService(%s) ", cidStr);
NS_Free(cidStr);
#endif
nsresult rv = CheckXPCPermissions(nsnull, nsnull, nsnull, nsnull, nsnull);
if (NS_FAILED(rv))
{
//-- Access denied, report an error
nsCAutoString errorMsg("Permission denied to get service. CID=");
char cidStr[NSID_LENGTH];
aCID.ToProvidedString(cidStr);
errorMsg.Append(cidStr);
SetPendingException(cx, errorMsg.get());
#ifdef DEBUG_CAPS_CanGetService
printf("DENIED\n");
}
else
{
printf("GRANTED\n");
#endif
}
return rv;
}
NS_IMETHODIMP
nsScriptSecurityManager::CanAccess(PRUint32 aAction,
nsAXPCNativeCallContext* aCallContext,
JSContext* cx,
JSObject* aJSObject,
nsISupports* aObj,
nsIClassInfo* aClassInfo,
2010-07-14 23:19:36 -07:00
jsid aPropertyName,
void** aPolicy)
{
return CheckPropertyAccessImpl(aAction, aCallContext, cx,
aJSObject, aObj, nsnull, aClassInfo,
nsnull, aPropertyName, aPolicy);
}
nsresult
nsScriptSecurityManager::CheckXPCPermissions(JSContext* cx,
nsISupports* aObj, JSObject* aJSObject,
nsIPrincipal* aSubjectPrincipal,
const char* aObjectSecurityLevel)
{
//-- Check for the all-powerful UniversalXPConnect privilege
bool ok = false;
if (NS_SUCCEEDED(IsCapabilityEnabled("UniversalXPConnect", &ok)) && ok)
return NS_OK;
//-- If the object implements nsISecurityCheckedComponent, it has a non-default policy.
if (aObjectSecurityLevel)
{
if (PL_strcasecmp(aObjectSecurityLevel, "allAccess") == 0)
return NS_OK;
if (cx && PL_strcasecmp(aObjectSecurityLevel, "sameOrigin") == 0)
{
nsresult rv;
if (!aJSObject)
{
nsCOMPtr<nsIXPConnectWrappedJS> xpcwrappedjs =
do_QueryInterface(aObj);
if (xpcwrappedjs)
{
rv = xpcwrappedjs->GetJSObject(&aJSObject);
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (!aSubjectPrincipal)
{
// No subject principal passed in. Compute it.
aSubjectPrincipal = GetSubjectPrincipal(cx, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
if (aSubjectPrincipal && aJSObject)
{
nsIPrincipal* objectPrincipal = doGetObjectPrincipal(aJSObject);
// Only do anything if we have both a subject and object
// principal.
if (objectPrincipal)
{
bool subsumes;
rv = aSubjectPrincipal->Subsumes(objectPrincipal, &subsumes);
NS_ENSURE_SUCCESS(rv, rv);
if (subsumes)
return NS_OK;
}
}
}
else if (PL_strcasecmp(aObjectSecurityLevel, "noAccess") != 0)
{
bool canAccess = false;
if (NS_SUCCEEDED(IsCapabilityEnabled(aObjectSecurityLevel, &canAccess)) &&
canAccess)
return NS_OK;
}
}
//-- Access tests failed
return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
}
/////////////////////////////////////////////
// Method implementing nsIChannelEventSink //
/////////////////////////////////////////////
NS_IMETHODIMP
nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel,
nsIChannel* newChannel,
PRUint32 redirFlags,
nsIAsyncVerifyRedirectCallback *cb)
{
nsCOMPtr<nsIPrincipal> oldPrincipal;
GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
nsCOMPtr<nsIURI> newURI;
newChannel->GetURI(getter_AddRefs(newURI));
nsCOMPtr<nsIURI> newOriginalURI;
newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
const PRUint32 flags =
nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
nsIScriptSecurityManager::DISALLOW_SCRIPT;
nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
}
if (NS_FAILED(rv))
return rv;
cb->OnRedirectVerifyCallback(NS_OK);
return NS_OK;
}
/////////////////////////////////////
// Method implementing nsIObserver //
/////////////////////////////////////
const char sJSEnabledPrefName[] = "javascript.enabled";
const char sFileOriginPolicyPrefName[] =
"security.fileuri.strict_origin_policy";
static const char sPrincipalPrefix[] = "capability.principal";
static const char sPolicyPrefix[] = "capability.policy.";
static const char* kObservedPrefs[] = {
sJSEnabledPrefName,
sFileOriginPolicyPrefName,
sPolicyPrefix,
sPrincipalPrefix,
nsnull
};
NS_IMETHODIMP
nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
const PRUnichar* aMessage)
{
nsresult rv = NS_OK;
NS_ConvertUTF16toUTF8 messageStr(aMessage);
const char *message = messageStr.get();
static const char jsPrefix[] = "javascript.";
static const char securityPrefix[] = "security.";
if ((PL_strncmp(message, jsPrefix, sizeof(jsPrefix)-1) == 0) ||
(PL_strncmp(message, securityPrefix, sizeof(securityPrefix)-1) == 0) )
{
ScriptSecurityPrefChanged();
}
else if (PL_strncmp(message, sPolicyPrefix, sizeof(sPolicyPrefix)-1) == 0)
{
// This will force re-initialization of the pref table
mPolicyPrefsChanged = true;
}
else if ((PL_strncmp(message, sPrincipalPrefix, sizeof(sPrincipalPrefix)-1) == 0) &&
!mIsWritingPrefs)
{
static const char id[] = "id";
char* lastDot = PL_strrchr(message, '.');
//-- This check makes sure the string copy below doesn't overwrite its bounds
if(PL_strlen(lastDot) >= sizeof(id))
{
PL_strcpy(lastDot + 1, id);
const char** idPrefArray = (const char**)&message;
rv = InitPrincipals(1, idPrefArray);
}
}
return rv;
}
/////////////////////////////////////////////
// Constructor, Destructor, Initialization //
/////////////////////////////////////////////
nsScriptSecurityManager::nsScriptSecurityManager(void)
: mOriginToPolicyMap(nsnull),
mDefaultPolicy(nsnull),
mCapabilities(nsnull),
mContextPrincipals(nsnull),
mPrefInitialized(false),
mIsJavaScriptEnabled(false),
mIsWritingPrefs(false),
mPolicyPrefsChanged(true)
{
MOZ_STATIC_ASSERT(sizeof(intptr_t) == sizeof(void*),
"intptr_t and void* have different lengths on this platform. "
"This may cause a security failure with the SecurityLevel union.");
mPrincipals.Init(31);
}
nsresult nsScriptSecurityManager::Init()
{
nsXPConnect* xpconnect = nsXPConnect::GetXPConnect();
if (!xpconnect)
return NS_ERROR_FAILURE;
NS_ADDREF(sXPConnect = xpconnect);
NS_ADDREF(sJSContextStack = xpconnect);
JSContext* cx = GetSafeJSContext();
if (!cx) return NS_ERROR_FAILURE; // this can happen of xpt loading fails
::JS_BeginRequest(cx);
2010-07-14 23:19:36 -07:00
if (sEnabledID == JSID_VOID)
sEnabledID = INTERNED_STRING_TO_JSID(cx, ::JS_InternString(cx, "enabled"));
::JS_EndRequest(cx);
InitPrefs();
nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStringBundleService> bundleService =
mozilla::services::GetStringBundleService();
if (!bundleService)
return NS_ERROR_FAILURE;
rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
NS_ENSURE_SUCCESS(rv, rv);
// Create our system principal singleton
nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
mSystemPrincipal = system;
//-- Register security check callback in the JS engine
// Currently this is used to control access to function.caller
nsCOMPtr<nsIJSRuntimeService> runtimeService =
do_QueryInterface(sXPConnect, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = runtimeService->GetRuntime(&sRuntime);
NS_ENSURE_SUCCESS(rv, rv);
static const JSSecurityCallbacks securityCallbacks = {
CheckObjectAccess,
nsJSPrincipals::Subsume,
ObjectPrincipalFinder,
ContentSecurityPolicyPermitsJSAction,
PushPrincipalCallback,
PopPrincipalCallback
};
MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
JS_SetSecurityCallbacks(sRuntime, &securityCallbacks);
JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy);
JS_SetTrustedPrincipals(sRuntime, system);
return NS_OK;
}
static nsScriptSecurityManager *gScriptSecMan = nsnull;
2010-07-14 23:19:36 -07:00
jsid nsScriptSecurityManager::sEnabledID = JSID_VOID;
nsScriptSecurityManager::~nsScriptSecurityManager(void)
{
Preferences::RemoveObservers(this, kObservedPrefs);
NS_ASSERTION(!mContextPrincipals, "Leaking mContextPrincipals");
delete mOriginToPolicyMap;
if(mDefaultPolicy)
mDefaultPolicy->Drop();
delete mCapabilities;
gScriptSecMan = nsnull;
}
void
nsScriptSecurityManager::Shutdown()
{
if (sRuntime) {
JS_SetSecurityCallbacks(sRuntime, NULL);
JS_SetTrustedPrincipals(sRuntime, NULL);
sRuntime = nsnull;
}
2010-07-14 23:19:36 -07:00
sEnabledID = JSID_VOID;
NS_IF_RELEASE(sIOService);
NS_IF_RELEASE(sXPConnect);
NS_IF_RELEASE(sJSContextStack);
NS_IF_RELEASE(sStrBundle);
}
nsScriptSecurityManager *
nsScriptSecurityManager::GetScriptSecurityManager()
{
if (!gScriptSecMan)
{
nsScriptSecurityManager* ssManager = new nsScriptSecurityManager();
if (!ssManager)
return nsnull;
nsresult rv;
rv = ssManager->Init();
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to initialize nsScriptSecurityManager");
if (NS_FAILED(rv)) {
delete ssManager;
return nsnull;
}
rv = sXPConnect->SetDefaultSecurityManager(ssManager,
nsIXPCSecurityManager::HOOK_ALL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to install xpconnect security manager!");
delete ssManager;
return nsnull;
}
gScriptSecMan = ssManager;
}
return gScriptSecMan;
}
// Currently this nsGenericFactory constructor is used only from FastLoad
// (XPCOM object deserialization) code, when "creating" the system principal
// singleton.
nsSystemPrincipal *
nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
{
nsIPrincipal *sysprin = nsnull;
if (gScriptSecMan)
NS_ADDREF(sysprin = gScriptSecMan->mSystemPrincipal);
return static_cast<nsSystemPrincipal*>(sysprin);
}
nsresult
nsScriptSecurityManager::InitPolicies()
{
// Clear any policies cached on XPConnect wrappers
NS_ENSURE_STATE(sXPConnect);
nsresult rv = sXPConnect->ClearAllWrappedNativeSecurityPolicies();
if (NS_FAILED(rv)) return rv;
//-- Clear mOriginToPolicyMap: delete mapped DomainEntry items,
//-- whose dtor decrements refcount of stored DomainPolicy object
delete mOriginToPolicyMap;
//-- Marks all the survivor DomainPolicy objects (those cached
//-- by nsPrincipal objects) as invalid: they will be released
//-- on first nsPrincipal::GetSecurityPolicy() attempt.
DomainPolicy::InvalidateAll();
//-- Release old default policy
if(mDefaultPolicy) {
mDefaultPolicy->Drop();
mDefaultPolicy = nsnull;
}
//-- Initialize a new mOriginToPolicyMap
mOriginToPolicyMap =
new nsObjectHashtable(nsnull, nsnull, DeleteDomainEntry, nsnull);
if (!mOriginToPolicyMap)
return NS_ERROR_OUT_OF_MEMORY;
//-- Create, refcount and initialize a new default policy
mDefaultPolicy = new DomainPolicy();
if (!mDefaultPolicy)
return NS_ERROR_OUT_OF_MEMORY;
mDefaultPolicy->Hold();
if (!mDefaultPolicy->Init())
return NS_ERROR_UNEXPECTED;
//-- Initialize the table of security levels
if (!mCapabilities)
{
mCapabilities =
new nsObjectHashtable(nsnull, nsnull, DeleteCapability, nsnull);
if (!mCapabilities)
return NS_ERROR_OUT_OF_MEMORY;
}
// Get a JS context - we need it to create internalized strings later.
JSContext* cx = GetSafeJSContext();
NS_ASSERTION(cx, "failed to get JS context");
AutoCxPusher autoPusher(sJSContextStack, cx);
rv = InitDomainPolicy(cx, "default", mDefaultPolicy);
NS_ENSURE_SUCCESS(rv, rv);
nsAdoptingCString policyNames =
Preferences::GetCString("capability.policy.policynames");
nsAdoptingCString defaultPolicyNames =
Preferences::GetCString("capability.policy.default_policynames");
policyNames += NS_LITERAL_CSTRING(" ") + defaultPolicyNames;
//-- Initialize domain policies
char* policyCurrent = policyNames.BeginWriting();
bool morePolicies = true;
while (morePolicies)
{
while(*policyCurrent == ' ' || *policyCurrent == ',')
policyCurrent++;
if (*policyCurrent == '\0')
break;
char* nameBegin = policyCurrent;
while(*policyCurrent != '\0' && *policyCurrent != ' ' && *policyCurrent != ',')
policyCurrent++;
morePolicies = (*policyCurrent != '\0');
*policyCurrent = '\0';
policyCurrent++;
nsCAutoString sitesPrefName(
NS_LITERAL_CSTRING(sPolicyPrefix) +
nsDependentCString(nameBegin) +
NS_LITERAL_CSTRING(".sites"));
nsAdoptingCString domainList =
Preferences::GetCString(sitesPrefName.get());
if (!domainList) {
continue;
}
DomainPolicy* domainPolicy = new DomainPolicy();
if (!domainPolicy)
return NS_ERROR_OUT_OF_MEMORY;
if (!domainPolicy->Init())
{
delete domainPolicy;
return NS_ERROR_UNEXPECTED;
}
domainPolicy->Hold();
//-- Parse list of sites and create an entry in mOriginToPolicyMap for each
char* domainStart = domainList.BeginWriting();
char* domainCurrent = domainStart;
char* lastDot = nsnull;
char* nextToLastDot = nsnull;
bool moreDomains = true;
while (moreDomains)
{
if (*domainCurrent == ' ' || *domainCurrent == '\0')
{
moreDomains = (*domainCurrent != '\0');
*domainCurrent = '\0';
nsCStringKey key(nextToLastDot ? nextToLastDot+1 : domainStart);
DomainEntry *newEntry = new DomainEntry(domainStart, domainPolicy);
if (!newEntry)
{
domainPolicy->Drop();
return NS_ERROR_OUT_OF_MEMORY;
}
#ifdef DEBUG
newEntry->mPolicyName_DEBUG = nameBegin;
#endif
DomainEntry *existingEntry = (DomainEntry *)
mOriginToPolicyMap->Get(&key);
if (!existingEntry)
mOriginToPolicyMap->Put(&key, newEntry);
else
{
if (existingEntry->Matches(domainStart))
{
newEntry->mNext = existingEntry;
mOriginToPolicyMap->Put(&key, newEntry);
}
else
{
while (existingEntry->mNext)
{
if (existingEntry->mNext->Matches(domainStart))
{
newEntry->mNext = existingEntry->mNext;
existingEntry->mNext = newEntry;
break;
}
existingEntry = existingEntry->mNext;
}
if (!existingEntry->mNext)
existingEntry->mNext = newEntry;
}
}
domainStart = domainCurrent + 1;
lastDot = nextToLastDot = nsnull;
}
else if (*domainCurrent == '.')
{
nextToLastDot = lastDot;
lastDot = domainCurrent;
}
domainCurrent++;
}
rv = InitDomainPolicy(cx, nameBegin, domainPolicy);
domainPolicy->Drop();
if (NS_FAILED(rv))
return rv;
}
// Reset the "dirty" flag
mPolicyPrefsChanged = false;
#ifdef DEBUG_CAPS_HACKER
PrintPolicyDB();
#endif
return NS_OK;
}
nsresult
nsScriptSecurityManager::InitDomainPolicy(JSContext* cx,
const char* aPolicyName,
DomainPolicy* aDomainPolicy)
{
nsresult rv;
nsCAutoString policyPrefix(NS_LITERAL_CSTRING(sPolicyPrefix) +
nsDependentCString(aPolicyName) +
NS_LITERAL_CSTRING("."));
PRUint32 prefixLength = policyPrefix.Length() - 1; // subtract the '.'
PRUint32 prefCount;
char** prefNames;
nsIPrefBranch* branch = Preferences::GetRootBranch();
NS_ASSERTION(branch, "failed to get the root pref branch");
rv = branch->GetChildList(policyPrefix.get(), &prefCount, &prefNames);
if (NS_FAILED(rv)) return rv;
if (prefCount == 0)
return NS_OK;
//-- Populate the policy
PRUint32 currentPref = 0;
for (; currentPref < prefCount; currentPref++)
{
// Get the class name
const char* start = prefNames[currentPref] + prefixLength + 1;
char* end = PL_strchr(start, '.');
if (!end) // malformed pref, bail on this one
continue;
static const char sitesStr[] = "sites";
// We dealt with "sites" in InitPolicies(), so no need to do
// that again...
if (PL_strncmp(start, sitesStr, sizeof(sitesStr)-1) == 0)
continue;
// Get the pref value
nsAdoptingCString prefValue =
Preferences::GetCString(prefNames[currentPref]);
if (!prefValue) {
continue;
}
SecurityLevel secLevel;
if (PL_strcasecmp(prefValue, "noAccess") == 0)
secLevel.level = SCRIPT_SECURITY_NO_ACCESS;
else if (PL_strcasecmp(prefValue, "allAccess") == 0)
secLevel.level = SCRIPT_SECURITY_ALL_ACCESS;
else if (PL_strcasecmp(prefValue, "sameOrigin") == 0)
secLevel.level = SCRIPT_SECURITY_SAME_ORIGIN_ACCESS;
else
{ //-- pref value is the name of a capability
nsCStringKey secLevelKey(prefValue);
secLevel.capability =
reinterpret_cast<char*>(mCapabilities->Get(&secLevelKey));
if (!secLevel.capability)
{
secLevel.capability = NS_strdup(prefValue);
if (!secLevel.capability)
break;
mCapabilities->Put(&secLevelKey,
secLevel.capability);
}
}
*end = '\0';
// Find or store this class in the classes table
ClassPolicy* cpolicy =
static_cast<ClassPolicy*>
(PL_DHashTableOperate(aDomainPolicy, start,
PL_DHASH_ADD));
if (!cpolicy)
break;
// If this is the wildcard class (class '*'), save it in mWildcardPolicy
// (we leave it stored in the hashtable too to take care of the cleanup)
if ((*start == '*') && (end == start + 1)) {
aDomainPolicy->mWildcardPolicy = cpolicy;
// Make sure that cpolicy knows about aDomainPolicy so it can reset
// the mWildcardPolicy pointer as needed if it gets moved in the
// hashtable.
cpolicy->mDomainWeAreWildcardFor = aDomainPolicy;
}
// Get the property name
start = end + 1;
end = PL_strchr(start, '.');
if (end)
*end = '\0';
JSAutoRequest ar(cx);
JSString* propertyKey = ::JS_InternString(cx, start);
if (!propertyKey)
return NS_ERROR_OUT_OF_MEMORY;
// Store this property in the class policy
PropertyPolicy* ppolicy =
static_cast<PropertyPolicy*>
2010-07-14 23:19:36 -07:00
(PL_DHashTableOperate(cpolicy->mPolicy, propertyKey,
PL_DHASH_ADD));
if (!ppolicy)
break;
if (end) // The pref specifies an access mode
{
start = end + 1;
if (PL_strcasecmp(start, "set") == 0)
ppolicy->mSet = secLevel;
else
ppolicy->mGet = secLevel;
}
else
{
if (ppolicy->mGet.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
ppolicy->mGet = secLevel;
if (ppolicy->mSet.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
ppolicy->mSet = secLevel;
}
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames);
if (currentPref < prefCount) // Loop exited early because of out-of-memory error
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
// XXXbz We should really just get a prefbranch to handle this...
nsresult
nsScriptSecurityManager::GetPrincipalPrefNames(const char* prefBase,
nsCString& grantedPref,
nsCString& deniedPref,
nsCString& subjectNamePref)
{
char* lastDot = PL_strrchr(prefBase, '.');
if (!lastDot) return NS_ERROR_FAILURE;
PRInt32 prefLen = lastDot - prefBase + 1;
grantedPref.Assign(prefBase, prefLen);
deniedPref.Assign(prefBase, prefLen);
subjectNamePref.Assign(prefBase, prefLen);
#define GRANTED "granted"
#define DENIED "denied"
#define SUBJECTNAME "subjectName"
grantedPref.AppendLiteral(GRANTED);
if (grantedPref.Length() != prefLen + sizeof(GRANTED) - 1) {
return NS_ERROR_OUT_OF_MEMORY;
}
deniedPref.AppendLiteral(DENIED);
if (deniedPref.Length() != prefLen + sizeof(DENIED) - 1) {
return NS_ERROR_OUT_OF_MEMORY;
}
subjectNamePref.AppendLiteral(SUBJECTNAME);
if (subjectNamePref.Length() != prefLen + sizeof(SUBJECTNAME) - 1) {
return NS_ERROR_OUT_OF_MEMORY;
}
#undef SUBJECTNAME
#undef DENIED
#undef GRANTED
return NS_OK;
}
nsresult
nsScriptSecurityManager::InitPrincipals(PRUint32 aPrefCount, const char** aPrefNames)
{
/* This is the principal preference syntax:
* capability.principal.[codebase|codebaseTrusted|certificate].<name>.[id|granted|denied]
* For example:
* user_pref("capability.principal.certificate.p1.id","12:34:AB:CD");
* user_pref("capability.principal.certificate.p1.granted","Capability1 Capability2");
* user_pref("capability.principal.certificate.p1.denied","Capability3");
*/
/* codebaseTrusted means a codebase principal that can enable capabilities even if
* codebase principals are disabled. Don't use trustedCodebase except with unspoofable
* URLs such as HTTPS URLs.
*/
static const char idSuffix[] = ".id";
for (PRUint32 c = 0; c < aPrefCount; c++)
{
PRInt32 prefNameLen = PL_strlen(aPrefNames[c]) -
(ArrayLength(idSuffix) - 1);
if (PL_strcasecmp(aPrefNames[c] + prefNameLen, idSuffix) != 0)
continue;
nsAdoptingCString id = Preferences::GetCString(aPrefNames[c]);
if (!id) {
return NS_ERROR_FAILURE;
}
nsCAutoString grantedPrefName;
nsCAutoString deniedPrefName;
nsCAutoString subjectNamePrefName;
nsresult rv = GetPrincipalPrefNames(aPrefNames[c],
grantedPrefName,
deniedPrefName,
subjectNamePrefName);
if (rv == NS_ERROR_OUT_OF_MEMORY)
return rv;
if (NS_FAILED(rv))
continue;
nsAdoptingCString grantedList =
Preferences::GetCString(grantedPrefName.get());
nsAdoptingCString deniedList =
Preferences::GetCString(deniedPrefName.get());
nsAdoptingCString subjectName =
Preferences::GetCString(subjectNamePrefName.get());
//-- Delete prefs if their value is the empty string
if (id.IsEmpty() || (grantedList.IsEmpty() && deniedList.IsEmpty()))
{
Preferences::ClearUser(aPrefNames[c]);
Preferences::ClearUser(grantedPrefName.get());
Preferences::ClearUser(deniedPrefName.get());
Preferences::ClearUser(subjectNamePrefName.get());
continue;
}
//-- Create a principal based on the prefs
static const char certificateName[] = "capability.principal.certificate";
static const char codebaseName[] = "capability.principal.codebase";
static const char codebaseTrustedName[] = "capability.principal.codebaseTrusted";
bool isCert = false;
bool isTrusted = false;
if (PL_strncmp(aPrefNames[c], certificateName,
sizeof(certificateName) - 1) == 0)
{
isCert = true;
}
else if (PL_strncmp(aPrefNames[c], codebaseName,
sizeof(codebaseName) - 1) == 0)
{
isTrusted = (PL_strncmp(aPrefNames[c], codebaseTrustedName,
sizeof(codebaseTrustedName) - 1) == 0);
}
else
{
NS_ERROR("Not a codebase or a certificate?!");
}
nsRefPtr<nsPrincipal> newPrincipal = new nsPrincipal();
if (!newPrincipal)
return NS_ERROR_OUT_OF_MEMORY;
rv = newPrincipal->InitFromPersistent(aPrefNames[c], id, subjectName,
EmptyCString(),
grantedList, deniedList, nsnull,
isCert, isTrusted);
if (NS_SUCCEEDED(rv))
mPrincipals.Put(newPrincipal, newPrincipal);
}
return NS_OK;
}
inline void
nsScriptSecurityManager::ScriptSecurityPrefChanged()
{
// JavaScript defaults to enabled in failure cases.
mIsJavaScriptEnabled = true;
sStrictFileOriginPolicy = true;
nsresult rv;
if (!mPrefInitialized) {
rv = InitPrefs();
if (NS_FAILED(rv))
return;
}
mIsJavaScriptEnabled =
Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
sStrictFileOriginPolicy =
Preferences::GetBool(sFileOriginPolicyPrefName, false);
}
nsresult
nsScriptSecurityManager::InitPrefs()
{
nsresult rv;
nsIPrefBranch* branch = Preferences::GetRootBranch();
NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
mPrefInitialized = true;
// Set the initial value of the "javascript.enabled" prefs
ScriptSecurityPrefChanged();
// set observer callbacks in case the value of the prefs change
Preferences::AddStrongObservers(this, kObservedPrefs);
PRUint32 prefCount;
char** prefNames;
//-- Initialize the principals database from prefs
rv = branch->GetChildList(sPrincipalPrefix, &prefCount, &prefNames);
if (NS_SUCCEEDED(rv) && prefCount > 0)
{
rv = InitPrincipals(prefCount, (const char**)prefNames);
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// The following code prints the contents of the policy DB to the console.
#ifdef DEBUG_CAPS_HACKER
//typedef PLDHashOperator
//(* PLDHashEnumerator)(PLDHashTable *table, PLDHashEntryHdr *hdr,
// PRUint32 number, void *arg);
static PLDHashOperator
PrintPropertyPolicy(PLDHashTable *table, PLDHashEntryHdr *entry,
PRUint32 number, void *arg)
{
PropertyPolicy* pp = (PropertyPolicy*)entry;
nsCAutoString prop(" ");
JSContext* cx = (JSContext*)arg;
prop.AppendInt((PRUint32)pp->key);
prop += ' ';
2010-07-14 23:19:36 -07:00
prop.AppendWithConversion((PRUnichar*)JS_GetStringChars(pp->key));
prop += ": Get=";
if (SECURITY_ACCESS_LEVEL_FLAG(pp->mGet))
prop.AppendInt(pp->mGet.level);
else
prop += pp->mGet.capability;
prop += " Set=";
if (SECURITY_ACCESS_LEVEL_FLAG(pp->mSet))
prop.AppendInt(pp->mSet.level);
else
prop += pp->mSet.capability;
printf("%s.\n", prop.get());
return PL_DHASH_NEXT;
}
static PLDHashOperator
PrintClassPolicy(PLDHashTable *table, PLDHashEntryHdr *entry,
PRUint32 number, void *arg)
{
ClassPolicy* cp = (ClassPolicy*)entry;
printf(" %s\n", cp->key);
PL_DHashTableEnumerate(cp->mPolicy, PrintPropertyPolicy, arg);
return PL_DHASH_NEXT;
}
// typedef bool
// (* nsHashtableEnumFunc)(nsHashKey *aKey, void *aData, void* aClosure);
static bool
PrintDomainPolicy(nsHashKey *aKey, void *aData, void* aClosure)
{
DomainEntry* de = (DomainEntry*)aData;
printf("----------------------------\n");
printf("Domain: %s Policy Name: %s.\n", de->mOrigin.get(),
de->mPolicyName_DEBUG.get());
PL_DHashTableEnumerate(de->mDomainPolicy, PrintClassPolicy, aClosure);
return true;
}
static bool
PrintCapability(nsHashKey *aKey, void *aData, void* aClosure)
{
char* cap = (char*)aData;
printf(" %s.\n", cap);
return true;
}
void
nsScriptSecurityManager::PrintPolicyDB()
{
printf("############## Security Policies ###############\n");
if(mOriginToPolicyMap)
{
JSContext* cx = GetCurrentJSContext();
if (!cx)
cx = GetSafeJSContext();
printf("----------------------------\n");
printf("Domain: Default.\n");
PL_DHashTableEnumerate(mDefaultPolicy, PrintClassPolicy, (void*)cx);
mOriginToPolicyMap->Enumerate(PrintDomainPolicy, (void*)cx);
}
printf("############ End Security Policies #############\n\n");
printf("############## Capabilities ###############\n");
mCapabilities->Enumerate(PrintCapability);
printf("############## End Capabilities ###############\n");
}
#endif