Bug 777072 - 3/7 - Update nsHostTable and nsHostEntry to make them aware of new security model. r=sicking,jlebar

This commit is contained in:
Mounir Lamouri 2012-08-23 11:47:55 -07:00
parent 101f5213c1
commit ede8f880d1
5 changed files with 238 additions and 197 deletions

View File

@ -389,6 +389,8 @@ function runTest() {
checkIFrame(this, data);
};
iframe.addChild = function() {
SpecialPowers.addPermission("browser", true, iframe.contentDocument);
var childFrame = document.createElement('iframe');
if (data.child.app) {
@ -429,7 +431,6 @@ function runTest() {
var gTestRunner = runTest();
SpecialPowers.addPermission("browser", true, "http://example.org");
SpecialPowers.pushPrefEnv({'set':[["dom.mozBrowserFramesEnabled", true]]},
function() { gTestRunner.next(); });

View File

@ -18,6 +18,11 @@ function runTest() {
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
iframe = document.createElement('iframe');
iframe.mozbrowser = true;
@ -55,6 +60,12 @@ function finish() {
// expected, but if we don't remove our listener, then we'll end up causing
// the /next/ test to fail!
iframe.removeEventListener('mozbrowsershowmodalprompt', checkMessage);
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
SimpleTest.finish();
}

View File

@ -12,6 +12,11 @@ function runTest() {
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
var iframe = document.createElement('iframe');
iframe.mozbrowser = true;
@ -35,7 +40,7 @@ function runTest() {
SimpleTest.executeSoon(function() {
SimpleTest.executeSoon(function() {
SimpleTest.executeSoon(function() {
SimpleTest.finish();
finish();
});
});
});
@ -50,4 +55,13 @@ function runTest() {
document.body.appendChild(iframe);
}
function finish() {
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
appId: principal.appId,
isInBrowserElement: true });
SimpleTest.finish();
}
runTest();

View File

@ -71,28 +71,6 @@ ChildProcess()
////////////////////////////////////////////////////////////////////////////////
#define PL_ARENA_CONST_ALIGN_MASK 3
#include "plarena.h"
static PLArenaPool *gHostArena = nullptr;
// making sHostArena 512b for nice allocation
// growing is quite cheap
#define HOST_ARENA_SIZE 512
// equivalent to strdup() - does no error checking,
// we're assuming we're only called with a valid pointer
static char *
ArenaStrDup(const char* str, PLArenaPool* aArena)
{
void* mem;
const uint32_t size = strlen(str) + 1;
PL_ARENA_ALLOCATE(mem, aArena, size);
if (mem)
memcpy(mem, str, size);
return static_cast<char*>(mem);
}
namespace {
nsresult
@ -109,22 +87,34 @@ GetPrincipalForHost(const nsACString& aHost, nsIPrincipal** aPrincipal)
return secMan->GetNoAppCodebasePrincipal(uri, aPrincipal);
}
nsresult
GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
uri = NS_GetInnermostURI(uri);
NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
rv = uri->GetAsciiHost(aHost);
if (NS_FAILED(rv) || aHost.IsEmpty()) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
} // anonymous namespace
nsHostEntry::nsHostEntry(const char* aHost)
{
mHost = ArenaStrDup(aHost, gHostArena);
}
// XXX this can fail on OOM
nsHostEntry::nsHostEntry(const nsHostEntry& toCopy)
: mHost(toCopy.mHost)
, mPermissions(toCopy.mPermissions)
{
}
////////////////////////////////////////////////////////////////////////////////
nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
{
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
}
/**
* Simple callback used by |AsyncClose| to trigger a treatment once
@ -287,7 +277,7 @@ nsPermissionManager::Init()
{
nsresult rv;
mHostTable.Init();
mPermissionTable.Init();
mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_SUCCEEDED(rv)) {
@ -558,12 +548,8 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
NotifyOperationType aNotifyOperation,
DBOperationType aDBOperation)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString host;
rv = GetHost(uri, host);
nsresult rv = GetHostForPrincipal(aPrincipal, host);
NS_ENSURE_SUCCESS(rv, rv);
if (!IsChildProcess()) {
@ -580,23 +566,17 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
}
}
if (!gHostArena) {
gHostArena = new PLArenaPool;
if (!gHostArena)
return NS_ERROR_OUT_OF_MEMORY;
PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
}
// look up the type index
int32_t typeIndex = GetTypeIndex(aType.get(), true);
NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
// When an entry already exists, PutEntry will return that, instead
// of adding a new one
nsHostEntry *entry = mHostTable.PutEntry(host.get());
nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal);
PermissionHashKey* entry = mPermissionTable.PutEntry(key);
if (!entry) return NS_ERROR_FAILURE;
if (!entry->GetKey()) {
mHostTable.RawRemoveEntry(entry);
mPermissionTable.RawRemoveEntry(entry);
return NS_ERROR_OUT_OF_MEMORY;
}
@ -610,7 +590,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
op = eOperationAdding;
} else {
nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
// remove the permission if the permission is UNKNOWN, update the
// permission if its value or expire type have changed OR if the time has
@ -648,7 +628,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
id = aID;
}
entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id, aExpireType, aExpireTime));
entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime);
@ -667,13 +647,13 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
case eOperationRemoving:
{
nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
id = oldPermissionEntry.mID;
entry->GetPermissions().RemoveElementAt(index);
// If no more types are present, remove the entry
if (entry->GetPermissions().IsEmpty())
mHostTable.RawRemoveEntry(entry);
mPermissionTable.RawRemoveEntry(entry);
if (aDBOperation == eWriteToDB)
UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
@ -888,7 +868,7 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString host;
rv = GetHost(uri, host);
rv = GetHostForPrincipal(aPrincipal, host);
// No host doesn't mean an error. Just return the default. Unless this is
// a file uri. In that case use a magic host.
@ -909,38 +889,64 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
// so just return NS_OK
if (typeIndex == -1) return NS_OK;
nsHostEntry *entry = GetHostEntry(host, typeIndex, aExactHostMatch);
if (entry)
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
typeIndex, aExactHostMatch);
if (entry) {
*aPermission = entry->GetPermission(typeIndex).mPermission;
}
return NS_OK;
}
// Get hostentry for given host string and permission type.
// walk up the domain if needed.
// return null if nothing found.
// Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
// This is not simply using PermissionKey because we will walk-up domains in
// case of |host| contains sub-domains.
// Returns null if nothing found.
// Also accepts host on the format "<foo>". This will perform an exact match
// lookup as the string doesn't contain any dots.
nsHostEntry *
nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
uint32_t aType,
bool aExactHostMatch)
nsPermissionManager::PermissionHashKey*
nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
uint32_t aAppId,
bool aIsInBrowserElement,
uint32_t aType,
bool aExactHostMatch)
{
uint32_t offset = 0;
nsHostEntry *entry;
PermissionHashKey* entry;
int64_t now = PR_Now() / 1000;
do {
entry = mHostTable.GetEntry(aHost.get() + offset);
nsRefPtr<PermissionKey> key = new PermissionKey(Substring(aHost, offset), aAppId, aIsInBrowserElement);
entry = mPermissionTable.GetEntry(key);
if (entry) {
nsPermissionEntry permEntry = entry->GetPermission(aType);
PermissionEntry permEntry = entry->GetPermission(aType);
// if the entry is expired, remove and keep looking for others.
if (permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME &&
permEntry.mExpireTime <= now)
Remove(aHost, mTypeArray[aType].get());
else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION)
permEntry.mExpireTime <= now) {
nsCOMPtr<nsIPrincipal> principal;
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(secMan, "No security manager!?");
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
if (NS_FAILED(secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
return nullptr;
}
RemoveFromPrincipal(principal, mTypeArray[aType].get());
} else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION) {
break;
}
// reset entry, to be able to return null on failure
entry = nullptr;
@ -968,14 +974,14 @@ struct nsGetEnumeratorData
};
static PLDHashOperator
AddPermissionsToList(nsHostEntry *entry, void *arg)
AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
{
nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
nsPermissionEntry &permEntry = entry->GetPermissions()[i];
nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
nsPermission *perm = new nsPermission(entry->GetHost(),
nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
data->types->ElementAt(permEntry.mType),
permEntry.mPermission,
permEntry.mExpireType,
@ -993,7 +999,7 @@ NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
nsCOMArray<nsIPermission> array;
nsGetEnumeratorData data(&array, &mTypeArray);
mHostTable.EnumerateEntries(AddPermissionsToList, &data);
mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
return NS_NewArrayEnumerator(aEnum, array);
}
@ -1031,12 +1037,8 @@ nsPermissionManager::RemoveAllFromMemory()
{
mLargestID = 0;
mTypeArray.Clear();
mHostTable.Clear();
if (gHostArena) {
PL_FinishArenaPool(gHostArena);
delete gHostArena;
}
gHostArena = nullptr;
mPermissionTable.Clear();
return NS_OK;
}
@ -1257,20 +1259,6 @@ nsPermissionManager::NormalizeToACE(nsCString &aHost)
return mIDNService->ConvertUTF8toACE(aHost, aHost);
}
nsresult
nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult)
{
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
if (!innerURI) return NS_ERROR_FAILURE;
nsresult rv = innerURI->GetAsciiHost(aResult);
if (NS_FAILED(rv) || aResult.IsEmpty())
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
void
nsPermissionManager::UpdateDB(OperationType aOp,
mozIStorageStatement* aStmt,

View File

@ -16,6 +16,8 @@
#include "nsTArray.h"
#include "nsString.h"
#include "nsPermission.h"
#include "nsHashKeys.h"
#include "nsAutoPtr.h"
class nsIPermission;
class nsIIDNService;
@ -24,107 +26,132 @@ class mozIStorageStatement;
////////////////////////////////////////////////////////////////////////////////
class nsPermissionEntry
{
public:
nsPermissionEntry(uint32_t aType, uint32_t aPermission, int64_t aID,
uint32_t aExpireType, int64_t aExpireTime)
: mType(aType)
, mPermission(aPermission)
, mID(aID)
, mExpireType(aExpireType)
, mExpireTime(aExpireTime) {}
uint32_t mType;
uint32_t mPermission;
int64_t mID;
uint32_t mExpireType;
int64_t mExpireTime;
};
class nsHostEntry : public PLDHashEntryHdr
{
public:
// Hash methods
typedef const char* KeyType;
typedef const char* KeyTypePointer;
nsHostEntry(const char* aHost);
nsHostEntry(const nsHostEntry& toCopy);
~nsHostEntry()
{
}
KeyType GetKey() const
{
return mHost;
}
bool KeyEquals(KeyTypePointer aKey) const
{
return !strcmp(mHost, aKey);
}
static KeyTypePointer KeyToPointer(KeyType aKey)
{
return aKey;
}
static PLDHashNumber HashKey(KeyTypePointer aKey)
{
// PL_DHashStringKey doesn't use the table parameter, so we can safely
// pass nullptr
return PL_DHashStringKey(nullptr, aKey);
}
// force the hashtable to use the copy constructor when shuffling entries
// around, otherwise the Auto part of our nsAutoTArray won't be happy!
enum { ALLOW_MEMMOVE = false };
// Permissions methods
inline const nsDependentCString GetHost() const
{
return nsDependentCString(mHost);
}
inline nsTArray<nsPermissionEntry> & GetPermissions()
{
return mPermissions;
}
inline int32_t GetPermissionIndex(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return i;
return -1;
}
inline nsPermissionEntry GetPermission(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return mPermissions[i];
// unknown permission... return relevant data
nsPermissionEntry unk = nsPermissionEntry(aType, nsIPermissionManager::UNKNOWN_ACTION,
-1, nsIPermissionManager::EXPIRE_NEVER, 0);
return unk;
}
private:
const char *mHost;
nsAutoTArray<nsPermissionEntry, 1> mPermissions;
};
class nsPermissionManager : public nsIPermissionManager,
public nsIObserver,
public nsSupportsWeakReference
{
public:
class PermissionEntry
{
public:
PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime)
: mID(aID)
, mType(aType)
, mPermission(aPermission)
, mExpireType(aExpireType)
, mExpireTime(aExpireTime)
{}
int64_t mID;
uint32_t mType;
uint32_t mPermission;
uint32_t mExpireType;
int64_t mExpireTime;
};
/**
* PermissionKey is the key used by PermissionHashKey hash table.
*
* NOTE: It could be implementing nsIHashable but there is no reason to worry
* with XPCOM interfaces while we don't need to.
*/
class PermissionKey
{
public:
PermissionKey(nsIPrincipal* aPrincipal);
PermissionKey(const nsACString& aHost,
uint32_t aAppId,
bool aIsInBrowserElement)
: mHost(aHost)
, mAppId(aAppId)
, mIsInBrowserElement(aIsInBrowserElement)
{
}
bool operator==(const PermissionKey& aKey) const {
return mHost.Equals(aKey.mHost) &&
mAppId == aKey.mAppId &&
mIsInBrowserElement == aKey.mIsInBrowserElement;
}
PLDHashNumber GetHashCode() const {
nsCAutoString str;
str.Assign(mHost);
str.AppendInt(mAppId);
str.AppendInt(static_cast<int32_t>(mIsInBrowserElement));
return mozilla::HashString(str);
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey);
nsCString mHost;
uint32_t mAppId;
bool mIsInBrowserElement;
private:
// Default ctor shouldn't be used.
PermissionKey() MOZ_DELETE;
// Dtor shouldn't be used outside of the class.
~PermissionKey() {};
};
class PermissionHashKey : public nsRefPtrHashKey<PermissionKey>
{
public:
PermissionHashKey(const PermissionKey* aPermissionKey)
: nsRefPtrHashKey<PermissionKey>(aPermissionKey)
{}
PermissionHashKey(const PermissionHashKey& toCopy)
: nsRefPtrHashKey<PermissionKey>(toCopy)
, mPermissions(toCopy.mPermissions)
{}
bool KeyEquals(const PermissionKey* aKey) const
{
return *aKey == *GetKey();
}
static PLDHashNumber HashKey(const PermissionKey* aKey)
{
return aKey->GetHashCode();
}
// Force the hashtable to use the copy constructor when shuffling entries
// around, otherwise the Auto part of our nsAutoTArray won't be happy!
enum { ALLOW_MEMMOVE = false };
inline nsTArray<PermissionEntry> & GetPermissions()
{
return mPermissions;
}
inline int32_t GetPermissionIndex(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return i;
return -1;
}
inline PermissionEntry GetPermission(uint32_t aType) const
{
for (uint32_t i = 0; i < mPermissions.Length(); ++i)
if (mPermissions[i].mType == aType)
return mPermissions[i];
// unknown permission... return relevant data
return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
nsIPermissionManager::EXPIRE_NEVER, 0);
}
private:
nsAutoTArray<PermissionEntry, 1> mPermissions;
};
// nsISupports
NS_DECL_ISUPPORTS
@ -164,13 +191,14 @@ public:
DBOperationType aDBOperation);
private:
int32_t GetTypeIndex(const char *aTypeString,
bool aAdd);
nsHostEntry *GetHostEntry(const nsAFlatCString &aHost,
uint32_t aType,
bool aExactHostMatch);
PermissionHashKey* GetPermissionHashKey(const nsACString& aHost,
uint32_t aAppId,
bool aIsInBrowserElement,
uint32_t aType,
bool aExactHostMatch);
nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
const char *aType,
@ -196,7 +224,6 @@ private:
nsresult RemoveAllInternal(bool aNotifyObservers);
nsresult RemoveAllFromMemory();
nsresult NormalizeToACE(nsCString &aHost);
nsresult GetHost(nsIURI *aURI, nsACString &aResult);
static void UpdateDB(OperationType aOp,
mozIStorageStatement* aStmt,
int64_t aID,
@ -214,7 +241,7 @@ private:
nsCOMPtr<mozIStorageStatement> mStmtDelete;
nsCOMPtr<mozIStorageStatement> mStmtUpdate;
nsTHashtable<nsHostEntry> mHostTable;
nsTHashtable<PermissionHashKey> mPermissionTable;
// a unique, monotonically increasing id used to identify each database entry
int64_t mLargestID;