From 5eef733ad5bd79022b24617717b76d6ddb222ada Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Wed, 8 Mar 2017 14:28:04 -0500 Subject: [PATCH] Bug 1345573 - Part 1: Key http, https, and ftp URIs on origin instead of eTLD+1, r=baku MozReview-Commit-ID: Gihc4QFf11R --- dom/ipc/ContentParent.cpp | 9 +- extensions/cookie/nsPermissionManager.cpp | 148 +++++++++++++--------- extensions/cookie/nsPermissionManager.h | 17 +++ 3 files changed, 113 insertions(+), 61 deletions(-) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index d99680c12ce9..41c6a0f1fa8c 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5096,9 +5096,12 @@ ContentParent::TransmitPermissionsFor(nsIChannel* aChannel) NS_ENSURE_SUCCESS(rv, rv); // Create the key, and send it down to the content process. - nsAutoCString key; - nsPermissionManager::GetKeyForPrincipal(principal, key); - EnsurePermissionsByKey(key); + nsTArray keys = + nsPermissionManager::GetAllKeysForPrincipal(principal); + MOZ_ASSERT(keys.Length() >= 1); + for (auto& key : keys) { + EnsurePermissionsByKey(key); + } #endif return NS_OK; diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index e48454e7bf9e..f1b53cbaa9ce 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -176,6 +176,54 @@ GetNextSubDomainForHost(const nsACString& aHost) return subDomain; } +// 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. +already_AddRefed +GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal) +{ + nsCOMPtr uri; + nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); + if (NS_FAILED(rv) || !uri) { + 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 newURI; + rv = uri->Clone(getter_AddRefs(newURI)); + if (NS_FAILED(rv)) { + return nullptr; + } + + rv = newURI->SetHost(domain); + if (NS_FAILED(rv)) { + return nullptr; + } + + // Copy the attributes over + mozilla::OriginAttributes attrs = aPrincipal->OriginAttributesRef(); + + // Disable userContext and firstParty isolation for permissions. + attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID | + mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN); + + nsCOMPtr principal = + mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs); + + return principal.forget(); +} + class ClearOriginDataObserver final : public nsIObserver { ~ClearOriginDataObserver() {} @@ -2185,46 +2233,11 @@ nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal, // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry. if (!aExactHostMatch) { - nsCOMPtr uri; - nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); - if (NS_FAILED(rv)) { - 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 newURI; - rv = uri->Clone(getter_AddRefs(newURI)); - if (NS_FAILED(rv)) { - return nullptr; - } - - rv = newURI->SetHost(domain); - if (NS_FAILED(rv)) { - return nullptr; - } - - // Copy the attributes over - mozilla::OriginAttributes attrs = aPrincipal->OriginAttributesRef(); - - // Disable userContext and firstParty isolation for permissions. - attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID | - mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN); - nsCOMPtr principal = - mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs); - - return GetPermissionHashKey(principal, aType, aExactHostMatch); + GetNextSubDomainPrincipal(aPrincipal); + if (principal) { + return GetPermissionHashKey(principal, aType, aExactHostMatch); + } } // No entry, really... @@ -3014,7 +3027,6 @@ nsPermissionManager::SetPermissionsWithKey(const nsACString& aPermissionKey, return NS_OK; } -// XXX: Support file URIs here as well! /* static */ void nsPermissionManager::GetKeyForPrincipal(nsIPrincipal* aPrincipal, nsACString& aKey) { @@ -3027,31 +3039,51 @@ nsPermissionManager::GetKeyForPrincipal(nsIPrincipal* aPrincipal, nsACString& aK // NOTE: We don't propagate the error here, instead we produce the default // "" permission key. This means that we can assign every principal a key, // even if the GetURI operation on that principal is not meaningful. + aKey.Truncate(); return; } - // If the URI isn't of one of the supported schemes, it has the "" permission - // key. We can do an early return in that case. nsAutoCString scheme; - uri->GetScheme(scheme); - if (!scheme.EqualsLiteral("http") && - !scheme.EqualsLiteral("https") && - !scheme.EqualsLiteral("ftp")) { + rv = uri->GetScheme(scheme); + if (NS_WARN_IF(NS_FAILED(rv))) { + // NOTE: Produce the default "" key as a fallback. + aKey.Truncate(); return; } - // We key sets of permissions to be sent over IPC based on their eTLD+1, or in - // the case where that isn't meaningful, on their IP address or spec. - nsCOMPtr etldService = - do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); - rv = etldService->GetBaseDomain(uri, 0, aKey); - if (NS_FAILED(rv)) { - rv = uri->GetHost(aKey); - } - if (NS_FAILED(rv)) { - rv = uri->GetSpec(aKey); + // URIs which have schemes other than http, https and ftp share the "" + // permission key. + if (scheme.EqualsLiteral("http") || + scheme.EqualsLiteral("https") || + scheme.EqualsLiteral("ftp")) { + rv = GetOriginFromPrincipal(aPrincipal, aKey); + if (NS_SUCCEEDED(rv)) { + return; + } } - if (NS_FAILED(rv)) { - aKey.Truncate(); + + // NOTE: Produce the default "" key as a fallback. + aKey.Truncate(); + return; +} + +/* static */ nsTArray +nsPermissionManager::GetAllKeysForPrincipal(nsIPrincipal* aPrincipal) +{ + MOZ_ASSERT(aPrincipal); + + nsTArray keys; + nsCOMPtr prin = aPrincipal; + while (prin) { + // Add the key to the list + nsCString* key = keys.AppendElement(); + GetKeyForPrincipal(prin, *key); + + // Get the next subdomain principal and loop back around. + prin = GetNextSubDomainPrincipal(prin); } + + MOZ_ASSERT(keys.Length() >= 1, + "Every principal should have at least one key."); + return keys; } diff --git a/extensions/cookie/nsPermissionManager.h b/extensions/cookie/nsPermissionManager.h index 5e32fc0d3dfb..dc48e6d2e5b6 100644 --- a/extensions/cookie/nsPermissionManager.h +++ b/extensions/cookie/nsPermissionManager.h @@ -223,6 +223,23 @@ class nsPermissionManager final : public nsIPermissionManager, */ static void GetKeyForPrincipal(nsIPrincipal* aPrincipal, nsACString& aPermissionKey); + /** + * See `nsIPermissionManager::GetPermissionsWithKey` for more info on + * permission keys. + * + * Get all permissions keys which could correspond to the given principal. + * This method, like GetKeyForPrincipal, is infallible and should always + * produce at least one key. + * + * Unlike GetKeyForPrincipal, this method also gets the keys for base domains + * of the given principal. All keys returned by this method must be avaliable + * in the content process for a given URL to successfully have its permissions + * checked in the `aExactHostMatch = false` situation. + * + * @param aPrincipal The Principal which the key is to be extracted from. + */ + static nsTArray GetAllKeysForPrincipal(nsIPrincipal* aPrincipal); + private: virtual ~nsPermissionManager();