Commit 3ffc5e3e authored by Nika Layzell's avatar Nika Layzell
Browse files

Bug 1345573 - Part 1: Key http, https, and ftp URIs on origin instead of eTLD+1, r=baku

MozReview-Commit-ID: Gihc4QFf11R
parent b6012319
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -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);
  nsTArray<nsCString> keys =
    nsPermissionManager::GetAllKeysForPrincipal(principal);
  MOZ_ASSERT(keys.Length() >= 1);
  for (auto& key : keys) {
    EnsurePermissionsByKey(key);
  }
#endif

  return NS_OK;
+90 −58
Original line number Diff line number Diff line
@@ -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<nsIPrincipal>
GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal)
{
  nsCOMPtr<nsIURI> 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<nsIURI> 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<nsIPrincipal> principal =
    mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs);

  return principal.forget();
}

class ClearOriginDataObserver final : public nsIObserver {
  ~ClearOriginDataObserver() {}

@@ -2185,47 +2233,12 @@ nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal,

  // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
  if (!aExactHostMatch) {
    nsCOMPtr<nsIURI> 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<nsIURI> 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<nsIPrincipal> principal =
      mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs);

      GetNextSubDomainPrincipal(aPrincipal);
    if (principal) {
      return GetPermissionHashKey(principal, aType, aExactHostMatch);
    }
  }

  // No entry, really...
  return nullptr;
@@ -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<nsIEffectiveTLDService> etldService =
    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
  rv = etldService->GetBaseDomain(uri, 0, aKey);
  if (NS_FAILED(rv)) {
    rv = uri->GetHost(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)) {
    rv = uri->GetSpec(aKey);
  }
  if (NS_FAILED(rv)) {

  // NOTE: Produce the default "" key as a fallback.
  aKey.Truncate();
  return;
}

/* static */ nsTArray<nsCString>
nsPermissionManager::GetAllKeysForPrincipal(nsIPrincipal* aPrincipal)
{
  MOZ_ASSERT(aPrincipal);

  nsTArray<nsCString> keys;
  nsCOMPtr<nsIPrincipal> 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;
}
+17 −0
Original line number Diff line number Diff line
@@ -223,6 +223,23 @@ public:
   */
  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<nsCString> GetAllKeysForPrincipal(nsIPrincipal* aPrincipal);

private:
  virtual ~nsPermissionManager();