Bug 1362791 - Enable testing permissions using URIs without having to mint principals; r=mystor

The permissions manager store uses principal origins with suffix in the
key entry, but for the API entry points where we accept a raw nsIURI, we
currently mint a new codebase principal with a blank OriginAttributes
only to read out the origin string effectively, since the suffix is
guaranteed to always be an empty string in this case.

This can be slow, so this patch adds a fast path to bypass minting a new
principal and uses ContentPrincipal::GenerateOriginNoSuffixFromURI() to
generate the origin string from the input nsIURI directly.
This commit is contained in:
Ehsan Akhgari 2017-05-06 16:17:52 -04:00
parent 05a604bb4f
commit 214e03003f
3 changed files with 175 additions and 43 deletions

View File

@ -14,6 +14,10 @@ UNIFIED_SOURCES += [
'nsPopupWindowManager.cpp',
]
LOCAL_INCLUDES += [
'/caps',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -41,6 +42,7 @@
#include "nsIObserverService.h"
#include "nsPrintfCString.h"
#include "mozilla/AbstractThread.h"
#include "ContentPrincipal.h"
static nsPermissionManager *gPermissionManager = nullptr;
@ -238,6 +240,37 @@ GetNextSubDomainForHost(const nsACString& aHost)
return subDomain;
}
// This function produces a nsIURI which is identical to the current
// nsIURI, except that it has one less subdomain segment. It returns
// `nullptr` if there are no more segments to remove.
already_AddRefed<nsIURI>
GetNextSubDomainURI(nsIURI* aURI)
{
nsAutoCString host;
nsresult rv = aURI->GetHost(host);
if (NS_FAILED(rv)) {
return nullptr;
}
nsCString domain = GetNextSubDomainForHost(host);
if (domain.IsEmpty()) {
return nullptr;
}
nsCOMPtr<nsIURI> uri;
rv = aURI->Clone(getter_AddRefs(uri));
if (NS_FAILED(rv) || !uri) {
return nullptr;
}
rv = uri->SetHost(domain);
if (NS_FAILED(rv)) {
return nullptr;
}
return uri.forget();
}
// This function produces a nsIPrincipal which is identical to the current
// nsIPrincipal, except that it has one less subdomain segment. It returns
// `nullptr` if there are no more segments to remove.
@ -250,26 +283,9 @@ GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal)
return nullptr;
}
nsAutoCString host;
rv = uri->GetHost(host);
if (NS_FAILED(rv)) {
return nullptr;
}
nsCString domain = GetNextSubDomainForHost(host);
if (domain.IsEmpty()) {
return nullptr;
}
// Create a new principal which is identical to the current one, but with the new host
nsCOMPtr<nsIURI> newURI;
rv = uri->Clone(getter_AddRefs(newURI));
if (NS_FAILED(rv)) {
return nullptr;
}
rv = newURI->SetHost(domain);
if (NS_FAILED(rv)) {
nsCOMPtr<nsIURI> newURI = GetNextSubDomainURI(uri);
if (!newURI) {
return nullptr;
}
@ -711,6 +727,18 @@ nsPermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal* aPrincipal
return new PermissionKey(origin);
}
nsPermissionManager::PermissionKey*
nsPermissionManager::PermissionKey::CreateFromURI(nsIURI* aURI, nsresult& aResult)
{
nsAutoCString origin;
aResult = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, origin);
if (NS_WARN_IF(NS_FAILED(aResult))) {
return nullptr;
}
return new PermissionKey(origin);
}
/**
* Simple callback used by |AsyncClose| to trigger a treatment once
* the database is closed.
@ -2069,11 +2097,7 @@ nsPermissionManager::TestExactPermission(nsIURI *aURI,
const char *aType,
uint32_t *aPermission)
{
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
return TestExactPermissionFromPrincipal(principal, aType, aPermission);
return CommonTestPermission(aURI, aType, aPermission, true, true);
}
NS_IMETHODIMP
@ -2097,11 +2121,7 @@ nsPermissionManager::TestPermission(nsIURI *aURI,
const char *aType,
uint32_t *aPermission)
{
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
return TestPermissionFromPrincipal(principal, aType, aPermission);
return CommonTestPermission(aURI, aType, aPermission, false, true);
}
NS_IMETHODIMP
@ -2198,16 +2218,19 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
}
nsresult
nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
const char *aType,
uint32_t *aPermission,
bool aExactHostMatch,
bool aIncludingSession)
nsPermissionManager::CommonTestPermissionInternal(nsIPrincipal* aPrincipal,
nsIURI * aURI,
const char * aType,
uint32_t * aPermission,
bool aExactHostMatch,
bool aIncludingSession)
{
NS_ENSURE_ARG_POINTER(aPrincipal);
MOZ_ASSERT(aPrincipal || aURI);
MOZ_ASSERT_IF(aPrincipal, !aURI);
NS_ENSURE_ARG_POINTER(aPrincipal || aURI);
NS_ENSURE_ARG_POINTER(aType);
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
if (aPrincipal && nsContentUtils::IsSystemPrincipal(aPrincipal)) {
*aPermission = nsIPermissionManager::ALLOW_ACTION;
return NS_OK;
}
@ -2225,8 +2248,8 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
for (size_t i = 0; i < whitelist->Length(); ++i) {
uint32_t perm;
rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm, aExactHostMatch,
aIncludingSession);
rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm,
aExactHostMatch, aIncludingSession);
NS_ENSURE_SUCCESS(rv, rv);
if (perm == nsIPermissionManager::ALLOW_ACTION) {
*aPermission = perm;
@ -2240,14 +2263,24 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
return NS_OK;
}
MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
#ifdef DEBUG
{
nsCOMPtr<nsIPrincipal> prin = aPrincipal;
if (!prin) {
prin = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, OriginAttributes());
}
MOZ_ASSERT(PermissionAvaliable(prin, aType));
}
#endif
int32_t typeIndex = GetTypeIndex(aType, false);
// If type == -1, the type isn't known,
// so just return NS_OK
if (typeIndex == -1) return NS_OK;
PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
PermissionHashKey* entry = aPrincipal ?
GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch) :
GetPermissionHashKey(aURI, typeIndex, aExactHostMatch);
if (!entry ||
(!aIncludingSession &&
entry->GetPermission(typeIndex).mNonSessionExpireType ==
@ -2317,6 +2350,74 @@ nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal,
return nullptr;
}
// 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.
nsPermissionManager::PermissionHashKey*
nsPermissionManager::GetPermissionHashKey(nsIURI* aURI,
uint32_t aType,
bool aExactHostMatch)
{
#ifdef DEBUG
{
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
MOZ_ASSERT_IF(NS_SUCCEEDED(rv),
PermissionAvaliable(principal, mTypeArray[aType].get()));
}
#endif
nsresult rv;
RefPtr<PermissionKey> key =
PermissionKey::CreateFromURI(aURI, rv);
if (!key) {
return nullptr;
}
PermissionHashKey* entry = mPermissionTable.GetEntry(key);
if (entry) {
PermissionEntry permEntry = entry->GetPermission(aType);
// if the entry is expired, remove and keep looking for others.
// Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME ||
(permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
permEntry.mExpireTime != 0)) &&
permEntry.mExpireTime <= (PR_Now() / 1000)) {
entry = nullptr;
// If we need to remove a permission we mint a principal. This is a bit
// inefficient, but hopefully this code path isn't super common.
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
RemoveFromPrincipal(principal, mTypeArray[aType].get());
} else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
entry = nullptr;
}
}
if (entry) {
return entry;
}
// If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
if (!aExactHostMatch) {
nsCOMPtr<nsIURI> uri = GetNextSubDomainURI(aURI);
if (uri) {
return GetPermissionHashKey(uri, aType, aExactHostMatch);
}
}
// No entry, really...
return nullptr;
}
NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
{
if (XRE_IsContentProcess()) {

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -76,6 +77,8 @@ public:
public:
static PermissionKey* CreateFromPrincipal(nsIPrincipal* aPrincipal,
nsresult& aResult);
static PermissionKey* CreateFromURI(nsIURI* aURI,
nsresult& aResult);
explicit PermissionKey(const nsACString& aOrigin)
: mOrigin(aOrigin)
@ -273,12 +276,36 @@ private:
PermissionHashKey* GetPermissionHashKey(nsIPrincipal* aPrincipal,
uint32_t aType,
bool aExactHostMatch);
PermissionHashKey* GetPermissionHashKey(nsIURI* aURI,
uint32_t aType,
bool aExactHostMatch);
nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
const char *aType,
uint32_t *aPermission,
const char * aType,
uint32_t * aPermission,
bool aExactHostMatch,
bool aIncludingSession)
{
return CommonTestPermissionInternal(aPrincipal, nullptr, aType,
aPermission, aExactHostMatch,
aIncludingSession);
}
nsresult CommonTestPermission(nsIURI * aURI,
const char* aType,
uint32_t * aPermission,
bool aExactHostMatch,
bool aIncludingSession);
bool aIncludingSession)
{
return CommonTestPermissionInternal(nullptr, aURI, aType, aPermission,
aExactHostMatch, aIncludingSession);
}
// Only one of aPrincipal or aURI is allowed to be passed in.
nsresult CommonTestPermissionInternal(nsIPrincipal* aPrincipal,
nsIURI * aURI,
const char * aType,
uint32_t * aPermission,
bool aExactHostMatch,
bool aIncludingSession);
nsresult OpenDatabase(nsIFile* permissionsFile);
nsresult InitDB(bool aRemoveFile);