Commit 214e0300 authored by Ehsan Akhgari's avatar Ehsan Akhgari
Browse files

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.
parent 05a604bb
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -14,6 +14,10 @@ UNIFIED_SOURCES += [
    'nsPopupWindowManager.cpp',
]

LOCAL_INCLUDES += [
    '/caps',
]

include('/ipc/chromium/chromium-config.mozbuild')

FINAL_LIBRARY = 'xul'
+139 −38
Original line number Diff line number Diff line
/* -*- 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,20 +240,14 @@ 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
// 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<nsIPrincipal>
GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal)
already_AddRefed<nsIURI>
GetNextSubDomainURI(nsIURI* aURI)
{
  nsCOMPtr<nsIURI> uri;
  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
  if (NS_FAILED(rv) || !uri) {
    return nullptr;
  }

  nsAutoCString host;
  rv = uri->GetHost(host);
  nsresult rv = aURI->GetHost(host);
  if (NS_FAILED(rv)) {
    return nullptr;
  }
@@ -261,18 +257,38 @@ GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal)
    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)) {
  nsCOMPtr<nsIURI> uri;
  rv = aURI->Clone(getter_AddRefs(uri));
  if (NS_FAILED(rv) || !uri) {
    return nullptr;
  }

  rv = newURI->SetHost(domain);
  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.
already_AddRefed<nsIPrincipal>
GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal)
{
  nsCOMPtr<nsIURI> uri;
  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
  if (NS_FAILED(rv) || !uri) {
    return nullptr;
  }

  // Create a new principal which is identical to the current one, but with the new host
  nsCOMPtr<nsIURI> newURI = GetNextSubDomainURI(uri);
  if (!newURI) {
    return nullptr;
  }

  // Copy the attributes over
  mozilla::OriginAttributes attrs = aPrincipal->OriginAttributesRef();

@@ -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,
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()) {
+30 −3
Original line number Diff line number Diff line
/* -*- 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,8 +276,32 @@ 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,
                                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)
  {
    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,