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/. */
6 
7 #ifndef nsPermissionManager_h__
8 #define nsPermissionManager_h__
9 
10 #include "nsIPermissionManager.h"
11 #include "nsIObserver.h"
12 #include "nsWeakReference.h"
13 #include "nsCOMPtr.h"
14 #include "nsIInputStream.h"
15 #include "nsTHashtable.h"
16 #include "nsTArray.h"
17 #include "nsString.h"
18 #include "nsPermission.h"
19 #include "nsIPrefBranch.h"
20 #include "nsHashKeys.h"
21 #include "nsCOMArray.h"
22 #include "nsDataHashtable.h"
23 #include "nsIRunnable.h"
24 #include "nsRefPtrHashtable.h"
25 #include "mozilla/MozPromise.h"
26 
27 namespace mozilla {
28 class OriginAttributesPattern;
29 }
30 
31 class nsIPermission;
32 class mozIStorageConnection;
33 class mozIStorageAsyncStatement;
34 
35 ////////////////////////////////////////////////////////////////////////////////
36 
37 class nsPermissionManager final : public nsIPermissionManager,
38                                   public nsIObserver,
39                                   public nsSupportsWeakReference {
40  public:
41   class PermissionEntry {
42    public:
PermissionEntry(int64_t aID,uint32_t aType,uint32_t aPermission,uint32_t aExpireType,int64_t aExpireTime,int64_t aModificationTime)43     PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
44                     uint32_t aExpireType, int64_t aExpireTime,
45                     int64_t aModificationTime)
46         : mID(aID),
47           mType(aType),
48           mPermission(aPermission),
49           mExpireType(aExpireType),
50           mExpireTime(aExpireTime),
51           mModificationTime(aModificationTime),
52           mNonSessionPermission(aPermission),
53           mNonSessionExpireType(aExpireType),
54           mNonSessionExpireTime(aExpireTime) {}
55 
56     int64_t mID;
57     uint32_t mType;
58     uint32_t mPermission;
59     uint32_t mExpireType;
60     int64_t mExpireTime;
61     int64_t mModificationTime;
62     uint32_t mNonSessionPermission;
63     uint32_t mNonSessionExpireType;
64     uint32_t mNonSessionExpireTime;
65   };
66 
67   /**
68    * PermissionKey is the key used by PermissionHashKey hash table.
69    *
70    * NOTE: It could be implementing nsIHashable but there is no reason to worry
71    * with XPCOM interfaces while we don't need to.
72    */
73   class PermissionKey {
74    public:
75     static PermissionKey* CreateFromPrincipal(nsIPrincipal* aPrincipal,
76                                               nsresult& aResult);
77     static PermissionKey* CreateFromURI(nsIURI* aURI, nsresult& aResult);
78 
PermissionKey(const nsACString & aOrigin)79     explicit PermissionKey(const nsACString& aOrigin) : mOrigin(aOrigin) {}
80 
81     bool operator==(const PermissionKey& aKey) const {
82       return mOrigin.Equals(aKey.mOrigin);
83     }
84 
GetHashCode()85     PLDHashNumber GetHashCode() const { return mozilla::HashString(mOrigin); }
86 
87     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey)
88 
89     nsCString mOrigin;
90 
91    private:
92     // Default ctor shouldn't be used.
93     PermissionKey() = delete;
94 
95     // Dtor shouldn't be used outside of the class.
~PermissionKey()96     ~PermissionKey(){};
97   };
98 
99   class PermissionHashKey : public nsRefPtrHashKey<PermissionKey> {
100    public:
PermissionHashKey(const PermissionKey * aPermissionKey)101     explicit PermissionHashKey(const PermissionKey* aPermissionKey)
102         : nsRefPtrHashKey<PermissionKey>(aPermissionKey) {}
103 
PermissionHashKey(PermissionHashKey && toCopy)104     PermissionHashKey(PermissionHashKey&& toCopy)
105         : nsRefPtrHashKey<PermissionKey>(mozilla::Move(toCopy)),
106           mPermissions(mozilla::Move(toCopy.mPermissions)) {}
107 
KeyEquals(const PermissionKey * aKey)108     bool KeyEquals(const PermissionKey* aKey) const {
109       return *aKey == *GetKey();
110     }
111 
HashKey(const PermissionKey * aKey)112     static PLDHashNumber HashKey(const PermissionKey* aKey) {
113       return aKey->GetHashCode();
114     }
115 
116     // Force the hashtable to use the copy constructor when shuffling entries
117     // around, otherwise the Auto part of our AutoTArray won't be happy!
118     enum { ALLOW_MEMMOVE = false };
119 
GetPermissions()120     inline nsTArray<PermissionEntry>& GetPermissions() { return mPermissions; }
121 
GetPermissionIndex(uint32_t aType)122     inline int32_t GetPermissionIndex(uint32_t aType) const {
123       for (uint32_t i = 0; i < mPermissions.Length(); ++i)
124         if (mPermissions[i].mType == aType) return i;
125 
126       return -1;
127     }
128 
GetPermission(uint32_t aType)129     inline PermissionEntry GetPermission(uint32_t aType) const {
130       for (uint32_t i = 0; i < mPermissions.Length(); ++i)
131         if (mPermissions[i].mType == aType) return mPermissions[i];
132 
133       // unknown permission... return relevant data
134       return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
135                              nsIPermissionManager::EXPIRE_NEVER, 0, 0);
136     }
137 
138    private:
139     AutoTArray<PermissionEntry, 1> mPermissions;
140   };
141 
142   // nsISupports
143   NS_DECL_ISUPPORTS
144   NS_DECL_NSIPERMISSIONMANAGER
145   NS_DECL_NSIOBSERVER
146 
147   nsPermissionManager();
148   static already_AddRefed<nsIPermissionManager> GetXPCOMSingleton();
149   nsresult Init();
150 
151   // enums for AddInternal()
152   enum OperationType {
153     eOperationNone,
154     eOperationAdding,
155     eOperationRemoving,
156     eOperationChanging,
157     eOperationReplacingDefault
158   };
159 
160   enum DBOperationType { eNoDBOperation, eWriteToDB };
161 
162   enum NotifyOperationType { eDontNotify, eNotify };
163 
164   // A special value for a permission ID that indicates the ID was loaded as
165   // a default value.  These will never be written to the database, but may
166   // be overridden with an explicit permission (including UNKNOWN_ACTION)
167   static const int64_t cIDPermissionIsDefault = -1;
168 
169   nsresult AddInternal(nsIPrincipal* aPrincipal, const nsCString& aType,
170                        uint32_t aPermission, int64_t aID, uint32_t aExpireType,
171                        int64_t aExpireTime, int64_t aModificationTime,
172                        NotifyOperationType aNotifyOperation,
173                        DBOperationType aDBOperation,
174                        const bool aIgnoreSessionPermissions = false);
175 
176   /**
177    * Initialize the "clear-origin-attributes-data" observing.
178    * Will create a nsPermissionManager instance if needed.
179    * That way, we can prevent have nsPermissionManager created at startup just
180    * to be able to clear data when an application is uninstalled.
181    */
182   static void ClearOriginDataObserverInit();
183 
184   nsresult RemovePermissionsWithAttributes(
185       mozilla::OriginAttributesPattern& aAttrs);
186 
187   /**
188    * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
189    * permission keys.
190    *
191    * Get the permission key corresponding to the given Principal. This method is
192    * intentionally infallible, as we want to provide an permission key to every
193    * principal. Principals which don't have meaningful URIs with http://,
194    * https://, or ftp:// schemes are given the default "" Permission Key.
195    *
196    * @param aPrincipal  The Principal which the key is to be extracted from.
197    * @param aPermissionKey  A string which will be filled with the permission
198    * key.
199    */
200   static void GetKeyForPrincipal(nsIPrincipal* aPrincipal,
201                                  nsACString& aPermissionKey);
202 
203   /**
204    * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
205    * permission keys.
206    *
207    * Get the permission key corresponding to the given Origin. This method is
208    * like GetKeyForPrincipal, except that it avoids creating a nsIPrincipal
209    * object when you already have access to an origin string.
210    *
211    * If this method is passed a nonsensical origin string it may produce a
212    * nonsensical permission key result.
213    *
214    * @param aOrigin  The origin which the key is to be extracted from.
215    * @param aPermissionKey  A string which will be filled with the permission
216    * key.
217    */
218   static void GetKeyForOrigin(const nsACString& aOrigin,
219                               nsACString& aPermissionKey);
220 
221   /**
222    * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
223    * permission keys.
224    *
225    * Get the permission key corresponding to the given Principal and type. This
226    * method is intentionally infallible, as we want to provide an permission key
227    * to every principal. Principals which don't have meaningful URIs with
228    * http://, https://, or ftp:// schemes are given the default "" Permission
229    * Key.
230    *
231    * This method is different from GetKeyForPrincipal in that it also takes
232    * permissions which must be sent down before loading a document into account.
233    *
234    * @param aPrincipal  The Principal which the key is to be extracted from.
235    * @param aType  The type of the permission to get the key for.
236    * @param aPermissionKey  A string which will be filled with the permission
237    * key.
238    */
239   static void GetKeyForPermission(nsIPrincipal* aPrincipal, const char* aType,
240                                   nsACString& aPermissionKey);
241 
242   /**
243    * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
244    * permission keys.
245    *
246    * Get all permissions keys which could correspond to the given principal.
247    * This method, like GetKeyForPrincipal, is infallible and should always
248    * produce at least one key.
249    *
250    * Unlike GetKeyForPrincipal, this method also gets the keys for base domains
251    * of the given principal. All keys returned by this method must be available
252    * in the content process for a given URL to successfully have its permissions
253    * checked in the `aExactHostMatch = false` situation.
254    *
255    * @param aPrincipal  The Principal which the key is to be extracted from.
256    */
257   static nsTArray<nsCString> GetAllKeysForPrincipal(nsIPrincipal* aPrincipal);
258 
259  private:
260   virtual ~nsPermissionManager();
261 
262   int32_t GetTypeIndex(const char* aTypeString, bool aAdd);
263 
264   PermissionHashKey* GetPermissionHashKey(nsIPrincipal* aPrincipal,
265                                           uint32_t aType, bool aExactHostMatch);
266   PermissionHashKey* GetPermissionHashKey(nsIURI* aURI, uint32_t aType,
267                                           bool aExactHostMatch);
268 
CommonTestPermission(nsIPrincipal * aPrincipal,const char * aType,uint32_t * aPermission,bool aExactHostMatch,bool aIncludingSession)269   nsresult CommonTestPermission(nsIPrincipal* aPrincipal, const char* aType,
270                                 uint32_t* aPermission, bool aExactHostMatch,
271                                 bool aIncludingSession) {
272     return CommonTestPermissionInternal(aPrincipal, nullptr, aType, aPermission,
273                                         aExactHostMatch, aIncludingSession);
274   }
CommonTestPermission(nsIURI * aURI,const char * aType,uint32_t * aPermission,bool aExactHostMatch,bool aIncludingSession)275   nsresult CommonTestPermission(nsIURI* aURI, const char* aType,
276                                 uint32_t* aPermission, bool aExactHostMatch,
277                                 bool aIncludingSession) {
278     return CommonTestPermissionInternal(nullptr, aURI, aType, aPermission,
279                                         aExactHostMatch, aIncludingSession);
280   }
281   // Only one of aPrincipal or aURI is allowed to be passed in.
282   nsresult CommonTestPermissionInternal(nsIPrincipal* aPrincipal, nsIURI* aURI,
283                                         const char* aType,
284                                         uint32_t* aPermission,
285                                         bool aExactHostMatch,
286                                         bool aIncludingSession);
287 
288   nsresult OpenDatabase(nsIFile* permissionsFile);
289   nsresult InitDB(bool aRemoveFile);
290   nsresult CreateTable();
291   nsresult Import();
292   nsresult ImportDefaults();
293   nsresult _DoImport(nsIInputStream* inputStream, mozIStorageConnection* aConn);
294   nsresult Read();
295   void NotifyObserversWithPermission(nsIPrincipal* aPrincipal,
296                                      const nsCString& aType,
297                                      uint32_t aPermission, uint32_t aExpireType,
298                                      int64_t aExpireTime,
299                                      const char16_t* aData);
300   void NotifyObservers(nsIPermission* aPermission, const char16_t* aData);
301 
302   // Finalize all statements, close the DB and null it.
303   // if aRebuildOnSuccess, reinitialize database
304   void CloseDB(bool aRebuildOnSuccess = false);
305 
306   nsresult RemoveAllInternal(bool aNotifyObservers);
307   nsresult RemoveAllFromMemory();
308   static void UpdateDB(OperationType aOp, mozIStorageAsyncStatement* aStmt,
309                        int64_t aID, const nsACString& aOrigin,
310                        const nsACString& aType, uint32_t aPermission,
311                        uint32_t aExpireType, int64_t aExpireTime,
312                        int64_t aModificationTime);
313 
314   /**
315    * This method removes all permissions modified after the specified time.
316    */
317   nsresult RemoveAllModifiedSince(int64_t aModificationTime);
318 
319   /**
320    * Returns false if this permission manager wouldn't have the permission
321    * requested available.
322    *
323    * If aType is nullptr, checks that the permission manager would have all
324    * permissions available for the given principal.
325    */
326   bool PermissionAvailable(nsIPrincipal* aPrincipal, const char* aType);
327 
328   nsRefPtrHashtable<nsCStringHashKey, mozilla::GenericPromise::Private>
329       mPermissionKeyPromiseMap;
330 
331   nsCOMPtr<mozIStorageConnection> mDBConn;
332   nsCOMPtr<mozIStorageAsyncStatement> mStmtInsert;
333   nsCOMPtr<mozIStorageAsyncStatement> mStmtDelete;
334   nsCOMPtr<mozIStorageAsyncStatement> mStmtUpdate;
335 
336   bool mMemoryOnlyDB;
337 
338   nsTHashtable<PermissionHashKey> mPermissionTable;
339   // a unique, monotonically increasing id used to identify each database entry
340   int64_t mLargestID;
341 
342   // An array to store the strings identifying the different types.
343   nsTArray<nsCString> mTypeArray;
344 
345   // Initially, |false|. Set to |true| once shutdown has started, to avoid
346   // reopening the database.
347   bool mIsShuttingDown;
348 
349   nsCOMPtr<nsIPrefBranch> mDefaultPrefBranch;
350 
351   friend class DeleteFromMozHostListener;
352   friend class CloseDatabaseListener;
353 };
354 
355 // {4F6B5E00-0C36-11d5-A535-0010A401EB10}
356 #define NS_PERMISSIONMANAGER_CID                   \
357   {                                                \
358     0x4f6b5e00, 0xc36, 0x11d5, {                   \
359       0xa5, 0x35, 0x0, 0x10, 0xa4, 0x1, 0xeb, 0x10 \
360     }                                              \
361   }
362 
363 #endif /* nsPermissionManager_h__ */
364