mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 07:13:20 +00:00
Bug 1353179
- Fix the content process permission assertion, and add support for pre-load permissions, r=baku
MozReview-Commit-ID: DAVPue8krnH
This commit is contained in:
parent
8b7ba021a4
commit
4966d64d16
@ -83,6 +83,59 @@ LogToConsole(const nsAString& aMsg)
|
||||
|
||||
namespace {
|
||||
|
||||
// These permissions are special permissions which must be transmitted to the
|
||||
// content process before documents with their principals have loaded within
|
||||
// that process. For example, the permissions which are used for content
|
||||
// blocking are sent using this mechanism.
|
||||
//
|
||||
// Permissions which are in this list are considered to have a "" permission
|
||||
// key, even if their principal would not normally have that key.
|
||||
static const char* kPreloadPermissions[] = {
|
||||
// NOTE: These permissions are the different nsContentBlocker permissions for
|
||||
// allowing or denying certain content types from being loaded. Every
|
||||
// permission listed in the `kTypeString` array in nsContentBlocker.cpp should
|
||||
// appear in this list.
|
||||
"other",
|
||||
"script",
|
||||
"image",
|
||||
"stylesheet",
|
||||
"object",
|
||||
"document",
|
||||
"subdocument",
|
||||
"refresh",
|
||||
"xbl",
|
||||
"ping",
|
||||
"xmlhttprequest",
|
||||
"objectsubrequest",
|
||||
"dtd",
|
||||
"font",
|
||||
"media",
|
||||
"websocket",
|
||||
"csp_report",
|
||||
"xslt",
|
||||
"beacon",
|
||||
"fetch",
|
||||
"image",
|
||||
"manifest"
|
||||
// ------------------------------------------
|
||||
};
|
||||
|
||||
// NOTE: nullptr can be passed as aType - if it is this function will return
|
||||
// "false" unconditionally.
|
||||
bool
|
||||
IsPreloadPermission(const char* aType)
|
||||
{
|
||||
if (aType) {
|
||||
for (uint32_t i = 0; i < mozilla::ArrayLength(kPreloadPermissions); ++i) {
|
||||
if (!strcmp(aType, kPreloadPermissions[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetOriginFromPrincipal(nsIPrincipal* aPrincipal, nsACString& aOrigin)
|
||||
{
|
||||
@ -650,22 +703,6 @@ nsPermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal* aPrincipal
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Creating a PermissionsKey to look up a permission if we haven't had those
|
||||
// keys synced down yet is problematic, so we do a check here and crash on
|
||||
// debug builds if we see it happening.
|
||||
if (XRE_IsContentProcess()) {
|
||||
nsAutoCString permissionKey;
|
||||
GetKeyForPrincipal(aPrincipal, permissionKey);
|
||||
|
||||
if (!gPermissionManager->mAvailablePermissionKeys.Contains(permissionKey)) {
|
||||
NS_WARNING(nsPrintfCString("This content process hasn't received the "
|
||||
"permissions for %s yet", permissionKey.get()).get());
|
||||
MOZ_CRASH("The content process hasn't recieved permissions for an origin yet.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return new PermissionKey(origin);
|
||||
}
|
||||
|
||||
@ -1619,7 +1656,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
aExpireType, aExpireTime);
|
||||
|
||||
nsAutoCString permissionKey;
|
||||
GetKeyForPrincipal(aPrincipal, permissionKey);
|
||||
GetKeyForPermission(aPrincipal, aType.get(), permissionKey);
|
||||
|
||||
nsTArray<ContentParent*> cplist;
|
||||
ContentParent::GetAll(cplist);
|
||||
@ -1630,6 +1667,8 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType.get()));
|
||||
|
||||
// look up the type index
|
||||
int32_t typeIndex = GetTypeIndex(aType.get(), true);
|
||||
NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
|
||||
@ -2096,6 +2135,8 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
|
||||
|
||||
int32_t typeIndex = GetTypeIndex(aType, false);
|
||||
// If type == -1, the type isn't known,
|
||||
// so just return NS_OK
|
||||
@ -2173,6 +2214,8 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
|
||||
|
||||
int32_t typeIndex = GetTypeIndex(aType, false);
|
||||
// If type == -1, the type isn't known,
|
||||
// so just return NS_OK
|
||||
@ -2204,6 +2247,8 @@ nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal,
|
||||
uint32_t aType,
|
||||
bool aExactHostMatch)
|
||||
{
|
||||
MOZ_ASSERT(PermissionAvaliable(aPrincipal, mTypeArray[aType].get()));
|
||||
|
||||
nsresult rv;
|
||||
RefPtr<PermissionKey> key =
|
||||
PermissionKey::CreateFromPrincipal(aPrincipal, rv);
|
||||
@ -2299,6 +2344,8 @@ NS_IMETHODIMP nsPermissionManager::GetAllForURI(nsIURI* aURI, nsISimpleEnumerato
|
||||
nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
MOZ_ASSERT(PermissionAvaliable(principal, nullptr));
|
||||
|
||||
RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal(principal, rv);
|
||||
if (!key) {
|
||||
MOZ_ASSERT(NS_FAILED(rv));
|
||||
@ -2912,6 +2959,8 @@ nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
|
||||
|
||||
int32_t typeIndex = GetTypeIndex(aType, false);
|
||||
// If type == -1, the type isn't known,
|
||||
// so just return NS_OK
|
||||
@ -2959,15 +3008,6 @@ nsPermissionManager::GetPermissionsWithKey(const nsACString& aPermissionKey,
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the permission key and make sure that it matches the aPermissionKey
|
||||
// passed in.
|
||||
nsAutoCString permissionKey;
|
||||
GetKeyForPrincipal(principal, permissionKey);
|
||||
|
||||
if (permissionKey != aPermissionKey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& permEntry : entry->GetPermissions()) {
|
||||
// Given how "default" permissions work and the possibility of them being
|
||||
// overridden with UNKNOWN_ACTION, we might see this value here - but we
|
||||
@ -2976,11 +3016,20 @@ nsPermissionManager::GetPermissionsWithKey(const nsACString& aPermissionKey,
|
||||
continue;
|
||||
}
|
||||
|
||||
aPerms.AppendElement(IPC::Permission(entry->GetKey()->mOrigin,
|
||||
mTypeArray.ElementAt(permEntry.mType),
|
||||
permEntry.mPermission,
|
||||
permEntry.mExpireType,
|
||||
permEntry.mExpireTime));
|
||||
// XXX: This performs extra work, such as in many cases re-computing the
|
||||
// Origin (which we just computed the nsIPrincipal from). We may want to
|
||||
// implement a custom version of this logic which avoids that extra work.
|
||||
// See bug 1354700.
|
||||
nsAutoCString permissionKey;
|
||||
GetKeyForPermission(principal, mTypeArray[permEntry.mType].get(), permissionKey);
|
||||
|
||||
if (permissionKey == aPermissionKey) {
|
||||
aPerms.AppendElement(IPC::Permission(entry->GetKey()->mOrigin,
|
||||
mTypeArray.ElementAt(permEntry.mType),
|
||||
permEntry.mPermission,
|
||||
permEntry.mExpireType,
|
||||
permEntry.mExpireTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3013,7 +3062,7 @@ nsPermissionManager::SetPermissionsWithKey(const nsACString& aPermissionKey,
|
||||
|
||||
#ifdef DEBUG
|
||||
nsAutoCString permissionKey;
|
||||
GetKeyForPrincipal(principal, permissionKey);
|
||||
GetKeyForPermission(principal, perm.type.get(), permissionKey);
|
||||
MOZ_ASSERT(permissionKey == aPermissionKey,
|
||||
"The permission keys which were sent over should match!");
|
||||
#endif
|
||||
@ -3069,6 +3118,18 @@ nsPermissionManager::GetKeyForPrincipal(nsIPrincipal* aPrincipal, nsACString& aK
|
||||
return;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsPermissionManager::GetKeyForPermission(nsIPrincipal* aPrincipal, const char* aType, nsACString& aKey)
|
||||
{
|
||||
// Preload permissions have the "" key.
|
||||
if (IsPreloadPermission(aType)) {
|
||||
aKey.Truncate();
|
||||
return;
|
||||
}
|
||||
|
||||
GetKeyForPrincipal(aPrincipal, aKey);
|
||||
}
|
||||
|
||||
/* static */ nsTArray<nsCString>
|
||||
nsPermissionManager::GetAllKeysForPrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
@ -3102,3 +3163,21 @@ nsPermissionManager::BroadcastPermissionsForPrincipalToAllContentProcesses(nsIPr
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsPermissionManager::PermissionAvaliable(nsIPrincipal* aPrincipal, const char* aType)
|
||||
{
|
||||
if (XRE_IsContentProcess()) {
|
||||
nsAutoCString permissionKey;
|
||||
// NOTE: GetKeyForPermission accepts a null aType.
|
||||
GetKeyForPermission(aPrincipal, aType, permissionKey);
|
||||
if (!mAvailablePermissionKeys.Contains(permissionKey)) {
|
||||
// Emit a useful diagnostic warning with the permissionKey for the process
|
||||
// which hasn't received permissions yet.
|
||||
NS_WARNING(nsPrintfCString("This content process hasn't received the "
|
||||
"permissions for %s yet", permissionKey.get()).get());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -223,6 +223,27 @@ public:
|
||||
*/
|
||||
static void GetKeyForPrincipal(nsIPrincipal* aPrincipal, nsACString& aPermissionKey);
|
||||
|
||||
/**
|
||||
* See `nsIPermissionManager::GetPermissionsWithKey` for more info on
|
||||
* permission keys.
|
||||
*
|
||||
* Get the permission key corresponding to the given Principal and type. This
|
||||
* method is intentionally infallible, as we want to provide an permission key
|
||||
* to every principal. Principals which don't have meaningful URIs with
|
||||
* http://, https://, or ftp:// schemes are given the default "" Permission
|
||||
* Key.
|
||||
*
|
||||
* This method is different from GetKeyForPrincipal in that it also takes
|
||||
* permissions which must be sent down before loading a document into account.
|
||||
*
|
||||
* @param aPrincipal The Principal which the key is to be extracted from.
|
||||
* @param aType The type of the permission to get the key for.
|
||||
* @param aPermissionKey A string which will be filled with the permission key.
|
||||
*/
|
||||
static void GetKeyForPermission(nsIPrincipal* aPrincipal,
|
||||
const char* aType,
|
||||
nsACString& aPermissionKey);
|
||||
|
||||
/**
|
||||
* See `nsIPermissionManager::GetPermissionsWithKey` for more info on
|
||||
* permission keys.
|
||||
@ -293,6 +314,15 @@ private:
|
||||
nsresult
|
||||
RemoveAllModifiedSince(int64_t aModificationTime);
|
||||
|
||||
/**
|
||||
* Returns false if this permission manager wouldn't have the permission
|
||||
* requested avaliable.
|
||||
*
|
||||
* If aType is nullptr, checks that the permission manager would have all
|
||||
* permissions avaliable for the given principal.
|
||||
*/
|
||||
bool PermissionAvaliable(nsIPrincipal* aPrincipal, const char* aType);
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> mDBConn;
|
||||
nsCOMPtr<mozIStorageAsyncStatement> mStmtInsert;
|
||||
nsCOMPtr<mozIStorageAsyncStatement> mStmtDelete;
|
||||
|
@ -37,22 +37,27 @@ add_task(function* () {
|
||||
addPerm("http://foo.bar.example.com", "perm2");
|
||||
addPerm("about:home", "perm3");
|
||||
addPerm("https://example.com", "perm4");
|
||||
// NOTE: This permission is a preload permission, so it should be avaliable in the content process from startup.
|
||||
addPerm("https://somerandomwebsite.com", "document");
|
||||
|
||||
yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (aBrowser) {
|
||||
yield ContentTask.spawn(aBrowser, null, function* () {
|
||||
// Before the load http URIs shouldn't have been sent down yet
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://example.com"),
|
||||
"perm1"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm1-1");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://foo.bar.example.com"),
|
||||
"perm2"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm2-1");
|
||||
is(Services.perms.testPermission(Services.io.newURI("about:home"),
|
||||
"perm3"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm3-1");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://example.com"),
|
||||
"perm4"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm4-1");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://somerandomwebsite.com"),
|
||||
"document"),
|
||||
Services.perms.ALLOW_ACTION, "document-1");
|
||||
|
||||
// Perform a load of example.com
|
||||
yield new Promise(resolve => {
|
||||
@ -65,50 +70,60 @@ add_task(function* () {
|
||||
// After the load finishes, we should know about example.com, but not foo.bar.example.com
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://example.com"),
|
||||
"perm1"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm1-2");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://foo.bar.example.com"),
|
||||
"perm2"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm2-2");
|
||||
is(Services.perms.testPermission(Services.io.newURI("about:home"),
|
||||
"perm3"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm3-2");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://example.com"),
|
||||
"perm4"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm4-2");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://somerandomwebsite.com"),
|
||||
"document"),
|
||||
Services.perms.ALLOW_ACTION, "document-2");
|
||||
});
|
||||
|
||||
addPerm("http://example.com", "newperm1");
|
||||
addPerm("http://foo.bar.example.com", "newperm2");
|
||||
addPerm("about:home", "newperm3");
|
||||
addPerm("https://example.com", "newperm4");
|
||||
addPerm("https://someotherrandomwebsite.com", "document");
|
||||
|
||||
yield ContentTask.spawn(aBrowser, null, function* () {
|
||||
// The new permissions should be avaliable, but only for
|
||||
// http://example.com, and about:home
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://example.com"),
|
||||
"perm1"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm1-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://example.com"),
|
||||
"newperm1"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "newperm1-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://foo.bar.example.com"),
|
||||
"perm2"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm2-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://foo.bar.example.com"),
|
||||
"newperm2"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "newperm2-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("about:home"),
|
||||
"perm3"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm3-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("about:home"),
|
||||
"newperm3"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "newperm3-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://example.com"),
|
||||
"perm4"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm4-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://example.com"),
|
||||
"newperm4"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "newperm4-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://somerandomwebsite.com"),
|
||||
"document"),
|
||||
Services.perms.ALLOW_ACTION, "document-3");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://someotherrandomwebsite.com"),
|
||||
"document"),
|
||||
Services.perms.ALLOW_ACTION, "otherdocument-3");
|
||||
|
||||
// Loading a subdomain now, on https
|
||||
yield new Promise(resolve => {
|
||||
@ -122,28 +137,34 @@ add_task(function* () {
|
||||
// permissions are also avaliable for its parent domain, https://example.com!
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://example.com"),
|
||||
"perm1"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm1-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://example.com"),
|
||||
"newperm1"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "newperm1-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://foo.bar.example.com"),
|
||||
"perm2"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "perm2-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("http://foo.bar.example.com"),
|
||||
"newperm2"),
|
||||
Services.perms.UNKNOWN_ACTION);
|
||||
Services.perms.UNKNOWN_ACTION, "newperm2-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("about:home"),
|
||||
"perm3"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm3-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("about:home"),
|
||||
"newperm3"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "newperm3-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://example.com"),
|
||||
"perm4"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "perm4-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://example.com"),
|
||||
"newperm4"),
|
||||
Services.perms.ALLOW_ACTION);
|
||||
Services.perms.ALLOW_ACTION, "newperm4-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://somerandomwebsite.com"),
|
||||
"document"),
|
||||
Services.perms.ALLOW_ACTION, "document-4");
|
||||
is(Services.perms.testPermission(Services.io.newURI("https://someotherrandomwebsite.com"),
|
||||
"document"),
|
||||
Services.perms.ALLOW_ACTION, "otherdocument-4");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user