diff options
Diffstat (limited to 'extensions/permissions/PermissionManager.h')
-rw-r--r-- | extensions/permissions/PermissionManager.h | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/extensions/permissions/PermissionManager.h b/extensions/permissions/PermissionManager.h new file mode 100644 index 0000000000..a92d583e49 --- /dev/null +++ b/extensions/permissions/PermissionManager.h @@ -0,0 +1,682 @@ +/* -*- 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/. */ + +#ifndef mozilla_PermissionManager_h +#define mozilla_PermissionManager_h + +#include "nsIPermissionManager.h" +#include "nsIAsyncShutdown.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" +#include "nsCOMPtr.h" +#include "nsIURI.h" +#include "nsTHashtable.h" +#include "nsTArray.h" +#include "nsString.h" +#include "nsHashKeys.h" +#include "nsRefPtrHashtable.h" +#include "mozilla/Atomics.h" +#include "mozilla/Monitor.h" +#include "mozilla/MozPromise.h" +#include "mozilla/OriginAttributes.h" +#include "mozilla/StaticMutex.h" +#include "mozilla/ThreadBound.h" +#include "mozilla/Variant.h" +#include "mozilla/Vector.h" + +#include <utility> + +class mozIStorageConnection; +class mozIStorageStatement; +class nsIInputStream; +class nsIPermission; +class nsIPrefBranch; + +namespace IPC { +struct Permission; +} + +namespace mozilla { +class OriginAttributesPattern; + +namespace dom { +class ContentChild; +} // namespace dom + +//////////////////////////////////////////////////////////////////////////////// + +class PermissionManager final : public nsIPermissionManager, + public nsIObserver, + public nsSupportsWeakReference, + public nsIAsyncShutdownBlocker { + friend class dom::ContentChild; + + public: + class PermissionEntry { + public: + PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission, + uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime) + : mID(aID), + mExpireTime(aExpireTime), + mModificationTime(aModificationTime), + mType(aType), + mPermission(aPermission), + mExpireType(aExpireType), + mNonSessionPermission(aPermission), + mNonSessionExpireType(aExpireType), + mNonSessionExpireTime(aExpireTime) {} + + int64_t mID; + int64_t mExpireTime; + int64_t mModificationTime; + uint32_t mType; + uint32_t mPermission; + uint32_t mExpireType; + uint32_t mNonSessionPermission; + uint32_t mNonSessionExpireType; + uint32_t mNonSessionExpireTime; + }; + + /** + * PermissionKey is the key used by PermissionHashKey hash table. + */ + class PermissionKey { + public: + static PermissionKey* CreateFromPrincipal(nsIPrincipal* aPrincipal, + bool aForceStripOA, + bool aScopeToSite, + nsresult& aResult); + static PermissionKey* CreateFromURI(nsIURI* aURI, nsresult& aResult); + static PermissionKey* CreateFromURIAndOriginAttributes( + nsIURI* aURI, const OriginAttributes* aOriginAttributes, + bool aForceStripOA, nsresult& aResult); + + explicit PermissionKey(const nsACString& aOrigin) + : mOrigin(aOrigin), mHashCode(HashString(aOrigin)) {} + + bool operator==(const PermissionKey& aKey) const { + return mOrigin.Equals(aKey.mOrigin); + } + + PLDHashNumber GetHashCode() const { return mHashCode; } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey) + + const nsCString mOrigin; + const PLDHashNumber mHashCode; + + private: + // Default ctor shouldn't be used. + PermissionKey() = delete; + + // Dtor shouldn't be used outside of the class. + ~PermissionKey(){}; + }; + + class PermissionHashKey : public nsRefPtrHashKey<PermissionKey> { + public: + explicit PermissionHashKey(const PermissionKey* aPermissionKey) + : nsRefPtrHashKey<PermissionKey>(aPermissionKey) {} + + PermissionHashKey(PermissionHashKey&& toCopy) + : nsRefPtrHashKey<PermissionKey>(std::move(toCopy)), + mPermissions(std::move(toCopy.mPermissions)) {} + + bool KeyEquals(const PermissionKey* aKey) const { + return *aKey == *GetKey(); + } + + static PLDHashNumber HashKey(const PermissionKey* aKey) { + return aKey->GetHashCode(); + } + + // Force the hashtable to use the copy constructor when shuffling entries + // around, otherwise the Auto part of our AutoTArray won't be happy! + enum { ALLOW_MEMMOVE = false }; + + inline nsTArray<PermissionEntry>& GetPermissions() { return mPermissions; } + inline const nsTArray<PermissionEntry>& GetPermissions() const { + return mPermissions; + } + + inline int32_t GetPermissionIndex(uint32_t aType) const { + for (uint32_t i = 0; i < mPermissions.Length(); ++i) + if (mPermissions[i].mType == aType) return i; + + return -1; + } + + inline PermissionEntry GetPermission(uint32_t aType) const { + for (uint32_t i = 0; i < mPermissions.Length(); ++i) + if (mPermissions[i].mType == aType) return mPermissions[i]; + + // unknown permission... return relevant data + return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION, + nsIPermissionManager::EXPIRE_NEVER, 0, 0); + } + + private: + AutoTArray<PermissionEntry, 1> mPermissions; + }; + + // nsISupports + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIPERMISSIONMANAGER + NS_DECL_NSIOBSERVER + NS_DECL_NSIASYNCSHUTDOWNBLOCKER + + PermissionManager(); + static already_AddRefed<nsIPermissionManager> GetXPCOMSingleton(); + static PermissionManager* GetInstance(); + nsresult Init(); + + // enums for AddInternal() + enum OperationType { + eOperationNone, + eOperationAdding, + eOperationRemoving, + eOperationChanging, + eOperationReplacingDefault + }; + + enum DBOperationType { eNoDBOperation, eWriteToDB }; + + enum NotifyOperationType { eDontNotify, eNotify }; + + // Similar to TestPermissionFromPrincipal, except that it is used only for + // permissions which can never have default values. + nsresult TestPermissionWithoutDefaultsFromPrincipal(nsIPrincipal* aPrincipal, + const nsACString& aType, + uint32_t* aPermission); + + nsresult LegacyTestPermissionFromURI( + nsIURI* aURI, const OriginAttributes* aOriginAttributes, + const nsACString& aType, uint32_t* aPermission); + + nsresult RemovePermissionsWithAttributes(OriginAttributesPattern& aAttrs); + + /** + * See `nsIPermissionManager::GetPermissionsWithKey` for more info on + * permission keys. + * + * Get the permission key corresponding to the given Principal. 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. + * + * @param aPrincipal The Principal which the key is to be extracted from. + * @param aForceStripOA Whether to force stripping the principals origin + * attributes prior to generating the key. + * @param aSiteScopePermissions Whether to prepare the key for permissions + * scoped to the Principal's site, rather than origin. These are looked + * up independently. Scoping of a permission is fully determined by its + * type and determined by calls to the function IsSiteScopedPermission. + * @param aKey A string which will be filled with the permission + * key. + */ + static nsresult GetKeyForPrincipal(nsIPrincipal* aPrincipal, + bool aForceStripOA, + bool aSiteScopePermissions, + nsACString& aKey); + + /** + * See `nsIPermissionManager::GetPermissionsWithKey` for more info on + * permission keys. + * + * Get the permission key corresponding to the given Origin. This method is + * like GetKeyForPrincipal, except that it avoids creating a nsIPrincipal + * object when you already have access to an origin string. + * + * If this method is passed a nonsensical origin string it may produce a + * nonsensical permission key result. + * + * @param aOrigin The origin which the key is to be extracted from. + * @param aForceStripOA Whether to force stripping the origins attributes + * prior to generating the key. + * @param aSiteScopePermissions Whether to prepare the key for permissions + * scoped to the Principal's site, rather than origin. These are looked + * up independently. Scoping of a permission is fully determined by its + * type and determined by calls to the function IsSiteScopedPermission. + * @param aKey A string which will be filled with the permission + * key. + */ + static nsresult GetKeyForOrigin(const nsACString& aOrigin, bool aForceStripOA, + bool aSiteScopePermissions, nsACString& aKey); + + /** + * 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 nsresult GetKeyForPermission(nsIPrincipal* aPrincipal, + const nsACString& aType, + nsACString& aKey); + + /** + * 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, origin) pair. + * + * Unlike GetKeyForPrincipal, this method also gets the keys for base domains + * of the given principal. All keys returned by this method must be available + * 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. + * @return returns an array of (key, origin) pairs. + */ + static nsTArray<std::pair<nsCString, nsCString>> GetAllKeysForPrincipal( + nsIPrincipal* aPrincipal); + + // From ContentChild. + nsresult RemoveAllFromIPC(); + + /** + * Returns false if this permission manager wouldn't have the permission + * requested available. + * + * If aType is empty, checks that the permission manager would have all + * permissions available for the given principal. + */ + bool PermissionAvailable(nsIPrincipal* aPrincipal, const nsACString& aType); + + /** + * The content process doesn't have access to every permission. Instead, when + * LOAD_DOCUMENT_URI channels for http://, https://, and ftp:// URIs are + * opened, the permissions for those channels are sent down to the content + * process before the OnStartRequest message. Permissions for principals with + * other schemes are sent down at process startup. + * + * Permissions are keyed and grouped by "Permission Key"s. + * `PermissionManager::GetKeyForPrincipal` provides the mechanism for + * determining the permission key for a given principal. + * + * This method may only be called in the parent process. It fills the nsTArray + * argument with the IPC::Permission objects which have a matching origin. + * + * @param origin The origin to use to find the permissions of interest. + * @param key The key to use to find the permissions of interest. Only used + * when the origin argument is empty. + * @param perms An array which will be filled with the permissions which + * match the given origin. + */ + bool GetPermissionsFromOriginOrKey(const nsACString& aOrigin, + const nsACString& aKey, + nsTArray<IPC::Permission>& aPerms); + + /** + * See `PermissionManager::GetPermissionsWithKey` for more info on + * Permission keys. + * + * `SetPermissionsWithKey` may only be called in the Child process, and + * initializes the permission manager with the permissions for a given + * Permission key. marking permissions with that key as available. + * + * @param permissionKey The key for the permissions which have been sent + * over. + * @param perms An array with the permissions which match the given key. + */ + void SetPermissionsWithKey(const nsACString& aPermissionKey, + nsTArray<IPC::Permission>& aPerms); + + /** + * Add a callback which should be run when all permissions are available for + * the given nsIPrincipal. This method invokes the callback runnable + * synchronously when the permissions are already available. Otherwise the + * callback will be run asynchronously in SystemGroup when all permissions + * are available in the future. + * + * NOTE: This method will not request the permissions be sent by the parent + * process. This should only be used to wait for permissions which may not + * have arrived yet in order to ensure they are present. + * + * @param aPrincipal The principal to wait for permissions to be available + * for. + * @param aRunnable The runnable to run when permissions are available for + * the given principal. + */ + void WhenPermissionsAvailable(nsIPrincipal* aPrincipal, + nsIRunnable* aRunnable); + + /** + * Strip origin attributes for permissions, depending on permission isolation + * pref state. + * @param aForceStrip If true, strips user context and private browsing id, + * ignoring permission isolation prefs. + * @param aOriginAttributes object to strip. + */ + static void MaybeStripOriginAttributes(bool aForceStrip, + OriginAttributes& aOriginAttributes); + + private: + ~PermissionManager(); + static StaticMutex sCreationMutex MOZ_UNANNOTATED; + + /** + * Get all permissions for a given principal, which should not be isolated + * by user context or private browsing. The principal has its origin + * attributes stripped before perm db lookup. This is currently only affects + * the "cookie" permission. + * @param aPrincipal Used for creating the permission key. + * @param aSiteScopePermissions Used to specify whether to get strip perms for + * site scoped permissions (defined in IsSiteScopedPermission) or all other + * permissions. Also used to create the permission key. + */ + nsresult GetStripPermsForPrincipal(nsIPrincipal* aPrincipal, + bool aSiteScopePermissions, + nsTArray<PermissionEntry>& aResult); + + // Returns -1 on failure + int32_t GetTypeIndex(const nsACString& aType, bool aAdd); + + // Returns whether the given combination of expire type and expire time are + // expired. Note that EXPIRE_SESSION only honors expireTime if it is nonzero. + bool HasExpired(uint32_t aExpireType, int64_t aExpireTime); + + // Appends the permissions associated with this principal to aResult. + // If the onlySiteScopePermissions argument is true, the permissions searched + // are those for the site of the principal and only the permissions that are + // site-scoped are used. + nsresult GetAllForPrincipalHelper(nsIPrincipal* aPrincipal, + bool aSiteScopePermissions, + nsTArray<RefPtr<nsIPermission>>& aResult); + + // Returns PermissionHashKey for a given { host, 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. + PermissionHashKey* GetPermissionHashKey(nsIPrincipal* aPrincipal, + uint32_t aType, bool aExactHostMatch); + + // Returns PermissionHashKey for a given { host, 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. + PermissionHashKey* GetPermissionHashKey( + nsIURI* aURI, const OriginAttributes* aOriginAttributes, uint32_t aType, + bool aExactHostMatch); + + // The int32_t is the type index, the nsresult is an early bail-out return + // code. + typedef Variant<int32_t, nsresult> TestPreparationResult; + TestPreparationResult CommonPrepareToTestPermission( + nsIPrincipal* aPrincipal, int32_t aTypeIndex, const nsACString& aType, + uint32_t* aPermission, uint32_t aDefaultPermission, + bool aDefaultPermissionIsValid, bool aExactHostMatch, + bool aIncludingSession); + + // If aTypeIndex is passed -1, we try to inder the type index from aType. + nsresult CommonTestPermission(nsIPrincipal* aPrincipal, int32_t aTypeIndex, + const nsACString& aType, uint32_t* aPermission, + uint32_t aDefaultPermission, + bool aDefaultPermissionIsValid, + bool aExactHostMatch, bool aIncludingSession); + + // If aTypeIndex is passed -1, we try to inder the type index from aType. + nsresult CommonTestPermission(nsIURI* aURI, int32_t aTypeIndex, + const nsACString& aType, uint32_t* aPermission, + uint32_t aDefaultPermission, + bool aDefaultPermissionIsValid, + bool aExactHostMatch, bool aIncludingSession); + + nsresult CommonTestPermission(nsIURI* aURI, + const OriginAttributes* aOriginAttributes, + int32_t aTypeIndex, const nsACString& aType, + uint32_t* aPermission, + uint32_t aDefaultPermission, + bool aDefaultPermissionIsValid, + bool aExactHostMatch, bool aIncludingSession); + + // Only one of aPrincipal or aURI is allowed to be passed in. + nsresult CommonTestPermissionInternal( + nsIPrincipal* aPrincipal, nsIURI* aURI, + const OriginAttributes* aOriginAttributes, int32_t aTypeIndex, + const nsACString& aType, uint32_t* aPermission, bool aExactHostMatch, + bool aIncludingSession); + + nsresult OpenDatabase(nsIFile* permissionsFile); + + void InitDB(bool aRemoveFile); + nsresult TryInitDB(bool aRemoveFile, nsIInputStream* aDefaultsInputStream); + + void AddIdleDailyMaintenanceJob(); + void RemoveIdleDailyMaintenanceJob(); + void PerformIdleDailyMaintenance(); + + nsresult ImportLatestDefaults(); + already_AddRefed<nsIInputStream> GetDefaultsInputStream(); + void ConsumeDefaultsInputStream(nsIInputStream* aDefaultsInputStream, + const MonitorAutoLock& aProofOfLock); + + nsresult CreateTable(); + void NotifyObserversWithPermission(nsIPrincipal* aPrincipal, + const nsACString& aType, + uint32_t aPermission, uint32_t aExpireType, + int64_t aExpireTime, + int64_t aModificationTime, + const char16_t* aData); + void NotifyObservers(nsIPermission* aPermission, const char16_t* aData); + + // Finalize all statements, close the DB and null it. + enum CloseDBNextOp { + eNone, + eRebuldOnSuccess, + eShutdown, + }; + void CloseDB(CloseDBNextOp aNextOp); + + nsresult RemoveAllInternal(bool aNotifyObservers); + nsresult RemoveAllFromMemory(); + + void UpdateDB(OperationType aOp, int64_t aID, const nsACString& aOrigin, + const nsACString& aType, uint32_t aPermission, + uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime); + + /** + * This method removes all permissions modified after the specified time. + */ + nsresult RemoveAllModifiedSince(int64_t aModificationTime); + + template <class T> + nsresult RemovePermissionEntries(T aCondition); + + template <class T> + nsresult GetPermissionEntries(T aCondition, + nsTArray<RefPtr<nsIPermission>>& aResult); + + // This method must be called before doing any operation to be sure that the + // DB reading has been completed. This method is also in charge to complete + // the migrations if needed. + void EnsureReadCompleted(); + + nsresult AddInternal(nsIPrincipal* aPrincipal, const nsACString& aType, + uint32_t aPermission, int64_t aID, uint32_t aExpireType, + int64_t aExpireTime, int64_t aModificationTime, + NotifyOperationType aNotifyOperation, + DBOperationType aDBOperation, + const bool aIgnoreSessionPermissions = false, + const nsACString* aOriginString = nullptr, + const bool aAllowPersistInPrivateBrowsing = false); + + void MaybeAddReadEntryFromMigration(const nsACString& aOrigin, + const nsCString& aType, + uint32_t aPermission, + uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, int64_t aId); + + nsCOMPtr<nsIAsyncShutdownClient> GetAsyncShutdownBarrier() const; + + void MaybeCompleteShutdown(); + + nsRefPtrHashtable<nsCStringHashKey, GenericNonExclusivePromise::Private> + mPermissionKeyPromiseMap; + + nsCOMPtr<nsIFile> mPermissionsFile; + + // This monitor is used to ensure the database reading before any other + // operation. The reading of the database happens OMT. See |State| to know the + // steps of the database reading. + Monitor mMonitor MOZ_UNANNOTATED; + + enum State { + // Initial state. The database has not been read yet. + // |TryInitDB| is called at startup time to read the database OMT. + // During the reading, |mReadEntries| will be populated with all the + // existing permissions. + eInitializing, + + // At the end of the database reading, we are in this state. A runnable is + // executed to call |EnsureReadCompleted| on the main thread. + // |EnsureReadCompleted| processes |mReadEntries| and goes to the next + // state. + eDBInitialized, + + // The permissions are fully read and any pending operation can proceed. + eReady, + + // The permission manager has been terminated. No extra database operations + // will be allowed. + eClosed, + }; + Atomic<State> mState; + + // A single entry, from the database. + struct ReadEntry { + ReadEntry() + : mId(0), + mPermission(0), + mExpireType(0), + mExpireTime(0), + mModificationTime(0) {} + + nsCString mOrigin; + nsCString mType; + int64_t mId; + uint32_t mPermission; + uint32_t mExpireType; + int64_t mExpireTime; + int64_t mModificationTime; + + // true if this entry is the result of a migration. + bool mFromMigration; + }; + + // List of entries read from the database. It will be populated OMT and + // consumed on the main-thread. + // This array is protected by the monitor. + nsTArray<ReadEntry> mReadEntries; + + // A single entry, from the database. + struct MigrationEntry { + MigrationEntry() + : mId(0), + mPermission(0), + mExpireType(0), + mExpireTime(0), + mModificationTime(0), + mIsInBrowserElement(false) {} + + nsCString mHost; + nsCString mType; + int64_t mId; + uint32_t mPermission; + uint32_t mExpireType; + int64_t mExpireTime; + int64_t mModificationTime; + + // Legacy, for migration. + bool mIsInBrowserElement; + }; + + // List of entries read from the database. It will be populated OMT and + // consumed on the main-thread. The migration entries will be converted to + // ReadEntry in |CompleteMigrations|. + // This array is protected by the monitor. + nsTArray<MigrationEntry> mMigrationEntries; + + // A single entry from the defaults URL. + struct DefaultEntry { + DefaultEntry() : mOp(eImportMatchTypeHost), mPermission(0) {} + + enum Op { + eImportMatchTypeHost, + eImportMatchTypeOrigin, + }; + + Op mOp; + + nsCString mHostOrOrigin; + nsCString mType; + uint32_t mPermission; + }; + + // List of entries read from the default settings. + // This array is protected by the monitor. + nsTArray<DefaultEntry> mDefaultEntries; + + nsresult Read(const MonitorAutoLock& aProofOfLock); + void CompleteRead(); + + void CompleteMigrations(); + + bool mMemoryOnlyDB; + + nsTHashtable<PermissionHashKey> mPermissionTable; + // a unique, monotonically increasing id used to identify each database entry + int64_t mLargestID; + + nsCOMPtr<nsIPrefBranch> mDefaultPrefBranch; + + // NOTE: Ensure this is the last member since it has a large inline buffer. + // An array to store the strings identifying the different types. + Vector<nsCString, 512> mTypeArray; + + nsCOMPtr<nsIThread> mThread; + + struct ThreadBoundData { + nsCOMPtr<mozIStorageConnection> mDBConn; + + nsCOMPtr<mozIStorageStatement> mStmtInsert; + nsCOMPtr<mozIStorageStatement> mStmtDelete; + nsCOMPtr<mozIStorageStatement> mStmtUpdate; + }; + ThreadBound<ThreadBoundData> mThreadBoundData; + + friend class DeleteFromMozHostListener; + friend class CloseDatabaseListener; +}; + +// {4F6B5E00-0C36-11d5-A535-0010A401EB10} +#define NS_PERMISSIONMANAGER_CID \ + { \ + 0x4f6b5e00, 0xc36, 0x11d5, { \ + 0xa5, 0x35, 0x0, 0x10, 0xa4, 0x1, 0xeb, 0x10 \ + } \ + } + +} // namespace mozilla + +#endif // mozilla_PermissionManager_h |