1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_PermissionManager_h
8 #define mozilla_PermissionManager_h
10 #include "nsIPermissionManager.h"
11 #include "nsIAsyncShutdown.h"
12 #include "nsIObserver.h"
13 #include "nsWeakReference.h"
16 #include "nsTHashtable.h"
19 #include "nsHashKeys.h"
20 #include "nsRefPtrHashtable.h"
21 #include "mozilla/Atomics.h"
22 #include "mozilla/Monitor.h"
23 #include "mozilla/MozPromise.h"
24 #include "mozilla/OriginAttributes.h"
25 #include "mozilla/StaticMutex.h"
26 #include "mozilla/ThreadBound.h"
27 #include "mozilla/Variant.h"
28 #include "mozilla/Vector.h"
32 class mozIStorageConnection
;
33 class mozIStorageStatement
;
43 class OriginAttributesPattern
;
49 ////////////////////////////////////////////////////////////////////////////////
51 class PermissionManager final
: public nsIPermissionManager
,
53 public nsSupportsWeakReference
,
54 public nsIAsyncShutdownBlocker
{
55 friend class dom::ContentChild
;
58 class PermissionEntry
{
60 PermissionEntry(int64_t aID
, uint32_t aType
, uint32_t aPermission
,
61 uint32_t aExpireType
, int64_t aExpireTime
,
62 int64_t aModificationTime
)
64 mExpireTime(aExpireTime
),
65 mModificationTime(aModificationTime
),
67 mPermission(aPermission
),
68 mExpireType(aExpireType
),
69 mNonSessionPermission(aPermission
),
70 mNonSessionExpireType(aExpireType
),
71 mNonSessionExpireTime(aExpireTime
) {}
75 int64_t mModificationTime
;
79 uint32_t mNonSessionPermission
;
80 uint32_t mNonSessionExpireType
;
81 uint32_t mNonSessionExpireTime
;
85 * PermissionKey is the key used by PermissionHashKey hash table.
89 static PermissionKey
* CreateFromPrincipal(nsIPrincipal
* aPrincipal
,
93 static PermissionKey
* CreateFromURI(nsIURI
* aURI
, nsresult
& aResult
);
94 static PermissionKey
* CreateFromURIAndOriginAttributes(
95 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
,
96 bool aForceStripOA
, nsresult
& aResult
);
98 explicit PermissionKey(const nsACString
& aOrigin
)
99 : mOrigin(aOrigin
), mHashCode(HashString(aOrigin
)) {}
101 bool operator==(const PermissionKey
& aKey
) const {
102 return mOrigin
.Equals(aKey
.mOrigin
);
105 PLDHashNumber
GetHashCode() const { return mHashCode
; }
107 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey
)
109 const nsCString mOrigin
;
110 const PLDHashNumber mHashCode
;
113 // Default ctor shouldn't be used.
114 PermissionKey() = delete;
116 // Dtor shouldn't be used outside of the class.
120 class PermissionHashKey
: public nsRefPtrHashKey
<PermissionKey
> {
122 explicit PermissionHashKey(const PermissionKey
* aPermissionKey
)
123 : nsRefPtrHashKey
<PermissionKey
>(aPermissionKey
) {}
125 PermissionHashKey(PermissionHashKey
&& toCopy
)
126 : nsRefPtrHashKey
<PermissionKey
>(std::move(toCopy
)),
127 mPermissions(std::move(toCopy
.mPermissions
)) {}
129 bool KeyEquals(const PermissionKey
* aKey
) const {
130 return *aKey
== *GetKey();
133 static PLDHashNumber
HashKey(const PermissionKey
* aKey
) {
134 return aKey
->GetHashCode();
137 // Force the hashtable to use the copy constructor when shuffling entries
138 // around, otherwise the Auto part of our AutoTArray won't be happy!
139 enum { ALLOW_MEMMOVE
= false };
141 inline nsTArray
<PermissionEntry
>& GetPermissions() { return mPermissions
; }
142 inline const nsTArray
<PermissionEntry
>& GetPermissions() const {
146 inline int32_t GetPermissionIndex(uint32_t aType
) const {
147 for (uint32_t i
= 0; i
< mPermissions
.Length(); ++i
)
148 if (mPermissions
[i
].mType
== aType
) return i
;
153 inline PermissionEntry
GetPermission(uint32_t aType
) const {
154 for (uint32_t i
= 0; i
< mPermissions
.Length(); ++i
)
155 if (mPermissions
[i
].mType
== aType
) return mPermissions
[i
];
157 // unknown permission... return relevant data
158 return PermissionEntry(-1, aType
, nsIPermissionManager::UNKNOWN_ACTION
,
159 nsIPermissionManager::EXPIRE_NEVER
, 0, 0);
163 AutoTArray
<PermissionEntry
, 1> mPermissions
;
167 NS_DECL_THREADSAFE_ISUPPORTS
168 NS_DECL_NSIPERMISSIONMANAGER
170 NS_DECL_NSIASYNCSHUTDOWNBLOCKER
173 static already_AddRefed
<nsIPermissionManager
> GetXPCOMSingleton();
174 static PermissionManager
* GetInstance();
177 // enums for AddInternal()
183 eOperationReplacingDefault
186 enum DBOperationType
{ eNoDBOperation
, eWriteToDB
};
188 enum NotifyOperationType
{ eDontNotify
, eNotify
};
190 // Similar to TestPermissionFromPrincipal, except that it is used only for
191 // permissions which can never have default values.
192 nsresult
TestPermissionWithoutDefaultsFromPrincipal(nsIPrincipal
* aPrincipal
,
193 const nsACString
& aType
,
194 uint32_t* aPermission
);
196 nsresult
LegacyTestPermissionFromURI(
197 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
,
198 const nsACString
& aType
, uint32_t* aPermission
);
200 nsresult
RemovePermissionsWithAttributes(OriginAttributesPattern
& aAttrs
);
203 * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
206 * Get the permission key corresponding to the given Principal. This method is
207 * intentionally infallible, as we want to provide an permission key to every
208 * principal. Principals which don't have meaningful URIs with http://,
209 * https://, or ftp:// schemes are given the default "" Permission Key.
211 * @param aPrincipal The Principal which the key is to be extracted from.
212 * @param aForceStripOA Whether to force stripping the principals origin
213 * attributes prior to generating the key.
214 * @param aSiteScopePermissions Whether to prepare the key for permissions
215 * scoped to the Principal's site, rather than origin. These are looked
216 * up independently. Scoping of a permission is fully determined by its
217 * type and determined by calls to the function IsSiteScopedPermission.
218 * @param aKey A string which will be filled with the permission
221 static nsresult
GetKeyForPrincipal(nsIPrincipal
* aPrincipal
,
223 bool aSiteScopePermissions
,
227 * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
230 * Get the permission key corresponding to the given Origin. This method is
231 * like GetKeyForPrincipal, except that it avoids creating a nsIPrincipal
232 * object when you already have access to an origin string.
234 * If this method is passed a nonsensical origin string it may produce a
235 * nonsensical permission key result.
237 * @param aOrigin The origin which the key is to be extracted from.
238 * @param aForceStripOA Whether to force stripping the origins attributes
239 * prior to generating the key.
240 * @param aSiteScopePermissions Whether to prepare the key for permissions
241 * scoped to the Principal's site, rather than origin. These are looked
242 * up independently. Scoping of a permission is fully determined by its
243 * type and determined by calls to the function IsSiteScopedPermission.
244 * @param aKey A string which will be filled with the permission
247 static nsresult
GetKeyForOrigin(const nsACString
& aOrigin
, bool aForceStripOA
,
248 bool aSiteScopePermissions
, nsACString
& aKey
);
251 * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
254 * Get the permission key corresponding to the given Principal and type. This
255 * method is intentionally infallible, as we want to provide an permission key
256 * to every principal. Principals which don't have meaningful URIs with
257 * http://, https://, or ftp:// schemes are given the default "" Permission
260 * This method is different from GetKeyForPrincipal in that it also takes
261 * permissions which must be sent down before loading a document into account.
263 * @param aPrincipal The Principal which the key is to be extracted from.
264 * @param aType The type of the permission to get the key for.
265 * @param aPermissionKey A string which will be filled with the permission
268 static nsresult
GetKeyForPermission(nsIPrincipal
* aPrincipal
,
269 const nsACString
& aType
,
273 * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
276 * Get all permissions keys which could correspond to the given principal.
277 * This method, like GetKeyForPrincipal, is infallible and should always
278 * produce at least one (key, origin) pair.
280 * Unlike GetKeyForPrincipal, this method also gets the keys for base domains
281 * of the given principal. All keys returned by this method must be available
282 * in the content process for a given URL to successfully have its permissions
283 * checked in the `aExactHostMatch = false` situation.
285 * @param aPrincipal The Principal which the key is to be extracted from.
286 * @return returns an array of (key, origin) pairs.
288 static nsTArray
<std::pair
<nsCString
, nsCString
>> GetAllKeysForPrincipal(
289 nsIPrincipal
* aPrincipal
);
291 // From ContentChild.
292 nsresult
RemoveAllFromIPC();
295 * Returns false if this permission manager wouldn't have the permission
296 * requested available.
298 * If aType is empty, checks that the permission manager would have all
299 * permissions available for the given principal.
301 bool PermissionAvailable(nsIPrincipal
* aPrincipal
, const nsACString
& aType
);
304 * The content process doesn't have access to every permission. Instead, when
305 * LOAD_DOCUMENT_URI channels for http://, https://, and ftp:// URIs are
306 * opened, the permissions for those channels are sent down to the content
307 * process before the OnStartRequest message. Permissions for principals with
308 * other schemes are sent down at process startup.
310 * Permissions are keyed and grouped by "Permission Key"s.
311 * `PermissionManager::GetKeyForPrincipal` provides the mechanism for
312 * determining the permission key for a given principal.
314 * This method may only be called in the parent process. It fills the nsTArray
315 * argument with the IPC::Permission objects which have a matching origin.
317 * @param origin The origin to use to find the permissions of interest.
318 * @param key The key to use to find the permissions of interest. Only used
319 * when the origin argument is empty.
320 * @param perms An array which will be filled with the permissions which
321 * match the given origin.
323 bool GetPermissionsFromOriginOrKey(const nsACString
& aOrigin
,
324 const nsACString
& aKey
,
325 nsTArray
<IPC::Permission
>& aPerms
);
328 * See `PermissionManager::GetPermissionsWithKey` for more info on
331 * `SetPermissionsWithKey` may only be called in the Child process, and
332 * initializes the permission manager with the permissions for a given
333 * Permission key. marking permissions with that key as available.
335 * @param permissionKey The key for the permissions which have been sent
337 * @param perms An array with the permissions which match the given key.
339 void SetPermissionsWithKey(const nsACString
& aPermissionKey
,
340 nsTArray
<IPC::Permission
>& aPerms
);
343 * Add a callback which should be run when all permissions are available for
344 * the given nsIPrincipal. This method invokes the callback runnable
345 * synchronously when the permissions are already available. Otherwise the
346 * callback will be run asynchronously in SystemGroup when all permissions
347 * are available in the future.
349 * NOTE: This method will not request the permissions be sent by the parent
350 * process. This should only be used to wait for permissions which may not
351 * have arrived yet in order to ensure they are present.
353 * @param aPrincipal The principal to wait for permissions to be available
355 * @param aRunnable The runnable to run when permissions are available for
356 * the given principal.
358 void WhenPermissionsAvailable(nsIPrincipal
* aPrincipal
,
359 nsIRunnable
* aRunnable
);
362 * Strip origin attributes for permissions, depending on permission isolation
364 * @param aForceStrip If true, strips user context and private browsing id,
365 * ignoring permission isolation prefs.
366 * @param aOriginAttributes object to strip.
368 static void MaybeStripOriginAttributes(bool aForceStrip
,
369 OriginAttributes
& aOriginAttributes
);
372 ~PermissionManager();
373 static StaticMutex sCreationMutex MOZ_UNANNOTATED
;
376 * Get all permissions for a given principal, which should not be isolated
377 * by user context or private browsing. The principal has its origin
378 * attributes stripped before perm db lookup. This is currently only affects
379 * the "cookie" permission.
380 * @param aPrincipal Used for creating the permission key.
381 * @param aSiteScopePermissions Used to specify whether to get strip perms for
382 * site scoped permissions (defined in IsSiteScopedPermission) or all other
383 * permissions. Also used to create the permission key.
385 nsresult
GetStripPermsForPrincipal(nsIPrincipal
* aPrincipal
,
386 bool aSiteScopePermissions
,
387 nsTArray
<PermissionEntry
>& aResult
);
389 // Returns -1 on failure
390 int32_t GetTypeIndex(const nsACString
& aType
, bool aAdd
);
392 // Returns whether the given combination of expire type and expire time are
393 // expired. Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
394 bool HasExpired(uint32_t aExpireType
, int64_t aExpireTime
);
396 // Appends the permissions associated with this principal to aResult.
397 // If the onlySiteScopePermissions argument is true, the permissions searched
398 // are those for the site of the principal and only the permissions that are
399 // site-scoped are used.
400 nsresult
GetAllForPrincipalHelper(nsIPrincipal
* aPrincipal
,
401 bool aSiteScopePermissions
,
402 nsTArray
<RefPtr
<nsIPermission
>>& aResult
);
404 // Returns PermissionHashKey for a given { host, isInBrowserElement } tuple.
405 // This is not simply using PermissionKey because we will walk-up domains in
406 // case of |host| contains sub-domains. Returns null if nothing found. Also
407 // accepts host on the format "<foo>". This will perform an exact match lookup
408 // as the string doesn't contain any dots.
409 PermissionHashKey
* GetPermissionHashKey(nsIPrincipal
* aPrincipal
,
410 uint32_t aType
, bool aExactHostMatch
);
412 // Returns PermissionHashKey for a given { host, isInBrowserElement } tuple.
413 // This is not simply using PermissionKey because we will walk-up domains in
414 // case of |host| contains sub-domains. Returns null if nothing found. Also
415 // accepts host on the format "<foo>". This will perform an exact match lookup
416 // as the string doesn't contain any dots.
417 PermissionHashKey
* GetPermissionHashKey(
418 nsIURI
* aURI
, const OriginAttributes
* aOriginAttributes
, uint32_t aType
,
419 bool aExactHostMatch
);
421 // The int32_t is the type index, the nsresult is an early bail-out return
423 typedef Variant
<int32_t, nsresult
> TestPreparationResult
;
424 TestPreparationResult
CommonPrepareToTestPermission(
425 nsIPrincipal
* aPrincipal
, int32_t aTypeIndex
, const nsACString
& aType
,
426 uint32_t* aPermission
, uint32_t aDefaultPermission
,
427 bool aDefaultPermissionIsValid
, bool aExactHostMatch
,
428 bool aIncludingSession
);
430 // If aTypeIndex is passed -1, we try to inder the type index from aType.
431 nsresult
CommonTestPermission(nsIPrincipal
* aPrincipal
, int32_t aTypeIndex
,
432 const nsACString
& aType
, uint32_t* aPermission
,
433 uint32_t aDefaultPermission
,
434 bool aDefaultPermissionIsValid
,
435 bool aExactHostMatch
, bool aIncludingSession
);
437 // If aTypeIndex is passed -1, we try to inder the type index from aType.
438 nsresult
CommonTestPermission(nsIURI
* aURI
, int32_t aTypeIndex
,
439 const nsACString
& aType
, uint32_t* aPermission
,
440 uint32_t aDefaultPermission
,
441 bool aDefaultPermissionIsValid
,
442 bool aExactHostMatch
, bool aIncludingSession
);
444 nsresult
CommonTestPermission(nsIURI
* aURI
,
445 const OriginAttributes
* aOriginAttributes
,
446 int32_t aTypeIndex
, const nsACString
& aType
,
447 uint32_t* aPermission
,
448 uint32_t aDefaultPermission
,
449 bool aDefaultPermissionIsValid
,
450 bool aExactHostMatch
, bool aIncludingSession
);
452 // Only one of aPrincipal or aURI is allowed to be passed in.
453 nsresult
CommonTestPermissionInternal(
454 nsIPrincipal
* aPrincipal
, nsIURI
* aURI
,
455 const OriginAttributes
* aOriginAttributes
, int32_t aTypeIndex
,
456 const nsACString
& aType
, uint32_t* aPermission
, bool aExactHostMatch
,
457 bool aIncludingSession
);
459 nsresult
OpenDatabase(nsIFile
* permissionsFile
);
461 void InitDB(bool aRemoveFile
);
462 nsresult
TryInitDB(bool aRemoveFile
, nsIInputStream
* aDefaultsInputStream
);
464 void AddIdleDailyMaintenanceJob();
465 void RemoveIdleDailyMaintenanceJob();
466 void PerformIdleDailyMaintenance();
468 nsresult
ImportLatestDefaults();
469 already_AddRefed
<nsIInputStream
> GetDefaultsInputStream();
470 void ConsumeDefaultsInputStream(nsIInputStream
* aDefaultsInputStream
,
471 const MonitorAutoLock
& aProofOfLock
);
473 nsresult
CreateTable();
474 void NotifyObserversWithPermission(nsIPrincipal
* aPrincipal
,
475 const nsACString
& aType
,
476 uint32_t aPermission
, uint32_t aExpireType
,
478 int64_t aModificationTime
,
479 const char16_t
* aData
);
480 void NotifyObservers(nsIPermission
* aPermission
, const char16_t
* aData
);
482 // Finalize all statements, close the DB and null it.
488 void CloseDB(CloseDBNextOp aNextOp
);
490 nsresult
RemoveAllInternal(bool aNotifyObservers
);
491 nsresult
RemoveAllFromMemory();
493 void UpdateDB(OperationType aOp
, int64_t aID
, const nsACString
& aOrigin
,
494 const nsACString
& aType
, uint32_t aPermission
,
495 uint32_t aExpireType
, int64_t aExpireTime
,
496 int64_t aModificationTime
);
499 * This method removes all permissions modified after the specified time.
501 nsresult
RemoveAllModifiedSince(int64_t aModificationTime
);
504 nsresult
RemovePermissionEntries(T aCondition
);
507 nsresult
GetPermissionEntries(T aCondition
,
508 nsTArray
<RefPtr
<nsIPermission
>>& aResult
);
510 // This method must be called before doing any operation to be sure that the
511 // DB reading has been completed. This method is also in charge to complete
512 // the migrations if needed.
513 void EnsureReadCompleted();
515 nsresult
AddInternal(nsIPrincipal
* aPrincipal
, const nsACString
& aType
,
516 uint32_t aPermission
, int64_t aID
, uint32_t aExpireType
,
517 int64_t aExpireTime
, int64_t aModificationTime
,
518 NotifyOperationType aNotifyOperation
,
519 DBOperationType aDBOperation
,
520 const bool aIgnoreSessionPermissions
= false,
521 const nsACString
* aOriginString
= nullptr,
522 const bool aAllowPersistInPrivateBrowsing
= false);
524 void MaybeAddReadEntryFromMigration(const nsACString
& aOrigin
,
525 const nsCString
& aType
,
526 uint32_t aPermission
,
527 uint32_t aExpireType
, int64_t aExpireTime
,
528 int64_t aModificationTime
, int64_t aId
);
530 nsCOMPtr
<nsIAsyncShutdownClient
> GetAsyncShutdownBarrier() const;
532 void MaybeCompleteShutdown();
534 nsRefPtrHashtable
<nsCStringHashKey
, GenericNonExclusivePromise::Private
>
535 mPermissionKeyPromiseMap
;
537 nsCOMPtr
<nsIFile
> mPermissionsFile
;
539 // This monitor is used to ensure the database reading before any other
540 // operation. The reading of the database happens OMT. See |State| to know the
541 // steps of the database reading.
542 Monitor mMonitor MOZ_UNANNOTATED
;
545 // Initial state. The database has not been read yet.
546 // |TryInitDB| is called at startup time to read the database OMT.
547 // During the reading, |mReadEntries| will be populated with all the
548 // existing permissions.
551 // At the end of the database reading, we are in this state. A runnable is
552 // executed to call |EnsureReadCompleted| on the main thread.
553 // |EnsureReadCompleted| processes |mReadEntries| and goes to the next
557 // The permissions are fully read and any pending operation can proceed.
560 // The permission manager has been terminated. No extra database operations
564 Atomic
<State
> mState
;
566 // A single entry, from the database.
573 mModificationTime(0) {}
578 uint32_t mPermission
;
579 uint32_t mExpireType
;
581 int64_t mModificationTime
;
583 // true if this entry is the result of a migration.
587 // List of entries read from the database. It will be populated OMT and
588 // consumed on the main-thread.
589 // This array is protected by the monitor.
590 nsTArray
<ReadEntry
> mReadEntries
;
592 // A single entry, from the database.
593 struct MigrationEntry
{
599 mModificationTime(0),
600 mIsInBrowserElement(false) {}
605 uint32_t mPermission
;
606 uint32_t mExpireType
;
608 int64_t mModificationTime
;
610 // Legacy, for migration.
611 bool mIsInBrowserElement
;
614 // List of entries read from the database. It will be populated OMT and
615 // consumed on the main-thread. The migration entries will be converted to
616 // ReadEntry in |CompleteMigrations|.
617 // This array is protected by the monitor.
618 nsTArray
<MigrationEntry
> mMigrationEntries
;
620 // A single entry from the defaults URL.
621 struct DefaultEntry
{
622 DefaultEntry() : mOp(eImportMatchTypeHost
), mPermission(0) {}
625 eImportMatchTypeHost
,
626 eImportMatchTypeOrigin
,
631 nsCString mHostOrOrigin
;
633 uint32_t mPermission
;
636 // List of entries read from the default settings.
637 // This array is protected by the monitor.
638 nsTArray
<DefaultEntry
> mDefaultEntries
;
640 nsresult
Read(const MonitorAutoLock
& aProofOfLock
);
643 void CompleteMigrations();
647 nsTHashtable
<PermissionHashKey
> mPermissionTable
;
648 // a unique, monotonically increasing id used to identify each database entry
651 nsCOMPtr
<nsIPrefBranch
> mDefaultPrefBranch
;
653 // NOTE: Ensure this is the last member since it has a large inline buffer.
654 // An array to store the strings identifying the different types.
655 Vector
<nsCString
, 512> mTypeArray
;
657 nsCOMPtr
<nsIThread
> mThread
;
659 struct ThreadBoundData
{
660 nsCOMPtr
<mozIStorageConnection
> mDBConn
;
662 nsCOMPtr
<mozIStorageStatement
> mStmtInsert
;
663 nsCOMPtr
<mozIStorageStatement
> mStmtDelete
;
664 nsCOMPtr
<mozIStorageStatement
> mStmtUpdate
;
666 ThreadBound
<ThreadBoundData
> mThreadBoundData
;
668 friend class DeleteFromMozHostListener
;
669 friend class CloseDatabaseListener
;
672 // {4F6B5E00-0C36-11d5-A535-0010A401EB10}
673 #define NS_PERMISSIONMANAGER_CID \
675 0x4f6b5e00, 0xc36, 0x11d5, { \
676 0xa5, 0x35, 0x0, 0x10, 0xa4, 0x1, 0xeb, 0x10 \
680 } // namespace mozilla
682 #endif // mozilla_PermissionManager_h