Coverage Report

Created: 2018-09-25 14:53

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