Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; 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 : #include "mozilla/Attributes.h"
8 : #include "mozilla/DebugOnly.h"
9 :
10 : #include "mozilla/dom/ContentParent.h"
11 : #include "mozilla/dom/ContentChild.h"
12 : #include "mozilla/BasePrincipal.h"
13 : #include "mozilla/Services.h"
14 : #include "mozilla/SystemGroup.h"
15 : #include "mozilla/Unused.h"
16 : #include "nsPermissionManager.h"
17 : #include "nsPermission.h"
18 : #include "nsCRT.h"
19 : #include "nsNetUtil.h"
20 : #include "nsCOMArray.h"
21 : #include "nsArrayEnumerator.h"
22 : #include "nsTArray.h"
23 : #include "nsReadableUtils.h"
24 : #include "nsILineInputStream.h"
25 : #include "nsAppDirectoryServiceDefs.h"
26 : #include "nsDirectoryServiceDefs.h"
27 : #include "mozilla/storage.h"
28 : #include "mozilla/Attributes.h"
29 : #include "nsXULAppAPI.h"
30 : #include "nsIPrincipal.h"
31 : #include "nsContentUtils.h"
32 : #include "nsIScriptSecurityManager.h"
33 : #include "nsIEffectiveTLDService.h"
34 : #include "nsPIDOMWindow.h"
35 : #include "nsIDocument.h"
36 : #include "mozilla/net/NeckoMessageUtils.h"
37 : #include "mozilla/Preferences.h"
38 : #include "nsReadLine.h"
39 : #include "mozilla/Telemetry.h"
40 : #include "nsIConsoleService.h"
41 : #include "nsINavHistoryService.h"
42 : #include "nsToolkitCompsCID.h"
43 : #include "nsIObserverService.h"
44 : #include "nsPrintfCString.h"
45 : #include "mozilla/AbstractThread.h"
46 : #include "ContentPrincipal.h"
47 :
48 : static nsPermissionManager *gPermissionManager = nullptr;
49 :
50 : using namespace mozilla;
51 : using namespace mozilla::dom;
52 :
53 : static bool
54 33 : IsChildProcess()
55 : {
56 33 : return XRE_IsContentProcess();
57 : }
58 :
59 : static void
60 0 : LogToConsole(const nsAString& aMsg)
61 : {
62 0 : nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
63 0 : if (!console) {
64 0 : NS_WARNING("Failed to log message to console.");
65 0 : return;
66 : }
67 :
68 0 : nsAutoString msg(aMsg);
69 0 : console->LogStringMessage(msg.get());
70 : }
71 :
72 : #define ENSURE_NOT_CHILD_PROCESS_(onError) \
73 : PR_BEGIN_MACRO \
74 : if (IsChildProcess()) { \
75 : NS_ERROR("Cannot perform action in content process!"); \
76 : onError \
77 : } \
78 : PR_END_MACRO
79 :
80 : #define ENSURE_NOT_CHILD_PROCESS \
81 : ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
82 :
83 : #define ENSURE_NOT_CHILD_PROCESS_NORET \
84 : ENSURE_NOT_CHILD_PROCESS_(;)
85 :
86 : ////////////////////////////////////////////////////////////////////////////////
87 :
88 : namespace {
89 :
90 : // The number of permissions from the kPreloadPermissions list which are present
91 : // in the permission manager. Used to determine if the permission manager should
92 : // be checked for one of these preload permissions in nsContentBlocker.
93 : static int32_t sPreloadPermissionCount = 0;
94 :
95 : // These permissions are special permissions which must be transmitted to the
96 : // content process before documents with their principals have loaded within
97 : // that process. This is because these permissions are used for content
98 : // blocking in nsContentBlocker.
99 : //
100 : // Permissions which are in this list are considered to have a "" permission
101 : // key, even if their principal would not normally have that key.
102 : static const char* kPreloadPermissions[] = {
103 : // NOTE: These permissions are the different nsContentBlocker permissions for
104 : // allowing or denying certain content types from being loaded. Every
105 : // permission listed in the `kTypeString` array in nsContentBlocker.cpp should
106 : // appear in this list.
107 : "other",
108 : "script",
109 : "image",
110 : "stylesheet",
111 : "object",
112 : "document",
113 : "subdocument",
114 : "refresh",
115 : "xbl",
116 : "ping",
117 : "xmlhttprequest",
118 : "objectsubrequest",
119 : "dtd",
120 : "font",
121 : "media",
122 : "websocket",
123 : "csp_report",
124 : "xslt",
125 : "beacon",
126 : "fetch",
127 : "image",
128 : "manifest"
129 : };
130 :
131 : // NOTE: nullptr can be passed as aType - if it is this function will return
132 : // "false" unconditionally.
133 : bool
134 58 : IsPreloadPermission(const char* aType)
135 : {
136 58 : if (aType) {
137 1334 : for (uint32_t i = 0; i < mozilla::ArrayLength(kPreloadPermissions); ++i) {
138 1276 : if (!strcmp(aType, kPreloadPermissions[i])) {
139 0 : return true;
140 : }
141 : }
142 : }
143 :
144 58 : return false;
145 : }
146 :
147 : nsresult
148 30 : GetOriginFromPrincipal(nsIPrincipal* aPrincipal, nsACString& aOrigin)
149 : {
150 30 : nsresult rv = aPrincipal->GetOriginNoSuffix(aOrigin);
151 : // The principal may belong to the about:blank content viewer, so this can be
152 : // expected to fail.
153 30 : if (NS_FAILED(rv)) {
154 0 : return rv;
155 : }
156 :
157 60 : nsAutoCString suffix;
158 30 : rv = aPrincipal->GetOriginSuffix(suffix);
159 30 : NS_ENSURE_SUCCESS(rv, rv);
160 :
161 60 : mozilla::OriginAttributes attrs;
162 30 : if (!attrs.PopulateFromSuffix(suffix)) {
163 0 : return NS_ERROR_FAILURE;
164 : }
165 :
166 : // mPrivateBrowsingId must be set to false because PermissionManager is not supposed to have
167 : // any knowledge of private browsing. Allowing it to be true changes the suffix being hashed.
168 30 : attrs.mPrivateBrowsingId = 0;
169 :
170 : // Disable userContext and firstParty isolation for permissions.
171 : attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
172 30 : mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
173 :
174 30 : attrs.CreateSuffix(suffix);
175 30 : aOrigin.Append(suffix);
176 30 : return NS_OK;
177 : }
178 :
179 : nsresult
180 54 : GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal)
181 : {
182 108 : nsAutoCString originNoSuffix;
183 108 : mozilla::OriginAttributes attrs;
184 54 : if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
185 0 : return NS_ERROR_FAILURE;
186 : }
187 :
188 : // mPrivateBrowsingId must be set to false because PermissionManager is not supposed to have
189 : // any knowledge of private browsing. Allowing it to be true changes the suffix being hashed.
190 54 : attrs.mPrivateBrowsingId = 0;
191 :
192 : // Disable userContext and firstParty isolation for permissions.
193 : attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
194 54 : mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
195 :
196 108 : nsCOMPtr<nsIURI> uri;
197 54 : nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
198 54 : NS_ENSURE_SUCCESS(rv, rv);
199 :
200 108 : nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
201 54 : principal.forget(aPrincipal);
202 54 : return NS_OK;
203 : }
204 :
205 : nsresult
206 0 : GetPrincipal(nsIURI* aURI, uint32_t aAppId, bool aIsInIsolatedMozBrowserElement, nsIPrincipal** aPrincipal)
207 : {
208 0 : mozilla::OriginAttributes attrs(aAppId, aIsInIsolatedMozBrowserElement);
209 0 : nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
210 0 : NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
211 :
212 0 : principal.forget(aPrincipal);
213 0 : return NS_OK;
214 : }
215 :
216 : nsresult
217 2 : GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
218 : {
219 4 : mozilla::OriginAttributes attrs;
220 4 : nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
221 2 : NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
222 :
223 2 : principal.forget(aPrincipal);
224 2 : return NS_OK;
225 : }
226 :
227 : nsCString
228 1 : GetNextSubDomainForHost(const nsACString& aHost)
229 : {
230 : nsCOMPtr<nsIEffectiveTLDService> tldService =
231 2 : do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
232 1 : if (!tldService) {
233 0 : NS_ERROR("Should have a tld service!");
234 0 : return EmptyCString();
235 : }
236 :
237 2 : nsCString subDomain;
238 1 : nsresult rv = tldService->GetNextSubDomain(aHost, subDomain);
239 : // We can fail if there is no more subdomain or if the host can't have a
240 : // subdomain.
241 1 : if (NS_FAILED(rv)) {
242 1 : return EmptyCString();
243 : }
244 :
245 0 : return subDomain;
246 : }
247 :
248 : // This function produces a nsIURI which is identical to the current
249 : // nsIURI, except that it has one less subdomain segment. It returns
250 : // `nullptr` if there are no more segments to remove.
251 : already_AddRefed<nsIURI>
252 1 : GetNextSubDomainURI(nsIURI* aURI)
253 : {
254 2 : nsAutoCString host;
255 1 : nsresult rv = aURI->GetHost(host);
256 1 : if (NS_FAILED(rv)) {
257 0 : return nullptr;
258 : }
259 :
260 2 : nsCString domain = GetNextSubDomainForHost(host);
261 1 : if (domain.IsEmpty()) {
262 1 : return nullptr;
263 : }
264 :
265 0 : nsCOMPtr<nsIURI> uri;
266 0 : rv = aURI->Clone(getter_AddRefs(uri));
267 0 : if (NS_FAILED(rv) || !uri) {
268 0 : return nullptr;
269 : }
270 :
271 0 : rv = uri->SetHost(domain);
272 0 : if (NS_FAILED(rv)) {
273 0 : return nullptr;
274 : }
275 :
276 0 : return uri.forget();
277 : }
278 :
279 : // This function produces a nsIPrincipal which is identical to the current
280 : // nsIPrincipal, except that it has one less subdomain segment. It returns
281 : // `nullptr` if there are no more segments to remove.
282 : already_AddRefed<nsIPrincipal>
283 1 : GetNextSubDomainPrincipal(nsIPrincipal* aPrincipal)
284 : {
285 2 : nsCOMPtr<nsIURI> uri;
286 1 : nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
287 1 : if (NS_FAILED(rv) || !uri) {
288 0 : return nullptr;
289 : }
290 :
291 : // Create a new principal which is identical to the current one, but with the new host
292 2 : nsCOMPtr<nsIURI> newURI = GetNextSubDomainURI(uri);
293 1 : if (!newURI) {
294 1 : return nullptr;
295 : }
296 :
297 : // Copy the attributes over
298 0 : mozilla::OriginAttributes attrs = aPrincipal->OriginAttributesRef();
299 :
300 : // Disable userContext and firstParty isolation for permissions.
301 : attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
302 0 : mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
303 :
304 : nsCOMPtr<nsIPrincipal> principal =
305 0 : mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs);
306 :
307 0 : return principal.forget();
308 : }
309 :
310 3 : class ClearOriginDataObserver final : public nsIObserver {
311 0 : ~ClearOriginDataObserver() {}
312 :
313 : public:
314 : NS_DECL_ISUPPORTS
315 :
316 : // nsIObserver implementation.
317 : NS_IMETHOD
318 0 : Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override
319 : {
320 0 : MOZ_ASSERT(!nsCRT::strcmp(aTopic, "clear-origin-attributes-data"));
321 :
322 0 : nsCOMPtr<nsIPermissionManager> permManager = do_GetService("@mozilla.org/permissionmanager;1");
323 0 : return permManager->RemovePermissionsWithAttributes(nsDependentString(aData));
324 : }
325 : };
326 :
327 3 : NS_IMPL_ISUPPORTS(ClearOriginDataObserver, nsIObserver)
328 :
329 0 : class MOZ_STACK_CLASS UpgradeHostToOriginHelper {
330 : public:
331 : virtual nsresult Insert(const nsACString& aOrigin, const nsCString& aType,
332 : uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
333 : int64_t aModificationTime) = 0;
334 : };
335 :
336 0 : class MOZ_STACK_CLASS UpgradeHostToOriginDBMigration final : public UpgradeHostToOriginHelper {
337 : public:
338 0 : UpgradeHostToOriginDBMigration(mozIStorageConnection* aDBConn, int64_t* aID) : mDBConn(aDBConn)
339 0 : , mID(aID)
340 : {
341 0 : mDBConn->CreateStatement(NS_LITERAL_CSTRING(
342 : "INSERT INTO moz_hosts_new "
343 : "(id, origin, type, permission, expireType, expireTime, modificationTime) "
344 0 : "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmt));
345 0 : }
346 :
347 : nsresult
348 0 : Insert(const nsACString& aOrigin, const nsCString& aType,
349 : uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
350 : int64_t aModificationTime) final
351 : {
352 0 : nsresult rv = mStmt->BindInt64ByIndex(0, *mID);
353 0 : NS_ENSURE_SUCCESS(rv, rv);
354 :
355 0 : rv = mStmt->BindUTF8StringByIndex(1, aOrigin);
356 0 : NS_ENSURE_SUCCESS(rv, rv);
357 :
358 0 : rv = mStmt->BindUTF8StringByIndex(2, aType);
359 0 : NS_ENSURE_SUCCESS(rv, rv);
360 :
361 0 : rv = mStmt->BindInt32ByIndex(3, aPermission);
362 0 : NS_ENSURE_SUCCESS(rv, rv);
363 :
364 0 : rv = mStmt->BindInt32ByIndex(4, aExpireType);
365 0 : NS_ENSURE_SUCCESS(rv, rv);
366 :
367 0 : rv = mStmt->BindInt64ByIndex(5, aExpireTime);
368 0 : NS_ENSURE_SUCCESS(rv, rv);
369 :
370 0 : rv = mStmt->BindInt64ByIndex(6, aModificationTime);
371 0 : NS_ENSURE_SUCCESS(rv, rv);
372 :
373 : // Increment the working identifier, as we are about to use this one
374 0 : (*mID)++;
375 :
376 0 : rv = mStmt->Execute();
377 0 : NS_ENSURE_SUCCESS(rv, rv);
378 :
379 0 : return NS_OK;
380 : }
381 :
382 : private:
383 : nsCOMPtr<mozIStorageStatement> mStmt;
384 : nsCOMPtr<mozIStorageConnection> mDBConn;
385 : int64_t* mID;
386 : };
387 :
388 0 : class MOZ_STACK_CLASS UpgradeHostToOriginHostfileImport final : public UpgradeHostToOriginHelper {
389 : public:
390 0 : UpgradeHostToOriginHostfileImport(nsPermissionManager* aPm,
391 : nsPermissionManager::DBOperationType aOperation,
392 0 : int64_t aID) : mPm(aPm)
393 : , mOperation(aOperation)
394 0 : , mID(aID)
395 0 : {}
396 :
397 : nsresult
398 0 : Insert(const nsACString& aOrigin, const nsCString& aType,
399 : uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
400 : int64_t aModificationTime) final
401 : {
402 0 : nsCOMPtr<nsIPrincipal> principal;
403 0 : nsresult rv = GetPrincipalFromOrigin(aOrigin, getter_AddRefs(principal));
404 0 : NS_ENSURE_SUCCESS(rv, rv);
405 :
406 0 : return mPm->AddInternal(principal, aType, aPermission, mID,
407 : aExpireType, aExpireTime, aModificationTime,
408 0 : nsPermissionManager::eDontNotify, mOperation);
409 : }
410 :
411 : private:
412 : RefPtr<nsPermissionManager> mPm;
413 : nsPermissionManager::DBOperationType mOperation;
414 : int64_t mID;
415 : };
416 :
417 0 : class MOZ_STACK_CLASS UpgradeIPHostToOriginDB final : public UpgradeHostToOriginHelper {
418 : public:
419 0 : UpgradeIPHostToOriginDB(mozIStorageConnection* aDBConn, int64_t* aID) : mDBConn(aDBConn)
420 0 : , mID(aID)
421 : {
422 0 : mDBConn->CreateStatement(NS_LITERAL_CSTRING(
423 : "INSERT INTO moz_perms"
424 : "(id, origin, type, permission, expireType, expireTime, modificationTime) "
425 0 : "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmt));
426 :
427 0 : mDBConn->CreateStatement(NS_LITERAL_CSTRING(
428 : "SELECT id FROM moz_perms WHERE origin = ?1 AND type = ?2"),
429 0 : getter_AddRefs(mLookupStmt));
430 0 : }
431 :
432 : nsresult
433 0 : Insert(const nsACString& aOrigin, const nsCString& aType,
434 : uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
435 : int64_t aModificationTime) final
436 : {
437 : // Every time the migration code wants to insert an origin into
438 : // the database we need to check to see if someone has already
439 : // created a permissions entry for that permission. If they have,
440 : // we don't want to insert a duplicate row.
441 : //
442 : // We can afford to do this lookup unconditionally and not perform
443 : // caching, as a origin type pair should only be attempted to be
444 : // inserted once.
445 :
446 0 : nsresult rv = mLookupStmt->Reset();
447 0 : NS_ENSURE_SUCCESS(rv, rv);
448 :
449 0 : rv = mLookupStmt->BindUTF8StringByIndex(0, aOrigin);
450 0 : NS_ENSURE_SUCCESS(rv, rv);
451 :
452 0 : rv = mLookupStmt->BindUTF8StringByIndex(1, aType);
453 0 : NS_ENSURE_SUCCESS(rv, rv);
454 :
455 : // Check if we already have the row in the database, if we do, then
456 : // we don't want to be inserting it again.
457 0 : bool moreStmts = false;
458 0 : if (NS_FAILED(mLookupStmt->ExecuteStep(&moreStmts)) || moreStmts) {
459 0 : mLookupStmt->Reset();
460 : NS_WARNING("A permissions entry was going to be re-migrated, "
461 0 : "but was already found in the permissions database.");
462 0 : return NS_OK;
463 : }
464 :
465 : // Actually insert the statement into the database.
466 0 : rv = mStmt->BindInt64ByIndex(0, *mID);
467 0 : NS_ENSURE_SUCCESS(rv, rv);
468 :
469 0 : rv = mStmt->BindUTF8StringByIndex(1, aOrigin);
470 0 : NS_ENSURE_SUCCESS(rv, rv);
471 :
472 0 : rv = mStmt->BindUTF8StringByIndex(2, aType);
473 0 : NS_ENSURE_SUCCESS(rv, rv);
474 :
475 0 : rv = mStmt->BindInt32ByIndex(3, aPermission);
476 0 : NS_ENSURE_SUCCESS(rv, rv);
477 :
478 0 : rv = mStmt->BindInt32ByIndex(4, aExpireType);
479 0 : NS_ENSURE_SUCCESS(rv, rv);
480 :
481 0 : rv = mStmt->BindInt64ByIndex(5, aExpireTime);
482 0 : NS_ENSURE_SUCCESS(rv, rv);
483 :
484 0 : rv = mStmt->BindInt64ByIndex(6, aModificationTime);
485 0 : NS_ENSURE_SUCCESS(rv, rv);
486 :
487 : // Increment the working identifier, as we are about to use this one
488 0 : (*mID)++;
489 :
490 0 : rv = mStmt->Execute();
491 0 : NS_ENSURE_SUCCESS(rv, rv);
492 :
493 0 : return NS_OK;
494 : }
495 :
496 : private:
497 : nsCOMPtr<mozIStorageStatement> mStmt;
498 : nsCOMPtr<mozIStorageStatement> mLookupStmt;
499 : nsCOMPtr<mozIStorageConnection> mDBConn;
500 : int64_t* mID;
501 : };
502 :
503 :
504 : nsresult
505 0 : UpgradeHostToOriginAndInsert(const nsACString& aHost, const nsCString& aType,
506 : uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
507 : int64_t aModificationTime, uint32_t aAppId, bool aIsInIsolatedMozBrowserElement,
508 : UpgradeHostToOriginHelper* aHelper)
509 : {
510 0 : if (aHost.EqualsLiteral("<file>")) {
511 : // We no longer support the magic host <file>
512 : NS_WARNING("The magic host <file> is no longer supported. "
513 0 : "It is being removed from the permissions database.");
514 0 : return NS_OK;
515 : }
516 :
517 : // First, we check to see if the host is a valid URI. If it is, it can be imported directly
518 0 : nsCOMPtr<nsIURI> uri;
519 0 : nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost);
520 0 : if (NS_SUCCEEDED(rv)) {
521 : // It was previously possible to insert useless entries to your permissions database
522 : // for URIs which have a null principal. This acts as a cleanup, getting rid of
523 : // these useless database entries
524 0 : bool nullpScheme = false;
525 0 : if (NS_SUCCEEDED(uri->SchemeIs("moz-nullprincipal", &nullpScheme)) && nullpScheme) {
526 0 : NS_WARNING("A moz-nullprincipal: permission is being discarded.");
527 0 : return NS_OK;
528 : }
529 :
530 0 : nsCOMPtr<nsIPrincipal> principal;
531 0 : rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
532 0 : NS_ENSURE_SUCCESS(rv, rv);
533 :
534 0 : nsAutoCString origin;
535 0 : rv = GetOriginFromPrincipal(principal, origin);
536 0 : NS_ENSURE_SUCCESS(rv, rv);
537 :
538 : return aHelper->Insert(origin, aType, aPermission,
539 0 : aExpireType, aExpireTime, aModificationTime);
540 : return NS_OK;
541 : }
542 :
543 : // The user may use this host at non-standard ports or protocols, we can use their history
544 : // to guess what ports and protocols we want to add permissions for.
545 : // We find every URI which they have visited with this host (or a subdomain of this host),
546 : // and try to add it as a principal.
547 0 : bool foundHistory = false;
548 :
549 0 : nsCOMPtr<nsINavHistoryService> histSrv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
550 :
551 0 : if (histSrv) {
552 0 : nsCOMPtr<nsINavHistoryQuery> histQuery;
553 0 : rv = histSrv->GetNewQuery(getter_AddRefs(histQuery));
554 0 : NS_ENSURE_SUCCESS(rv, rv);
555 :
556 : // Get the eTLD+1 of the domain
557 0 : nsAutoCString eTLD1;
558 : nsCOMPtr<nsIEffectiveTLDService> tldService =
559 0 : do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
560 0 : MOZ_ASSERT(tldService); // We should always have a tldService
561 0 : if (tldService) {
562 0 : rv = tldService->GetBaseDomainFromHost(aHost, 0, eTLD1);
563 : }
564 :
565 0 : if (!tldService || NS_FAILED(rv)) {
566 : // If the lookup on the tldService for the base domain for the host failed,
567 : // that means that we just want to directly use the host as the host name
568 : // for the lookup.
569 0 : eTLD1 = aHost;
570 : }
571 :
572 : // We want to only find history items for this particular eTLD+1, and subdomains
573 0 : rv = histQuery->SetDomain(eTLD1);
574 0 : NS_ENSURE_SUCCESS(rv, rv);
575 :
576 0 : rv = histQuery->SetDomainIsHost(false);
577 0 : NS_ENSURE_SUCCESS(rv, rv);
578 :
579 0 : nsCOMPtr<nsINavHistoryQueryOptions> histQueryOpts;
580 0 : rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts));
581 0 : NS_ENSURE_SUCCESS(rv, rv);
582 :
583 : // We want to get the URIs for every item in the user's history with the given host
584 0 : rv = histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
585 0 : NS_ENSURE_SUCCESS(rv, rv);
586 :
587 : // We only search history, because searching both bookmarks and history
588 : // is not supported, and history tends to be more comprehensive.
589 0 : rv = histQueryOpts->SetQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
590 0 : NS_ENSURE_SUCCESS(rv, rv);
591 :
592 : // We include hidden URIs (such as those visited via iFrames) as they may have permissions too
593 0 : rv = histQueryOpts->SetIncludeHidden(true);
594 0 : NS_ENSURE_SUCCESS(rv, rv);
595 :
596 0 : nsCOMPtr<nsINavHistoryResult> histResult;
597 0 : rv = histSrv->ExecuteQuery(histQuery, histQueryOpts, getter_AddRefs(histResult));
598 0 : NS_ENSURE_SUCCESS(rv, rv);
599 :
600 0 : nsCOMPtr<nsINavHistoryContainerResultNode> histResultContainer;
601 0 : rv = histResult->GetRoot(getter_AddRefs(histResultContainer));
602 0 : NS_ENSURE_SUCCESS(rv, rv);
603 :
604 0 : rv = histResultContainer->SetContainerOpen(true);
605 0 : NS_ENSURE_SUCCESS(rv, rv);
606 :
607 0 : uint32_t childCount = 0;
608 0 : rv = histResultContainer->GetChildCount(&childCount);
609 0 : NS_ENSURE_SUCCESS(rv, rv);
610 :
611 0 : nsTHashtable<nsCStringHashKey> insertedOrigins;
612 0 : for (uint32_t i = 0; i < childCount; i++) {
613 0 : nsCOMPtr<nsINavHistoryResultNode> child;
614 0 : histResultContainer->GetChild(i, getter_AddRefs(child));
615 0 : if (NS_WARN_IF(NS_FAILED(rv))) continue;
616 :
617 : uint32_t type;
618 0 : rv = child->GetType(&type);
619 0 : if (NS_WARN_IF(NS_FAILED(rv)) || type != nsINavHistoryResultNode::RESULT_TYPE_URI) {
620 : NS_WARNING("Unexpected non-RESULT_TYPE_URI node in "
621 0 : "UpgradeHostToOriginAndInsert()");
622 0 : continue;
623 : }
624 :
625 0 : nsAutoCString uriSpec;
626 0 : rv = child->GetUri(uriSpec);
627 0 : if (NS_WARN_IF(NS_FAILED(rv))) continue;
628 :
629 0 : nsCOMPtr<nsIURI> uri;
630 0 : rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
631 0 : if (NS_WARN_IF(NS_FAILED(rv))) continue;
632 :
633 : // Use the provided host - this URI may be for a subdomain, rather than the host we care about.
634 0 : rv = uri->SetHost(aHost);
635 0 : if (NS_WARN_IF(NS_FAILED(rv))) continue;
636 :
637 : // We now have a URI which we can make a nsIPrincipal out of
638 0 : nsCOMPtr<nsIPrincipal> principal;
639 0 : rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
640 0 : if (NS_WARN_IF(NS_FAILED(rv))) continue;
641 :
642 0 : nsAutoCString origin;
643 0 : rv = GetOriginFromPrincipal(principal, origin);
644 0 : if (NS_WARN_IF(NS_FAILED(rv))) continue;
645 :
646 : // Ensure that we don't insert the same origin repeatedly
647 0 : if (insertedOrigins.Contains(origin)) {
648 0 : continue;
649 : }
650 :
651 0 : foundHistory = true;
652 : rv = aHelper->Insert(origin, aType, aPermission,
653 0 : aExpireType, aExpireTime, aModificationTime);
654 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Insert failed");
655 0 : insertedOrigins.PutEntry(origin);
656 : }
657 :
658 0 : rv = histResultContainer->SetContainerOpen(false);
659 0 : NS_ENSURE_SUCCESS(rv, rv);
660 : }
661 :
662 : // If we didn't find any origins for this host in the poermissions database,
663 : // we can insert the default http:// and https:// permissions into the database.
664 : // This has a relatively high liklihood of applying the permission to the correct
665 : // origin.
666 0 : if (!foundHistory) {
667 0 : nsAutoCString hostSegment;
668 0 : nsCOMPtr<nsIPrincipal> principal;
669 0 : nsAutoCString origin;
670 :
671 : // If this is an ipv6 URI, we need to surround it in '[', ']' before trying to
672 : // parse it as a URI.
673 0 : if (aHost.FindChar(':') != -1) {
674 0 : hostSegment.Assign("[");
675 0 : hostSegment.Append(aHost);
676 0 : hostSegment.Append("]");
677 : } else {
678 0 : hostSegment.Assign(aHost);
679 : }
680 :
681 : // http:// URI default
682 0 : rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + hostSegment);
683 0 : NS_ENSURE_SUCCESS(rv, rv);
684 :
685 0 : rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
686 0 : NS_ENSURE_SUCCESS(rv, rv);
687 :
688 0 : rv = GetOriginFromPrincipal(principal, origin);
689 0 : NS_ENSURE_SUCCESS(rv, rv);
690 :
691 : aHelper->Insert(origin, aType, aPermission,
692 0 : aExpireType, aExpireTime, aModificationTime);
693 :
694 : // https:// URI default
695 0 : rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + hostSegment);
696 0 : NS_ENSURE_SUCCESS(rv, rv);
697 :
698 0 : rv = GetPrincipal(uri, aAppId, aIsInIsolatedMozBrowserElement, getter_AddRefs(principal));
699 0 : NS_ENSURE_SUCCESS(rv, rv);
700 :
701 0 : rv = GetOriginFromPrincipal(principal, origin);
702 0 : NS_ENSURE_SUCCESS(rv, rv);
703 :
704 : aHelper->Insert(origin, aType, aPermission,
705 0 : aExpireType, aExpireTime, aModificationTime);
706 : }
707 :
708 0 : return NS_OK;
709 : }
710 :
711 : static bool
712 0 : IsExpandedPrincipal(nsIPrincipal* aPrincipal)
713 : {
714 0 : nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
715 0 : return !!ep;
716 : }
717 :
718 : } // namespace
719 :
720 : ////////////////////////////////////////////////////////////////////////////////
721 :
722 : nsPermissionManager::PermissionKey*
723 16 : nsPermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal* aPrincipal,
724 : nsresult& aResult)
725 : {
726 32 : nsAutoCString origin;
727 16 : aResult = GetOriginFromPrincipal(aPrincipal, origin);
728 16 : if (NS_WARN_IF(NS_FAILED(aResult))) {
729 0 : return nullptr;
730 : }
731 :
732 16 : return new PermissionKey(origin);
733 : }
734 :
735 : nsPermissionManager::PermissionKey*
736 0 : nsPermissionManager::PermissionKey::CreateFromURI(nsIURI* aURI, nsresult& aResult)
737 : {
738 0 : nsAutoCString origin;
739 0 : aResult = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, origin);
740 0 : if (NS_WARN_IF(NS_FAILED(aResult))) {
741 0 : return nullptr;
742 : }
743 :
744 0 : return new PermissionKey(origin);
745 : }
746 :
747 : /**
748 : * Simple callback used by |AsyncClose| to trigger a treatment once
749 : * the database is closed.
750 : *
751 : * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
752 : * |nsPermissionManager|, this will create a cycle.
753 : *
754 : * Note: Once the callback has been called this DeleteFromMozHostListener cannot
755 : * be reused.
756 : */
757 : class CloseDatabaseListener final : public mozIStorageCompletionCallback
758 : {
759 0 : ~CloseDatabaseListener() {}
760 :
761 : public:
762 : NS_DECL_ISUPPORTS
763 : NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
764 : /**
765 : * @param aManager The owning manager.
766 : * @param aRebuildOnSuccess If |true|, reinitialize the database once
767 : * it has been closed. Otherwise, do nothing such.
768 : */
769 : CloseDatabaseListener(nsPermissionManager* aManager,
770 : bool aRebuildOnSuccess);
771 :
772 : protected:
773 : RefPtr<nsPermissionManager> mManager;
774 : bool mRebuildOnSuccess;
775 : };
776 :
777 0 : NS_IMPL_ISUPPORTS(CloseDatabaseListener, mozIStorageCompletionCallback)
778 :
779 0 : CloseDatabaseListener::CloseDatabaseListener(nsPermissionManager* aManager,
780 0 : bool aRebuildOnSuccess)
781 : : mManager(aManager)
782 0 : , mRebuildOnSuccess(aRebuildOnSuccess)
783 : {
784 0 : }
785 :
786 : NS_IMETHODIMP
787 0 : CloseDatabaseListener::Complete(nsresult, nsISupports*)
788 : {
789 : // Help breaking cycles
790 0 : RefPtr<nsPermissionManager> manager = mManager.forget();
791 0 : if (mRebuildOnSuccess && !manager->mIsShuttingDown) {
792 0 : return manager->InitDB(true);
793 : }
794 0 : return NS_OK;
795 : }
796 :
797 :
798 : /**
799 : * Simple callback used by |RemoveAllInternal| to trigger closing
800 : * the database and reinitializing it.
801 : *
802 : * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a
803 : * |nsPermissionManager|, this will create a cycle.
804 : *
805 : * Note: Once the callback has been called this DeleteFromMozHostListener cannot
806 : * be reused.
807 : */
808 : class DeleteFromMozHostListener final : public mozIStorageStatementCallback
809 : {
810 0 : ~DeleteFromMozHostListener() {}
811 :
812 : public:
813 : NS_DECL_ISUPPORTS
814 : NS_DECL_MOZISTORAGESTATEMENTCALLBACK
815 :
816 : /**
817 : * @param aManager The owning manager.
818 : */
819 : explicit DeleteFromMozHostListener(nsPermissionManager* aManager);
820 :
821 : protected:
822 : RefPtr<nsPermissionManager> mManager;
823 : };
824 :
825 0 : NS_IMPL_ISUPPORTS(DeleteFromMozHostListener, mozIStorageStatementCallback)
826 :
827 0 : DeleteFromMozHostListener::
828 0 : DeleteFromMozHostListener(nsPermissionManager* aManager)
829 0 : : mManager(aManager)
830 : {
831 0 : }
832 :
833 0 : NS_IMETHODIMP DeleteFromMozHostListener::HandleResult(mozIStorageResultSet *)
834 : {
835 0 : MOZ_CRASH("Should not get any results");
836 : }
837 :
838 0 : NS_IMETHODIMP DeleteFromMozHostListener::HandleError(mozIStorageError *)
839 : {
840 : // Errors are handled in |HandleCompletion|
841 0 : return NS_OK;
842 : }
843 :
844 0 : NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason)
845 : {
846 : // Help breaking cycles
847 0 : RefPtr<nsPermissionManager> manager = mManager.forget();
848 :
849 0 : if (aReason == REASON_ERROR) {
850 0 : manager->CloseDB(true);
851 : }
852 :
853 0 : return NS_OK;
854 : }
855 :
856 : /* static */ void
857 3 : nsPermissionManager::ClearOriginDataObserverInit()
858 : {
859 : nsCOMPtr<nsIObserverService> observerService =
860 6 : mozilla::services::GetObserverService();
861 6 : observerService->AddObserver(new ClearOriginDataObserver(), "clear-origin-attributes-data", /* ownsWeak= */ false);
862 3 : }
863 :
864 : ////////////////////////////////////////////////////////////////////////////////
865 : // nsPermissionManager Implementation
866 :
867 : #define PERMISSIONS_FILE_NAME "permissions.sqlite"
868 : #define HOSTS_SCHEMA_VERSION 9
869 :
870 : #define HOSTPERM_FILE_NAME "hostperm.1"
871 :
872 : // Default permissions are read from a URL - this is the preference we read
873 : // to find that URL. If not set, don't use any default permissions.
874 : static const char kDefaultsUrlPrefName[] = "permissions.manager.defaultsUrl";
875 :
876 : static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
877 :
878 277 : NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
879 :
880 3 : nsPermissionManager::nsPermissionManager()
881 : : mMemoryOnlyDB(false)
882 : , mLargestID(0)
883 3 : , mIsShuttingDown(false)
884 : {
885 3 : }
886 :
887 0 : nsPermissionManager::~nsPermissionManager()
888 : {
889 : // NOTE: Make sure to reject each of the promises in mPermissionKeyPromiseMap
890 : // before destroying.
891 0 : for (auto iter = mPermissionKeyPromiseMap.Iter(); !iter.Done(); iter.Next()) {
892 0 : if (iter.Data()) {
893 0 : iter.Data()->Reject(NS_ERROR_FAILURE, __func__);
894 : }
895 : }
896 0 : mPermissionKeyPromiseMap.Clear();
897 :
898 0 : RemoveAllFromMemory();
899 0 : gPermissionManager = nullptr;
900 0 : }
901 :
902 : // static
903 : nsIPermissionManager*
904 3 : nsPermissionManager::GetXPCOMSingleton()
905 : {
906 3 : if (gPermissionManager) {
907 0 : NS_ADDREF(gPermissionManager);
908 0 : return gPermissionManager;
909 : }
910 :
911 : // Create a new singleton nsPermissionManager.
912 : // We AddRef only once since XPCOM has rules about the ordering of module
913 : // teardowns - by the time our module destructor is called, it's too late to
914 : // Release our members, since GC cycles have already been completed and
915 : // would result in serious leaks.
916 : // See bug 209571.
917 3 : gPermissionManager = new nsPermissionManager();
918 3 : if (gPermissionManager) {
919 3 : NS_ADDREF(gPermissionManager);
920 3 : if (NS_FAILED(gPermissionManager->Init())) {
921 0 : NS_RELEASE(gPermissionManager);
922 : }
923 : }
924 :
925 3 : return gPermissionManager;
926 : }
927 :
928 : nsresult
929 3 : nsPermissionManager::Init()
930 : {
931 : // If the 'permissions.memory_only' pref is set to true, then don't write any
932 : // permission settings to disk, but keep them in a memory-only database.
933 3 : mMemoryOnlyDB = mozilla::Preferences::GetBool("permissions.memory_only", false);
934 :
935 3 : if (IsChildProcess()) {
936 : // Stop here; we don't need the DB in the child process. Instead we will be
937 : // sent permissions as we need them by our parent process.
938 2 : return NS_OK;
939 : }
940 :
941 : nsCOMPtr<nsIObserverService> observerService =
942 2 : mozilla::services::GetObserverService();
943 1 : if (observerService) {
944 1 : observerService->AddObserver(this, "profile-before-change", true);
945 1 : observerService->AddObserver(this, "profile-do-change", true);
946 : }
947 :
948 : // ignore failure here, since it's non-fatal (we can run fine without
949 : // persistent storage - e.g. if there's no profile).
950 : // XXX should we tell the user about this?
951 1 : InitDB(false);
952 :
953 1 : return NS_OK;
954 : }
955 :
956 : nsresult
957 1 : nsPermissionManager::OpenDatabase(nsIFile* aPermissionsFile)
958 : {
959 : nsresult rv;
960 2 : nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
961 1 : if (!storage) {
962 0 : return NS_ERROR_UNEXPECTED;
963 : }
964 : // cache a connection to the hosts database
965 1 : if (mMemoryOnlyDB) {
966 0 : rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
967 : } else {
968 1 : rv = storage->OpenDatabase(aPermissionsFile, getter_AddRefs(mDBConn));
969 : }
970 1 : return rv;
971 : }
972 :
973 : nsresult
974 1 : nsPermissionManager::InitDB(bool aRemoveFile)
975 : {
976 2 : nsCOMPtr<nsIFile> permissionsFile;
977 1 : nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, getter_AddRefs(permissionsFile));
978 1 : if (NS_FAILED(rv)) {
979 1 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
980 : }
981 1 : NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
982 :
983 1 : rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(PERMISSIONS_FILE_NAME));
984 1 : NS_ENSURE_SUCCESS(rv, rv);
985 :
986 1 : if (aRemoveFile) {
987 0 : bool exists = false;
988 0 : rv = permissionsFile->Exists(&exists);
989 0 : NS_ENSURE_SUCCESS(rv, rv);
990 0 : if (exists) {
991 0 : rv = permissionsFile->Remove(false);
992 0 : NS_ENSURE_SUCCESS(rv, rv);
993 : }
994 : }
995 :
996 1 : rv = OpenDatabase(permissionsFile);
997 1 : if (rv == NS_ERROR_FILE_CORRUPTED) {
998 0 : LogToConsole(NS_LITERAL_STRING("permissions.sqlite is corrupted! Try again!"));
999 :
1000 : // Add telemetry probe
1001 0 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::PERMISSIONS_SQL_CORRUPTED, 1);
1002 :
1003 : // delete corrupted permissions.sqlite and try again
1004 0 : rv = permissionsFile->Remove(false);
1005 0 : NS_ENSURE_SUCCESS(rv, rv);
1006 0 : LogToConsole(NS_LITERAL_STRING("Corrupted permissions.sqlite has been removed."));
1007 :
1008 0 : rv = OpenDatabase(permissionsFile);
1009 0 : NS_ENSURE_SUCCESS(rv, rv);
1010 0 : LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));
1011 1 : } else if (NS_FAILED(rv)) {
1012 0 : return rv;
1013 : }
1014 :
1015 : bool ready;
1016 1 : mDBConn->GetConnectionReady(&ready);
1017 1 : if (!ready) {
1018 0 : LogToConsole(NS_LITERAL_STRING("Fail to get connection to permissions.sqlite! Try again!"));
1019 :
1020 : // delete and try again
1021 0 : rv = permissionsFile->Remove(false);
1022 0 : NS_ENSURE_SUCCESS(rv, rv);
1023 0 : LogToConsole(NS_LITERAL_STRING("Defective permissions.sqlite has been removed."));
1024 :
1025 : // Add telemetry probe
1026 0 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::DEFECTIVE_PERMISSIONS_SQL_REMOVED, 1);
1027 :
1028 0 : rv = OpenDatabase(permissionsFile);
1029 0 : NS_ENSURE_SUCCESS(rv, rv);
1030 0 : LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));
1031 :
1032 0 : mDBConn->GetConnectionReady(&ready);
1033 0 : if (!ready)
1034 0 : return NS_ERROR_UNEXPECTED;
1035 : }
1036 :
1037 :
1038 1 : bool tableExists = false;
1039 1 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &tableExists);
1040 1 : if (!tableExists) {
1041 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
1042 : }
1043 1 : if (!tableExists) {
1044 0 : rv = CreateTable();
1045 0 : NS_ENSURE_SUCCESS(rv, rv);
1046 : } else {
1047 : // table already exists; check the schema version before reading
1048 : int32_t dbSchemaVersion;
1049 1 : rv = mDBConn->GetSchemaVersion(&dbSchemaVersion);
1050 1 : NS_ENSURE_SUCCESS(rv, rv);
1051 :
1052 1 : switch (dbSchemaVersion) {
1053 : // upgrading.
1054 : // every time you increment the database schema, you need to implement
1055 : // the upgrading code from the previous version to the new one.
1056 : // fall through to current version
1057 :
1058 : case 1:
1059 : {
1060 : // previous non-expiry version of database. Upgrade it by adding the
1061 : // expiration columns
1062 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1063 0 : "ALTER TABLE moz_hosts ADD expireType INTEGER"));
1064 0 : NS_ENSURE_SUCCESS(rv, rv);
1065 :
1066 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1067 0 : "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
1068 0 : NS_ENSURE_SUCCESS(rv, rv);
1069 : }
1070 :
1071 : // fall through to the next upgrade
1072 : MOZ_FALLTHROUGH;
1073 :
1074 : // TODO: we want to make default version as version 2 in order to fix bug 784875.
1075 : case 0:
1076 : case 2:
1077 : {
1078 : // Add appId/isInBrowserElement fields.
1079 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1080 0 : "ALTER TABLE moz_hosts ADD appId INTEGER"));
1081 0 : NS_ENSURE_SUCCESS(rv, rv);
1082 :
1083 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1084 0 : "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
1085 0 : NS_ENSURE_SUCCESS(rv, rv);
1086 :
1087 0 : rv = mDBConn->SetSchemaVersion(3);
1088 0 : NS_ENSURE_SUCCESS(rv, rv);
1089 : }
1090 :
1091 : // fall through to the next upgrade
1092 : MOZ_FALLTHROUGH;
1093 :
1094 : // Version 3->4 is the creation of the modificationTime field.
1095 : case 3:
1096 : {
1097 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1098 0 : "ALTER TABLE moz_hosts ADD modificationTime INTEGER"));
1099 0 : NS_ENSURE_SUCCESS(rv, rv);
1100 :
1101 : // We leave the modificationTime at zero for all existing records; using
1102 : // now() would mean, eg, that doing "remove all from the last hour"
1103 : // within the first hour after migration would remove all permissions.
1104 :
1105 0 : rv = mDBConn->SetSchemaVersion(4);
1106 0 : NS_ENSURE_SUCCESS(rv, rv);
1107 : }
1108 :
1109 : // fall through to the next upgrade
1110 : MOZ_FALLTHROUGH;
1111 :
1112 : // In version 5, host appId, and isInBrowserElement were merged into a
1113 : // single origin entry
1114 : //
1115 : // In version 6, the tables were renamed for backwards compatability reasons
1116 : // with version 4 and earlier.
1117 : //
1118 : // In version 7, a bug in the migration used for version 4->5 was discovered
1119 : // which could have triggered data-loss. Because of that, all users with a
1120 : // version 4, 5, or 6 database will be re-migrated from the backup database.
1121 : // (bug 1186034). This migration bug is not present after bug 1185340, and the
1122 : // re-migration ensures that all users have the fix.
1123 : case 5:
1124 : // This branch could also be reached via dbSchemaVersion == 3, in which case
1125 : // we want to fall through to the dbSchemaVersion == 4 case.
1126 : // The easiest way to do that is to perform this extra check here to make
1127 : // sure that we didn't get here via a fallthrough from v3
1128 0 : if (dbSchemaVersion == 5) {
1129 : // In version 5, the backup database is named moz_hosts_v4. We perform
1130 : // the version 5->6 migration to get the tables to have consistent
1131 : // naming conventions.
1132 :
1133 : // Version 5->6 is the renaming of moz_hosts to moz_perms, and
1134 : // moz_hosts_v4 to moz_hosts (bug 1185343)
1135 : //
1136 : // In version 5, we performed the modifications to the permissions
1137 : // database in place, this meant that if you upgraded to a version which
1138 : // used V5, and then downgraded to a version which used v4 or earlier,
1139 : // the fallback path would drop the table, and your permissions data
1140 : // would be lost. This migration undoes that mistake, by restoring the
1141 : // old moz_hosts table (if it was present), and instead using the new
1142 : // table moz_perms for the new permissions schema.
1143 : //
1144 : // NOTE: If you downgrade, store new permissions, and then upgrade
1145 : // again, these new permissions won't be migrated or reflected in the
1146 : // updated database. This migration only occurs once, as if moz_perms
1147 : // exists, it will skip creating it. In addition, permissions added
1148 : // after the migration will not be visible in previous versions of
1149 : // firefox.
1150 :
1151 0 : bool permsTableExists = false;
1152 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &permsTableExists);
1153 0 : if (!permsTableExists) {
1154 : // Move the upgraded database to moz_perms
1155 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1156 0 : "ALTER TABLE moz_hosts RENAME TO moz_perms"));
1157 0 : NS_ENSURE_SUCCESS(rv, rv);
1158 : } else {
1159 : NS_WARNING("moz_hosts was not renamed to moz_perms, "
1160 0 : "as a moz_perms table already exists");
1161 :
1162 : // In the situation where a moz_perms table already exists, but the
1163 : // schema is lower than 6, a migration has already previously occured
1164 : // to V6, but a downgrade has caused the moz_hosts table to be
1165 : // dropped. This should only occur in the case of a downgrade to a V5
1166 : // database, which was only present in a few day's nightlies. As that
1167 : // version was likely used only on a temporary basis, we assume that
1168 : // the database from the previous V6 has the permissions which the
1169 : // user actually wants to use. We have to get rid of moz_hosts such
1170 : // that moz_hosts_v4 can be moved into its place if it exists.
1171 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
1172 0 : NS_ENSURE_SUCCESS(rv, rv);
1173 : }
1174 :
1175 : #ifdef DEBUG
1176 : // The moz_hosts table shouldn't exist anymore
1177 0 : bool hostsTableExists = false;
1178 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &hostsTableExists);
1179 0 : MOZ_ASSERT(!hostsTableExists);
1180 : #endif
1181 :
1182 : // Rename moz_hosts_v4 back to it's original location, if it exists
1183 0 : bool v4TableExists = false;
1184 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_v4"), &v4TableExists);
1185 0 : if (v4TableExists) {
1186 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1187 0 : "ALTER TABLE moz_hosts_v4 RENAME TO moz_hosts"));
1188 0 : NS_ENSURE_SUCCESS(rv, rv);
1189 : }
1190 :
1191 0 : rv = mDBConn->SetSchemaVersion(6);
1192 0 : NS_ENSURE_SUCCESS(rv, rv);
1193 : }
1194 :
1195 : // fall through to the next upgrade
1196 : MOZ_FALLTHROUGH;
1197 :
1198 : // At this point, the version 5 table has been migrated to a version 6 table
1199 : // We are guaranteed to have at least one of moz_hosts and moz_perms. If
1200 : // we have moz_hosts, we will migrate moz_hosts into moz_perms (even if
1201 : // we already have a moz_perms, as we need a re-migration due to bug 1186034).
1202 : //
1203 : // After this migration, we are guaranteed to have both a moz_hosts (for backwards
1204 : // compatability), and a moz_perms table. The moz_hosts table will have a v4 schema,
1205 : // and the moz_perms table will have a v6 schema.
1206 : case 4:
1207 : case 6:
1208 : {
1209 0 : bool hostsTableExists = false;
1210 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &hostsTableExists);
1211 0 : if (hostsTableExists) {
1212 0 : bool migrationError = false;
1213 :
1214 : // Both versions 4 and 6 have a version 4 formatted hosts table named
1215 : // moz_hosts. We can migrate this table to our version 7 table moz_perms.
1216 : // If moz_perms is present, then we can use it as a basis for comparison.
1217 :
1218 0 : rv = mDBConn->BeginTransaction();
1219 0 : NS_ENSURE_SUCCESS(rv, rv);
1220 :
1221 0 : bool tableExists = false;
1222 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_new"), &tableExists);
1223 0 : if (tableExists) {
1224 0 : NS_WARNING("The temporary database moz_hosts_new already exists, dropping it.");
1225 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts_new"));
1226 0 : NS_ENSURE_SUCCESS(rv, rv);
1227 : }
1228 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1229 : "CREATE TABLE moz_hosts_new ("
1230 : " id INTEGER PRIMARY KEY"
1231 : ",origin TEXT"
1232 : ",type TEXT"
1233 : ",permission INTEGER"
1234 : ",expireType INTEGER"
1235 : ",expireTime INTEGER"
1236 : ",modificationTime INTEGER"
1237 0 : ")"));
1238 0 : NS_ENSURE_SUCCESS(rv, rv);
1239 :
1240 0 : nsCOMPtr<mozIStorageStatement> stmt;
1241 0 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1242 : "SELECT host, type, permission, expireType, expireTime, "
1243 : "modificationTime, appId, isInBrowserElement FROM moz_hosts"),
1244 0 : getter_AddRefs(stmt));
1245 0 : NS_ENSURE_SUCCESS(rv, rv);
1246 :
1247 0 : int64_t id = 0;
1248 0 : nsAutoCString host, type;
1249 : uint32_t permission;
1250 : uint32_t expireType;
1251 : int64_t expireTime;
1252 : int64_t modificationTime;
1253 : uint32_t appId;
1254 : bool isInBrowserElement;
1255 : bool hasResult;
1256 :
1257 0 : while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
1258 : // Read in the old row
1259 0 : rv = stmt->GetUTF8String(0, host);
1260 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1261 0 : migrationError = true;
1262 0 : continue;
1263 : }
1264 0 : rv = stmt->GetUTF8String(1, type);
1265 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1266 0 : migrationError = true;
1267 0 : continue;
1268 : }
1269 0 : permission = stmt->AsInt32(2);
1270 0 : expireType = stmt->AsInt32(3);
1271 0 : expireTime = stmt->AsInt64(4);
1272 0 : modificationTime = stmt->AsInt64(5);
1273 0 : if (NS_WARN_IF(stmt->AsInt64(6) < 0)) {
1274 0 : migrationError = true;
1275 0 : continue;
1276 : }
1277 0 : appId = static_cast<uint32_t>(stmt->AsInt64(6));
1278 0 : isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
1279 :
1280 : // Perform the meat of the migration by deferring to the
1281 : // UpgradeHostToOriginAndInsert function.
1282 0 : UpgradeHostToOriginDBMigration upHelper(mDBConn, &id);
1283 0 : rv = UpgradeHostToOriginAndInsert(host, type, permission,
1284 : expireType, expireTime,
1285 : modificationTime, appId,
1286 : isInBrowserElement,
1287 0 : &upHelper);
1288 0 : if (NS_FAILED(rv)) {
1289 : NS_WARNING("Unexpected failure when upgrading migrating permission "
1290 0 : "from host to origin");
1291 0 : migrationError = true;
1292 : }
1293 : }
1294 :
1295 : // We don't drop the moz_hosts table such that it is avaliable for
1296 : // backwards-compatability and for future migrations in case of
1297 : // migration errors in the current code.
1298 : // Create a marker empty table which will indicate that the moz_hosts
1299 : // table is intended to act as a backup. If this table is not present,
1300 : // then the moz_hosts table was created as a random empty table.
1301 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1302 0 : "CREATE TABLE moz_hosts_is_backup (dummy INTEGER PRIMARY KEY)"));
1303 0 : NS_ENSURE_SUCCESS(rv, rv);
1304 :
1305 0 : bool permsTableExists = false;
1306 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &permsTableExists);
1307 0 : if (permsTableExists) {
1308 : // The user already had a moz_perms table, and we are performing a
1309 : // re-migration. We count the rows in the old table for telemetry,
1310 : // and then back up their old database as moz_perms_v6
1311 :
1312 0 : nsCOMPtr<mozIStorageStatement> countStmt;
1313 0 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(*) FROM moz_perms"),
1314 0 : getter_AddRefs(countStmt));
1315 0 : bool hasResult = false;
1316 0 : if (NS_SUCCEEDED(rv) &&
1317 0 : NS_SUCCEEDED(countStmt->ExecuteStep(&hasResult)) &&
1318 : hasResult) {
1319 0 : int32_t permsCount = countStmt->AsInt32(0);
1320 :
1321 : // The id variable contains the number of rows inserted into the
1322 : // moz_hosts_new table (as one ID was used per entry)
1323 : uint32_t telemetryValue;
1324 0 : if (permsCount > id) {
1325 0 : telemetryValue = 3; // NEW > OLD
1326 0 : } else if (permsCount == id) {
1327 0 : telemetryValue = 2; // NEW == OLD
1328 0 : } else if (permsCount == 0) {
1329 0 : telemetryValue = 0; // NEW = 0
1330 : } else {
1331 0 : telemetryValue = 1; // NEW < OLD
1332 : }
1333 :
1334 : // Report the telemetry value to telemetry
1335 : mozilla::Telemetry::Accumulate(
1336 : mozilla::Telemetry::PERMISSIONS_REMIGRATION_COMPARISON,
1337 0 : telemetryValue);
1338 : } else {
1339 0 : NS_WARNING("Could not count the rows in moz_perms");
1340 : }
1341 :
1342 : // Back up the old moz_perms database as moz_perms_v6 before we
1343 : // move the new table into its position
1344 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1345 0 : "ALTER TABLE moz_perms RENAME TO moz_perms_v6"));
1346 0 : NS_ENSURE_SUCCESS(rv, rv);
1347 : }
1348 :
1349 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1350 0 : "ALTER TABLE moz_hosts_new RENAME TO moz_perms"));
1351 0 : NS_ENSURE_SUCCESS(rv, rv);
1352 :
1353 0 : rv = mDBConn->CommitTransaction();
1354 0 : NS_ENSURE_SUCCESS(rv, rv);
1355 :
1356 0 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::PERMISSIONS_MIGRATION_7_ERROR,
1357 0 : NS_WARN_IF(migrationError));
1358 : } else {
1359 : // We don't have a moz_hosts table, so we create one for downgrading purposes.
1360 : // This table is empty.
1361 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1362 : "CREATE TABLE moz_hosts ("
1363 : " id INTEGER PRIMARY KEY"
1364 : ",host TEXT"
1365 : ",type TEXT"
1366 : ",permission INTEGER"
1367 : ",expireType INTEGER"
1368 : ",expireTime INTEGER"
1369 : ",modificationTime INTEGER"
1370 : ",appId INTEGER"
1371 : ",isInBrowserElement INTEGER"
1372 0 : ")"));
1373 0 : NS_ENSURE_SUCCESS(rv, rv);
1374 :
1375 : // We are guaranteed to have a moz_perms table at this point.
1376 : }
1377 :
1378 : #ifdef DEBUG
1379 : {
1380 : // At this point, both the moz_hosts and moz_perms tables should exist
1381 0 : bool hostsTableExists = false;
1382 0 : bool permsTableExists = false;
1383 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &hostsTableExists);
1384 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_perms"), &permsTableExists);
1385 0 : MOZ_ASSERT(hostsTableExists && permsTableExists);
1386 : }
1387 : #endif
1388 :
1389 0 : rv = mDBConn->SetSchemaVersion(7);
1390 0 : NS_ENSURE_SUCCESS(rv, rv);
1391 : }
1392 :
1393 : // fall through to the next upgrade
1394 : MOZ_FALLTHROUGH;
1395 :
1396 : // The version 7-8 migration is the re-migration of localhost and ip-address
1397 : // entries due to errors in the previous version 7 migration which caused
1398 : // localhost and ip-address entries to be incorrectly discarded.
1399 : // The version 7 migration logic has been corrected, and thus this logic only
1400 : // needs to execute if the user is currently on version 7.
1401 : case 7:
1402 : {
1403 : // This migration will be relatively expensive as we need to perform
1404 : // database lookups for each origin which we want to insert. Fortunately,
1405 : // it shouldn't be too expensive as we only want to insert a small number
1406 : // of entries created for localhost or IP addresses.
1407 :
1408 : // We only want to perform the re-migration if moz_hosts is a backup
1409 0 : bool hostsIsBackupExists = false;
1410 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_is_backup"),
1411 0 : &hostsIsBackupExists);
1412 :
1413 : // Only perform this migration if the original schema version was 7, and
1414 : // the moz_hosts table is a backup.
1415 0 : if (dbSchemaVersion == 7 && hostsIsBackupExists) {
1416 : nsCOMPtr<nsIEffectiveTLDService> tldService =
1417 0 : do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
1418 0 : MOZ_ASSERT(tldService); // We should always have a tldService
1419 :
1420 0 : nsCOMPtr<mozIStorageStatement> stmt;
1421 0 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1422 : "SELECT host, type, permission, expireType, expireTime, "
1423 : "modificationTime, appId, isInBrowserElement FROM moz_hosts"),
1424 0 : getter_AddRefs(stmt));
1425 0 : NS_ENSURE_SUCCESS(rv, rv);
1426 :
1427 0 : nsCOMPtr<mozIStorageStatement> idStmt;
1428 0 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1429 0 : "SELECT MAX(id) FROM moz_hosts"), getter_AddRefs(idStmt));
1430 0 : int64_t id = 0;
1431 0 : bool hasResult = false;
1432 0 : if (NS_SUCCEEDED(rv) &&
1433 0 : NS_SUCCEEDED(idStmt->ExecuteStep(&hasResult)) &&
1434 : hasResult) {
1435 0 : id = idStmt->AsInt32(0) + 1;
1436 : }
1437 :
1438 0 : nsAutoCString host, type;
1439 : uint32_t permission;
1440 : uint32_t expireType;
1441 : int64_t expireTime;
1442 : int64_t modificationTime;
1443 : uint32_t appId;
1444 : bool isInBrowserElement;
1445 :
1446 0 : while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
1447 : // Read in the old row
1448 0 : rv = stmt->GetUTF8String(0, host);
1449 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1450 0 : continue;
1451 : }
1452 :
1453 0 : nsAutoCString eTLD1;
1454 0 : rv = tldService->GetBaseDomainFromHost(host, 0, eTLD1);
1455 0 : if (NS_SUCCEEDED(rv)) {
1456 : // We only care about entries which the tldService can't handle
1457 0 : continue;
1458 : }
1459 :
1460 0 : rv = stmt->GetUTF8String(1, type);
1461 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1462 0 : continue;
1463 : }
1464 0 : permission = stmt->AsInt32(2);
1465 0 : expireType = stmt->AsInt32(3);
1466 0 : expireTime = stmt->AsInt64(4);
1467 0 : modificationTime = stmt->AsInt64(5);
1468 0 : if (NS_WARN_IF(stmt->AsInt64(6) < 0)) {
1469 0 : continue;
1470 : }
1471 0 : appId = static_cast<uint32_t>(stmt->AsInt64(6));
1472 0 : isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
1473 :
1474 : // Perform the meat of the migration by deferring to the
1475 : // UpgradeHostToOriginAndInsert function.
1476 0 : UpgradeIPHostToOriginDB upHelper(mDBConn, &id);
1477 0 : rv = UpgradeHostToOriginAndInsert(host, type, permission,
1478 : expireType, expireTime,
1479 : modificationTime, appId,
1480 : isInBrowserElement,
1481 0 : &upHelper);
1482 0 : if (NS_FAILED(rv)) {
1483 : NS_WARNING("Unexpected failure when upgrading migrating permission "
1484 0 : "from host to origin");
1485 : }
1486 : }
1487 : }
1488 :
1489 : // Even if we didn't perform the migration, we want to bump the schema
1490 : // version to 8.
1491 0 : rv = mDBConn->SetSchemaVersion(8);
1492 0 : NS_ENSURE_SUCCESS(rv, rv);
1493 : }
1494 :
1495 : // fall through to the next upgrade
1496 : MOZ_FALLTHROUGH;
1497 :
1498 : // The version 8-9 migration removes the unnecessary backup moz-hosts database contents.
1499 : // as the data no longer needs to be migrated
1500 : case 8:
1501 : {
1502 : // We only want to clear out the old table if it is a backup. If it isn't a backup,
1503 : // we don't need to touch it.
1504 0 : bool hostsIsBackupExists = false;
1505 0 : mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_is_backup"),
1506 0 : &hostsIsBackupExists);
1507 0 : if (hostsIsBackupExists) {
1508 : // Delete everything from the backup, we want to keep around the table so that
1509 : // you can still downgrade and not break things, but we don't need to keep the
1510 : // rows around.
1511 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_hosts"));
1512 0 : NS_ENSURE_SUCCESS(rv, rv);
1513 :
1514 : // The table is no longer a backup, so get rid of it.
1515 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts_is_backup"));
1516 0 : NS_ENSURE_SUCCESS(rv, rv);
1517 : }
1518 :
1519 0 : rv = mDBConn->SetSchemaVersion(9);
1520 0 : NS_ENSURE_SUCCESS(rv, rv);
1521 : }
1522 :
1523 : // fall through to the next upgrade
1524 : MOZ_FALLTHROUGH;
1525 :
1526 : // current version.
1527 : case HOSTS_SCHEMA_VERSION:
1528 1 : break;
1529 :
1530 : // downgrading.
1531 : // if columns have been added to the table, we can still use the ones we
1532 : // understand safely. if columns have been deleted or altered, just
1533 : // blow away the table and start from scratch! if you change the way
1534 : // a column is interpreted, make sure you also change its name so this
1535 : // check will catch it.
1536 : default:
1537 : {
1538 : // check if all the expected columns exist
1539 0 : nsCOMPtr<mozIStorageStatement> stmt;
1540 0 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1541 : "SELECT origin, type, permission, expireType, expireTime, "
1542 : "modificationTime FROM moz_perms"),
1543 0 : getter_AddRefs(stmt));
1544 0 : if (NS_SUCCEEDED(rv))
1545 0 : break;
1546 :
1547 : // our columns aren't there - drop the table!
1548 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_perms"));
1549 0 : NS_ENSURE_SUCCESS(rv, rv);
1550 :
1551 0 : rv = CreateTable();
1552 0 : NS_ENSURE_SUCCESS(rv, rv);
1553 : }
1554 0 : break;
1555 : }
1556 : }
1557 :
1558 : // cache frequently used statements (for insertion, deletion, and updating)
1559 4 : rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
1560 : "INSERT INTO moz_perms "
1561 : "(id, origin, type, permission, expireType, expireTime, modificationTime) "
1562 4 : "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmtInsert));
1563 1 : NS_ENSURE_SUCCESS(rv, rv);
1564 :
1565 4 : rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
1566 : "DELETE FROM moz_perms "
1567 4 : "WHERE id = ?1"), getter_AddRefs(mStmtDelete));
1568 1 : NS_ENSURE_SUCCESS(rv, rv);
1569 :
1570 4 : rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
1571 : "UPDATE moz_perms "
1572 : "SET permission = ?2, expireType= ?3, expireTime = ?4, modificationTime = ?5 WHERE id = ?1"),
1573 4 : getter_AddRefs(mStmtUpdate));
1574 1 : NS_ENSURE_SUCCESS(rv, rv);
1575 :
1576 : // Always import default permissions.
1577 1 : ImportDefaults();
1578 : // check whether to import or just read in the db
1579 1 : if (tableExists)
1580 1 : return Read();
1581 :
1582 0 : return Import();
1583 : }
1584 :
1585 : // sets the schema version and creates the moz_perms table.
1586 : nsresult
1587 0 : nsPermissionManager::CreateTable()
1588 : {
1589 : // set the schema version, before creating the table
1590 0 : nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
1591 0 : if (NS_FAILED(rv)) return rv;
1592 :
1593 : // create the table
1594 : // SQL also lives in automation.py.in. If you change this SQL change that
1595 : // one too
1596 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1597 : "CREATE TABLE moz_perms ("
1598 : " id INTEGER PRIMARY KEY"
1599 : ",origin TEXT"
1600 : ",type TEXT"
1601 : ",permission INTEGER"
1602 : ",expireType INTEGER"
1603 : ",expireTime INTEGER"
1604 : ",modificationTime INTEGER"
1605 0 : ")"));
1606 0 : if (NS_FAILED(rv)) return rv;
1607 :
1608 : // We also create a legacy V4 table, for backwards compatability,
1609 : // and to ensure that downgrades don't trigger a schema version change.
1610 0 : return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1611 : "CREATE TABLE moz_hosts ("
1612 : " id INTEGER PRIMARY KEY"
1613 : ",host TEXT"
1614 : ",type TEXT"
1615 : ",permission INTEGER"
1616 : ",expireType INTEGER"
1617 : ",expireTime INTEGER"
1618 : ",modificationTime INTEGER"
1619 : ",appId INTEGER"
1620 : ",isInBrowserElement INTEGER"
1621 0 : ")"));
1622 : }
1623 :
1624 : NS_IMETHODIMP
1625 0 : nsPermissionManager::Add(nsIURI *aURI,
1626 : const char *aType,
1627 : uint32_t aPermission,
1628 : uint32_t aExpireType,
1629 : int64_t aExpireTime)
1630 : {
1631 0 : NS_ENSURE_ARG_POINTER(aURI);
1632 :
1633 0 : nsCOMPtr<nsIPrincipal> principal;
1634 0 : nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
1635 0 : NS_ENSURE_SUCCESS(rv, rv);
1636 :
1637 0 : return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime);
1638 : }
1639 :
1640 : NS_IMETHODIMP
1641 0 : nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
1642 : const char* aType, uint32_t aPermission,
1643 : uint32_t aExpireType, int64_t aExpireTime)
1644 : {
1645 0 : ENSURE_NOT_CHILD_PROCESS;
1646 0 : NS_ENSURE_ARG_POINTER(aPrincipal);
1647 0 : NS_ENSURE_ARG_POINTER(aType);
1648 0 : NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
1649 : aExpireType == nsIPermissionManager::EXPIRE_TIME ||
1650 : aExpireType == nsIPermissionManager::EXPIRE_SESSION,
1651 : NS_ERROR_INVALID_ARG);
1652 :
1653 : // Skip addition if the permission is already expired. Note that EXPIRE_SESSION only
1654 : // honors expireTime if it is nonzero.
1655 0 : if ((aExpireType == nsIPermissionManager::EXPIRE_TIME ||
1656 0 : (aExpireType == nsIPermissionManager::EXPIRE_SESSION && aExpireTime != 0)) &&
1657 0 : aExpireTime <= (PR_Now() / 1000)) {
1658 0 : return NS_OK;
1659 : }
1660 :
1661 : // We don't add the system principal because it actually has no URI and we
1662 : // always allow action for them.
1663 0 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
1664 0 : return NS_OK;
1665 : }
1666 :
1667 : // Null principals can't meaningfully have persisted permissions attached to
1668 : // them, so we don't allow adding permissions for them.
1669 0 : if (aPrincipal->GetIsNullPrincipal()) {
1670 0 : return NS_OK;
1671 : }
1672 :
1673 : // Permissions may not be added to expanded principals.
1674 0 : if (IsExpandedPrincipal(aPrincipal)) {
1675 0 : return NS_ERROR_INVALID_ARG;
1676 : }
1677 :
1678 : // A modificationTime of zero will cause AddInternal to use now().
1679 0 : int64_t modificationTime = 0;
1680 :
1681 0 : return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
1682 0 : aExpireType, aExpireTime, modificationTime, eNotify, eWriteToDB);
1683 : }
1684 :
1685 : nsresult
1686 14 : nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
1687 : const nsCString& aType,
1688 : uint32_t aPermission,
1689 : int64_t aID,
1690 : uint32_t aExpireType,
1691 : int64_t aExpireTime,
1692 : int64_t aModificationTime,
1693 : NotifyOperationType aNotifyOperation,
1694 : DBOperationType aDBOperation,
1695 : const bool aIgnoreSessionPermissions)
1696 : {
1697 28 : nsAutoCString origin;
1698 14 : nsresult rv = GetOriginFromPrincipal(aPrincipal, origin);
1699 14 : NS_ENSURE_SUCCESS(rv, rv);
1700 :
1701 14 : if (!IsChildProcess()) {
1702 : IPC::Permission permission(origin, aType, aPermission,
1703 20 : aExpireType, aExpireTime);
1704 :
1705 20 : nsAutoCString permissionKey;
1706 10 : GetKeyForPermission(aPrincipal, aType.get(), permissionKey);
1707 :
1708 20 : nsTArray<ContentParent*> cplist;
1709 10 : ContentParent::GetAll(cplist);
1710 10 : for (uint32_t i = 0; i < cplist.Length(); ++i) {
1711 0 : ContentParent* cp = cplist[i];
1712 0 : if (cp->NeedsPermissionsUpdate(permissionKey))
1713 0 : Unused << cp->SendAddPermission(permission);
1714 : }
1715 : }
1716 :
1717 14 : MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType.get()));
1718 :
1719 : // look up the type index
1720 14 : int32_t typeIndex = GetTypeIndex(aType.get(), true);
1721 14 : NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
1722 :
1723 : // When an entry already exists, PutEntry will return that, instead
1724 : // of adding a new one
1725 : RefPtr<PermissionKey> key =
1726 28 : PermissionKey::CreateFromPrincipal(aPrincipal, rv);
1727 14 : if (!key) {
1728 0 : MOZ_ASSERT(NS_FAILED(rv));
1729 0 : return rv;
1730 : }
1731 :
1732 14 : PermissionHashKey* entry = mPermissionTable.PutEntry(key);
1733 14 : if (!entry) return NS_ERROR_FAILURE;
1734 14 : if (!entry->GetKey()) {
1735 0 : mPermissionTable.RemoveEntry(entry);
1736 0 : return NS_ERROR_OUT_OF_MEMORY;
1737 : }
1738 :
1739 : // figure out the transaction type, and get any existing permission value
1740 : OperationType op;
1741 14 : int32_t index = entry->GetPermissionIndex(typeIndex);
1742 14 : if (index == -1) {
1743 14 : if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
1744 0 : op = eOperationNone;
1745 : else
1746 14 : op = eOperationAdding;
1747 :
1748 : } else {
1749 0 : PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
1750 :
1751 : // remove the permission if the permission is UNKNOWN, update the
1752 : // permission if its value or expire type have changed OR if the time has
1753 : // changed and the expire type is time, otherwise, don't modify. There's
1754 : // no need to modify a permission that doesn't expire with time when the
1755 : // only thing changed is the expire time.
1756 0 : if (aPermission == oldPermissionEntry.mPermission &&
1757 0 : aExpireType == oldPermissionEntry.mExpireType &&
1758 0 : (aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
1759 0 : aExpireTime == oldPermissionEntry.mExpireTime))
1760 0 : op = eOperationNone;
1761 0 : else if (oldPermissionEntry.mID == cIDPermissionIsDefault)
1762 : // The existing permission is one added as a default and the new permission
1763 : // doesn't exactly match so we are replacing the default. This is true
1764 : // even if the new permission is UNKNOWN_ACTION (which means a "logical
1765 : // remove" of the default)
1766 0 : op = eOperationReplacingDefault;
1767 0 : else if (aID == cIDPermissionIsDefault)
1768 : // We are adding a default permission but a "real" permission already
1769 : // exists. This almost-certainly means we just did a removeAllSince and
1770 : // are re-importing defaults - so we can ignore this.
1771 0 : op = eOperationNone;
1772 0 : else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
1773 0 : op = eOperationRemoving;
1774 : else
1775 0 : op = eOperationChanging;
1776 : }
1777 :
1778 : // child processes should *always* be passed a modificationTime of zero.
1779 14 : MOZ_ASSERT(!IsChildProcess() || aModificationTime == 0);
1780 :
1781 : // do the work for adding, deleting, or changing a permission:
1782 : // update the in-memory list, write to the db, and notify consumers.
1783 : int64_t id;
1784 14 : if (aModificationTime == 0) {
1785 14 : aModificationTime = PR_Now() / 1000;
1786 : }
1787 :
1788 14 : switch (op) {
1789 : case eOperationNone:
1790 : {
1791 : // nothing to do
1792 0 : return NS_OK;
1793 : }
1794 :
1795 : case eOperationAdding:
1796 : {
1797 14 : if (aDBOperation == eWriteToDB) {
1798 : // we'll be writing to the database - generate a known unique id
1799 0 : id = ++mLargestID;
1800 : } else {
1801 : // we're reading from the database - use the id already assigned
1802 14 : id = aID;
1803 : }
1804 :
1805 : #ifdef MOZ_B2G
1806 : // When we do the initial addition of the permissions we don't want to
1807 : // inherit session specific permissions from other tabs or apps
1808 : // so we ignore them and set the permission to PROMPT_ACTION if it was
1809 : // previously allowed or denied by the user.
1810 : if (aIgnoreSessionPermissions &&
1811 : aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
1812 : aPermission = nsIPermissionManager::PROMPT_ACTION;
1813 : aExpireType = nsIPermissionManager::EXPIRE_NEVER;
1814 : }
1815 : #endif // MOZ_B2G
1816 :
1817 28 : entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission,
1818 : aExpireType, aExpireTime,
1819 14 : aModificationTime));
1820 :
1821 : // Record a count of the number of preload permissions present in the
1822 : // content process.
1823 14 : if (IsPreloadPermission(mTypeArray[typeIndex].get())) {
1824 0 : sPreloadPermissionCount++;
1825 : }
1826 :
1827 14 : if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
1828 0 : UpdateDB(op, mStmtInsert, id, origin, aType, aPermission, aExpireType, aExpireTime, aModificationTime);
1829 : }
1830 :
1831 14 : if (aNotifyOperation == eNotify) {
1832 : NotifyObserversWithPermission(aPrincipal,
1833 4 : mTypeArray[typeIndex],
1834 : aPermission,
1835 : aExpireType,
1836 : aExpireTime,
1837 4 : u"added");
1838 : }
1839 :
1840 14 : break;
1841 : }
1842 :
1843 : case eOperationRemoving:
1844 : {
1845 0 : PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
1846 0 : id = oldPermissionEntry.mID;
1847 0 : entry->GetPermissions().RemoveElementAt(index);
1848 :
1849 : // Record a count of the number of preload permissions present in the
1850 : // content process.
1851 0 : if (IsPreloadPermission(mTypeArray[typeIndex].get())) {
1852 0 : sPreloadPermissionCount--;
1853 : }
1854 :
1855 0 : if (aDBOperation == eWriteToDB)
1856 : // We care only about the id here so we pass dummy values for all other
1857 : // parameters.
1858 0 : UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
1859 0 : nsIPermissionManager::EXPIRE_NEVER, 0, 0);
1860 :
1861 0 : if (aNotifyOperation == eNotify) {
1862 0 : NotifyObserversWithPermission(aPrincipal,
1863 0 : mTypeArray[typeIndex],
1864 : oldPermissionEntry.mPermission,
1865 : oldPermissionEntry.mExpireType,
1866 : oldPermissionEntry.mExpireTime,
1867 0 : u"deleted");
1868 : }
1869 :
1870 : // If there are no more permissions stored for that entry, clear it.
1871 0 : if (entry->GetPermissions().IsEmpty()) {
1872 0 : mPermissionTable.RemoveEntry(entry);
1873 : }
1874 :
1875 0 : break;
1876 : }
1877 :
1878 : case eOperationChanging:
1879 : {
1880 0 : id = entry->GetPermissions()[index].mID;
1881 :
1882 : // If the new expireType is EXPIRE_SESSION, then we have to keep a
1883 : // copy of the previous permission/expireType values. This cached value will be
1884 : // used when restoring the permissions of an app.
1885 0 : if (entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION &&
1886 : aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
1887 0 : entry->GetPermissions()[index].mNonSessionPermission = entry->GetPermissions()[index].mPermission;
1888 0 : entry->GetPermissions()[index].mNonSessionExpireType = entry->GetPermissions()[index].mExpireType;
1889 0 : entry->GetPermissions()[index].mNonSessionExpireTime = entry->GetPermissions()[index].mExpireTime;
1890 0 : } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
1891 0 : entry->GetPermissions()[index].mNonSessionPermission = aPermission;
1892 0 : entry->GetPermissions()[index].mNonSessionExpireType = aExpireType;
1893 0 : entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime;
1894 : }
1895 :
1896 0 : entry->GetPermissions()[index].mPermission = aPermission;
1897 0 : entry->GetPermissions()[index].mExpireType = aExpireType;
1898 0 : entry->GetPermissions()[index].mExpireTime = aExpireTime;
1899 0 : entry->GetPermissions()[index].mModificationTime = aModificationTime;
1900 :
1901 0 : if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
1902 : // We care only about the id, the permission and expireType/expireTime/modificationTime here.
1903 : // We pass dummy values for all other parameters.
1904 0 : UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
1905 0 : aPermission, aExpireType, aExpireTime, aModificationTime);
1906 :
1907 0 : if (aNotifyOperation == eNotify) {
1908 : NotifyObserversWithPermission(aPrincipal,
1909 0 : mTypeArray[typeIndex],
1910 : aPermission,
1911 : aExpireType,
1912 : aExpireTime,
1913 0 : u"changed");
1914 : }
1915 :
1916 0 : break;
1917 : }
1918 : case eOperationReplacingDefault:
1919 : {
1920 : // this is handling the case when we have an existing permission
1921 : // entry that was created as a "default" (and thus isn't in the DB) with
1922 : // an explicit permission (that may include UNKNOWN_ACTION.)
1923 : // Note we will *not* get here if we are replacing an already replaced
1924 : // default value - that is handled as eOperationChanging.
1925 :
1926 : // So this is a hybrid of eOperationAdding (as we are writing a new entry
1927 : // to the DB) and eOperationChanging (as we are replacing the in-memory
1928 : // repr and sending a "changed" notification).
1929 :
1930 : // We want a new ID even if not writing to the DB, so the modified entry
1931 : // in memory doesn't have the magic cIDPermissionIsDefault value.
1932 0 : id = ++mLargestID;
1933 :
1934 : // The default permission being replaced can't have session expiry.
1935 0 : NS_ENSURE_TRUE(entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION,
1936 : NS_ERROR_UNEXPECTED);
1937 : // We don't support the new entry having any expiry - supporting that would
1938 : // make things far more complex and none of the permissions we set as a
1939 : // default support that.
1940 0 : NS_ENSURE_TRUE(aExpireType == EXPIRE_NEVER, NS_ERROR_UNEXPECTED);
1941 :
1942 : // update the existing entry in memory.
1943 0 : entry->GetPermissions()[index].mID = id;
1944 0 : entry->GetPermissions()[index].mPermission = aPermission;
1945 0 : entry->GetPermissions()[index].mExpireType = aExpireType;
1946 0 : entry->GetPermissions()[index].mExpireTime = aExpireTime;
1947 0 : entry->GetPermissions()[index].mModificationTime = aModificationTime;
1948 :
1949 : // If requested, create the entry in the DB.
1950 0 : if (aDBOperation == eWriteToDB) {
1951 0 : UpdateDB(eOperationAdding, mStmtInsert, id, origin, aType, aPermission,
1952 0 : aExpireType, aExpireTime, aModificationTime);
1953 : }
1954 :
1955 0 : if (aNotifyOperation == eNotify) {
1956 : NotifyObserversWithPermission(aPrincipal,
1957 0 : mTypeArray[typeIndex],
1958 : aPermission,
1959 : aExpireType,
1960 : aExpireTime,
1961 0 : u"changed");
1962 : }
1963 :
1964 : }
1965 0 : break;
1966 : }
1967 :
1968 14 : return NS_OK;
1969 : }
1970 :
1971 : NS_IMETHODIMP
1972 0 : nsPermissionManager::Remove(nsIURI* aURI,
1973 : const char* aType)
1974 : {
1975 0 : NS_ENSURE_ARG_POINTER(aURI);
1976 :
1977 0 : nsCOMPtr<nsIPrincipal> principal;
1978 0 : nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
1979 0 : NS_ENSURE_SUCCESS(rv, rv);
1980 :
1981 0 : return RemoveFromPrincipal(principal, aType);
1982 : }
1983 :
1984 : NS_IMETHODIMP
1985 0 : nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
1986 : const char* aType)
1987 : {
1988 0 : ENSURE_NOT_CHILD_PROCESS;
1989 0 : NS_ENSURE_ARG_POINTER(aPrincipal);
1990 0 : NS_ENSURE_ARG_POINTER(aType);
1991 :
1992 : // System principals are never added to the database, no need to remove them.
1993 0 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
1994 0 : return NS_OK;
1995 : }
1996 :
1997 : // Permissions may not be added to expanded principals.
1998 0 : if (IsExpandedPrincipal(aPrincipal)) {
1999 0 : return NS_ERROR_INVALID_ARG;
2000 : }
2001 :
2002 : // AddInternal() handles removal, just let it do the work
2003 : return AddInternal(aPrincipal,
2004 0 : nsDependentCString(aType),
2005 : nsIPermissionManager::UNKNOWN_ACTION,
2006 : 0,
2007 : nsIPermissionManager::EXPIRE_NEVER,
2008 : 0,
2009 : 0,
2010 : eNotify,
2011 0 : eWriteToDB);
2012 : }
2013 :
2014 : NS_IMETHODIMP
2015 0 : nsPermissionManager::RemovePermission(nsIPermission* aPerm)
2016 : {
2017 0 : if (!aPerm) {
2018 0 : return NS_OK;
2019 : }
2020 0 : nsCOMPtr<nsIPrincipal> principal;
2021 0 : nsresult rv = aPerm->GetPrincipal(getter_AddRefs(principal));
2022 0 : NS_ENSURE_SUCCESS(rv, rv);
2023 :
2024 0 : nsAutoCString type;
2025 0 : rv = aPerm->GetType(type);
2026 0 : NS_ENSURE_SUCCESS(rv, rv);
2027 :
2028 : // Permissions are uniquely identified by their principal and type.
2029 : // We remove the permission using these two pieces of data.
2030 0 : return RemoveFromPrincipal(principal, type.get());
2031 : }
2032 :
2033 : NS_IMETHODIMP
2034 0 : nsPermissionManager::RemoveAll()
2035 : {
2036 0 : ENSURE_NOT_CHILD_PROCESS;
2037 0 : return RemoveAllInternal(true);
2038 : }
2039 :
2040 : NS_IMETHODIMP
2041 0 : nsPermissionManager::RemoveAllSince(int64_t aSince)
2042 : {
2043 0 : ENSURE_NOT_CHILD_PROCESS;
2044 0 : return RemoveAllModifiedSince(aSince);
2045 : }
2046 :
2047 : void
2048 0 : nsPermissionManager::CloseDB(bool aRebuildOnSuccess)
2049 : {
2050 : // Null the statements, this will finalize them.
2051 0 : mStmtInsert = nullptr;
2052 0 : mStmtDelete = nullptr;
2053 0 : mStmtUpdate = nullptr;
2054 0 : if (mDBConn) {
2055 : mozIStorageCompletionCallback* cb = new CloseDatabaseListener(this,
2056 0 : aRebuildOnSuccess);
2057 0 : mozilla::DebugOnly<nsresult> rv = mDBConn->AsyncClose(cb);
2058 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2059 0 : mDBConn = nullptr; // Avoid race conditions
2060 : }
2061 0 : }
2062 :
2063 : nsresult
2064 0 : nsPermissionManager::RemoveAllInternal(bool aNotifyObservers)
2065 : {
2066 : // Remove from memory and notify immediately. Since the in-memory
2067 : // database is authoritative, we do not need confirmation from the
2068 : // on-disk database to notify observers.
2069 0 : RemoveAllFromMemory();
2070 :
2071 : // Re-import the defaults
2072 0 : ImportDefaults();
2073 :
2074 0 : if (aNotifyObservers) {
2075 0 : NotifyObservers(nullptr, u"cleared");
2076 : }
2077 :
2078 : // clear the db
2079 0 : if (mDBConn) {
2080 0 : nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
2081 0 : nsresult rv = mDBConn->
2082 0 : CreateAsyncStatement(NS_LITERAL_CSTRING(
2083 : "DELETE FROM moz_perms"
2084 0 : ), getter_AddRefs(removeStmt));
2085 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2086 0 : if (!removeStmt) {
2087 0 : return NS_ERROR_UNEXPECTED;
2088 : }
2089 0 : nsCOMPtr<mozIStoragePendingStatement> pending;
2090 0 : mozIStorageStatementCallback* cb = new DeleteFromMozHostListener(this);
2091 0 : rv = removeStmt->ExecuteAsync(cb, getter_AddRefs(pending));
2092 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2093 :
2094 0 : return rv;
2095 : }
2096 :
2097 0 : return NS_OK;
2098 : }
2099 :
2100 : NS_IMETHODIMP
2101 0 : nsPermissionManager::TestExactPermission(nsIURI *aURI,
2102 : const char *aType,
2103 : uint32_t *aPermission)
2104 : {
2105 0 : return CommonTestPermission(aURI, aType, aPermission, true, true);
2106 : }
2107 :
2108 : NS_IMETHODIMP
2109 1 : nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
2110 : const char* aType,
2111 : uint32_t* aPermission)
2112 : {
2113 1 : return CommonTestPermission(aPrincipal, aType, aPermission, true, true);
2114 : }
2115 :
2116 : NS_IMETHODIMP
2117 0 : nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal,
2118 : const char* aType,
2119 : uint32_t* aPermission)
2120 : {
2121 0 : return CommonTestPermission(aPrincipal, aType, aPermission, true, false);
2122 : }
2123 :
2124 : NS_IMETHODIMP
2125 6 : nsPermissionManager::TestPermission(nsIURI *aURI,
2126 : const char *aType,
2127 : uint32_t *aPermission)
2128 : {
2129 6 : return CommonTestPermission(aURI, aType, aPermission, false, true);
2130 : }
2131 :
2132 : NS_IMETHODIMP
2133 0 : nsPermissionManager::TestPermissionFromWindow(mozIDOMWindow* aWindow,
2134 : const char* aType,
2135 : uint32_t* aPermission)
2136 : {
2137 0 : NS_ENSURE_ARG(aWindow);
2138 0 : nsCOMPtr<nsPIDOMWindowInner> window = nsPIDOMWindowInner::From(aWindow);
2139 :
2140 : // Get the document for security check
2141 0 : nsCOMPtr<nsIDocument> document = window->GetExtantDoc();
2142 0 : NS_ENSURE_TRUE(document, NS_NOINTERFACE);
2143 :
2144 0 : nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
2145 0 : return TestPermissionFromPrincipal(principal, aType, aPermission);
2146 : }
2147 :
2148 : NS_IMETHODIMP
2149 13 : nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
2150 : const char* aType,
2151 : uint32_t* aPermission)
2152 : {
2153 13 : return CommonTestPermission(aPrincipal, aType, aPermission, false, true);
2154 : }
2155 :
2156 : NS_IMETHODIMP
2157 0 : nsPermissionManager::GetPermissionObjectForURI(nsIURI* aURI,
2158 : const char* aType,
2159 : bool aExactHostMatch,
2160 : nsIPermission** aResult)
2161 : {
2162 0 : nsCOMPtr<nsIPrincipal> principal;
2163 0 : nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
2164 0 : NS_ENSURE_SUCCESS(rv, rv);
2165 :
2166 0 : return GetPermissionObject(principal, aType, aExactHostMatch, aResult);
2167 : }
2168 :
2169 : NS_IMETHODIMP
2170 0 : nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
2171 : const char* aType,
2172 : bool aExactHostMatch,
2173 : nsIPermission** aResult)
2174 : {
2175 0 : NS_ENSURE_ARG_POINTER(aPrincipal);
2176 0 : NS_ENSURE_ARG_POINTER(aType);
2177 :
2178 0 : *aResult = nullptr;
2179 :
2180 0 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
2181 0 : return NS_OK;
2182 : }
2183 :
2184 : // Querying the permission object of an nsEP is non-sensical.
2185 0 : if (IsExpandedPrincipal(aPrincipal)) {
2186 0 : return NS_ERROR_INVALID_ARG;
2187 : }
2188 :
2189 0 : MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
2190 :
2191 0 : int32_t typeIndex = GetTypeIndex(aType, false);
2192 : // If type == -1, the type isn't known,
2193 : // so just return NS_OK
2194 0 : if (typeIndex == -1) return NS_OK;
2195 :
2196 0 : PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
2197 0 : if (!entry) {
2198 0 : return NS_OK;
2199 : }
2200 :
2201 : // We don't call GetPermission(typeIndex) because that returns a fake
2202 : // UNKNOWN_ACTION entry if there is no match.
2203 0 : int32_t idx = entry->GetPermissionIndex(typeIndex);
2204 0 : if (-1 == idx) {
2205 0 : return NS_OK;
2206 : }
2207 :
2208 0 : nsCOMPtr<nsIPrincipal> principal;
2209 0 : nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
2210 0 : NS_ENSURE_SUCCESS(rv, rv);
2211 :
2212 0 : PermissionEntry& perm = entry->GetPermissions()[idx];
2213 0 : nsCOMPtr<nsIPermission> r = nsPermission::Create(principal,
2214 0 : mTypeArray.ElementAt(perm.mType),
2215 : perm.mPermission,
2216 : perm.mExpireType,
2217 0 : perm.mExpireTime);
2218 0 : if (NS_WARN_IF(!r)) {
2219 0 : return NS_ERROR_FAILURE;
2220 : }
2221 0 : r.forget(aResult);
2222 0 : return NS_OK;
2223 : }
2224 :
2225 : nsresult
2226 20 : nsPermissionManager::CommonTestPermissionInternal(nsIPrincipal* aPrincipal,
2227 : nsIURI * aURI,
2228 : const char * aType,
2229 : uint32_t * aPermission,
2230 : bool aExactHostMatch,
2231 : bool aIncludingSession)
2232 : {
2233 20 : MOZ_ASSERT(aPrincipal || aURI);
2234 20 : MOZ_ASSERT_IF(aPrincipal, !aURI);
2235 20 : NS_ENSURE_ARG_POINTER(aPrincipal || aURI);
2236 20 : NS_ENSURE_ARG_POINTER(aType);
2237 :
2238 20 : if (aPrincipal && nsContentUtils::IsSystemPrincipal(aPrincipal)) {
2239 0 : *aPermission = nsIPermissionManager::ALLOW_ACTION;
2240 0 : return NS_OK;
2241 : }
2242 :
2243 : // Set the default.
2244 20 : *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
2245 :
2246 : // For expanded principals, we want to iterate over the whitelist and see
2247 : // if the permission is granted for any of them.
2248 40 : nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
2249 20 : if (ep) {
2250 : nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist;
2251 0 : nsresult rv = ep->GetWhiteList(&whitelist);
2252 0 : NS_ENSURE_SUCCESS(rv, rv);
2253 :
2254 0 : for (size_t i = 0; i < whitelist->Length(); ++i) {
2255 : uint32_t perm;
2256 0 : rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm,
2257 0 : aExactHostMatch, aIncludingSession);
2258 0 : NS_ENSURE_SUCCESS(rv, rv);
2259 0 : if (perm == nsIPermissionManager::ALLOW_ACTION) {
2260 0 : *aPermission = perm;
2261 0 : return NS_OK;
2262 0 : } else if (perm == nsIPermissionManager::PROMPT_ACTION) {
2263 : // Store it, but keep going to see if we can do better.
2264 0 : *aPermission = perm;
2265 : }
2266 : }
2267 :
2268 0 : return NS_OK;
2269 : }
2270 :
2271 : #ifdef DEBUG
2272 : {
2273 40 : nsCOMPtr<nsIPrincipal> prin = aPrincipal;
2274 20 : if (!prin) {
2275 6 : prin = mozilla::BasePrincipal::CreateCodebasePrincipal(aURI, OriginAttributes());
2276 : }
2277 20 : MOZ_ASSERT(PermissionAvaliable(prin, aType));
2278 : }
2279 : #endif
2280 :
2281 20 : int32_t typeIndex = GetTypeIndex(aType, false);
2282 : // If type == -1, the type isn't known,
2283 : // so just return NS_OK
2284 20 : if (typeIndex == -1) return NS_OK;
2285 :
2286 0 : PermissionHashKey* entry = aPrincipal ?
2287 0 : GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch) :
2288 0 : GetPermissionHashKey(aURI, typeIndex, aExactHostMatch);
2289 0 : if (!entry ||
2290 0 : (!aIncludingSession &&
2291 0 : entry->GetPermission(typeIndex).mNonSessionExpireType ==
2292 : nsIPermissionManager::EXPIRE_SESSION)) {
2293 0 : return NS_OK;
2294 : }
2295 :
2296 0 : *aPermission = aIncludingSession
2297 0 : ? entry->GetPermission(typeIndex).mPermission
2298 0 : : entry->GetPermission(typeIndex).mNonSessionPermission;
2299 :
2300 0 : return NS_OK;
2301 : }
2302 :
2303 : // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
2304 : // This is not simply using PermissionKey because we will walk-up domains in
2305 : // case of |host| contains sub-domains.
2306 : // Returns null if nothing found.
2307 : // Also accepts host on the format "<foo>". This will perform an exact match
2308 : // lookup as the string doesn't contain any dots.
2309 : nsPermissionManager::PermissionHashKey*
2310 0 : nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal,
2311 : uint32_t aType,
2312 : bool aExactHostMatch)
2313 : {
2314 0 : MOZ_ASSERT(PermissionAvaliable(aPrincipal, mTypeArray[aType].get()));
2315 :
2316 : nsresult rv;
2317 : RefPtr<PermissionKey> key =
2318 0 : PermissionKey::CreateFromPrincipal(aPrincipal, rv);
2319 0 : if (!key) {
2320 0 : return nullptr;
2321 : }
2322 :
2323 0 : PermissionHashKey* entry = mPermissionTable.GetEntry(key);
2324 :
2325 0 : if (entry) {
2326 0 : PermissionEntry permEntry = entry->GetPermission(aType);
2327 :
2328 : // if the entry is expired, remove and keep looking for others.
2329 : // Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
2330 0 : if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME ||
2331 0 : (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
2332 0 : permEntry.mExpireTime != 0)) &&
2333 0 : permEntry.mExpireTime <= (PR_Now() / 1000)) {
2334 0 : entry = nullptr;
2335 0 : RemoveFromPrincipal(aPrincipal, mTypeArray[aType].get());
2336 0 : } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
2337 0 : entry = nullptr;
2338 : }
2339 : }
2340 :
2341 0 : if (entry) {
2342 0 : return entry;
2343 : }
2344 :
2345 : // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
2346 0 : if (!aExactHostMatch) {
2347 : nsCOMPtr<nsIPrincipal> principal =
2348 0 : GetNextSubDomainPrincipal(aPrincipal);
2349 0 : if (principal) {
2350 0 : return GetPermissionHashKey(principal, aType, aExactHostMatch);
2351 : }
2352 : }
2353 :
2354 : // No entry, really...
2355 0 : return nullptr;
2356 : }
2357 :
2358 : // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
2359 : // This is not simply using PermissionKey because we will walk-up domains in
2360 : // case of |host| contains sub-domains.
2361 : // Returns null if nothing found.
2362 : // Also accepts host on the format "<foo>". This will perform an exact match
2363 : // lookup as the string doesn't contain any dots.
2364 : nsPermissionManager::PermissionHashKey*
2365 0 : nsPermissionManager::GetPermissionHashKey(nsIURI* aURI,
2366 : uint32_t aType,
2367 : bool aExactHostMatch)
2368 : {
2369 : #ifdef DEBUG
2370 : {
2371 0 : nsCOMPtr<nsIPrincipal> principal;
2372 0 : nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
2373 0 : MOZ_ASSERT_IF(NS_SUCCEEDED(rv),
2374 : PermissionAvaliable(principal, mTypeArray[aType].get()));
2375 : }
2376 : #endif
2377 :
2378 : nsresult rv;
2379 : RefPtr<PermissionKey> key =
2380 0 : PermissionKey::CreateFromURI(aURI, rv);
2381 0 : if (!key) {
2382 0 : return nullptr;
2383 : }
2384 :
2385 0 : PermissionHashKey* entry = mPermissionTable.GetEntry(key);
2386 :
2387 0 : if (entry) {
2388 0 : PermissionEntry permEntry = entry->GetPermission(aType);
2389 :
2390 : // if the entry is expired, remove and keep looking for others.
2391 : // Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
2392 0 : if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME ||
2393 0 : (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
2394 0 : permEntry.mExpireTime != 0)) &&
2395 0 : permEntry.mExpireTime <= (PR_Now() / 1000)) {
2396 0 : entry = nullptr;
2397 : // If we need to remove a permission we mint a principal. This is a bit
2398 : // inefficient, but hopefully this code path isn't super common.
2399 0 : nsCOMPtr<nsIPrincipal> principal;
2400 0 : nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
2401 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2402 0 : return nullptr;
2403 : }
2404 0 : RemoveFromPrincipal(principal, mTypeArray[aType].get());
2405 0 : } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
2406 0 : entry = nullptr;
2407 : }
2408 : }
2409 :
2410 0 : if (entry) {
2411 0 : return entry;
2412 : }
2413 :
2414 : // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
2415 0 : if (!aExactHostMatch) {
2416 0 : nsCOMPtr<nsIURI> uri = GetNextSubDomainURI(aURI);
2417 0 : if (uri) {
2418 0 : return GetPermissionHashKey(uri, aType, aExactHostMatch);
2419 : }
2420 : }
2421 :
2422 : // No entry, really...
2423 0 : return nullptr;
2424 : }
2425 :
2426 1 : NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
2427 : {
2428 1 : if (XRE_IsContentProcess()) {
2429 : NS_WARNING("nsPermissionManager's enumerator is not avaliable in the "
2430 0 : "content process, as not all permissions may be avaliable.");
2431 0 : *aEnum = nullptr;
2432 0 : return NS_ERROR_NOT_AVAILABLE;
2433 : }
2434 :
2435 : // roll an nsCOMArray of all our permissions, then hand out an enumerator
2436 2 : nsCOMArray<nsIPermission> array;
2437 :
2438 9 : for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
2439 8 : PermissionHashKey* entry = iter.Get();
2440 18 : for (const auto& permEntry : entry->GetPermissions()) {
2441 : // Given how "default" permissions work and the possibility of them being
2442 : // overridden with UNKNOWN_ACTION, we might see this value here - but we
2443 : // do *not* want to return them via the enumerator.
2444 10 : if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
2445 0 : continue;
2446 : }
2447 :
2448 20 : nsCOMPtr<nsIPrincipal> principal;
2449 10 : nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
2450 20 : getter_AddRefs(principal));
2451 10 : if (NS_FAILED(rv)) {
2452 0 : continue;
2453 : }
2454 :
2455 : nsCOMPtr<nsIPermission> permission =
2456 20 : nsPermission::Create(principal,
2457 10 : mTypeArray.ElementAt(permEntry.mType),
2458 10 : permEntry.mPermission,
2459 10 : permEntry.mExpireType,
2460 30 : permEntry.mExpireTime);
2461 10 : if (NS_WARN_IF(!permission)) {
2462 0 : continue;
2463 : }
2464 10 : array.AppendObject(permission);
2465 : }
2466 : }
2467 :
2468 1 : return NS_NewArrayEnumerator(aEnum, array);
2469 : }
2470 :
2471 2 : NS_IMETHODIMP nsPermissionManager::GetAllForURI(nsIURI* aURI, nsISimpleEnumerator **aEnum)
2472 : {
2473 4 : nsCOMArray<nsIPermission> array;
2474 :
2475 4 : nsCOMPtr<nsIPrincipal> principal;
2476 2 : nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
2477 2 : NS_ENSURE_SUCCESS(rv, rv);
2478 :
2479 2 : MOZ_ASSERT(PermissionAvaliable(principal, nullptr));
2480 :
2481 4 : RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal(principal, rv);
2482 2 : if (!key) {
2483 0 : MOZ_ASSERT(NS_FAILED(rv));
2484 0 : return rv;
2485 : }
2486 :
2487 2 : PermissionHashKey* entry = mPermissionTable.GetEntry(key);
2488 :
2489 2 : if (entry) {
2490 0 : for (const auto& permEntry : entry->GetPermissions()) {
2491 : // Only return custom permissions
2492 0 : if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
2493 0 : continue;
2494 : }
2495 :
2496 : nsCOMPtr<nsIPermission> permission =
2497 0 : nsPermission::Create(principal,
2498 0 : mTypeArray.ElementAt(permEntry.mType),
2499 0 : permEntry.mPermission,
2500 0 : permEntry.mExpireType,
2501 0 : permEntry.mExpireTime);
2502 0 : if (NS_WARN_IF(!permission)) {
2503 0 : continue;
2504 : }
2505 0 : array.AppendObject(permission);
2506 : }
2507 : }
2508 :
2509 2 : return NS_NewArrayEnumerator(aEnum, array);
2510 : }
2511 :
2512 0 : NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
2513 : {
2514 0 : ENSURE_NOT_CHILD_PROCESS;
2515 :
2516 0 : if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
2517 : // The profile is about to change,
2518 : // or is going away because the application is shutting down.
2519 0 : mIsShuttingDown = true;
2520 0 : RemoveAllFromMemory();
2521 0 : CloseDB(false);
2522 0 : } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
2523 : // the profile has already changed; init the db from the new location
2524 0 : InitDB(false);
2525 : }
2526 :
2527 0 : return NS_OK;
2528 : }
2529 :
2530 : nsresult
2531 0 : nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime)
2532 : {
2533 0 : ENSURE_NOT_CHILD_PROCESS;
2534 :
2535 0 : nsCOMArray<nsIPermission> array;
2536 0 : for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
2537 0 : PermissionHashKey* entry = iter.Get();
2538 0 : for (const auto& permEntry : entry->GetPermissions()) {
2539 0 : if (aModificationTime > permEntry.mModificationTime) {
2540 0 : continue;
2541 : }
2542 :
2543 0 : nsCOMPtr<nsIPrincipal> principal;
2544 0 : nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
2545 0 : getter_AddRefs(principal));
2546 0 : if (NS_FAILED(rv)) {
2547 0 : continue;
2548 : }
2549 :
2550 : nsCOMPtr<nsIPermission> permission =
2551 0 : nsPermission::Create(principal,
2552 0 : mTypeArray.ElementAt(permEntry.mType),
2553 0 : permEntry.mPermission,
2554 0 : permEntry.mExpireType,
2555 0 : permEntry.mExpireTime);
2556 0 : if (NS_WARN_IF(!permission)) {
2557 0 : continue;
2558 : }
2559 0 : array.AppendObject(permission);
2560 : }
2561 : }
2562 :
2563 0 : for (int32_t i = 0; i<array.Count(); ++i) {
2564 0 : nsCOMPtr<nsIPrincipal> principal;
2565 0 : nsAutoCString type;
2566 :
2567 0 : nsresult rv = array[i]->GetPrincipal(getter_AddRefs(principal));
2568 0 : if (NS_FAILED(rv)) {
2569 0 : NS_ERROR("GetPrincipal() failed!");
2570 0 : continue;
2571 : }
2572 :
2573 0 : rv = array[i]->GetType(type);
2574 0 : if (NS_FAILED(rv)) {
2575 0 : NS_ERROR("GetType() failed!");
2576 0 : continue;
2577 : }
2578 :
2579 : // AddInternal handles removal, so let it do the work...
2580 0 : AddInternal(
2581 : principal,
2582 : type,
2583 : nsIPermissionManager::UNKNOWN_ACTION,
2584 : 0,
2585 : nsIPermissionManager::EXPIRE_NEVER, 0, 0,
2586 : nsPermissionManager::eNotify,
2587 0 : nsPermissionManager::eWriteToDB);
2588 : }
2589 : // now re-import any defaults as they may now be required if we just deleted
2590 : // an override.
2591 0 : ImportDefaults();
2592 0 : return NS_OK;
2593 : }
2594 :
2595 : NS_IMETHODIMP
2596 0 : nsPermissionManager::RemovePermissionsWithAttributes(const nsAString& aPattern)
2597 : {
2598 0 : ENSURE_NOT_CHILD_PROCESS;
2599 0 : mozilla::OriginAttributesPattern pattern;
2600 0 : if (!pattern.Init(aPattern)) {
2601 0 : return NS_ERROR_INVALID_ARG;
2602 : }
2603 :
2604 0 : return RemovePermissionsWithAttributes(pattern);
2605 : }
2606 :
2607 : nsresult
2608 0 : nsPermissionManager::RemovePermissionsWithAttributes(mozilla::OriginAttributesPattern& aPattern)
2609 : {
2610 0 : nsCOMArray<nsIPermission> permissions;
2611 0 : for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
2612 0 : PermissionHashKey* entry = iter.Get();
2613 :
2614 0 : nsCOMPtr<nsIPrincipal> principal;
2615 0 : nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
2616 0 : getter_AddRefs(principal));
2617 0 : if (NS_FAILED(rv)) {
2618 0 : continue;
2619 : }
2620 :
2621 0 : if (!aPattern.Matches(principal->OriginAttributesRef())) {
2622 0 : continue;
2623 : }
2624 :
2625 0 : for (const auto& permEntry : entry->GetPermissions()) {
2626 : nsCOMPtr<nsIPermission> permission =
2627 0 : nsPermission::Create(principal,
2628 0 : mTypeArray.ElementAt(permEntry.mType),
2629 0 : permEntry.mPermission,
2630 0 : permEntry.mExpireType,
2631 0 : permEntry.mExpireTime);
2632 0 : if (NS_WARN_IF(!permission)) {
2633 0 : continue;
2634 : }
2635 0 : permissions.AppendObject(permission);
2636 : }
2637 : }
2638 :
2639 0 : for (int32_t i = 0; i < permissions.Count(); ++i) {
2640 0 : nsCOMPtr<nsIPrincipal> principal;
2641 0 : nsAutoCString type;
2642 :
2643 0 : permissions[i]->GetPrincipal(getter_AddRefs(principal));
2644 0 : permissions[i]->GetType(type);
2645 :
2646 0 : AddInternal(principal,
2647 : type,
2648 : nsIPermissionManager::UNKNOWN_ACTION,
2649 : 0,
2650 : nsIPermissionManager::EXPIRE_NEVER,
2651 : 0,
2652 : 0,
2653 : nsPermissionManager::eNotify,
2654 0 : nsPermissionManager::eWriteToDB);
2655 : }
2656 :
2657 0 : return NS_OK;
2658 : }
2659 :
2660 : //*****************************************************************************
2661 : //*** nsPermissionManager private methods
2662 : //*****************************************************************************
2663 :
2664 : nsresult
2665 0 : nsPermissionManager::RemoveAllFromMemory()
2666 : {
2667 0 : mLargestID = 0;
2668 0 : mTypeArray.Clear();
2669 0 : mPermissionTable.Clear();
2670 :
2671 0 : return NS_OK;
2672 : }
2673 :
2674 : // Returns -1 on failure
2675 : int32_t
2676 34 : nsPermissionManager::GetTypeIndex(const char *aType,
2677 : bool aAdd)
2678 : {
2679 88 : for (uint32_t i = 0; i < mTypeArray.Length(); ++i)
2680 63 : if (mTypeArray[i].Equals(aType))
2681 9 : return i;
2682 :
2683 25 : if (!aAdd) {
2684 : // Not found, but that is ok - we were just looking.
2685 20 : return -1;
2686 : }
2687 :
2688 : // This type was not registered before.
2689 : // append it to the array, without copy-constructing the string
2690 5 : nsCString *elem = mTypeArray.AppendElement();
2691 5 : if (!elem)
2692 0 : return -1;
2693 :
2694 5 : elem->Assign(aType);
2695 5 : return mTypeArray.Length() - 1;
2696 : }
2697 :
2698 : // wrapper function for mangling (host,type,perm,expireType,expireTime)
2699 : // set into an nsIPermission.
2700 : void
2701 4 : nsPermissionManager::NotifyObserversWithPermission(nsIPrincipal* aPrincipal,
2702 : const nsCString &aType,
2703 : uint32_t aPermission,
2704 : uint32_t aExpireType,
2705 : int64_t aExpireTime,
2706 : const char16_t *aData)
2707 : {
2708 : nsCOMPtr<nsIPermission> permission =
2709 8 : nsPermission::Create(aPrincipal, aType, aPermission,
2710 8 : aExpireType, aExpireTime);
2711 4 : if (permission)
2712 4 : NotifyObservers(permission, aData);
2713 4 : }
2714 :
2715 : // notify observers that the permission list changed. there are four possible
2716 : // values for aData:
2717 : // "deleted" means a permission was deleted. aPermission is the deleted permission.
2718 : // "added" means a permission was added. aPermission is the added permission.
2719 : // "changed" means a permission was altered. aPermission is the new permission.
2720 : // "cleared" means the entire permission list was cleared. aPermission is null.
2721 : void
2722 4 : nsPermissionManager::NotifyObservers(nsIPermission *aPermission,
2723 : const char16_t *aData)
2724 : {
2725 : nsCOMPtr<nsIObserverService> observerService =
2726 8 : mozilla::services::GetObserverService();
2727 4 : if (observerService)
2728 4 : observerService->NotifyObservers(aPermission,
2729 : kPermissionChangeNotification,
2730 4 : aData);
2731 4 : }
2732 :
2733 : nsresult
2734 1 : nsPermissionManager::Read()
2735 : {
2736 1 : ENSURE_NOT_CHILD_PROCESS;
2737 :
2738 : nsresult rv;
2739 :
2740 : // delete expired permissions before we read in the db
2741 : {
2742 : // this deletion has its own scope so the write lock is released when done.
2743 2 : nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
2744 4 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2745 : "DELETE FROM moz_perms WHERE expireType = ?1 AND expireTime <= ?2"),
2746 4 : getter_AddRefs(stmtDeleteExpired));
2747 1 : NS_ENSURE_SUCCESS(rv, rv);
2748 :
2749 1 : rv = stmtDeleteExpired->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME);
2750 1 : NS_ENSURE_SUCCESS(rv, rv);
2751 :
2752 1 : rv = stmtDeleteExpired->BindInt64ByIndex(1, PR_Now() / 1000);
2753 1 : NS_ENSURE_SUCCESS(rv, rv);
2754 :
2755 : bool hasResult;
2756 1 : rv = stmtDeleteExpired->ExecuteStep(&hasResult);
2757 1 : NS_ENSURE_SUCCESS(rv, rv);
2758 : }
2759 :
2760 2 : nsCOMPtr<mozIStorageStatement> stmt;
2761 4 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2762 : "SELECT id, origin, type, permission, expireType, expireTime, modificationTime "
2763 4 : "FROM moz_perms"), getter_AddRefs(stmt));
2764 1 : NS_ENSURE_SUCCESS(rv, rv);
2765 :
2766 : int64_t id;
2767 2 : nsAutoCString origin, type;
2768 : uint32_t permission;
2769 : uint32_t expireType;
2770 : int64_t expireTime;
2771 : int64_t modificationTime;
2772 : bool hasResult;
2773 1 : bool readError = false;
2774 :
2775 1 : while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
2776 : // explicitly set our entry id counter for use in AddInternal(),
2777 : // and keep track of the largest id so we know where to pick up.
2778 0 : id = stmt->AsInt64(0);
2779 0 : if (id > mLargestID)
2780 0 : mLargestID = id;
2781 :
2782 0 : rv = stmt->GetUTF8String(1, origin);
2783 0 : if (NS_FAILED(rv)) {
2784 0 : readError = true;
2785 0 : continue;
2786 : }
2787 :
2788 0 : rv = stmt->GetUTF8String(2, type);
2789 0 : if (NS_FAILED(rv)) {
2790 0 : readError = true;
2791 0 : continue;
2792 : }
2793 :
2794 0 : permission = stmt->AsInt32(3);
2795 0 : expireType = stmt->AsInt32(4);
2796 :
2797 : // convert into int64_t values (milliseconds)
2798 0 : expireTime = stmt->AsInt64(5);
2799 0 : modificationTime = stmt->AsInt64(6);
2800 :
2801 0 : nsCOMPtr<nsIPrincipal> principal;
2802 0 : nsresult rv = GetPrincipalFromOrigin(origin, getter_AddRefs(principal));
2803 0 : if (NS_FAILED(rv)) {
2804 0 : readError = true;
2805 0 : continue;
2806 : }
2807 :
2808 0 : rv = AddInternal(principal, type, permission, id, expireType, expireTime,
2809 0 : modificationTime, eDontNotify, eNoDBOperation);
2810 0 : if (NS_FAILED(rv)) {
2811 0 : readError = true;
2812 0 : continue;
2813 : }
2814 : }
2815 :
2816 1 : if (readError) {
2817 0 : NS_ERROR("Error occured while reading the permissions database!");
2818 0 : return NS_ERROR_FAILURE;
2819 : }
2820 :
2821 1 : return NS_OK;
2822 : }
2823 :
2824 : static const char kMatchTypeHost[] = "host";
2825 : static const char kMatchTypeOrigin[] = "origin";
2826 :
2827 : // Import() will read a file from the profile directory and add them to the
2828 : // database before deleting the file - ie, this is a one-shot operation that
2829 : // will not succeed on subsequent runs as the file imported from is removed.
2830 : nsresult
2831 0 : nsPermissionManager::Import()
2832 : {
2833 : nsresult rv;
2834 :
2835 0 : nsCOMPtr<nsIFile> permissionsFile;
2836 0 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
2837 0 : if (NS_FAILED(rv)) return rv;
2838 :
2839 0 : rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(HOSTPERM_FILE_NAME));
2840 0 : NS_ENSURE_SUCCESS(rv, rv);
2841 :
2842 0 : nsCOMPtr<nsIInputStream> fileInputStream;
2843 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
2844 0 : permissionsFile);
2845 0 : NS_ENSURE_SUCCESS(rv, rv);
2846 :
2847 0 : rv = _DoImport(fileInputStream, mDBConn);
2848 0 : NS_ENSURE_SUCCESS(rv, rv);
2849 :
2850 : // we successfully imported and wrote to the DB - delete the old file.
2851 0 : permissionsFile->Remove(false);
2852 0 : return NS_OK;
2853 : }
2854 :
2855 : // ImportDefaults will read a URL with default permissions and add them to the
2856 : // in-memory copy of permissions. The database is *not* written to.
2857 : nsresult
2858 1 : nsPermissionManager::ImportDefaults()
2859 : {
2860 2 : nsCString defaultsURL = mozilla::Preferences::GetCString(kDefaultsUrlPrefName);
2861 1 : if (defaultsURL.IsEmpty()) { // == Don't use built-in permissions.
2862 0 : return NS_OK;
2863 : }
2864 :
2865 2 : nsCOMPtr<nsIURI> defaultsURI;
2866 1 : nsresult rv = NS_NewURI(getter_AddRefs(defaultsURI), defaultsURL);
2867 1 : NS_ENSURE_SUCCESS(rv, rv);
2868 :
2869 2 : nsCOMPtr<nsIChannel> channel;
2870 2 : rv = NS_NewChannel(getter_AddRefs(channel),
2871 : defaultsURI,
2872 : nsContentUtils::GetSystemPrincipal(),
2873 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
2874 1 : nsIContentPolicy::TYPE_OTHER);
2875 1 : NS_ENSURE_SUCCESS(rv, rv);
2876 :
2877 2 : nsCOMPtr<nsIInputStream> inputStream;
2878 1 : rv = channel->Open2(getter_AddRefs(inputStream));
2879 1 : NS_ENSURE_SUCCESS(rv, rv);
2880 :
2881 1 : rv = _DoImport(inputStream, nullptr);
2882 1 : inputStream->Close();
2883 1 : return rv;
2884 : }
2885 :
2886 : // _DoImport reads the specified stream and adds the parsed elements. If
2887 : // |conn| is passed, the imported data will be written to the database, but if
2888 : // |conn| is null the data will be added only to the in-memory copy of the
2889 : // database.
2890 : nsresult
2891 1 : nsPermissionManager::_DoImport(nsIInputStream *inputStream, mozIStorageConnection *conn)
2892 : {
2893 1 : ENSURE_NOT_CHILD_PROCESS;
2894 :
2895 : nsresult rv;
2896 : // start a transaction on the storage db, to optimize insertions.
2897 : // transaction will automically commit on completion
2898 : // (note the transaction is a no-op if a null connection is passed)
2899 2 : mozStorageTransaction transaction(conn, true);
2900 :
2901 : // The DB operation - we only try and write if a connection was passed.
2902 1 : DBOperationType operation = conn ? eWriteToDB : eNoDBOperation;
2903 : // and if no DB connection was passed we assume this is a "default" permission,
2904 : // so use the special ID which indicates this.
2905 1 : int64_t id = conn ? 0 : cIDPermissionIsDefault;
2906 :
2907 : /* format is:
2908 : * matchtype \t type \t permission \t host
2909 : * Only "host" is supported for matchtype
2910 : * type is a string that identifies the type of permission (e.g. "cookie")
2911 : * permission is an integer between 1 and 15
2912 : */
2913 :
2914 : // Ideally we'd do this with nsILineInputString, but this is called with an
2915 : // nsIInputStream that comes from a resource:// URI, which doesn't support
2916 : // that interface. So NS_ReadLine to the rescue...
2917 1 : nsLineBuffer<char> lineBuffer;
2918 2 : nsCString line;
2919 1 : bool isMore = true;
2920 23 : do {
2921 23 : rv = NS_ReadLine(inputStream, &lineBuffer, line, &isMore);
2922 23 : NS_ENSURE_SUCCESS(rv, rv);
2923 :
2924 23 : if (line.IsEmpty() || line.First() == '#') {
2925 26 : continue;
2926 : }
2927 :
2928 20 : nsTArray<nsCString> lineArray;
2929 :
2930 : // Split the line at tabs
2931 10 : ParseString(line, '\t', lineArray);
2932 :
2933 10 : if (lineArray[0].EqualsLiteral(kMatchTypeHost) &&
2934 0 : lineArray.Length() == 4) {
2935 0 : nsresult error = NS_OK;
2936 0 : uint32_t permission = lineArray[2].ToInteger(&error);
2937 0 : if (NS_FAILED(error))
2938 0 : continue;
2939 :
2940 : // the import file format doesn't handle modification times, so we use
2941 : // 0, which AddInternal will convert to now()
2942 0 : int64_t modificationTime = 0;
2943 :
2944 0 : UpgradeHostToOriginHostfileImport upHelper(this, operation, id);
2945 0 : error = UpgradeHostToOriginAndInsert(lineArray[3], lineArray[1], permission,
2946 : nsIPermissionManager::EXPIRE_NEVER, 0,
2947 : modificationTime, nsIScriptSecurityManager::NO_APP_ID,
2948 : false, &upHelper);
2949 0 : if (NS_FAILED(error)) {
2950 0 : NS_WARNING("There was a problem importing a host permission");
2951 : }
2952 20 : } else if (lineArray[0].EqualsLiteral(kMatchTypeOrigin) &&
2953 10 : lineArray.Length() == 4) {
2954 10 : nsresult error = NS_OK;
2955 10 : uint32_t permission = lineArray[2].ToInteger(&error);
2956 10 : if (NS_FAILED(error))
2957 0 : continue;
2958 :
2959 20 : nsCOMPtr<nsIPrincipal> principal;
2960 10 : error = GetPrincipalFromOrigin(lineArray[3], getter_AddRefs(principal));
2961 10 : if (NS_FAILED(error)) {
2962 0 : NS_WARNING("Couldn't import an origin permission - malformed origin");
2963 0 : continue;
2964 : }
2965 :
2966 : // the import file format doesn't handle modification times, so we use
2967 : // 0, which AddInternal will convert to now()
2968 10 : int64_t modificationTime = 0;
2969 :
2970 10 : error = AddInternal(principal, lineArray[1], permission, id,
2971 : nsIPermissionManager::EXPIRE_NEVER, 0,
2972 : modificationTime,
2973 : eDontNotify, operation);
2974 10 : if (NS_FAILED(error)) {
2975 0 : NS_WARNING("There was a problem importing an origin permission");
2976 : }
2977 : }
2978 :
2979 : } while (isMore);
2980 :
2981 1 : return NS_OK;
2982 : }
2983 :
2984 : void
2985 0 : nsPermissionManager::UpdateDB(OperationType aOp,
2986 : mozIStorageAsyncStatement* aStmt,
2987 : int64_t aID,
2988 : const nsACString &aOrigin,
2989 : const nsACString &aType,
2990 : uint32_t aPermission,
2991 : uint32_t aExpireType,
2992 : int64_t aExpireTime,
2993 : int64_t aModificationTime)
2994 : {
2995 0 : ENSURE_NOT_CHILD_PROCESS_NORET;
2996 :
2997 : nsresult rv;
2998 :
2999 : // no statement is ok - just means we don't have a profile
3000 0 : if (!aStmt)
3001 0 : return;
3002 :
3003 0 : switch (aOp) {
3004 : case eOperationAdding:
3005 : {
3006 0 : rv = aStmt->BindInt64ByIndex(0, aID);
3007 0 : if (NS_FAILED(rv)) break;
3008 :
3009 0 : rv = aStmt->BindUTF8StringByIndex(1, aOrigin);
3010 0 : if (NS_FAILED(rv)) break;
3011 :
3012 0 : rv = aStmt->BindUTF8StringByIndex(2, aType);
3013 0 : if (NS_FAILED(rv)) break;
3014 :
3015 0 : rv = aStmt->BindInt32ByIndex(3, aPermission);
3016 0 : if (NS_FAILED(rv)) break;
3017 :
3018 0 : rv = aStmt->BindInt32ByIndex(4, aExpireType);
3019 0 : if (NS_FAILED(rv)) break;
3020 :
3021 0 : rv = aStmt->BindInt64ByIndex(5, aExpireTime);
3022 0 : if (NS_FAILED(rv)) break;
3023 :
3024 0 : rv = aStmt->BindInt64ByIndex(6, aModificationTime);
3025 0 : break;
3026 : }
3027 :
3028 : case eOperationRemoving:
3029 : {
3030 0 : rv = aStmt->BindInt64ByIndex(0, aID);
3031 0 : break;
3032 : }
3033 :
3034 : case eOperationChanging:
3035 : {
3036 0 : rv = aStmt->BindInt64ByIndex(0, aID);
3037 0 : if (NS_FAILED(rv)) break;
3038 :
3039 0 : rv = aStmt->BindInt32ByIndex(1, aPermission);
3040 0 : if (NS_FAILED(rv)) break;
3041 :
3042 0 : rv = aStmt->BindInt32ByIndex(2, aExpireType);
3043 0 : if (NS_FAILED(rv)) break;
3044 :
3045 0 : rv = aStmt->BindInt64ByIndex(3, aExpireTime);
3046 0 : if (NS_FAILED(rv)) break;
3047 :
3048 0 : rv = aStmt->BindInt64ByIndex(4, aModificationTime);
3049 0 : break;
3050 : }
3051 :
3052 : default:
3053 : {
3054 0 : NS_NOTREACHED("need a valid operation in UpdateDB()!");
3055 0 : rv = NS_ERROR_UNEXPECTED;
3056 0 : break;
3057 : }
3058 : }
3059 :
3060 0 : if (NS_FAILED(rv)) {
3061 0 : NS_WARNING("db change failed!");
3062 0 : return;
3063 : }
3064 :
3065 0 : nsCOMPtr<mozIStoragePendingStatement> pending;
3066 0 : rv = aStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
3067 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
3068 : }
3069 :
3070 : NS_IMETHODIMP
3071 0 : nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
3072 : const char* aType,
3073 : bool aExactHostMatch,
3074 : uint64_t aSessionExpireTime,
3075 : uint64_t aPersistentExpireTime)
3076 : {
3077 0 : NS_ENSURE_ARG_POINTER(aPrincipal);
3078 0 : NS_ENSURE_ARG_POINTER(aType);
3079 :
3080 0 : uint64_t nowms = PR_Now() / 1000;
3081 0 : if (aSessionExpireTime < nowms || aPersistentExpireTime < nowms) {
3082 0 : return NS_ERROR_INVALID_ARG;
3083 : }
3084 :
3085 0 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
3086 0 : return NS_OK;
3087 : }
3088 :
3089 : // Setting the expire time of an nsEP is non-sensical.
3090 0 : if (IsExpandedPrincipal(aPrincipal)) {
3091 0 : return NS_ERROR_INVALID_ARG;
3092 : }
3093 :
3094 0 : MOZ_ASSERT(PermissionAvaliable(aPrincipal, aType));
3095 :
3096 0 : int32_t typeIndex = GetTypeIndex(aType, false);
3097 : // If type == -1, the type isn't known,
3098 : // so just return NS_OK
3099 0 : if (typeIndex == -1) return NS_OK;
3100 :
3101 0 : PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
3102 0 : if (!entry) {
3103 0 : return NS_OK;
3104 : }
3105 :
3106 0 : int32_t idx = entry->GetPermissionIndex(typeIndex);
3107 0 : if (-1 == idx) {
3108 0 : return NS_OK;
3109 : }
3110 :
3111 0 : PermissionEntry& perm = entry->GetPermissions()[idx];
3112 0 : if (perm.mExpireType == EXPIRE_TIME) {
3113 0 : perm.mExpireTime = aPersistentExpireTime;
3114 0 : } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) {
3115 0 : perm.mExpireTime = aSessionExpireTime;
3116 : }
3117 0 : return NS_OK;
3118 : }
3119 :
3120 : NS_IMETHODIMP
3121 3 : nsPermissionManager::GetPermissionsWithKey(const nsACString& aPermissionKey,
3122 : nsTArray<IPC::Permission>& aPerms)
3123 : {
3124 3 : aPerms.Clear();
3125 3 : if (NS_WARN_IF(XRE_IsContentProcess())) {
3126 0 : return NS_ERROR_NOT_AVAILABLE;
3127 : }
3128 :
3129 27 : for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
3130 24 : PermissionHashKey* entry = iter.Get();
3131 :
3132 40 : nsAutoCString permissionKey;
3133 24 : GetKeyForOrigin(entry->GetKey()->mOrigin, permissionKey);
3134 :
3135 : // If the keys don't match, and we aren't getting the default "" key, then
3136 : // we can exit early. We have to keep looking if we're getting the default
3137 : // key, as we may see a preload permission which should be transmitted.
3138 24 : if (aPermissionKey != permissionKey && !aPermissionKey.IsEmpty()) {
3139 8 : continue;
3140 : }
3141 :
3142 36 : for (const auto& permEntry : entry->GetPermissions()) {
3143 : // Given how "default" permissions work and the possibility of them being
3144 : // overridden with UNKNOWN_ACTION, we might see this value here - but we
3145 : // do not want to send it to the content process.
3146 20 : if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
3147 0 : continue;
3148 : }
3149 :
3150 20 : bool isPreload = IsPreloadPermission(mTypeArray[permEntry.mType].get());
3151 20 : if ((isPreload && aPermissionKey.IsEmpty()) || (!isPreload && aPermissionKey == permissionKey)) {
3152 8 : aPerms.AppendElement(IPC::Permission(entry->GetKey()->mOrigin,
3153 4 : mTypeArray.ElementAt(permEntry.mType),
3154 4 : permEntry.mPermission,
3155 4 : permEntry.mExpireType,
3156 8 : permEntry.mExpireTime));
3157 : }
3158 : }
3159 : }
3160 :
3161 3 : return NS_OK;
3162 : }
3163 :
3164 : NS_IMETHODIMP
3165 3 : nsPermissionManager::SetPermissionsWithKey(const nsACString& aPermissionKey,
3166 : nsTArray<IPC::Permission>& aPerms)
3167 : {
3168 3 : if (NS_WARN_IF(XRE_IsParentProcess())) {
3169 0 : return NS_ERROR_NOT_AVAILABLE;
3170 : }
3171 :
3172 6 : RefPtr<GenericPromise::Private> promise;
3173 3 : bool foundKey = mPermissionKeyPromiseMap.Get(aPermissionKey, getter_AddRefs(promise));
3174 3 : if (promise) {
3175 0 : MOZ_ASSERT(foundKey);
3176 : // NOTE: This will resolve asynchronously, so we can mark it as resolved
3177 : // now, and be confident that we will have filled in the database before any
3178 : // callbacks run.
3179 0 : promise->Resolve(true, __func__);
3180 3 : } else if (foundKey) {
3181 : // NOTE: We shouldn't be sent two InitializePermissionsWithKey for the same
3182 : // key, but it's possible.
3183 0 : return NS_OK;
3184 : }
3185 3 : mPermissionKeyPromiseMap.Put(aPermissionKey, nullptr);
3186 :
3187 : // Add the permissions locally to our process
3188 7 : for (IPC::Permission& perm : aPerms) {
3189 8 : nsCOMPtr<nsIPrincipal> principal;
3190 4 : nsresult rv = GetPrincipalFromOrigin(perm.origin, getter_AddRefs(principal));
3191 4 : if (NS_WARN_IF(NS_FAILED(rv))) {
3192 0 : continue;
3193 : }
3194 :
3195 : #ifdef DEBUG
3196 8 : nsAutoCString permissionKey;
3197 4 : GetKeyForPermission(principal, perm.type.get(), permissionKey);
3198 4 : MOZ_ASSERT(permissionKey == aPermissionKey,
3199 : "The permission keys which were sent over should match!");
3200 : #endif
3201 :
3202 : // The child process doesn't care about modification times - it neither
3203 : // reads nor writes, nor removes them based on the date - so 0 (which
3204 : // will end up as now()) is fine.
3205 4 : uint64_t modificationTime = 0;
3206 4 : AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
3207 : perm.expireTime, modificationTime, eNotify, eNoDBOperation,
3208 4 : true /* ignoreSessionPermissions */);
3209 : }
3210 3 : return NS_OK;
3211 : }
3212 :
3213 : /* static */ void
3214 49 : nsPermissionManager::GetKeyForOrigin(const nsACString& aOrigin, nsACString& aKey)
3215 : {
3216 49 : aKey.Truncate();
3217 :
3218 : // We only key origins for http, https, and ftp URIs. All origins begin with
3219 : // the URL which they apply to, which means that they should begin with their
3220 : // scheme in the case where they are one of these interesting URIs. We don't
3221 : // want to actually parse the URL here however, because this can be called on
3222 : // hot paths.
3223 286 : if (!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("http:")) &&
3224 279 : !StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("https:")) &&
3225 87 : !StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("ftp:"))) {
3226 38 : return;
3227 : }
3228 :
3229 : // We need to look at the originAttributes if they are present, to make sure
3230 : // to remove any which we don't want. We put the rest of the origin, not
3231 : // including the attributes, into the key.
3232 60 : OriginAttributes attrs;
3233 30 : if (!attrs.PopulateFromOrigin(aOrigin, aKey)) {
3234 0 : aKey.Truncate();
3235 0 : return;
3236 : }
3237 :
3238 : // mPrivateBrowsingId must be set to false because PermissionManager is not supposed to have
3239 : // any knowledge of private browsing. Allowing it to be true changes the suffix being hashed.
3240 30 : attrs.mPrivateBrowsingId = 0;
3241 :
3242 : // Disable userContext and firstParty isolation for permissions.
3243 : attrs.StripAttributes(OriginAttributes::STRIP_USER_CONTEXT_ID |
3244 30 : OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
3245 :
3246 : #ifdef DEBUG
3247 : // Parse the origin string into a principal, and extract some useful
3248 : // information from it for assertions.
3249 60 : nsCOMPtr<nsIPrincipal> dbgPrincipal;
3250 30 : MOZ_ALWAYS_SUCCEEDS(GetPrincipalFromOrigin(aOrigin, getter_AddRefs(dbgPrincipal)));
3251 60 : nsCOMPtr<nsIURI> dbgUri;
3252 30 : MOZ_ALWAYS_SUCCEEDS(dbgPrincipal->GetURI(getter_AddRefs(dbgUri)));
3253 60 : nsAutoCString dbgScheme;
3254 30 : MOZ_ALWAYS_SUCCEEDS(dbgUri->GetScheme(dbgScheme));
3255 30 : MOZ_ASSERT(dbgScheme.EqualsLiteral("http") ||
3256 : dbgScheme.EqualsLiteral("https") ||
3257 : dbgScheme.EqualsLiteral("ftp"));
3258 30 : MOZ_ASSERT(dbgPrincipal->OriginAttributesRef() == attrs);
3259 : #endif
3260 :
3261 : // Append the stripped suffix to the output origin key.
3262 60 : nsAutoCString suffix;
3263 30 : attrs.CreateSuffix(suffix);
3264 30 : aKey.Append(suffix);
3265 : }
3266 :
3267 : /* static */ void
3268 25 : nsPermissionManager::GetKeyForPrincipal(nsIPrincipal* aPrincipal, nsACString& aKey)
3269 : {
3270 50 : nsAutoCString origin;
3271 25 : nsresult rv = aPrincipal->GetOrigin(origin);
3272 25 : if (NS_WARN_IF(NS_FAILED(rv))) {
3273 0 : aKey.Truncate();
3274 0 : return;
3275 : }
3276 25 : GetKeyForOrigin(origin, aKey);
3277 : }
3278 :
3279 : /* static */ void
3280 24 : nsPermissionManager::GetKeyForPermission(nsIPrincipal* aPrincipal, const char* aType, nsACString& aKey)
3281 : {
3282 : // Preload permissions have the "" key.
3283 24 : if (IsPreloadPermission(aType)) {
3284 0 : aKey.Truncate();
3285 0 : return;
3286 : }
3287 :
3288 24 : GetKeyForPrincipal(aPrincipal, aKey);
3289 : }
3290 :
3291 : /* static */ nsTArray<nsCString>
3292 1 : nsPermissionManager::GetAllKeysForPrincipal(nsIPrincipal* aPrincipal)
3293 : {
3294 1 : MOZ_ASSERT(aPrincipal);
3295 :
3296 1 : nsTArray<nsCString> keys;
3297 2 : nsCOMPtr<nsIPrincipal> prin = aPrincipal;
3298 3 : while (prin) {
3299 : // Add the key to the list
3300 1 : nsCString* key = keys.AppendElement();
3301 1 : GetKeyForPrincipal(prin, *key);
3302 :
3303 : // Get the next subdomain principal and loop back around.
3304 1 : prin = GetNextSubDomainPrincipal(prin);
3305 : }
3306 :
3307 1 : MOZ_ASSERT(keys.Length() >= 1,
3308 : "Every principal should have at least one key.");
3309 2 : return keys;
3310 : }
3311 :
3312 : NS_IMETHODIMP
3313 0 : nsPermissionManager::BroadcastPermissionsForPrincipalToAllContentProcesses(nsIPrincipal* aPrincipal)
3314 : {
3315 0 : nsTArray<ContentParent*> cps;
3316 0 : ContentParent::GetAll(cps);
3317 0 : for (ContentParent* cp : cps) {
3318 0 : nsresult rv = cp->TransmitPermissionsForPrincipal(aPrincipal);
3319 0 : NS_ENSURE_SUCCESS(rv, rv);
3320 : }
3321 :
3322 0 : return NS_OK;
3323 : }
3324 :
3325 : bool
3326 36 : nsPermissionManager::PermissionAvaliable(nsIPrincipal* aPrincipal, const char* aType)
3327 : {
3328 36 : if (XRE_IsContentProcess()) {
3329 20 : nsAutoCString permissionKey;
3330 : // NOTE: GetKeyForPermission accepts a null aType.
3331 10 : GetKeyForPermission(aPrincipal, aType, permissionKey);
3332 :
3333 : // If we have a pending promise for the permission key in question, we don't
3334 : // have the permission avaliable, so report a warning and return false.
3335 20 : RefPtr<GenericPromise::Private> promise;
3336 10 : if (!mPermissionKeyPromiseMap.Get(permissionKey, getter_AddRefs(promise)) || promise) {
3337 : // Emit a useful diagnostic warning with the permissionKey for the process
3338 : // which hasn't received permissions yet.
3339 0 : NS_WARNING(nsPrintfCString("This content process hasn't received the "
3340 0 : "permissions for %s yet", permissionKey.get()).get());
3341 0 : return false;
3342 : }
3343 : }
3344 36 : return true;
3345 : }
3346 :
3347 : NS_IMETHODIMP
3348 0 : nsPermissionManager::WhenPermissionsAvailable(nsIPrincipal* aPrincipal,
3349 : nsIRunnable* aRunnable)
3350 : {
3351 0 : MOZ_ASSERT(aRunnable);
3352 :
3353 0 : if (!XRE_IsContentProcess()) {
3354 0 : aRunnable->Run();
3355 0 : return NS_OK;
3356 : }
3357 :
3358 0 : nsTArray<RefPtr<GenericPromise>> promises;
3359 0 : for (auto& key : GetAllKeysForPrincipal(aPrincipal)) {
3360 0 : RefPtr<GenericPromise::Private> promise;
3361 0 : if (!mPermissionKeyPromiseMap.Get(key, getter_AddRefs(promise))) {
3362 : // In this case we have found a permission which isn't avaliable in the
3363 : // content process and hasn't been requested yet. We need to create a new
3364 : // promise, and send the request to the parent (if we have not already
3365 : // done so).
3366 0 : promise = new GenericPromise::Private(__func__);
3367 0 : mPermissionKeyPromiseMap.Put(key, RefPtr<GenericPromise::Private>(promise).forget());
3368 : }
3369 :
3370 0 : if (promise) {
3371 0 : promises.AppendElement(Move(promise));
3372 : }
3373 : }
3374 :
3375 : // If all of our permissions are avaliable, immediately run the runnable. This
3376 : // avoids any extra overhead during fetch interception which is performance
3377 : // sensitive.
3378 0 : if (promises.IsEmpty()) {
3379 0 : aRunnable->Run();
3380 0 : return NS_OK;
3381 : }
3382 :
3383 0 : auto* thread = SystemGroup::AbstractMainThreadFor(TaskCategory::Other);
3384 :
3385 0 : RefPtr<nsIRunnable> runnable = aRunnable;
3386 0 : GenericPromise::All(thread, promises)->Then(
3387 : thread, __func__,
3388 0 : [runnable] () { runnable->Run(); },
3389 0 : [] () {
3390 0 : NS_WARNING("nsPermissionManager permission promise rejected. We're probably shutting down.");
3391 0 : });
3392 0 : return NS_OK;
3393 : }
3394 :
3395 : NS_IMETHODIMP
3396 7 : nsPermissionManager::GetHasPreloadPermissions(bool* aResult)
3397 : {
3398 7 : *aResult = sPreloadPermissionCount > 0;
3399 7 : return NS_OK;
3400 : }
|