PermissionManager.cpp (148709B)
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/AbstractThread.h" 8 #include "mozilla/AppShutdown.h" 9 #ifdef MOZ_BACKGROUNDTASKS 10 # include "mozilla/BackgroundTasks.h" 11 #endif 12 #include "mozilla/BasePrincipal.h" 13 #include "mozilla/ClearOnShutdown.h" 14 #include "mozilla/Components.h" 15 #include "mozilla/ContentPrincipal.h" 16 #include "mozilla/DebugOnly.h" 17 #include "mozilla/dom/CanonicalBrowsingContext.h" 18 #include "mozilla/dom/ContentParent.h" 19 #include "mozilla/dom/Document.h" 20 #include "mozilla/dom/WindowGlobalParent.h" 21 #include "mozilla/ExpandedPrincipal.h" 22 #include "mozilla/net/NeckoMessageUtils.h" 23 #include "mozilla/Permission.h" 24 #include "mozilla/PermissionManager.h" 25 #include "mozilla/Preferences.h" 26 #include "mozilla/ScopeExit.h" 27 #include "mozilla/StaticPrefs_permissions.h" 28 #include "mozilla/glean/ExtensionsPermissionsMetrics.h" 29 30 #include "mozIStorageService.h" 31 #include "mozIStorageConnection.h" 32 #include "mozIStorageStatement.h" 33 #include "mozStorageCID.h" 34 35 #include "nsAppDirectoryServiceDefs.h" 36 #include "nsComponentManagerUtils.h" 37 #include "nsContentUtils.h" 38 #include "nsCRT.h" 39 #include "nsDebug.h" 40 #include "nsIConsoleService.h" 41 #include "nsIEffectiveTLDService.h" 42 #include "nsIUserIdleService.h" 43 #include "nsIInputStream.h" 44 #include "nsINavHistoryService.h" 45 #include "nsIObserverService.h" 46 #include "nsIPrefBranch.h" 47 #include "nsIPrincipal.h" 48 #include "nsIURIMutator.h" 49 #include "nsIWritablePropertyBag2.h" 50 #include "nsReadLine.h" 51 #include "nsStringFwd.h" 52 #include "nsTHashSet.h" 53 #include "nsToolkitCompsCID.h" 54 55 using namespace mozilla::dom; 56 57 namespace mozilla { 58 59 #define PERMISSIONS_FILE_NAME "permissions.sqlite" 60 #define HOSTS_SCHEMA_VERSION 12 61 62 // Default permissions are read from a URL - this is the preference we read 63 // to find that URL. If not set, don't use any default permissions. 64 constexpr char kDefaultsUrlPrefName[] = "permissions.manager.defaultsUrl"; 65 66 constexpr char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION; 67 68 // A special value for a permission ID that indicates the ID was loaded as 69 // a default value. These will never be written to the database, but may 70 // be overridden with an explicit permission (including UNKNOWN_ACTION) 71 constexpr int64_t cIDPermissionIsDefault = -1; 72 73 #define ENSURE_NOT_CHILD_PROCESS_(onError) \ 74 PR_BEGIN_MACRO \ 75 if (IsChildProcess()) { \ 76 NS_ERROR("Cannot perform action in content process!"); \ 77 onError \ 78 } \ 79 PR_END_MACRO 80 81 #define ENSURE_NOT_CHILD_PROCESS \ 82 ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; }) 83 84 #define ENSURE_NOT_CHILD_PROCESS_NORET ENSURE_NOT_CHILD_PROCESS_(;) 85 86 #define EXPIRY_NOW PR_Now() / 1000 87 88 //////////////////////////////////////////////////////////////////////////////// 89 90 namespace { 91 92 inline bool IsChildProcess() { return XRE_IsContentProcess(); } 93 94 void LogToConsole(const nsAString& aMsg) { 95 nsCOMPtr<nsIConsoleService> console( 96 do_GetService("@mozilla.org/consoleservice;1")); 97 if (!console) { 98 NS_WARNING("Failed to log message to console."); 99 return; 100 } 101 102 nsAutoString msg(aMsg); 103 console->LogStringMessage(msg.get()); 104 } 105 106 // NOTE: an empty string can be passed as aType - if it is this function will 107 // return "false" unconditionally. 108 bool HasDefaultPref(const nsACString& aType) { 109 // A list of permissions that can have a fallback default permission 110 // set under the permissions.default.* pref. 111 static const nsLiteralCString kPermissionsWithDefaults[] = { 112 "camera"_ns, "microphone"_ns, "geo"_ns, "desktop-notification"_ns, 113 "shortcuts"_ns, "screen-wake-lock"_ns}; 114 115 if (!aType.IsEmpty()) { 116 for (const auto& perm : kPermissionsWithDefaults) { 117 if (perm.Equals(aType)) { 118 return true; 119 } 120 } 121 } 122 123 return false; 124 } 125 126 // These permissions are special permissions which must be transmitted to the 127 // content process before documents with their principals have loaded within 128 // that process. 129 // 130 // Permissions which are in this list are considered to have a "" permission 131 // key, even if their principal would not normally have that key. 132 static const nsLiteralCString kPreloadPermissions[] = { 133 // This permission is preloaded to support properly blocking service worker 134 // interception when a user has disabled storage for a specific site. Once 135 // service worker interception moves to the parent process this should be 136 // removed. See bug 1428130. 137 "cookie"_ns, "https-only-load-insecure"_ns}; 138 139 // NOTE: nullptr can be passed as aType - if it is this function will return 140 // "false" unconditionally. 141 bool IsPreloadPermission(const nsACString& aType) { 142 if (!aType.IsEmpty()) { 143 for (const auto& perm : kPreloadPermissions) { 144 if (perm.Equals(aType)) { 145 return true; 146 } 147 } 148 } 149 150 return false; 151 } 152 153 // Array of permission types which should not be isolated by origin attributes, 154 // for user context and private browsing. 155 // Keep this array in sync with 'STRIPPED_PERMS' in 156 // 'test_permmanager_oa_strip.js' 157 // Currently only preloaded permissions are supported. 158 // This is because perms are sent to the content process in bulk by perm key. 159 // Non-preloaded, but OA stripped permissions would not be accessible by sites 160 // in private browsing / non-default user context. 161 static constexpr std::array<nsLiteralCString, 3> kStripOAPermissions = { 162 {"cookie"_ns, "https-only-load-insecure"_ns, "ipp-vpn"_ns}}; 163 164 bool IsOAForceStripPermission(const nsACString& aType) { 165 if (aType.IsEmpty()) { 166 return false; 167 } 168 for (const auto& perm : kStripOAPermissions) { 169 if (perm.Equals(aType)) { 170 return true; 171 } 172 } 173 return false; 174 } 175 176 // Array of permission prefixes which should be isolated only by site. 177 // These site-scoped permissions are stored under their site's principal. 178 // GetAllForPrincipal also needs to look for these especially. 179 static constexpr std::array<nsLiteralCString, 3> kSiteScopedPermissions = { 180 {"3rdPartyStorage^"_ns, "AllowStorageAccessRequest^"_ns, 181 "3rdPartyFrameStorage^"_ns}}; 182 183 bool IsSiteScopedPermission(const nsACString& aType) { 184 if (aType.IsEmpty()) { 185 return false; 186 } 187 for (const auto& perm : kSiteScopedPermissions) { 188 if (aType.Length() >= perm.Length() && 189 Substring(aType, 0, perm.Length()) == perm) { 190 return true; 191 } 192 } 193 return false; 194 } 195 196 // Array of permission type prefixes which have a secondary key encoded in the 197 // permission type. These permissions will not be stored in-process with the 198 // secondary key, but updates to them will cause "perm-changed" notifications on 199 // processes for that key. 200 static constexpr std::array<nsLiteralCString, 3> kSecondaryKeyedPermissions = { 201 {"3rdPartyStorage^"_ns, "AllowStorageAccessRequest^"_ns, 202 "3rdPartyFrameStorage^"_ns}}; 203 204 bool GetSecondaryKey(const nsACString& aType, nsACString& aSecondaryKey) { 205 aSecondaryKey.Truncate(); 206 if (aType.IsEmpty()) { 207 return false; 208 } 209 for (const auto& perm : kSecondaryKeyedPermissions) { 210 if (aType.Length() > perm.Length() && 211 Substring(aType, 0, perm.Length()) == perm) { 212 aSecondaryKey = Substring(aType, perm.Length()); 213 return true; 214 } 215 } 216 return false; 217 } 218 219 void OriginAppendOASuffix(OriginAttributes aOriginAttributes, 220 bool aForceStripOA, nsACString& aOrigin) { 221 PermissionManager::MaybeStripOriginAttributes(aForceStripOA, 222 aOriginAttributes); 223 224 nsAutoCString oaSuffix; 225 aOriginAttributes.CreateSuffix(oaSuffix); 226 aOrigin.Append(oaSuffix); 227 } 228 229 nsresult GetOriginFromPrincipal(nsIPrincipal* aPrincipal, bool aForceStripOA, 230 nsACString& aOrigin) { 231 nsresult rv = aPrincipal->GetOriginNoSuffix(aOrigin); 232 // The principal may belong to the about:blank content viewer, so this can be 233 // expected to fail. 234 if (NS_FAILED(rv)) { 235 return rv; 236 } 237 238 nsAutoCString suffix; 239 rv = aPrincipal->GetOriginSuffix(suffix); 240 NS_ENSURE_SUCCESS(rv, rv); 241 242 OriginAttributes attrs; 243 NS_ENSURE_TRUE(attrs.PopulateFromSuffix(suffix), NS_ERROR_FAILURE); 244 245 OriginAppendOASuffix(attrs, aForceStripOA, aOrigin); 246 247 return NS_OK; 248 } 249 250 // Returns the site of the principal, including OA, given a principal. 251 nsresult GetSiteFromPrincipal(nsIPrincipal* aPrincipal, bool aForceStripOA, 252 nsACString& aSite) { 253 nsCOMPtr<nsIURI> uri = aPrincipal->GetURI(); 254 nsCOMPtr<nsIEffectiveTLDService> etld = 255 mozilla::components::EffectiveTLD::Service(); 256 NS_ENSURE_TRUE(etld, NS_ERROR_FAILURE); 257 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); 258 nsresult rv = etld->GetSite(uri, aSite); 259 260 // The principal may belong to the about:blank content viewer, so this can be 261 // expected to fail. 262 if (NS_FAILED(rv)) { 263 rv = aPrincipal->GetOrigin(aSite); 264 NS_ENSURE_SUCCESS(rv, rv); 265 return NS_OK; 266 } 267 268 nsAutoCString suffix; 269 rv = aPrincipal->GetOriginSuffix(suffix); 270 NS_ENSURE_SUCCESS(rv, rv); 271 272 OriginAttributes attrs; 273 NS_ENSURE_TRUE(attrs.PopulateFromSuffix(suffix), NS_ERROR_FAILURE); 274 275 OriginAppendOASuffix(attrs, aForceStripOA, aSite); 276 277 return NS_OK; 278 } 279 280 nsresult GetOriginFromURIAndOA(nsIURI* aURI, 281 const OriginAttributes* aOriginAttributes, 282 bool aForceStripOA, nsACString& aOrigin) { 283 nsAutoCString origin(aOrigin); 284 nsresult rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, origin); 285 NS_ENSURE_SUCCESS(rv, rv); 286 287 OriginAppendOASuffix(*aOriginAttributes, aForceStripOA, origin); 288 289 aOrigin = origin; 290 291 return NS_OK; 292 } 293 294 nsresult GetPrincipalFromOrigin(const nsACString& aOrigin, bool aForceStripOA, 295 nsIPrincipal** aPrincipal) { 296 nsAutoCString originNoSuffix; 297 OriginAttributes attrs; 298 if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) { 299 return NS_ERROR_FAILURE; 300 } 301 302 PermissionManager::MaybeStripOriginAttributes(aForceStripOA, attrs); 303 304 nsCOMPtr<nsIURI> uri; 305 nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); 306 NS_ENSURE_SUCCESS(rv, rv); 307 308 nsCOMPtr<nsIPrincipal> principal = 309 BasePrincipal::CreateContentPrincipal(uri, attrs); 310 principal.forget(aPrincipal); 311 return NS_OK; 312 } 313 314 nsresult GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal) { 315 OriginAttributes attrs; 316 nsCOMPtr<nsIPrincipal> principal = 317 BasePrincipal::CreateContentPrincipal(aURI, attrs); 318 NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE); 319 320 principal.forget(aPrincipal); 321 return NS_OK; 322 } 323 324 nsCString GetNextSubDomainForHost(const nsACString& aHost) { 325 nsCString subDomain; 326 nsCOMPtr<nsIEffectiveTLDService> etld = 327 mozilla::components::EffectiveTLD::Service(); 328 nsresult rv = etld->GetNextSubDomain(aHost, subDomain); 329 // We can fail if there is no more subdomain or if the host can't have a 330 // subdomain. 331 if (NS_FAILED(rv)) { 332 return ""_ns; 333 } 334 335 return subDomain; 336 } 337 338 // This function produces a nsIURI which is identical to the current 339 // nsIURI, except that it has one less subdomain segment. It returns 340 // `nullptr` if there are no more segments to remove. 341 already_AddRefed<nsIURI> GetNextSubDomainURI(nsIURI* aURI) { 342 nsAutoCString host; 343 nsresult rv = aURI->GetHost(host); 344 if (NS_FAILED(rv)) { 345 return nullptr; 346 } 347 348 nsCString domain = GetNextSubDomainForHost(host); 349 if (domain.IsEmpty()) { 350 return nullptr; 351 } 352 353 nsCOMPtr<nsIURI> uri; 354 rv = NS_MutateURI(aURI).SetHost(domain).Finalize(uri); 355 if (NS_FAILED(rv) || !uri) { 356 return nullptr; 357 } 358 359 return uri.forget(); 360 } 361 362 nsresult UpgradeHostToOriginAndInsert( 363 const nsACString& aHost, const nsCString& aType, uint32_t aPermission, 364 uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime, 365 std::function<nsresult(const nsACString& aOrigin, const nsCString& aType, 366 uint32_t aPermission, uint32_t aExpireType, 367 int64_t aExpireTime, int64_t aModificationTime)>&& 368 aCallback) { 369 if (aHost.EqualsLiteral("<file>")) { 370 // We no longer support the magic host <file> 371 NS_WARNING( 372 "The magic host <file> is no longer supported. " 373 "It is being removed from the permissions database."); 374 return NS_OK; 375 } 376 377 // First, we check to see if the host is a valid URI. If it is, it can be 378 // imported directly 379 nsCOMPtr<nsIURI> uri; 380 nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost); 381 if (NS_SUCCEEDED(rv)) { 382 // It was previously possible to insert useless entries to your permissions 383 // database for URIs which have a null principal. This acts as a cleanup, 384 // getting rid of these useless database entries 385 if (uri->SchemeIs("moz-nullprincipal")) { 386 NS_WARNING("A moz-nullprincipal: permission is being discarded."); 387 return NS_OK; 388 } 389 390 nsCOMPtr<nsIPrincipal> principal; 391 rv = GetPrincipal(uri, getter_AddRefs(principal)); 392 NS_ENSURE_SUCCESS(rv, rv); 393 394 nsAutoCString origin; 395 rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType), 396 origin); 397 NS_ENSURE_SUCCESS(rv, rv); 398 399 aCallback(origin, aType, aPermission, aExpireType, aExpireTime, 400 aModificationTime); 401 return NS_OK; 402 } 403 404 // The user may use this host at non-standard ports or protocols, we can use 405 // their history to guess what ports and protocols we want to add permissions 406 // for. We find every URI which they have visited with this host (or a 407 // subdomain of this host), and try to add it as a principal. 408 bool foundHistory = false; 409 410 nsCOMPtr<nsINavHistoryService> histSrv = nullptr; 411 if (NS_IsMainThread()) { 412 histSrv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID); 413 } 414 415 if (histSrv) { 416 nsCOMPtr<nsINavHistoryQuery> histQuery; 417 rv = histSrv->GetNewQuery(getter_AddRefs(histQuery)); 418 NS_ENSURE_SUCCESS(rv, rv); 419 420 // Get the eTLD+1 of the domain 421 nsAutoCString eTLD1; 422 nsCOMPtr<nsIEffectiveTLDService> etld = 423 mozilla::components::EffectiveTLD::Service(); 424 rv = etld->GetBaseDomainFromHost(aHost, 0, eTLD1); 425 426 if (NS_FAILED(rv)) { 427 // If the lookup on the tldService for the base domain for the host 428 // failed, that means that we just want to directly use the host as the 429 // host name for the lookup. 430 eTLD1 = aHost; 431 } 432 433 // We want to only find history items for this particular eTLD+1, and 434 // subdomains 435 rv = histQuery->SetDomain(eTLD1); 436 NS_ENSURE_SUCCESS(rv, rv); 437 438 rv = histQuery->SetDomainIsHost(false); 439 NS_ENSURE_SUCCESS(rv, rv); 440 441 nsCOMPtr<nsINavHistoryQueryOptions> histQueryOpts; 442 rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts)); 443 NS_ENSURE_SUCCESS(rv, rv); 444 445 // We want to get the URIs for every item in the user's history with the 446 // given host 447 rv = 448 histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI); 449 NS_ENSURE_SUCCESS(rv, rv); 450 451 // We only search history, because searching both bookmarks and history 452 // is not supported, and history tends to be more comprehensive. 453 rv = histQueryOpts->SetQueryType( 454 nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY); 455 NS_ENSURE_SUCCESS(rv, rv); 456 457 // We include hidden URIs (such as those visited via iFrames) as they may 458 // have permissions too 459 rv = histQueryOpts->SetIncludeHidden(true); 460 NS_ENSURE_SUCCESS(rv, rv); 461 462 nsCOMPtr<nsINavHistoryResult> histResult; 463 rv = histSrv->ExecuteQuery(histQuery, histQueryOpts, 464 getter_AddRefs(histResult)); 465 NS_ENSURE_SUCCESS(rv, rv); 466 467 nsCOMPtr<nsINavHistoryContainerResultNode> histResultContainer; 468 rv = histResult->GetRoot(getter_AddRefs(histResultContainer)); 469 NS_ENSURE_SUCCESS(rv, rv); 470 471 rv = histResultContainer->SetContainerOpen(true); 472 NS_ENSURE_SUCCESS(rv, rv); 473 474 uint32_t childCount = 0; 475 rv = histResultContainer->GetChildCount(&childCount); 476 NS_ENSURE_SUCCESS(rv, rv); 477 478 nsTHashSet<nsCString> insertedOrigins; 479 for (uint32_t i = 0; i < childCount; i++) { 480 nsCOMPtr<nsINavHistoryResultNode> child; 481 histResultContainer->GetChild(i, getter_AddRefs(child)); 482 if (NS_WARN_IF(NS_FAILED(rv))) continue; 483 484 uint32_t type; 485 rv = child->GetType(&type); 486 if (NS_WARN_IF(NS_FAILED(rv)) || 487 type != nsINavHistoryResultNode::RESULT_TYPE_URI) { 488 NS_WARNING( 489 "Unexpected non-RESULT_TYPE_URI node in " 490 "UpgradeHostToOriginAndInsert()"); 491 continue; 492 } 493 494 nsAutoCString uriSpec; 495 rv = child->GetUri(uriSpec); 496 if (NS_WARN_IF(NS_FAILED(rv))) continue; 497 498 nsCOMPtr<nsIURI> uri; 499 rv = NS_NewURI(getter_AddRefs(uri), uriSpec); 500 if (NS_WARN_IF(NS_FAILED(rv))) continue; 501 502 // Use the provided host - this URI may be for a subdomain, rather than 503 // the host we care about. 504 rv = NS_MutateURI(uri).SetHost(aHost).Finalize(uri); 505 if (NS_WARN_IF(NS_FAILED(rv))) continue; 506 507 // We now have a URI which we can make a nsIPrincipal out of 508 nsCOMPtr<nsIPrincipal> principal; 509 rv = GetPrincipal(uri, getter_AddRefs(principal)); 510 if (NS_WARN_IF(NS_FAILED(rv))) continue; 511 512 nsAutoCString origin; 513 rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType), 514 origin); 515 if (NS_WARN_IF(NS_FAILED(rv))) continue; 516 517 // Ensure that we don't insert the same origin repeatedly 518 if (insertedOrigins.Contains(origin)) { 519 continue; 520 } 521 522 foundHistory = true; 523 rv = aCallback(origin, aType, aPermission, aExpireType, aExpireTime, 524 aModificationTime); 525 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Insert failed"); 526 insertedOrigins.Insert(origin); 527 } 528 529 rv = histResultContainer->SetContainerOpen(false); 530 NS_ENSURE_SUCCESS(rv, rv); 531 } 532 533 // If we didn't find any origins for this host in the poermissions database, 534 // we can insert the default http:// and https:// permissions into the 535 // database. This has a relatively high likelihood of applying the permission 536 // to the correct origin. 537 if (!foundHistory) { 538 nsAutoCString hostSegment; 539 nsCOMPtr<nsIPrincipal> principal; 540 nsAutoCString origin; 541 542 // If this is an ipv6 URI, we need to surround it in '[', ']' before trying 543 // to parse it as a URI. 544 if (aHost.FindChar(':') != -1) { 545 hostSegment.AssignLiteral("["); 546 hostSegment.Append(aHost); 547 hostSegment.AppendLiteral("]"); 548 } else { 549 hostSegment.Assign(aHost); 550 } 551 552 // http:// URI default 553 rv = NS_NewURI(getter_AddRefs(uri), "http://"_ns + hostSegment); 554 NS_ENSURE_SUCCESS(rv, rv); 555 556 rv = GetPrincipal(uri, getter_AddRefs(principal)); 557 NS_ENSURE_SUCCESS(rv, rv); 558 559 rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType), 560 origin); 561 NS_ENSURE_SUCCESS(rv, rv); 562 563 aCallback(origin, aType, aPermission, aExpireType, aExpireTime, 564 aModificationTime); 565 566 // https:// URI default 567 rv = NS_NewURI(getter_AddRefs(uri), "https://"_ns + hostSegment); 568 NS_ENSURE_SUCCESS(rv, rv); 569 570 rv = GetPrincipal(uri, getter_AddRefs(principal)); 571 NS_ENSURE_SUCCESS(rv, rv); 572 573 rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType), 574 origin); 575 NS_ENSURE_SUCCESS(rv, rv); 576 577 aCallback(origin, aType, aPermission, aExpireType, aExpireTime, 578 aModificationTime); 579 } 580 581 return NS_OK; 582 } 583 584 bool IsExpandedPrincipal(nsIPrincipal* aPrincipal) { 585 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal); 586 return !!ep; 587 } 588 589 // We only want to persist permissions which don't have session or policy 590 // expiration. 591 bool IsPersistentExpire(uint32_t aExpire, const nsACString& aType) { 592 bool res = (aExpire != nsIPermissionManager::EXPIRE_SESSION && 593 aExpire != nsIPermissionManager::EXPIRE_POLICY); 594 return res; 595 } 596 597 nsresult NotifySecondaryKeyPermissionUpdateInContentProcess( 598 const nsACString& aType, uint32_t aPermission, 599 const nsACString& aSecondaryKey, nsIPrincipal* aTopPrincipal) { 600 NS_ENSURE_ARG_POINTER(aTopPrincipal); 601 MOZ_ASSERT(XRE_IsParentProcess()); 602 AutoTArray<RefPtr<BrowsingContextGroup>, 5> bcGroups; 603 BrowsingContextGroup::GetAllGroups(bcGroups); 604 for (const auto& bcGroup : bcGroups) { 605 for (const auto& topBC : bcGroup->Toplevels()) { 606 CanonicalBrowsingContext* topCBC = topBC->Canonical(); 607 RefPtr<nsIURI> topURI = topCBC->GetCurrentURI(); 608 if (!topURI) { 609 continue; 610 } 611 bool thirdParty; 612 nsresult rv = aTopPrincipal->IsThirdPartyURI(topURI, &thirdParty); 613 if (NS_FAILED(rv)) { 614 continue; 615 } 616 if (!thirdParty) { 617 AutoTArray<RefPtr<BrowsingContext>, 5> bcs; 618 topBC->GetAllBrowsingContextsInSubtree(bcs); 619 for (const auto& bc : bcs) { 620 CanonicalBrowsingContext* cbc = bc->Canonical(); 621 ContentParent* cp = cbc->GetContentParent(); 622 if (!cp) { 623 continue; 624 } 625 if (cp->NeedsSecondaryKeyPermissionsUpdate(aSecondaryKey)) { 626 WindowGlobalParent* wgp = cbc->GetCurrentWindowGlobal(); 627 if (!wgp) { 628 continue; 629 } 630 bool success = wgp->SendNotifyPermissionChange(aType, aPermission); 631 (void)NS_WARN_IF(!success); 632 } 633 } 634 } 635 } 636 } 637 return NS_OK; 638 } 639 640 } // namespace 641 642 //////////////////////////////////////////////////////////////////////////////// 643 644 PermissionManager::PermissionKey* 645 PermissionManager::PermissionKey::CreateFromPrincipal(nsIPrincipal* aPrincipal, 646 bool aForceStripOA, 647 bool aScopeToSite, 648 nsresult& aResult) { 649 nsAutoCString keyString; 650 if (aScopeToSite) { 651 aResult = GetSiteFromPrincipal(aPrincipal, aForceStripOA, keyString); 652 } else { 653 aResult = GetOriginFromPrincipal(aPrincipal, aForceStripOA, keyString); 654 } 655 if (NS_WARN_IF(NS_FAILED(aResult))) { 656 return nullptr; 657 } 658 return new PermissionKey(keyString); 659 } 660 661 PermissionManager::PermissionKey* 662 PermissionManager::PermissionKey::CreateFromURIAndOriginAttributes( 663 nsIURI* aURI, const OriginAttributes* aOriginAttributes, bool aForceStripOA, 664 nsresult& aResult) { 665 nsAutoCString origin; 666 aResult = 667 GetOriginFromURIAndOA(aURI, aOriginAttributes, aForceStripOA, origin); 668 if (NS_WARN_IF(NS_FAILED(aResult))) { 669 return nullptr; 670 } 671 672 return new PermissionKey(origin); 673 } 674 675 PermissionManager::PermissionKey* 676 PermissionManager::PermissionKey::CreateFromURI(nsIURI* aURI, 677 nsresult& aResult) { 678 nsAutoCString origin; 679 aResult = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, origin); 680 if (NS_WARN_IF(NS_FAILED(aResult))) { 681 return nullptr; 682 } 683 684 return new PermissionKey(origin); 685 } 686 687 //////////////////////////////////////////////////////////////////////////////// 688 // PermissionManager Implementation 689 690 NS_IMPL_ISUPPORTS(PermissionManager, nsIPermissionManager, nsIObserver, 691 nsISupportsWeakReference, nsIAsyncShutdownBlocker) 692 693 PermissionManager::PermissionManager() 694 : mMonitor("PermissionManager::mMonitor"), 695 mState(eInitializing), 696 mMemoryOnlyDB(false), 697 mLargestID(0) {} 698 699 PermissionManager::~PermissionManager() { 700 MonitorAutoLock lock{mMonitor}; 701 702 // NOTE: Make sure to reject each of the promises in mPermissionKeyPromiseMap 703 // before destroying. 704 for (const auto& promise : mPermissionKeyPromiseMap.Values()) { 705 if (promise) { 706 promise->Reject(NS_ERROR_FAILURE, __func__); 707 } 708 } 709 mPermissionKeyPromiseMap.Clear(); 710 711 if (mThread) { 712 mThread->Shutdown(); 713 mThread = nullptr; 714 } 715 } 716 717 /* static */ 718 StaticMutex PermissionManager::sCreationMutex; 719 StaticRefPtr<PermissionManager> PermissionManager::sInstanceHolder; 720 bool PermissionManager::sInstanceDead(false); 721 722 // static 723 already_AddRefed<nsIPermissionManager> PermissionManager::GetXPCOMSingleton() { 724 return GetInstance(); 725 } 726 727 // static 728 already_AddRefed<PermissionManager> PermissionManager::GetInstance() { 729 // The lazy initialization could race. 730 StaticMutexAutoLock lock(sCreationMutex); 731 732 if (sInstanceDead) { 733 return nullptr; 734 } 735 736 if (sInstanceHolder) { 737 RefPtr<PermissionManager> ret(sInstanceHolder); 738 return ret.forget(); 739 } 740 741 auto permManager = MakeRefPtr<PermissionManager>(); 742 if (NS_SUCCEEDED(permManager->Init())) { 743 // Note that this does an extra AddRef on purpose to keep us alive 744 // until shutdown. 745 sInstanceHolder = permManager.get(); 746 return permManager.forget(); 747 } 748 749 sInstanceDead = true; 750 return nullptr; 751 } 752 753 nsresult PermissionManager::Init() { 754 // If we are already shutting down, do not permit a creation. 755 // This must match the phase in GetAsyncShutdownBarrier. 756 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) { 757 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; 758 } 759 760 MOZ_ASSERT(NS_IsMainThread()); 761 762 MonitorAutoLock lock{mMonitor}; 763 MOZ_ASSERT(mState == eInitializing); 764 765 // If the 'permissions.memory_only' pref is set to true, then don't write any 766 // permission settings to disk, but keep them in a memory-only database. 767 mMemoryOnlyDB = Preferences::GetBool("permissions.memory_only", false); 768 769 nsresult rv; 770 nsCOMPtr<nsIPrefService> prefService = 771 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); 772 NS_ENSURE_SUCCESS(rv, rv); 773 774 rv = prefService->GetBranch("permissions.default.", 775 getter_AddRefs(mDefaultPrefBranch)); 776 NS_ENSURE_SUCCESS(rv, rv); 777 778 if (IsChildProcess()) { 779 // Stop here; we don't need the DB in the child process. Instead we will be 780 // sent permissions as we need them by our parent process. 781 mState = eReady; 782 783 // We use ClearOnShutdown on the content process only because on the parent 784 // process we need to block the shutdown for the final closeDB() call. 785 ClearOnShutdown(&sInstanceHolder); 786 return NS_OK; 787 } 788 789 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 790 if (observerService) { 791 observerService->AddObserver(this, "profile-do-change", true); 792 observerService->AddObserver(this, "testonly-reload-permissions-from-disk", 793 true); 794 observerService->AddObserver(this, "last-pb-context-exited", true); 795 } 796 797 if (XRE_IsParentProcess()) { 798 nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier(); 799 if (!asc) { 800 return NS_ERROR_NOT_AVAILABLE; 801 } 802 nsAutoString blockerName; 803 MOZ_ALWAYS_SUCCEEDS(GetName(blockerName)); 804 805 nsresult rv = asc->AddBlocker( 806 this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, blockerName); 807 NS_ENSURE_SUCCESS(rv, rv); 808 } 809 810 AddIdleDailyMaintenanceJob(); 811 812 MOZ_ASSERT(!mThread); 813 NS_ENSURE_SUCCESS(NS_NewNamedThread("Permission", getter_AddRefs(mThread)), 814 NS_ERROR_FAILURE); 815 816 PRThread* prThread; 817 MOZ_ALWAYS_SUCCEEDS(mThread->GetPRThread(&prThread)); 818 MOZ_ASSERT(prThread); 819 820 mThreadBoundData.Transfer(prThread); 821 822 InitDB(false); 823 824 return NS_OK; 825 } 826 827 nsresult PermissionManager::OpenDatabase(nsIFile* aPermissionsFile) { 828 MOZ_ASSERT(!NS_IsMainThread()); 829 auto data = mThreadBoundData.Access(); 830 831 nsresult rv; 832 nsCOMPtr<mozIStorageService> storage = 833 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); 834 if (!storage) { 835 return NS_ERROR_UNEXPECTED; 836 } 837 // cache a connection to the hosts database 838 if (mMemoryOnlyDB) { 839 rv = storage->OpenSpecialDatabase( 840 kMozStorageMemoryStorageKey, VoidCString(), 841 mozIStorageService::CONNECTION_DEFAULT, getter_AddRefs(data->mDBConn)); 842 } else { 843 rv = storage->OpenDatabase(aPermissionsFile, 844 mozIStorageService::CONNECTION_DEFAULT, 845 getter_AddRefs(data->mDBConn)); 846 } 847 return rv; 848 } 849 850 void PermissionManager::InitDB(bool aRemoveFile) { 851 mState = eInitializing; 852 MOZ_ASSERT(NS_IsMainThread()); 853 854 mReadEntries.Clear(); 855 856 auto readyIfFailed = MakeScopeExit([&]() { 857 // ignore failure here, since it's non-fatal (we can run fine without 858 // persistent storage - e.g. if there's no profile). 859 // XXX should we tell the user about this? 860 mState = eReady; 861 }); 862 863 if (!mPermissionsFile) { 864 nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, 865 getter_AddRefs(mPermissionsFile)); 866 if (NS_FAILED(rv)) { 867 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, 868 getter_AddRefs(mPermissionsFile)); 869 if (NS_FAILED(rv)) { 870 return; 871 } 872 } 873 874 rv = 875 mPermissionsFile->AppendNative(nsLiteralCString(PERMISSIONS_FILE_NAME)); 876 NS_ENSURE_SUCCESS_VOID(rv); 877 } 878 879 nsCOMPtr<nsIInputStream> defaultsInputStream = GetDefaultsInputStream(); 880 881 RefPtr<PermissionManager> self = this; 882 mThread->Dispatch(NS_NewRunnableFunction( 883 "PermissionManager::InitDB", [self, aRemoveFile, defaultsInputStream] { 884 MonitorAutoLock lock(self->mMonitor); 885 886 nsresult rv = self->TryInitDB(aRemoveFile, defaultsInputStream, lock); 887 (void)NS_WARN_IF(NS_FAILED(rv)); 888 889 // This extra runnable calls EnsureReadCompleted to finialize the 890 // initialization. If there is something blocked by the monitor, it will 891 // be NOP. 892 NS_DispatchToMainThread(NS_NewRunnableFunction( 893 "PermissionManager::InitDB-MainThread", [self] { 894 MonitorAutoLock lock{self->mMonitor}; 895 self->EnsureReadCompleted(); 896 })); 897 898 self->mMonitor.Notify(); 899 })); 900 901 readyIfFailed.release(); 902 } 903 904 nsresult PermissionManager::TryInitDB(bool aRemoveFile, 905 nsIInputStream* aDefaultsInputStream, 906 const MonitorAutoLock& aProofOfLock) { 907 MOZ_ASSERT(!NS_IsMainThread()); 908 909 auto raii = MakeScopeExit([&]() { 910 if (aDefaultsInputStream) { 911 aDefaultsInputStream->Close(); 912 } 913 914 mState = eDBInitialized; 915 }); 916 917 auto data = mThreadBoundData.Access(); 918 919 auto raiiFailure = MakeScopeExit([&]() { 920 if (data->mDBConn) { 921 DebugOnly<nsresult> rv = data->mDBConn->Close(); 922 MOZ_ASSERT(NS_SUCCEEDED(rv)); 923 data->mDBConn = nullptr; 924 } 925 }); 926 927 nsresult rv; 928 929 if (aRemoveFile) { 930 bool exists = false; 931 rv = mPermissionsFile->Exists(&exists); 932 NS_ENSURE_SUCCESS(rv, rv); 933 if (exists) { 934 rv = mPermissionsFile->Remove(false); 935 NS_ENSURE_SUCCESS(rv, rv); 936 } 937 } 938 939 rv = OpenDatabase(mPermissionsFile); 940 if (rv == NS_ERROR_FILE_CORRUPTED) { 941 LogToConsole(u"permissions.sqlite is corrupted! Try again!"_ns); 942 943 // Add telemetry probe 944 glean::permissions::sql_corrupted.Add(1); 945 946 // delete corrupted permissions.sqlite and try again 947 rv = mPermissionsFile->Remove(false); 948 NS_ENSURE_SUCCESS(rv, rv); 949 LogToConsole(u"Corrupted permissions.sqlite has been removed."_ns); 950 951 rv = OpenDatabase(mPermissionsFile); 952 NS_ENSURE_SUCCESS(rv, rv); 953 LogToConsole(u"OpenDatabase to permissions.sqlite is successful!"_ns); 954 } 955 956 if (NS_WARN_IF(NS_FAILED(rv))) { 957 return rv; 958 } 959 960 bool ready; 961 data->mDBConn->GetConnectionReady(&ready); 962 if (!ready) { 963 LogToConsole(nsLiteralString( 964 u"Fail to get connection to permissions.sqlite! Try again!")); 965 966 // delete and try again 967 rv = mPermissionsFile->Remove(false); 968 NS_ENSURE_SUCCESS(rv, rv); 969 LogToConsole(u"Defective permissions.sqlite has been removed."_ns); 970 971 // Add telemetry probe 972 glean::permissions::defective_sql_removed.Add(1); 973 974 rv = OpenDatabase(mPermissionsFile); 975 NS_ENSURE_SUCCESS(rv, rv); 976 LogToConsole(u"OpenDatabase to permissions.sqlite is successful!"_ns); 977 978 data->mDBConn->GetConnectionReady(&ready); 979 if (!ready) return NS_ERROR_UNEXPECTED; 980 } 981 982 bool tableExists = false; 983 data->mDBConn->TableExists("moz_perms"_ns, &tableExists); 984 if (!tableExists) { 985 data->mDBConn->TableExists("moz_hosts"_ns, &tableExists); 986 } 987 if (!tableExists) { 988 rv = CreateTable(); 989 NS_ENSURE_SUCCESS(rv, rv); 990 } else { 991 // table already exists; check the schema version before reading 992 int32_t dbSchemaVersion; 993 rv = data->mDBConn->GetSchemaVersion(&dbSchemaVersion); 994 NS_ENSURE_SUCCESS(rv, rv); 995 996 switch (dbSchemaVersion) { 997 // upgrading. 998 // every time you increment the database schema, you need to 999 // implement the upgrading code from the previous version to the 1000 // new one. fall through to current version 1001 1002 case 1: { 1003 // previous non-expiry version of database. Upgrade it by adding 1004 // the expiration columns 1005 rv = data->mDBConn->ExecuteSimpleSQL( 1006 "ALTER TABLE moz_hosts ADD expireType INTEGER"_ns); 1007 NS_ENSURE_SUCCESS(rv, rv); 1008 1009 rv = data->mDBConn->ExecuteSimpleSQL( 1010 "ALTER TABLE moz_hosts ADD expireTime INTEGER"_ns); 1011 NS_ENSURE_SUCCESS(rv, rv); 1012 } 1013 1014 // fall through to the next upgrade 1015 [[fallthrough]]; 1016 1017 // TODO: we want to make default version as version 2 in order to 1018 // fix bug 784875. 1019 case 0: 1020 case 2: { 1021 // Add appId/isInBrowserElement fields. 1022 rv = data->mDBConn->ExecuteSimpleSQL( 1023 "ALTER TABLE moz_hosts ADD appId INTEGER"_ns); 1024 NS_ENSURE_SUCCESS(rv, rv); 1025 1026 rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( 1027 "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER")); 1028 NS_ENSURE_SUCCESS(rv, rv); 1029 1030 rv = data->mDBConn->SetSchemaVersion(3); 1031 NS_ENSURE_SUCCESS(rv, rv); 1032 } 1033 1034 // fall through to the next upgrade 1035 [[fallthrough]]; 1036 1037 // Version 3->4 is the creation of the modificationTime field. 1038 case 3: { 1039 rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( 1040 "ALTER TABLE moz_hosts ADD modificationTime INTEGER")); 1041 NS_ENSURE_SUCCESS(rv, rv); 1042 1043 // We leave the modificationTime at zero for all existing records; 1044 // using now() would mean, eg, that doing "remove all from the 1045 // last hour" within the first hour after migration would remove 1046 // all permissions. 1047 1048 rv = data->mDBConn->SetSchemaVersion(4); 1049 NS_ENSURE_SUCCESS(rv, rv); 1050 } 1051 1052 // fall through to the next upgrade 1053 [[fallthrough]]; 1054 1055 // In version 5, host appId, and isInBrowserElement were merged into 1056 // a single origin entry 1057 // 1058 // In version 6, the tables were renamed for backwards compatability 1059 // reasons with version 4 and earlier. 1060 // 1061 // In version 7, a bug in the migration used for version 4->5 was 1062 // discovered which could have triggered data-loss. Because of that, 1063 // all users with a version 4, 5, or 6 database will be re-migrated 1064 // from the backup database. (bug 1186034). This migration bug is 1065 // not present after bug 1185340, and the re-migration ensures that 1066 // all users have the fix. 1067 case 5: 1068 // This branch could also be reached via dbSchemaVersion == 3, in 1069 // which case we want to fall through to the dbSchemaVersion == 4 1070 // case. The easiest way to do that is to perform this extra check 1071 // here to make sure that we didn't get here via a fallthrough 1072 // from v3 1073 if (dbSchemaVersion == 5) { 1074 // In version 5, the backup database is named moz_hosts_v4. We 1075 // perform the version 5->6 migration to get the tables to have 1076 // consistent naming conventions. 1077 1078 // Version 5->6 is the renaming of moz_hosts to moz_perms, and 1079 // moz_hosts_v4 to moz_hosts (bug 1185343) 1080 // 1081 // In version 5, we performed the modifications to the 1082 // permissions database in place, this meant that if you 1083 // upgraded to a version which used V5, and then downgraded to a 1084 // version which used v4 or earlier, the fallback path would 1085 // drop the table, and your permissions data would be lost. This 1086 // migration undoes that mistake, by restoring the old moz_hosts 1087 // table (if it was present), and instead using the new table 1088 // moz_perms for the new permissions schema. 1089 // 1090 // NOTE: If you downgrade, store new permissions, and then 1091 // upgrade again, these new permissions won't be migrated or 1092 // reflected in the updated database. This migration only occurs 1093 // once, as if moz_perms exists, it will skip creating it. In 1094 // addition, permissions added after the migration will not be 1095 // visible in previous versions of firefox. 1096 1097 bool permsTableExists = false; 1098 data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists); 1099 if (!permsTableExists) { 1100 // Move the upgraded database to moz_perms 1101 rv = data->mDBConn->ExecuteSimpleSQL( 1102 "ALTER TABLE moz_hosts RENAME TO moz_perms"_ns); 1103 NS_ENSURE_SUCCESS(rv, rv); 1104 } else { 1105 NS_WARNING( 1106 "moz_hosts was not renamed to moz_perms, " 1107 "as a moz_perms table already exists"); 1108 1109 // In the situation where a moz_perms table already exists, 1110 // but the schema is lower than 6, a migration has already 1111 // previously occured to V6, but a downgrade has caused the 1112 // moz_hosts table to be dropped. This should only occur in 1113 // the case of a downgrade to a V5 database, which was only 1114 // present in a few day's nightlies. As that version was 1115 // likely used only on a temporary basis, we assume that the 1116 // database from the previous V6 has the permissions which the 1117 // user actually wants to use. We have to get rid of moz_hosts 1118 // such that moz_hosts_v4 can be moved into its place if it 1119 // exists. 1120 rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_hosts"_ns); 1121 NS_ENSURE_SUCCESS(rv, rv); 1122 } 1123 1124 #ifdef DEBUG 1125 // The moz_hosts table shouldn't exist anymore 1126 bool hostsTableExists = false; 1127 data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists); 1128 MOZ_ASSERT(!hostsTableExists); 1129 #endif 1130 1131 // Rename moz_hosts_v4 back to it's original location, if it 1132 // exists 1133 bool v4TableExists = false; 1134 data->mDBConn->TableExists("moz_hosts_v4"_ns, &v4TableExists); 1135 if (v4TableExists) { 1136 rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( 1137 "ALTER TABLE moz_hosts_v4 RENAME TO moz_hosts")); 1138 NS_ENSURE_SUCCESS(rv, rv); 1139 } 1140 1141 rv = data->mDBConn->SetSchemaVersion(6); 1142 NS_ENSURE_SUCCESS(rv, rv); 1143 } 1144 1145 // fall through to the next upgrade 1146 [[fallthrough]]; 1147 1148 // At this point, the version 5 table has been migrated to a version 1149 // 6 table We are guaranteed to have at least one of moz_hosts and 1150 // moz_perms. If we have moz_hosts, we will migrate moz_hosts into 1151 // moz_perms (even if we already have a moz_perms, as we need a 1152 // re-migration due to bug 1186034). 1153 // 1154 // After this migration, we are guaranteed to have both a moz_hosts 1155 // (for backwards compatability), and a moz_perms table. The 1156 // moz_hosts table will have a v4 schema, and the moz_perms table 1157 // will have a v6 schema. 1158 case 4: 1159 case 6: { 1160 bool hostsTableExists = false; 1161 data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists); 1162 if (hostsTableExists) { 1163 // Both versions 4 and 6 have a version 4 formatted hosts table 1164 // named moz_hosts. We can migrate this table to our version 7 1165 // table moz_perms. If moz_perms is present, then we can use it 1166 // as a basis for comparison. 1167 1168 rv = data->mDBConn->BeginTransaction(); 1169 NS_ENSURE_SUCCESS(rv, rv); 1170 1171 bool tableExists = false; 1172 data->mDBConn->TableExists("moz_hosts_new"_ns, &tableExists); 1173 if (tableExists) { 1174 NS_WARNING( 1175 "The temporary database moz_hosts_new already exists, " 1176 "dropping " 1177 "it."); 1178 rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_hosts_new"_ns); 1179 NS_ENSURE_SUCCESS(rv, rv); 1180 } 1181 rv = data->mDBConn->ExecuteSimpleSQL( 1182 nsLiteralCString("CREATE TABLE moz_hosts_new (" 1183 " id INTEGER PRIMARY KEY" 1184 ",origin TEXT" 1185 ",type TEXT" 1186 ",permission INTEGER" 1187 ",expireType INTEGER" 1188 ",expireTime INTEGER" 1189 ",modificationTime INTEGER" 1190 ")")); 1191 NS_ENSURE_SUCCESS(rv, rv); 1192 1193 nsCOMPtr<mozIStorageStatement> stmt; 1194 rv = data->mDBConn->CreateStatement( 1195 nsLiteralCString( 1196 "SELECT host, type, permission, expireType, " 1197 "expireTime, " 1198 "modificationTime, isInBrowserElement FROM moz_hosts"), 1199 getter_AddRefs(stmt)); 1200 NS_ENSURE_SUCCESS(rv, rv); 1201 1202 int64_t id = 0; 1203 bool hasResult; 1204 1205 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 1206 MigrationEntry entry; 1207 1208 // Read in the old row 1209 rv = stmt->GetUTF8String(0, entry.mHost); 1210 if (NS_WARN_IF(NS_FAILED(rv))) { 1211 continue; 1212 } 1213 rv = stmt->GetUTF8String(1, entry.mType); 1214 if (NS_WARN_IF(NS_FAILED(rv))) { 1215 continue; 1216 } 1217 1218 entry.mId = id++; 1219 entry.mPermission = stmt->AsInt32(2); 1220 entry.mExpireType = stmt->AsInt32(3); 1221 entry.mExpireTime = stmt->AsInt64(4); 1222 entry.mModificationTime = stmt->AsInt64(5); 1223 1224 mMigrationEntries.AppendElement(entry); 1225 } 1226 1227 // We don't drop the moz_hosts table such that it is available 1228 // for backwards-compatability and for future migrations in case 1229 // of migration errors in the current code. Create a marker 1230 // empty table which will indicate that the moz_hosts table is 1231 // intended to act as a backup. If this table is not present, 1232 // then the moz_hosts table was created as a random empty table. 1233 rv = data->mDBConn->ExecuteSimpleSQL( 1234 nsLiteralCString("CREATE TABLE moz_hosts_is_backup (dummy " 1235 "INTEGER PRIMARY KEY)")); 1236 NS_ENSURE_SUCCESS(rv, rv); 1237 1238 bool permsTableExists = false; 1239 data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists); 1240 if (permsTableExists) { 1241 // The user already had a moz_perms table, and we are 1242 // performing a re-migration. We count the rows in the old 1243 // table for telemetry, and then back up their old database as 1244 // moz_perms_v6 1245 1246 nsCOMPtr<mozIStorageStatement> countStmt; 1247 rv = data->mDBConn->CreateStatement( 1248 "SELECT COUNT(*) FROM moz_perms"_ns, getter_AddRefs(countStmt)); 1249 bool hasResult = false; 1250 if (NS_FAILED(rv) || 1251 NS_FAILED(countStmt->ExecuteStep(&hasResult)) || !hasResult) { 1252 NS_WARNING("Could not count the rows in moz_perms"); 1253 } 1254 1255 // Back up the old moz_perms database as moz_perms_v6 before 1256 // we move the new table into its position 1257 rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( 1258 "ALTER TABLE moz_perms RENAME TO moz_perms_v6")); 1259 NS_ENSURE_SUCCESS(rv, rv); 1260 } 1261 1262 rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( 1263 "ALTER TABLE moz_hosts_new RENAME TO moz_perms")); 1264 NS_ENSURE_SUCCESS(rv, rv); 1265 1266 rv = data->mDBConn->CommitTransaction(); 1267 NS_ENSURE_SUCCESS(rv, rv); 1268 } else { 1269 // We don't have a moz_hosts table, so we create one for 1270 // downgrading purposes. This table is empty. 1271 rv = data->mDBConn->ExecuteSimpleSQL( 1272 nsLiteralCString("CREATE TABLE moz_hosts (" 1273 " id INTEGER PRIMARY KEY" 1274 ",host TEXT" 1275 ",type TEXT" 1276 ",permission INTEGER" 1277 ",expireType INTEGER" 1278 ",expireTime INTEGER" 1279 ",modificationTime INTEGER" 1280 ",appId INTEGER" 1281 ",isInBrowserElement INTEGER" 1282 ")")); 1283 NS_ENSURE_SUCCESS(rv, rv); 1284 1285 // We are guaranteed to have a moz_perms table at this point. 1286 } 1287 1288 #ifdef DEBUG 1289 { 1290 // At this point, both the moz_hosts and moz_perms tables should 1291 // exist 1292 bool hostsTableExists = false; 1293 bool permsTableExists = false; 1294 data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists); 1295 data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists); 1296 MOZ_ASSERT(hostsTableExists && permsTableExists); 1297 } 1298 #endif 1299 1300 rv = data->mDBConn->SetSchemaVersion(7); 1301 NS_ENSURE_SUCCESS(rv, rv); 1302 } 1303 1304 // fall through to the next upgrade 1305 [[fallthrough]]; 1306 1307 // The version 7-8 migration is the re-migration of localhost and 1308 // ip-address entries due to errors in the previous version 7 1309 // migration which caused localhost and ip-address entries to be 1310 // incorrectly discarded. The version 7 migration logic has been 1311 // corrected, and thus this logic only needs to execute if the user 1312 // is currently on version 7. 1313 case 7: { 1314 // This migration will be relatively expensive as we need to 1315 // perform database lookups for each origin which we want to 1316 // insert. Fortunately, it shouldn't be too expensive as we only 1317 // want to insert a small number of entries created for localhost 1318 // or IP addresses. 1319 1320 // We only want to perform the re-migration if moz_hosts is a 1321 // backup 1322 bool hostsIsBackupExists = false; 1323 data->mDBConn->TableExists("moz_hosts_is_backup"_ns, 1324 &hostsIsBackupExists); 1325 1326 // Only perform this migration if the original schema version was 1327 // 7, and the moz_hosts table is a backup. 1328 if (dbSchemaVersion == 7 && hostsIsBackupExists) { 1329 nsCOMPtr<mozIStorageStatement> stmt; 1330 rv = data->mDBConn->CreateStatement( 1331 nsLiteralCString( 1332 "SELECT host, type, permission, expireType, " 1333 "expireTime, " 1334 "modificationTime, isInBrowserElement FROM moz_hosts"), 1335 getter_AddRefs(stmt)); 1336 NS_ENSURE_SUCCESS(rv, rv); 1337 1338 nsCOMPtr<mozIStorageStatement> idStmt; 1339 rv = data->mDBConn->CreateStatement( 1340 "SELECT MAX(id) FROM moz_hosts"_ns, getter_AddRefs(idStmt)); 1341 1342 int64_t id = 0; 1343 bool hasResult = false; 1344 if (NS_SUCCEEDED(rv) && 1345 NS_SUCCEEDED(idStmt->ExecuteStep(&hasResult)) && hasResult) { 1346 id = idStmt->AsInt32(0) + 1; 1347 } 1348 1349 nsCOMPtr<nsIEffectiveTLDService> etld = 1350 mozilla::components::EffectiveTLD::Service(); 1351 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 1352 MigrationEntry entry; 1353 1354 // Read in the old row 1355 rv = stmt->GetUTF8String(0, entry.mHost); 1356 if (NS_WARN_IF(NS_FAILED(rv))) { 1357 continue; 1358 } 1359 1360 nsAutoCString eTLD1; 1361 rv = etld->GetBaseDomainFromHost(entry.mHost, 0, eTLD1); 1362 if (NS_SUCCEEDED(rv)) { 1363 // We only care about entries which the tldService can't 1364 // handle 1365 continue; 1366 } 1367 1368 rv = stmt->GetUTF8String(1, entry.mType); 1369 if (NS_WARN_IF(NS_FAILED(rv))) { 1370 continue; 1371 } 1372 1373 entry.mId = id++; 1374 entry.mPermission = stmt->AsInt32(2); 1375 entry.mExpireType = stmt->AsInt32(3); 1376 entry.mExpireTime = stmt->AsInt64(4); 1377 entry.mModificationTime = stmt->AsInt64(5); 1378 1379 mMigrationEntries.AppendElement(entry); 1380 } 1381 } 1382 1383 // Even if we didn't perform the migration, we want to bump the 1384 // schema version to 8. 1385 rv = data->mDBConn->SetSchemaVersion(8); 1386 NS_ENSURE_SUCCESS(rv, rv); 1387 } 1388 1389 // fall through to the next upgrade 1390 [[fallthrough]]; 1391 1392 // The version 8-9 migration removes the unnecessary backup 1393 // moz-hosts database contents. as the data no longer needs to be 1394 // migrated 1395 case 8: { 1396 // We only want to clear out the old table if it is a backup. If 1397 // it isn't a backup, we don't need to touch it. 1398 bool hostsIsBackupExists = false; 1399 data->mDBConn->TableExists("moz_hosts_is_backup"_ns, 1400 &hostsIsBackupExists); 1401 if (hostsIsBackupExists) { 1402 // Delete everything from the backup, we want to keep around the 1403 // table so that you can still downgrade and not break things, 1404 // but we don't need to keep the rows around. 1405 rv = data->mDBConn->ExecuteSimpleSQL("DELETE FROM moz_hosts"_ns); 1406 NS_ENSURE_SUCCESS(rv, rv); 1407 1408 // The table is no longer a backup, so get rid of it. 1409 rv = data->mDBConn->ExecuteSimpleSQL( 1410 "DROP TABLE moz_hosts_is_backup"_ns); 1411 NS_ENSURE_SUCCESS(rv, rv); 1412 } 1413 1414 rv = data->mDBConn->SetSchemaVersion(9); 1415 NS_ENSURE_SUCCESS(rv, rv); 1416 } 1417 1418 // fall through to the next upgrade 1419 [[fallthrough]]; 1420 1421 case 9: { 1422 rv = data->mDBConn->SetSchemaVersion(10); 1423 NS_ENSURE_SUCCESS(rv, rv); 1424 } 1425 1426 // fall through to the next upgrade 1427 [[fallthrough]]; 1428 1429 case 10: { 1430 // Filter out the rows with storage access API permissions with a 1431 // granted origin, and remove the granted origin part from the 1432 // permission type. 1433 rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( 1434 "UPDATE moz_perms " 1435 "SET type=SUBSTR(type, 0, INSTR(SUBSTR(type, INSTR(type, " 1436 "'^') + " 1437 "1), '^') + INSTR(type, '^')) " 1438 "WHERE INSTR(SUBSTR(type, INSTR(type, '^') + 1), '^') AND " 1439 "SUBSTR(type, 0, 18) == \"storageAccessAPI^\";")); 1440 NS_ENSURE_SUCCESS(rv, rv); 1441 1442 rv = data->mDBConn->SetSchemaVersion(11); 1443 NS_ENSURE_SUCCESS(rv, rv); 1444 } 1445 1446 // fall through to the next upgrade 1447 [[fallthrough]]; 1448 1449 case 11: { 1450 // Migrate 3rdPartyStorage keys to a site scope 1451 rv = data->mDBConn->BeginTransaction(); 1452 NS_ENSURE_SUCCESS(rv, rv); 1453 nsCOMPtr<mozIStorageStatement> updateStmt; 1454 rv = data->mDBConn->CreateStatement( 1455 nsLiteralCString("UPDATE moz_perms SET origin = ?2 WHERE id = ?1"), 1456 getter_AddRefs(updateStmt)); 1457 NS_ENSURE_SUCCESS(rv, rv); 1458 1459 nsCOMPtr<mozIStorageStatement> deleteStmt; 1460 rv = data->mDBConn->CreateStatement( 1461 nsLiteralCString("DELETE FROM moz_perms WHERE id = ?1"), 1462 getter_AddRefs(deleteStmt)); 1463 NS_ENSURE_SUCCESS(rv, rv); 1464 1465 nsCOMPtr<mozIStorageStatement> selectStmt; 1466 rv = data->mDBConn->CreateStatement( 1467 nsLiteralCString("SELECT id, origin, type FROM moz_perms WHERE " 1468 " SUBSTR(type, 0, 17) == \"3rdPartyStorage^\""), 1469 getter_AddRefs(selectStmt)); 1470 NS_ENSURE_SUCCESS(rv, rv); 1471 1472 nsTHashSet<nsCStringHashKey> deduplicationSet; 1473 bool hasResult; 1474 nsCOMPtr<nsIEffectiveTLDService> etld = 1475 mozilla::components::EffectiveTLD::Service(); 1476 while (NS_SUCCEEDED(selectStmt->ExecuteStep(&hasResult)) && hasResult) { 1477 int64_t id; 1478 rv = selectStmt->GetInt64(0, &id); 1479 NS_ENSURE_SUCCESS(rv, rv); 1480 1481 nsCString origin; 1482 rv = selectStmt->GetUTF8String(1, origin); 1483 NS_ENSURE_SUCCESS(rv, rv); 1484 1485 nsCString type; 1486 rv = selectStmt->GetUTF8String(2, type); 1487 NS_ENSURE_SUCCESS(rv, rv); 1488 1489 nsCOMPtr<nsIURI> uri; 1490 rv = NS_NewURI(getter_AddRefs(uri), origin); 1491 if (NS_FAILED(rv)) { 1492 continue; 1493 } 1494 nsCString site; 1495 rv = etld->GetSite(uri, site); 1496 if (NS_WARN_IF(NS_FAILED(rv))) { 1497 continue; 1498 } 1499 1500 nsCString deduplicationKey = 1501 nsPrintfCString("%s,%s", site.get(), type.get()); 1502 if (deduplicationSet.Contains(deduplicationKey)) { 1503 rv = deleteStmt->BindInt64ByIndex(0, id); 1504 NS_ENSURE_SUCCESS(rv, rv); 1505 1506 rv = deleteStmt->Execute(); 1507 NS_ENSURE_SUCCESS(rv, rv); 1508 } else { 1509 deduplicationSet.Insert(deduplicationKey); 1510 rv = updateStmt->BindInt64ByIndex(0, id); 1511 NS_ENSURE_SUCCESS(rv, rv); 1512 rv = updateStmt->BindUTF8StringByIndex(1, site); 1513 NS_ENSURE_SUCCESS(rv, rv); 1514 1515 rv = updateStmt->Execute(); 1516 NS_ENSURE_SUCCESS(rv, rv); 1517 } 1518 } 1519 rv = data->mDBConn->CommitTransaction(); 1520 NS_ENSURE_SUCCESS(rv, rv); 1521 1522 rv = data->mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); 1523 NS_ENSURE_SUCCESS(rv, rv); 1524 } 1525 1526 // fall through to the next upgrade 1527 [[fallthrough]]; 1528 1529 // current version. 1530 case HOSTS_SCHEMA_VERSION: 1531 break; 1532 1533 // downgrading. 1534 // if columns have been added to the table, we can still use the 1535 // ones we understand safely. if columns have been deleted or 1536 // altered, just blow away the table and start from scratch! if you 1537 // change the way a column is interpreted, make sure you also change 1538 // its name so this check will catch it. 1539 default: { 1540 // check if all the expected columns exist 1541 nsCOMPtr<mozIStorageStatement> stmt; 1542 rv = data->mDBConn->CreateStatement( 1543 nsLiteralCString("SELECT origin, type, permission, " 1544 "expireType, expireTime, " 1545 "modificationTime FROM moz_perms"), 1546 getter_AddRefs(stmt)); 1547 if (NS_SUCCEEDED(rv)) break; 1548 1549 // our columns aren't there - drop the table! 1550 rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_perms"_ns); 1551 NS_ENSURE_SUCCESS(rv, rv); 1552 1553 rv = CreateTable(); 1554 NS_ENSURE_SUCCESS(rv, rv); 1555 } break; 1556 } 1557 } 1558 1559 // cache frequently used statements (for insertion, deletion, and 1560 // updating) 1561 rv = data->mDBConn->CreateStatement( 1562 nsLiteralCString("INSERT INTO moz_perms " 1563 "(id, origin, type, permission, expireType, " 1564 "expireTime, modificationTime) " 1565 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), 1566 getter_AddRefs(data->mStmtInsert)); 1567 NS_ENSURE_SUCCESS(rv, rv); 1568 1569 rv = data->mDBConn->CreateStatement(nsLiteralCString("DELETE FROM moz_perms " 1570 "WHERE id = ?1"), 1571 getter_AddRefs(data->mStmtDelete)); 1572 NS_ENSURE_SUCCESS(rv, rv); 1573 1574 rv = data->mDBConn->CreateStatement( 1575 nsLiteralCString("UPDATE moz_perms " 1576 "SET permission = ?2, expireType= ?3, expireTime = " 1577 "?4, modificationTime = ?5 WHERE id = ?1"), 1578 getter_AddRefs(data->mStmtUpdate)); 1579 NS_ENSURE_SUCCESS(rv, rv); 1580 1581 // Always import default permissions. 1582 ConsumeDefaultsInputStream(aDefaultsInputStream, aProofOfLock); 1583 1584 // check whether to import or just read in the db 1585 if (tableExists) { 1586 rv = Read(aProofOfLock); 1587 NS_ENSURE_SUCCESS(rv, rv); 1588 } 1589 1590 raiiFailure.release(); 1591 1592 return NS_OK; 1593 } 1594 1595 void PermissionManager::AddIdleDailyMaintenanceJob() { 1596 MOZ_ASSERT(NS_IsMainThread()); 1597 1598 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 1599 NS_ENSURE_TRUE_VOID(observerService); 1600 1601 nsresult rv = 1602 observerService->AddObserver(this, OBSERVER_TOPIC_IDLE_DAILY, false); 1603 NS_ENSURE_SUCCESS_VOID(rv); 1604 } 1605 1606 void PermissionManager::RemoveIdleDailyMaintenanceJob() { 1607 MOZ_ASSERT(NS_IsMainThread()); 1608 1609 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 1610 NS_ENSURE_TRUE_VOID(observerService); 1611 1612 nsresult rv = 1613 observerService->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY); 1614 NS_ENSURE_SUCCESS_VOID(rv); 1615 } 1616 1617 void PermissionManager::PerformIdleDailyMaintenance() { 1618 MOZ_ASSERT(NS_IsMainThread()); 1619 1620 RefPtr<PermissionManager> self = this; 1621 mThread->Dispatch(NS_NewRunnableFunction( 1622 "PermissionManager::PerformIdleDailyMaintenance", [self] { 1623 auto data = self->mThreadBoundData.Access(); 1624 1625 if (self->mState == eClosed || !data->mDBConn) { 1626 return; 1627 } 1628 1629 nsCOMPtr<mozIStorageStatement> stmtDeleteExpired; 1630 nsresult rv = data->mDBConn->CreateStatement( 1631 nsLiteralCString("DELETE FROM moz_perms WHERE expireType = " 1632 "?1 AND expireTime <= ?2"), 1633 getter_AddRefs(stmtDeleteExpired)); 1634 NS_ENSURE_SUCCESS_VOID(rv); 1635 1636 rv = stmtDeleteExpired->BindInt32ByIndex( 1637 0, nsIPermissionManager::EXPIRE_TIME); 1638 NS_ENSURE_SUCCESS_VOID(rv); 1639 1640 rv = stmtDeleteExpired->BindInt64ByIndex(1, EXPIRY_NOW); 1641 NS_ENSURE_SUCCESS_VOID(rv); 1642 1643 rv = stmtDeleteExpired->Execute(); 1644 NS_ENSURE_SUCCESS_VOID(rv); 1645 })); 1646 } 1647 1648 // sets the schema version and creates the moz_perms table. 1649 nsresult PermissionManager::CreateTable() { 1650 MOZ_ASSERT(!NS_IsMainThread()); 1651 auto data = mThreadBoundData.Access(); 1652 1653 // set the schema version, before creating the table 1654 nsresult rv = data->mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); 1655 if (NS_FAILED(rv)) return rv; 1656 1657 // create the table 1658 // SQL also lives in automation.py.in. If you change this SQL change that 1659 // one too 1660 rv = data->mDBConn->ExecuteSimpleSQL( 1661 nsLiteralCString("CREATE TABLE moz_perms (" 1662 " id INTEGER PRIMARY KEY" 1663 ",origin TEXT" 1664 ",type TEXT" 1665 ",permission INTEGER" 1666 ",expireType INTEGER" 1667 ",expireTime INTEGER" 1668 ",modificationTime INTEGER" 1669 ")")); 1670 if (NS_FAILED(rv)) return rv; 1671 1672 // We also create a legacy V4 table, for backwards compatability, 1673 // and to ensure that downgrades don't trigger a schema version change. 1674 return data->mDBConn->ExecuteSimpleSQL( 1675 nsLiteralCString("CREATE TABLE moz_hosts (" 1676 " id INTEGER PRIMARY KEY" 1677 ",host TEXT" 1678 ",type TEXT" 1679 ",permission INTEGER" 1680 ",expireType INTEGER" 1681 ",expireTime INTEGER" 1682 ",modificationTime INTEGER" 1683 ",isInBrowserElement INTEGER" 1684 ")")); 1685 } 1686 1687 // Returns whether the given combination of expire type and expire time are 1688 // expired. Note that EXPIRE_SESSION only honors expireTime if it is nonzero. 1689 bool PermissionManager::HasExpired(uint32_t aExpireType, int64_t aExpireTime) { 1690 return (aExpireType == nsIPermissionManager::EXPIRE_TIME || 1691 (aExpireType == nsIPermissionManager::EXPIRE_SESSION && 1692 aExpireTime != 0)) && 1693 aExpireTime <= EXPIRY_NOW; 1694 } 1695 1696 NS_IMETHODIMP 1697 PermissionManager::AddFromPrincipalAndPersistInPrivateBrowsing( 1698 nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aPermission) { 1699 ENSURE_NOT_CHILD_PROCESS; 1700 1701 bool isValidPermissionPrincipal = false; 1702 nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal, 1703 isValidPermissionPrincipal); 1704 1705 NS_ENSURE_SUCCESS(rv, rv); 1706 if (!isValidPermissionPrincipal) { 1707 // return early if the principal is invalid for permissions 1708 return rv; 1709 } 1710 1711 // A modificationTime of zero will cause AddInternal to use now(). 1712 int64_t modificationTime = 0; 1713 1714 MonitorAutoLock lock{mMonitor}; 1715 1716 return AddInternal(aPrincipal, aType, aPermission, 0, 1717 nsIPermissionManager::EXPIRE_NEVER, 1718 /* aExpireTime */ 0, modificationTime, eNotify, eWriteToDB, 1719 /* aOriginString*/ nullptr, 1720 /* aAllowPersistInPrivateBrowsing */ true); 1721 } 1722 1723 NS_IMETHODIMP 1724 PermissionManager::AddDefaultFromPrincipal(nsIPrincipal* aPrincipal, 1725 const nsACString& aType, 1726 uint32_t aPermission) { 1727 ENSURE_NOT_CHILD_PROCESS; 1728 1729 bool isValidPermissionPrincipal = false; 1730 nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal, 1731 isValidPermissionPrincipal); 1732 NS_ENSURE_SUCCESS(rv, rv); 1733 if (!isValidPermissionPrincipal) { 1734 // return early if the principal is invalid for permissions 1735 return rv; 1736 } 1737 1738 nsCString origin; 1739 rv = GetOriginFromPrincipal(aPrincipal, IsOAForceStripPermission(aType), 1740 origin); 1741 NS_ENSURE_SUCCESS(rv, rv); 1742 1743 MonitorAutoLock lock(mMonitor); 1744 1745 DefaultEntry entry; 1746 { 1747 // Try to update existing entry in mDefaultEntriesForImport, which will 1748 // later be used to restore the default permissions when permissions are 1749 // cleared 1750 bool updatedExistingEntry = false; 1751 nsTArray<DefaultEntry>::iterator defaultEntry = 1752 mDefaultEntriesForImport.begin(); 1753 while (defaultEntry != mDefaultEntriesForImport.end()) { 1754 if (defaultEntry->mType == aType && defaultEntry->mOrigin == origin) { 1755 defaultEntry->mPermission = aPermission; 1756 entry = *defaultEntry; 1757 if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) { 1758 mDefaultEntriesForImport.RemoveElementAt(defaultEntry); 1759 } 1760 updatedExistingEntry = true; 1761 break; 1762 } 1763 ++defaultEntry; 1764 } 1765 1766 // Or add a new entry if there wasn't already one and we aren't deleting the 1767 // default permission 1768 if (!updatedExistingEntry) { 1769 entry.mOrigin = origin; 1770 entry.mPermission = aPermission; 1771 entry.mType = aType; 1772 if (aPermission != nsIPermissionManager::UNKNOWN_ACTION) { 1773 mDefaultEntriesForImport.AppendElement(entry); 1774 } 1775 } 1776 } 1777 1778 // So far, we have only updated mDefaultEntriesForImport for later recovery. 1779 // Now, we actually need to import this change into the permission manager. 1780 return ImportDefaultEntry(entry); 1781 } 1782 1783 NS_IMETHODIMP 1784 PermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal, 1785 const nsACString& aType, 1786 uint32_t aPermission, uint32_t aExpireType, 1787 int64_t aExpireTime) { 1788 ENSURE_NOT_CHILD_PROCESS; 1789 NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER || 1790 aExpireType == nsIPermissionManager::EXPIRE_TIME || 1791 aExpireType == nsIPermissionManager::EXPIRE_SESSION || 1792 aExpireType == nsIPermissionManager::EXPIRE_POLICY, 1793 NS_ERROR_INVALID_ARG); 1794 1795 // Skip addition if the permission is already expired. 1796 if (HasExpired(aExpireType, aExpireTime)) { 1797 return NS_OK; 1798 } 1799 1800 bool isValidPermissionPrincipal = false; 1801 nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal, 1802 isValidPermissionPrincipal); 1803 1804 NS_ENSURE_SUCCESS(rv, rv); 1805 if (!isValidPermissionPrincipal) { 1806 // return early if the principal is invalid for permissions 1807 return rv; 1808 } 1809 1810 // A modificationTime of zero will cause AddInternal to use now(). 1811 int64_t modificationTime = 0; 1812 MonitorAutoLock lock{mMonitor}; 1813 return AddInternal(aPrincipal, aType, aPermission, 0, aExpireType, 1814 aExpireTime, modificationTime, eNotify, eWriteToDB); 1815 } 1816 1817 NS_IMETHODIMP 1818 PermissionManager::TestAddFromPrincipalByTime(nsIPrincipal* aPrincipal, 1819 const nsACString& aType, 1820 uint32_t aPermission, 1821 int64_t aModificationTime) { 1822 ENSURE_NOT_CHILD_PROCESS; 1823 1824 bool isValidPermissionPrincipal = false; 1825 nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal, 1826 isValidPermissionPrincipal); 1827 1828 NS_ENSURE_SUCCESS(rv, rv); 1829 if (!isValidPermissionPrincipal) { 1830 // return early if the principal is invalid for permissions 1831 return rv; 1832 } 1833 1834 MonitorAutoLock lock{mMonitor}; 1835 return AddInternal(aPrincipal, aType, aPermission, 0, 1836 nsIPermissionManager::EXPIRE_NEVER, 0, aModificationTime, 1837 eNotify, eWriteToDB); 1838 } 1839 1840 nsresult PermissionManager::Add(nsIPrincipal* aPrincipal, 1841 const nsACString& aType, uint32_t aPermission, 1842 int64_t aID, uint32_t aExpireType, 1843 int64_t aExpireTime, int64_t aModificationTime, 1844 NotifyOperationType aNotifyOperation, 1845 DBOperationType aDBOperation, 1846 const nsACString* aOriginString, 1847 const bool aAllowPersistInPrivateBrowsing) { 1848 MOZ_ASSERT(IsChildProcess()); 1849 1850 MonitorAutoLock lock{mMonitor}; 1851 return AddInternal(aPrincipal, aType, aPermission, aID, aExpireType, 1852 aExpireTime, aModificationTime, aNotifyOperation, 1853 aDBOperation, aOriginString, 1854 aAllowPersistInPrivateBrowsing); 1855 } 1856 1857 nsresult PermissionManager::AddInternal( 1858 nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aPermission, 1859 int64_t aID, uint32_t aExpireType, int64_t aExpireTime, 1860 int64_t aModificationTime, NotifyOperationType aNotifyOperation, 1861 DBOperationType aDBOperation, const nsACString* aOriginString, 1862 const bool aAllowPersistInPrivateBrowsing) { 1863 MOZ_ASSERT_IF(!IsChildProcess(), NS_IsMainThread()); 1864 1865 // If this is a default permission, no changes should not be written to disk. 1866 MOZ_ASSERT((aID != cIDPermissionIsDefault) || (aDBOperation != eWriteToDB)); 1867 1868 EnsureReadCompleted(); 1869 1870 nsresult rv = NS_OK; 1871 nsAutoCString origin; 1872 // Only attempt to compute the origin string when it is going to be needed 1873 // later on in the function. 1874 if (!IsChildProcess() || 1875 (aDBOperation == eWriteToDB && IsPersistentExpire(aExpireType, aType))) { 1876 if (aOriginString) { 1877 // Use the origin string provided by the caller. 1878 origin = *aOriginString; 1879 } else { 1880 if (IsSiteScopedPermission(aType)) { 1881 rv = GetSiteFromPrincipal(aPrincipal, IsOAForceStripPermission(aType), 1882 origin); 1883 } else { 1884 // Compute it from the principal provided. 1885 rv = GetOriginFromPrincipal(aPrincipal, IsOAForceStripPermission(aType), 1886 origin); 1887 } 1888 NS_ENSURE_SUCCESS(rv, rv); 1889 } 1890 } 1891 1892 // Unless the caller sets aAllowPersistInPrivateBrowsing, only store 1893 // permissions for the session in Private Browsing. Except for default 1894 // permissions which are stored in-memory only and imported each startup. We 1895 // also allow setting persistent UKNOWN_ACTION, to support removing default 1896 // private browsing permissions. 1897 if (!aAllowPersistInPrivateBrowsing && aID != cIDPermissionIsDefault && 1898 aPermission != UNKNOWN_ACTION && aExpireType != EXPIRE_SESSION) { 1899 uint32_t privateBrowsingId = 1900 nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID; 1901 nsresult rv = aPrincipal->GetPrivateBrowsingId(&privateBrowsingId); 1902 if (NS_SUCCEEDED(rv) && 1903 privateBrowsingId != 1904 nsScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID) { 1905 aExpireType = EXPIRE_SESSION; 1906 } 1907 } 1908 1909 // Let's send the new permission to the content process only if it has to be 1910 // notified. 1911 if (!IsChildProcess() && aNotifyOperation == eNotify) { 1912 IPC::Permission permission(origin, aType, aPermission, aExpireType, 1913 aExpireTime); 1914 1915 nsAutoCString permissionKey; 1916 GetKeyForPermission(aPrincipal, aType, permissionKey); 1917 bool isSecondaryKeyed; 1918 nsAutoCString secondaryKey; 1919 isSecondaryKeyed = GetSecondaryKey(aType, secondaryKey); 1920 if (isSecondaryKeyed) { 1921 NotifySecondaryKeyPermissionUpdateInContentProcess( 1922 aType, aPermission, secondaryKey, aPrincipal); 1923 } 1924 1925 nsTArray<ContentParent*> cplist; 1926 ContentParent::GetAll(cplist); 1927 for (uint32_t i = 0; i < cplist.Length(); ++i) { 1928 ContentParent* cp = cplist[i]; 1929 if (cp->NeedsPermissionsUpdate(permissionKey)) { 1930 (void)cp->SendAddPermission(permission); 1931 } 1932 } 1933 } 1934 1935 MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, aType)); 1936 1937 // look up the type index 1938 int32_t typeIndex = GetTypeIndex(aType, true); 1939 NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY); 1940 1941 // When an entry already exists, PutEntry will return that, instead 1942 // of adding a new one 1943 RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal( 1944 aPrincipal, IsOAForceStripPermission(aType), 1945 IsSiteScopedPermission(aType), rv); 1946 if (!key) { 1947 MOZ_ASSERT(NS_FAILED(rv)); 1948 return rv; 1949 } 1950 1951 PermissionHashKey* entry = mPermissionTable.PutEntry(key); 1952 if (!entry) return NS_ERROR_FAILURE; 1953 if (!entry->GetKey()) { 1954 mPermissionTable.RemoveEntry(entry); 1955 return NS_ERROR_OUT_OF_MEMORY; 1956 } 1957 1958 // figure out the transaction type, and get any existing permission value 1959 OperationType op; 1960 int32_t index = entry->GetPermissionIndex(typeIndex); 1961 if (index == -1) { 1962 if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) { 1963 op = eOperationNone; 1964 } else { 1965 op = eOperationAdding; 1966 } 1967 1968 } else { 1969 PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; 1970 1971 // remove the permission if the permission is UNKNOWN, update the 1972 // permission if its value or expire type have changed OR if the time has 1973 // changed and the expire type is time, otherwise, don't modify. There's 1974 // no need to modify a permission that doesn't expire with time when the 1975 // only thing changed is the expire time. 1976 if (aPermission == oldPermissionEntry.mPermission && 1977 aExpireType == oldPermissionEntry.mExpireType && 1978 (aExpireType == nsIPermissionManager::EXPIRE_NEVER || 1979 aExpireTime == oldPermissionEntry.mExpireTime)) { 1980 op = eOperationNone; 1981 } else if (oldPermissionEntry.mID == cIDPermissionIsDefault && 1982 aID != cIDPermissionIsDefault) { 1983 // An existing default permission already exists, but the new permission 1984 // isn't a default permission. This case requires some special handing. 1985 op = eOperationReplacingDefault; 1986 } else if (oldPermissionEntry.mID != cIDPermissionIsDefault && 1987 aID == cIDPermissionIsDefault) { 1988 // We are adding a default permission but a "real" permission already 1989 // exists. This means we don't have to do anything here. 1990 op = eOperationNone; 1991 } else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) { 1992 // At this point, both the old and new permission are either both default 1993 // permissions, or both not default permissions. Now we only need to check 1994 // wether to change or remove the old permission. 1995 op = eOperationRemoving; 1996 } else { 1997 op = eOperationChanging; 1998 } 1999 } 2000 2001 // child processes should *always* be passed a modificationTime of zero. 2002 MOZ_ASSERT(!IsChildProcess() || aModificationTime == 0); 2003 2004 // do the work for adding, deleting, or changing a permission: 2005 // update the in-memory list, write to the db, and notify consumers. 2006 int64_t id; 2007 if (aModificationTime == 0) { 2008 aModificationTime = EXPIRY_NOW; 2009 } 2010 2011 switch (op) { 2012 case eOperationNone: { 2013 // nothing to do 2014 return NS_OK; 2015 } 2016 2017 case eOperationAdding: { 2018 if (aDBOperation == eWriteToDB) { 2019 // we'll be writing to the database - generate a known unique id 2020 id = ++mLargestID; 2021 } else { 2022 // we're reading from the database - use the id already assigned 2023 id = aID; 2024 } 2025 2026 entry->GetPermissions().AppendElement( 2027 PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime, 2028 aModificationTime)); 2029 2030 if (aDBOperation == eWriteToDB && 2031 IsPersistentExpire(aExpireType, aType)) { 2032 UpdateDB(op, id, origin, aType, aPermission, aExpireType, aExpireTime, 2033 aModificationTime); 2034 } 2035 2036 if (aNotifyOperation == eNotify) { 2037 NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex], 2038 aPermission, aExpireType, aExpireTime, 2039 aModificationTime, u"added"_ns); 2040 } 2041 2042 break; 2043 } 2044 2045 case eOperationRemoving: { 2046 PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; 2047 id = oldPermissionEntry.mID; 2048 2049 // If the type we want to remove is EXPIRE_POLICY, we need to reject 2050 // attempts to change the permission. 2051 if (entry->GetPermissions()[index].mExpireType == EXPIRE_POLICY) { 2052 NS_WARNING("Attempting to remove EXPIRE_POLICY permission"); 2053 break; 2054 } 2055 2056 entry->GetPermissions().RemoveElementAt(index); 2057 2058 if (aDBOperation == eWriteToDB) { 2059 // We care only about the id here so we pass dummy values for all other 2060 // parameters. 2061 UpdateDB(op, id, ""_ns, ""_ns, 0, nsIPermissionManager::EXPIRE_NEVER, 0, 2062 0); 2063 } 2064 2065 if (aNotifyOperation == eNotify) { 2066 NotifyObserversWithPermission( 2067 aPrincipal, mTypeArray[typeIndex], oldPermissionEntry.mPermission, 2068 oldPermissionEntry.mExpireType, oldPermissionEntry.mExpireTime, 2069 oldPermissionEntry.mModificationTime, u"deleted"_ns); 2070 } 2071 2072 // If there are no more permissions stored for that entry, clear it. 2073 if (entry->GetPermissions().IsEmpty()) { 2074 mPermissionTable.RemoveEntry(entry); 2075 } 2076 2077 // If the entry we are removing is not a default, restore the potential 2078 // default entry in-memory 2079 if (oldPermissionEntry.mID != cIDPermissionIsDefault) { 2080 for (const DefaultEntry& defaultEntry : mDefaultEntriesForImport) { 2081 if (defaultEntry.mType == aType && defaultEntry.mOrigin == origin && 2082 defaultEntry.mPermission != 2083 nsIPermissionManager::UNKNOWN_ACTION) { 2084 rv = ImportDefaultEntry(defaultEntry); 2085 NS_ENSURE_SUCCESS(rv, rv); 2086 break; 2087 } 2088 } 2089 } 2090 2091 break; 2092 } 2093 2094 case eOperationChanging: { 2095 id = entry->GetPermissions()[index].mID; 2096 2097 // If the existing type is EXPIRE_POLICY, we need to reject attempts to 2098 // change the permission. 2099 if (entry->GetPermissions()[index].mExpireType == EXPIRE_POLICY) { 2100 NS_WARNING("Attempting to modify EXPIRE_POLICY permission"); 2101 break; 2102 } 2103 2104 PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; 2105 2106 // If the new expireType is EXPIRE_SESSION, then we have to keep a 2107 // copy of the previous permission/expireType values. This cached value 2108 // will be used when restoring the permissions of an app. 2109 if (entry->GetPermissions()[index].mExpireType != 2110 nsIPermissionManager::EXPIRE_SESSION && 2111 aExpireType == nsIPermissionManager::EXPIRE_SESSION) { 2112 entry->GetPermissions()[index].mNonSessionPermission = 2113 entry->GetPermissions()[index].mPermission; 2114 entry->GetPermissions()[index].mNonSessionExpireType = 2115 entry->GetPermissions()[index].mExpireType; 2116 entry->GetPermissions()[index].mNonSessionExpireTime = 2117 entry->GetPermissions()[index].mExpireTime; 2118 } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) { 2119 entry->GetPermissions()[index].mNonSessionPermission = aPermission; 2120 entry->GetPermissions()[index].mNonSessionExpireType = aExpireType; 2121 entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime; 2122 } 2123 2124 entry->GetPermissions()[index].mPermission = aPermission; 2125 entry->GetPermissions()[index].mExpireType = aExpireType; 2126 entry->GetPermissions()[index].mExpireTime = aExpireTime; 2127 entry->GetPermissions()[index].mModificationTime = aModificationTime; 2128 2129 if (aDBOperation == eWriteToDB) { 2130 bool newIsPersistentExpire = IsPersistentExpire(aExpireType, aType); 2131 bool oldIsPersistentExpire = 2132 IsPersistentExpire(oldPermissionEntry.mExpireType, aType); 2133 2134 if (!newIsPersistentExpire && oldIsPersistentExpire) { 2135 // Maybe we have to remove the previous permission if that was 2136 // persistent. 2137 UpdateDB(eOperationRemoving, id, ""_ns, ""_ns, 0, 2138 nsIPermissionManager::EXPIRE_NEVER, 0, 0); 2139 } else if (newIsPersistentExpire && !oldIsPersistentExpire) { 2140 // It could also be that the previous permission was session-only but 2141 // this needs to be written into the DB. In this case, we have to run 2142 // an Adding operation. 2143 UpdateDB(eOperationAdding, id, origin, aType, aPermission, 2144 aExpireType, aExpireTime, aModificationTime); 2145 } else if (newIsPersistentExpire) { 2146 // This is the a simple update. We care only about the id, the 2147 // permission and expireType/expireTime/modificationTime here. We pass 2148 // dummy values for all other parameters. 2149 UpdateDB(op, id, ""_ns, ""_ns, aPermission, aExpireType, aExpireTime, 2150 aModificationTime); 2151 } 2152 } 2153 2154 if (aNotifyOperation == eNotify) { 2155 NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex], 2156 aPermission, aExpireType, aExpireTime, 2157 aModificationTime, u"changed"_ns); 2158 } 2159 2160 break; 2161 } 2162 case eOperationReplacingDefault: { 2163 // this is handling the case when we have an existing permission 2164 // entry that was created as a "default" (and thus isn't in the DB) with 2165 // an explicit permission (that may include UNKNOWN_ACTION.) 2166 // Note we will *not* get here if we are replacing an already replaced 2167 // default value - that is handled as eOperationChanging. 2168 2169 // So this is a hybrid of eOperationAdding (as we are writing a new entry 2170 // to the DB) and eOperationChanging (as we are replacing the in-memory 2171 // repr and sending a "changed" notification). 2172 2173 // We want a new ID even if not writing to the DB, so the modified entry 2174 // in memory doesn't have the magic cIDPermissionIsDefault value. 2175 id = ++mLargestID; 2176 2177 // The default permission being replaced can't have session expiry or 2178 // policy expiry. 2179 NS_ENSURE_TRUE(entry->GetPermissions()[index].mExpireType != 2180 nsIPermissionManager::EXPIRE_SESSION, 2181 NS_ERROR_UNEXPECTED); 2182 NS_ENSURE_TRUE(entry->GetPermissions()[index].mExpireType != 2183 nsIPermissionManager::EXPIRE_POLICY, 2184 NS_ERROR_UNEXPECTED); 2185 // We don't support the new entry having any expiry - supporting that 2186 // would make things far more complex and none of the permissions we set 2187 // as a default support that. 2188 NS_ENSURE_TRUE(aExpireType == EXPIRE_NEVER, NS_ERROR_UNEXPECTED); 2189 2190 // update the existing entry in memory. 2191 entry->GetPermissions()[index].mID = id; 2192 entry->GetPermissions()[index].mPermission = aPermission; 2193 entry->GetPermissions()[index].mExpireType = aExpireType; 2194 entry->GetPermissions()[index].mExpireTime = aExpireTime; 2195 entry->GetPermissions()[index].mModificationTime = aModificationTime; 2196 2197 // If requested, create the entry in the DB. 2198 if (aDBOperation == eWriteToDB && 2199 IsPersistentExpire(aExpireType, aType)) { 2200 UpdateDB(eOperationAdding, id, origin, aType, aPermission, aExpireType, 2201 aExpireTime, aModificationTime); 2202 } 2203 2204 if (aNotifyOperation == eNotify) { 2205 NotifyObserversWithPermission(aPrincipal, mTypeArray[typeIndex], 2206 aPermission, aExpireType, aExpireTime, 2207 aModificationTime, u"changed"_ns); 2208 } 2209 2210 } break; 2211 } 2212 2213 return NS_OK; 2214 } 2215 2216 NS_IMETHODIMP 2217 PermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal, 2218 const nsACString& aType) { 2219 ENSURE_NOT_CHILD_PROCESS; 2220 2221 MonitorAutoLock lock{mMonitor}; 2222 return RemoveFromPrincipalInternal(aPrincipal, aType); 2223 } 2224 2225 nsresult PermissionManager::RemoveFromPrincipalInternal( 2226 nsIPrincipal* aPrincipal, const nsACString& aType) { 2227 ENSURE_NOT_CHILD_PROCESS; 2228 NS_ENSURE_ARG_POINTER(aPrincipal); 2229 2230 // System principals are never added to the database, no need to remove them. 2231 if (aPrincipal->IsSystemPrincipal()) { 2232 return NS_OK; 2233 } 2234 2235 // Permissions may not be added to expanded principals. 2236 if (IsExpandedPrincipal(aPrincipal)) { 2237 return NS_ERROR_INVALID_ARG; 2238 } 2239 2240 // AddInternal() handles removal, just let it do the work 2241 return AddInternal(aPrincipal, aType, nsIPermissionManager::UNKNOWN_ACTION, 0, 2242 nsIPermissionManager::EXPIRE_NEVER, 0, 0, eNotify, 2243 eWriteToDB); 2244 } 2245 2246 NS_IMETHODIMP 2247 PermissionManager::RemovePermission(nsIPermission* aPerm) { 2248 if (!aPerm) { 2249 return NS_OK; 2250 } 2251 nsCOMPtr<nsIPrincipal> principal; 2252 nsresult rv = aPerm->GetPrincipal(getter_AddRefs(principal)); 2253 NS_ENSURE_SUCCESS(rv, rv); 2254 2255 nsAutoCString type; 2256 rv = aPerm->GetType(type); 2257 NS_ENSURE_SUCCESS(rv, rv); 2258 2259 MonitorAutoLock lock{mMonitor}; 2260 2261 // Permissions are uniquely identified by their principal and type. 2262 // We remove the permission using these two pieces of data. 2263 return RemoveFromPrincipalInternal(principal, type); 2264 } 2265 2266 NS_IMETHODIMP 2267 PermissionManager::RemoveAll() { 2268 ENSURE_NOT_CHILD_PROCESS; 2269 2270 MonitorAutoLock lock{mMonitor}; 2271 return RemoveAllInternal(true); 2272 } 2273 2274 NS_IMETHODIMP 2275 PermissionManager::RemoveAllSince(int64_t aSince) { 2276 ENSURE_NOT_CHILD_PROCESS; 2277 MonitorAutoLock lock{mMonitor}; 2278 return RemoveAllModifiedSince(aSince); 2279 } 2280 2281 NS_IMETHODIMP 2282 PermissionManager::RemoveAllExceptTypes( 2283 const nsTArray<nsCString>& aTypeExceptions) { 2284 ENSURE_NOT_CHILD_PROCESS; 2285 2286 MonitorAutoLock lock{mMonitor}; 2287 // Need to make sure read is done before we get the type index. Type indexes 2288 // are populated from DB. 2289 EnsureReadCompleted(); 2290 2291 if (aTypeExceptions.IsEmpty()) { 2292 return RemoveAllInternal(true); 2293 } 2294 2295 return RemovePermissionEntries( 2296 [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) { 2297 return !aTypeExceptions.Contains(mTypeArray[aPermEntry.mType]); 2298 }); 2299 } 2300 2301 nsresult PermissionManager::RemovePermissionEntries( 2302 const std::function<bool(const PermissionEntry& aPermEntry, 2303 const nsCOMPtr<nsIPrincipal>& aPrincipal)>& 2304 aCondition, 2305 bool aComputePrincipalForCondition) { 2306 EnsureReadCompleted(); 2307 2308 Vector<std::tuple<nsCOMPtr<nsIPrincipal>, nsCString, nsCString>, 10> array; 2309 for (const PermissionHashKey& entry : mPermissionTable) { 2310 for (const auto& permEntry : entry.GetPermissions()) { 2311 // Depending on whether the principal is needed in the condition check, we 2312 // may already check the condition here, to avoid needing to compute the 2313 // principal if the condition is true. 2314 if (!aComputePrincipalForCondition && !aCondition(permEntry, nullptr)) { 2315 continue; 2316 } 2317 2318 nsCOMPtr<nsIPrincipal> principal; 2319 nsresult rv = GetPrincipalFromOrigin( 2320 entry.GetKey()->mOrigin, 2321 IsOAForceStripPermission(mTypeArray[permEntry.mType]), 2322 getter_AddRefs(principal)); 2323 if (NS_FAILED(rv)) { 2324 continue; 2325 } 2326 2327 if (aComputePrincipalForCondition && !aCondition(permEntry, principal)) { 2328 continue; 2329 } 2330 2331 if (!array.emplaceBack(principal, mTypeArray[permEntry.mType], 2332 entry.GetKey()->mOrigin)) { 2333 continue; 2334 } 2335 } 2336 } 2337 2338 for (auto& i : array) { 2339 // AddInternal handles removal, so let it do the work... 2340 AddInternal( 2341 std::get<0>(i), std::get<1>(i), nsIPermissionManager::UNKNOWN_ACTION, 0, 2342 nsIPermissionManager::EXPIRE_NEVER, 0, 0, PermissionManager::eNotify, 2343 PermissionManager::eWriteToDB, &std::get<2>(i)); 2344 } 2345 2346 return NS_OK; 2347 } 2348 2349 nsresult PermissionManager::RemovePermissionEntries( 2350 const std::function<bool(const PermissionEntry& aPermEntry)>& aCondition) { 2351 return RemovePermissionEntries( 2352 [&](const PermissionEntry& aPermEntry, 2353 const nsCOMPtr<nsIPrincipal>& aPrincipal) { 2354 return aCondition(aPermEntry); 2355 }, 2356 false); 2357 } 2358 2359 NS_IMETHODIMP 2360 PermissionManager::RemoveByType(const nsACString& aType) { 2361 ENSURE_NOT_CHILD_PROCESS; 2362 2363 MonitorAutoLock lock{mMonitor}; 2364 2365 // Need to make sure read is done before we get the type index. Type indexes 2366 // are populated from DB. 2367 EnsureReadCompleted(); 2368 2369 int32_t typeIndex = GetTypeIndex(aType, false); 2370 // If type == -1, the type isn't known, 2371 // so just return NS_OK 2372 if (typeIndex == -1) { 2373 return NS_OK; 2374 } 2375 2376 return RemovePermissionEntries( 2377 [typeIndex](const PermissionEntry& aPermEntry) { 2378 return static_cast<uint32_t>(typeIndex) == aPermEntry.mType; 2379 }); 2380 } 2381 2382 NS_IMETHODIMP 2383 PermissionManager::RemoveByTypeSince(const nsACString& aType, 2384 int64_t aModificationTime) { 2385 ENSURE_NOT_CHILD_PROCESS; 2386 2387 MonitorAutoLock lock{mMonitor}; 2388 2389 // Need to make sure read is done before we get the type index. Type indexes 2390 // are populated from DB. 2391 EnsureReadCompleted(); 2392 2393 int32_t typeIndex = GetTypeIndex(aType, false); 2394 // If type == -1, the type isn't known, 2395 // so just return NS_OK 2396 if (typeIndex == -1) { 2397 return NS_OK; 2398 } 2399 2400 return RemovePermissionEntries( 2401 [typeIndex, aModificationTime](const PermissionEntry& aPermEntry) { 2402 return uint32_t(typeIndex) == aPermEntry.mType && 2403 aModificationTime <= aPermEntry.mModificationTime; 2404 }); 2405 } 2406 2407 NS_IMETHODIMP 2408 PermissionManager::RemoveAllSinceWithTypeExceptions( 2409 int64_t aModificationTime, const nsTArray<nsCString>& aTypeExceptions) { 2410 ENSURE_NOT_CHILD_PROCESS; 2411 2412 MonitorAutoLock lock{mMonitor}; 2413 2414 // Need to make sure read is done before we get the type index. Type indexes 2415 // are populated from DB. 2416 EnsureReadCompleted(); 2417 2418 return RemovePermissionEntries( 2419 [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) { 2420 return !aTypeExceptions.Contains(mTypeArray[aPermEntry.mType]) && 2421 aModificationTime <= aPermEntry.mModificationTime; 2422 }); 2423 } 2424 2425 void PermissionManager::CloseDB(CloseDBNextOp aNextOp) { 2426 EnsureReadCompleted(); 2427 2428 mState = eClosed; 2429 2430 nsCOMPtr<nsIInputStream> defaultsInputStream; 2431 if (aNextOp == eRebuldOnSuccess) { 2432 defaultsInputStream = GetDefaultsInputStream(); 2433 } 2434 2435 RefPtr<PermissionManager> self = this; 2436 mThread->Dispatch(NS_NewRunnableFunction( 2437 "PermissionManager::CloseDB", [self, aNextOp, defaultsInputStream] { 2438 auto data = self->mThreadBoundData.Access(); 2439 // Null the statements, this will finalize them. 2440 data->mStmtInsert = nullptr; 2441 data->mStmtDelete = nullptr; 2442 data->mStmtUpdate = nullptr; 2443 if (data->mDBConn) { 2444 DebugOnly<nsresult> rv = data->mDBConn->Close(); 2445 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2446 data->mDBConn = nullptr; 2447 2448 if (aNextOp == eRebuldOnSuccess) { 2449 MonitorAutoLock lock{self->mMonitor}; 2450 self->TryInitDB(true, defaultsInputStream, lock); 2451 } 2452 } 2453 2454 if (aNextOp == eShutdown) { 2455 NS_DispatchToMainThread( 2456 NS_NewRunnableFunction("PermissionManager::FinishAsyncShutdown", 2457 [self] { self->FinishAsyncShutdown(); })); 2458 } 2459 })); 2460 } 2461 2462 nsresult PermissionManager::RemoveAllFromIPC() { 2463 MOZ_ASSERT(IsChildProcess()); 2464 2465 MonitorAutoLock lock{mMonitor}; 2466 2467 // Remove from memory and notify immediately. Since the in-memory 2468 // database is authoritative, we do not need confirmation from the 2469 // on-disk database to notify observers. 2470 RemoveAllFromMemory(); 2471 2472 return NS_OK; 2473 } 2474 2475 nsresult PermissionManager::RemoveAllInternal(bool aNotifyObservers) { 2476 ENSURE_NOT_CHILD_PROCESS; 2477 2478 EnsureReadCompleted(); 2479 2480 // Let's broadcast the removeAll() to any content process. 2481 nsTArray<ContentParent*> parents; 2482 ContentParent::GetAll(parents); 2483 for (ContentParent* parent : parents) { 2484 (void)parent->SendRemoveAllPermissions(); 2485 } 2486 2487 // Remove from memory and notify immediately. Since the in-memory 2488 // database is authoritative, we do not need confirmation from the 2489 // on-disk database to notify observers. 2490 RemoveAllFromMemory(); 2491 2492 // Re-import the defaults 2493 ImportLatestDefaults(); 2494 2495 if (aNotifyObservers) { 2496 NotifyObservers(nullptr, u"cleared"_ns); 2497 } 2498 2499 RefPtr<PermissionManager> self = this; 2500 mThread->Dispatch( 2501 NS_NewRunnableFunction("PermissionManager::RemoveAllInternal", [self] { 2502 auto data = self->mThreadBoundData.Access(); 2503 2504 if (self->mState == eClosed || !data->mDBConn) { 2505 return; 2506 } 2507 2508 // clear the db 2509 nsresult rv = 2510 data->mDBConn->ExecuteSimpleSQL("DELETE FROM moz_perms"_ns); 2511 if (NS_WARN_IF(NS_FAILED(rv))) { 2512 NS_DispatchToMainThread(NS_NewRunnableFunction( 2513 "PermissionManager::RemoveAllInternal-Failure", [self] { 2514 MonitorAutoLock lock{self->mMonitor}; 2515 self->CloseDB(eRebuldOnSuccess); 2516 })); 2517 } 2518 })); 2519 2520 return NS_OK; 2521 } 2522 2523 NS_IMETHODIMP 2524 PermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal, 2525 const nsACString& aType, 2526 uint32_t* aPermission) { 2527 MonitorAutoLock lock{mMonitor}; 2528 return CommonTestPermission(aPrincipal, -1, aType, aPermission, 2529 nsIPermissionManager::UNKNOWN_ACTION, false, true, 2530 true); 2531 } 2532 2533 NS_IMETHODIMP 2534 PermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal, 2535 const nsACString& aType, 2536 uint32_t* aPermission) { 2537 MonitorAutoLock lock{mMonitor}; 2538 return CommonTestPermission(aPrincipal, -1, aType, aPermission, 2539 nsIPermissionManager::UNKNOWN_ACTION, false, true, 2540 false); 2541 } 2542 2543 NS_IMETHODIMP 2544 PermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal, 2545 const nsACString& aType, 2546 uint32_t* aPermission) { 2547 MonitorAutoLock lock{mMonitor}; 2548 return CommonTestPermission(aPrincipal, -1, aType, aPermission, 2549 nsIPermissionManager::UNKNOWN_ACTION, false, 2550 false, true); 2551 } 2552 2553 NS_IMETHODIMP 2554 PermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal, 2555 const nsACString& aType, 2556 bool aExactHostMatch, 2557 nsIPermission** aResult) { 2558 NS_ENSURE_ARG_POINTER(aPrincipal); 2559 *aResult = nullptr; 2560 2561 MonitorAutoLock lock{mMonitor}; 2562 2563 EnsureReadCompleted(); 2564 2565 if (aPrincipal->IsSystemPrincipal()) { 2566 return NS_OK; 2567 } 2568 2569 // Querying the permission object of an nsEP is non-sensical. 2570 if (IsExpandedPrincipal(aPrincipal)) { 2571 return NS_ERROR_INVALID_ARG; 2572 } 2573 2574 MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, aType)); 2575 2576 int32_t typeIndex = GetTypeIndex(aType, false); 2577 // If type == -1, the type isn't known, 2578 // so just return NS_OK 2579 if (typeIndex == -1) return NS_OK; 2580 2581 PermissionHashKey* entry = 2582 GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch); 2583 if (!entry) { 2584 return NS_OK; 2585 } 2586 2587 // We don't call GetPermission(typeIndex) because that returns a fake 2588 // UNKNOWN_ACTION entry if there is no match. 2589 int32_t idx = entry->GetPermissionIndex(typeIndex); 2590 if (-1 == idx) { 2591 return NS_OK; 2592 } 2593 2594 nsCOMPtr<nsIPrincipal> principal; 2595 nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin, 2596 IsOAForceStripPermission(aType), 2597 getter_AddRefs(principal)); 2598 NS_ENSURE_SUCCESS(rv, rv); 2599 2600 PermissionEntry& perm = entry->GetPermissions()[idx]; 2601 nsCOMPtr<nsIPermission> r = Permission::Create( 2602 principal, mTypeArray[perm.mType], perm.mPermission, perm.mExpireType, 2603 perm.mExpireTime, perm.mModificationTime); 2604 if (NS_WARN_IF(!r)) { 2605 return NS_ERROR_FAILURE; 2606 } 2607 r.forget(aResult); 2608 return NS_OK; 2609 } 2610 2611 nsresult PermissionManager::CommonTestPermissionInternal( 2612 nsIPrincipal* aPrincipal, nsIURI* aURI, 2613 const OriginAttributes* aOriginAttributes, int32_t aTypeIndex, 2614 const nsACString& aType, uint32_t* aPermission, bool aExactHostMatch, 2615 bool aIncludingSession) { 2616 MOZ_ASSERT(aPrincipal || aURI); 2617 NS_ENSURE_ARG_POINTER(aPrincipal || aURI); 2618 MOZ_ASSERT_IF(aPrincipal, !aURI && !aOriginAttributes); 2619 MOZ_ASSERT_IF(aURI || aOriginAttributes, !aPrincipal); 2620 2621 EnsureReadCompleted(); 2622 2623 #ifdef DEBUG 2624 { 2625 nsCOMPtr<nsIPrincipal> prin = aPrincipal; 2626 if (!prin) { 2627 if (aURI) { 2628 prin = BasePrincipal::CreateContentPrincipal(aURI, OriginAttributes()); 2629 } 2630 } 2631 MOZ_ASSERT(prin); 2632 MOZ_ASSERT(PermissionAvailableInternal(prin, aType)); 2633 } 2634 #endif 2635 2636 PermissionHashKey* entry = 2637 aPrincipal ? GetPermissionHashKey(aPrincipal, aTypeIndex, aExactHostMatch) 2638 : GetPermissionHashKey(aURI, aOriginAttributes, aTypeIndex, 2639 aExactHostMatch); 2640 if (!entry || (!aIncludingSession && 2641 entry->GetPermission(aTypeIndex).mNonSessionExpireType == 2642 nsIPermissionManager::EXPIRE_SESSION)) { 2643 return NS_OK; 2644 } 2645 2646 *aPermission = aIncludingSession 2647 ? entry->GetPermission(aTypeIndex).mPermission 2648 : entry->GetPermission(aTypeIndex).mNonSessionPermission; 2649 return NS_OK; 2650 } 2651 2652 nsresult PermissionManager::GetPermissionEntries( 2653 const std::function<bool(const PermissionEntry& aPermEntry)>& aCondition, 2654 nsTArray<RefPtr<nsIPermission>>& aResult) { 2655 aResult.Clear(); 2656 if (XRE_IsContentProcess()) { 2657 NS_WARNING( 2658 "Iterating over all permissions is not available in the " 2659 "content process, as not all permissions may be available."); 2660 return NS_ERROR_NOT_AVAILABLE; 2661 } 2662 2663 EnsureReadCompleted(); 2664 2665 for (const PermissionHashKey& entry : mPermissionTable) { 2666 for (const auto& permEntry : entry.GetPermissions()) { 2667 // Given how "default" permissions work and the possibility of them being 2668 // overridden with UNKNOWN_ACTION, we might see this value here - but we 2669 // do *not* want to return them via the enumerator. 2670 if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { 2671 continue; 2672 } 2673 2674 // If the permission is expired, skip it. We're not deleting it here 2675 // because we're iterating over a lot of permissions. 2676 // It will be removed as part of the daily maintenance later. 2677 if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) { 2678 continue; 2679 } 2680 2681 if (!aCondition(permEntry)) { 2682 continue; 2683 } 2684 2685 nsCOMPtr<nsIPrincipal> principal; 2686 nsresult rv = GetPrincipalFromOrigin( 2687 entry.GetKey()->mOrigin, 2688 IsOAForceStripPermission(mTypeArray[permEntry.mType]), 2689 getter_AddRefs(principal)); 2690 if (NS_FAILED(rv)) { 2691 continue; 2692 } 2693 2694 RefPtr<nsIPermission> permission = Permission::Create( 2695 principal, mTypeArray[permEntry.mType], permEntry.mPermission, 2696 permEntry.mExpireType, permEntry.mExpireTime, 2697 permEntry.mModificationTime); 2698 if (NS_WARN_IF(!permission)) { 2699 continue; 2700 } 2701 aResult.AppendElement(std::move(permission)); 2702 } 2703 } 2704 2705 return NS_OK; 2706 } 2707 2708 NS_IMETHODIMP PermissionManager::GetAll( 2709 nsTArray<RefPtr<nsIPermission>>& aResult) { 2710 MonitorAutoLock lock{mMonitor}; 2711 return GetPermissionEntries( 2712 [](const PermissionEntry& aPermEntry) { return true; }, aResult); 2713 } 2714 2715 NS_IMETHODIMP PermissionManager::GetAllByTypeSince( 2716 const nsACString& aPrefix, int64_t aSince, 2717 nsTArray<RefPtr<nsIPermission>>& aResult) { 2718 // Check that aSince is a reasonable point in time, not in the future 2719 if (aSince > (PR_Now() / PR_USEC_PER_MSEC)) { 2720 return NS_ERROR_INVALID_ARG; 2721 } 2722 2723 MonitorAutoLock lock{mMonitor}; 2724 return GetPermissionEntries( 2725 [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) { 2726 return mTypeArray[aPermEntry.mType].Equals(aPrefix) && 2727 aSince <= aPermEntry.mModificationTime; 2728 }, 2729 aResult); 2730 } 2731 2732 NS_IMETHODIMP PermissionManager::GetAllWithTypePrefix( 2733 const nsACString& aPrefix, nsTArray<RefPtr<nsIPermission>>& aResult) { 2734 MonitorAutoLock lock{mMonitor}; 2735 return GetPermissionEntries( 2736 [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) { 2737 return StringBeginsWith(mTypeArray[aPermEntry.mType], aPrefix); 2738 }, 2739 aResult); 2740 } 2741 2742 NS_IMETHODIMP PermissionManager::GetAllByTypes( 2743 const nsTArray<nsCString>& aTypes, 2744 nsTArray<RefPtr<nsIPermission>>& aResult) { 2745 if (aTypes.IsEmpty()) { 2746 return NS_OK; 2747 } 2748 2749 MonitorAutoLock lock{mMonitor}; 2750 return GetPermissionEntries( 2751 [&](const PermissionEntry& aPermEntry) MOZ_REQUIRES(mMonitor) { 2752 return aTypes.Contains(mTypeArray[aPermEntry.mType]); 2753 }, 2754 aResult); 2755 } 2756 2757 nsresult PermissionManager::ShouldHandlePrincipalForPermission( 2758 nsIPrincipal* aPrincipal, bool& aIsPermissionPrincipalValid) { 2759 NS_ENSURE_ARG_POINTER(aPrincipal); 2760 // We don't add the system principal because it actually has no URI and we 2761 // always allow action for them. 2762 if (aPrincipal->IsSystemPrincipal()) { 2763 aIsPermissionPrincipalValid = false; 2764 return NS_OK; 2765 } 2766 2767 // Null principals can't meaningfully have persisted permissions attached to 2768 // them, so we don't allow adding permissions for them. 2769 if (aPrincipal->GetIsNullPrincipal()) { 2770 aIsPermissionPrincipalValid = false; 2771 return NS_OK; 2772 } 2773 2774 // Permissions may not be added to expanded principals. 2775 if (IsExpandedPrincipal(aPrincipal)) { 2776 aIsPermissionPrincipalValid = false; 2777 return NS_ERROR_INVALID_ARG; 2778 } 2779 2780 // Permission principal is valid 2781 aIsPermissionPrincipalValid = true; 2782 return NS_OK; 2783 } 2784 2785 nsresult PermissionManager::GetAllForPrincipalHelper( 2786 nsIPrincipal* aPrincipal, bool aSiteScopePermissions, 2787 nsTArray<RefPtr<nsIPermission>>& aResult) { 2788 nsresult rv; 2789 RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal( 2790 aPrincipal, false, aSiteScopePermissions, rv); 2791 if (!key) { 2792 MOZ_ASSERT(NS_FAILED(rv)); 2793 return rv; 2794 } 2795 PermissionHashKey* entry = mPermissionTable.GetEntry(key); 2796 2797 nsTArray<PermissionEntry> strippedPerms; 2798 rv = GetStripPermsForPrincipal(aPrincipal, aSiteScopePermissions, 2799 strippedPerms); 2800 if (NS_FAILED(rv)) { 2801 return rv; 2802 } 2803 2804 if (entry) { 2805 for (const auto& permEntry : entry->GetPermissions()) { 2806 // Only return custom permissions 2807 if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { 2808 continue; 2809 } 2810 2811 // If the permission is expired, skip it. We're not deleting it here 2812 // because we're iterating over a lot of permissions. 2813 // It will be removed as part of the daily maintenance later. 2814 if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) { 2815 continue; 2816 } 2817 2818 // Make sure that we only get site scoped permissions if this 2819 // helper is being invoked for that purpose. 2820 if (aSiteScopePermissions != 2821 IsSiteScopedPermission(mTypeArray[permEntry.mType])) { 2822 continue; 2823 } 2824 2825 // Stripped principal permissions overwrite regular ones 2826 // For each permission check if there is a stripped permission we should 2827 // use instead 2828 PermissionEntry perm = permEntry; 2829 nsTArray<PermissionEntry>::index_type index = 0; 2830 for (const auto& strippedPerm : strippedPerms) { 2831 if (strippedPerm.mType == permEntry.mType) { 2832 perm = strippedPerm; 2833 strippedPerms.RemoveElementAt(index); 2834 break; 2835 } 2836 index++; 2837 } 2838 2839 RefPtr<nsIPermission> permission = Permission::Create( 2840 aPrincipal, mTypeArray[perm.mType], perm.mPermission, 2841 perm.mExpireType, perm.mExpireTime, perm.mModificationTime); 2842 if (NS_WARN_IF(!permission)) { 2843 continue; 2844 } 2845 aResult.AppendElement(permission); 2846 } 2847 } 2848 2849 for (const auto& perm : strippedPerms) { 2850 RefPtr<nsIPermission> permission = Permission::Create( 2851 aPrincipal, mTypeArray[perm.mType], perm.mPermission, perm.mExpireType, 2852 perm.mExpireTime, perm.mModificationTime); 2853 if (NS_WARN_IF(!permission)) { 2854 continue; 2855 } 2856 aResult.AppendElement(permission); 2857 } 2858 2859 return NS_OK; 2860 } 2861 2862 NS_IMETHODIMP 2863 PermissionManager::GetAllForPrincipal( 2864 nsIPrincipal* aPrincipal, nsTArray<RefPtr<nsIPermission>>& aResult) { 2865 nsresult rv; 2866 aResult.Clear(); 2867 2868 MonitorAutoLock lock{mMonitor}; 2869 EnsureReadCompleted(); 2870 2871 MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, ""_ns)); 2872 2873 // First, append the non-site-scoped permissions. 2874 rv = GetAllForPrincipalHelper(aPrincipal, false, aResult); 2875 NS_ENSURE_SUCCESS(rv, rv); 2876 2877 // Second, append the site-scoped permissions. 2878 return GetAllForPrincipalHelper(aPrincipal, true, aResult); 2879 } 2880 2881 NS_IMETHODIMP PermissionManager::Observe(nsISupports* aSubject, 2882 const char* aTopic, 2883 const char16_t* someData) { 2884 ENSURE_NOT_CHILD_PROCESS; 2885 2886 MonitorAutoLock lock{mMonitor}; 2887 2888 if (!nsCRT::strcmp(aTopic, "profile-do-change") && !mPermissionsFile) { 2889 // profile startup is complete, and we didn't have the permissions file 2890 // before; init the db from the new location 2891 InitDB(false); 2892 } else if (!nsCRT::strcmp(aTopic, "testonly-reload-permissions-from-disk")) { 2893 // Testing mechanism to reload all permissions from disk. Because the 2894 // permission manager automatically initializes itself at startup, tests 2895 // that directly manipulate the permissions database need some way to reload 2896 // the database for their changes to have any effect. This mechanism was 2897 // introduced when moving the permissions manager from on-demand startup to 2898 // always being initialized. This is not guarded by a pref because it's not 2899 // dangerous to reload permissions from disk, just bad for performance. 2900 RemoveAllFromMemory(); 2901 CloseDB(eNone); 2902 InitDB(false); 2903 } else if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) { 2904 PerformIdleDailyMaintenance(); 2905 } else if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) { 2906 DebugOnly<nsresult> rv = RemoveAllForPrivateBrowsing(); 2907 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 2908 "Failed to clear private browsing permissions"); 2909 } 2910 2911 return NS_OK; 2912 } 2913 2914 nsresult PermissionManager::RemoveAllModifiedSince(int64_t aModificationTime) { 2915 ENSURE_NOT_CHILD_PROCESS; 2916 // Skip remove calls for default permissions to avoid 2917 // creating UNKNOWN_ACTION overrides in AddInternal 2918 return RemovePermissionEntries( 2919 [aModificationTime](const PermissionEntry& aPermEntry) { 2920 return aModificationTime <= aPermEntry.mModificationTime && 2921 aPermEntry.mID != cIDPermissionIsDefault; 2922 }); 2923 } 2924 2925 nsresult PermissionManager::RemoveAllForPrivateBrowsing() { 2926 ENSURE_NOT_CHILD_PROCESS; 2927 return RemovePermissionEntries([](const PermissionEntry& aPermEntry, 2928 const nsCOMPtr<nsIPrincipal>& aPrincipal) { 2929 return aPrincipal->GetIsInPrivateBrowsing() && 2930 aPermEntry.mID != cIDPermissionIsDefault; 2931 }); 2932 } 2933 2934 NS_IMETHODIMP 2935 PermissionManager::RemovePermissionsWithAttributes( 2936 const nsAString& aPattern, const nsTArray<nsCString>& aTypeInclusions, 2937 const nsTArray<nsCString>& aTypeExceptions) { 2938 ENSURE_NOT_CHILD_PROCESS; 2939 2940 OriginAttributesPattern pattern; 2941 if (!pattern.Init(aPattern)) { 2942 return NS_ERROR_INVALID_ARG; 2943 } 2944 2945 MonitorAutoLock lock{mMonitor}; 2946 return RemovePermissionsWithAttributes(pattern, aTypeInclusions, 2947 aTypeExceptions); 2948 } 2949 2950 nsresult PermissionManager::RemovePermissionsWithAttributes( 2951 OriginAttributesPattern& aPattern, 2952 const nsTArray<nsCString>& aTypeInclusions, 2953 const nsTArray<nsCString>& aTypeExceptions) { 2954 EnsureReadCompleted(); 2955 2956 Vector<std::tuple<nsCOMPtr<nsIPrincipal>, nsCString, nsCString>, 10> 2957 permissions; 2958 for (const PermissionHashKey& entry : mPermissionTable) { 2959 nsCOMPtr<nsIPrincipal> principal; 2960 nsresult rv = GetPrincipalFromOrigin(entry.GetKey()->mOrigin, false, 2961 getter_AddRefs(principal)); 2962 if (NS_FAILED(rv)) { 2963 continue; 2964 } 2965 2966 if (!aPattern.Matches(principal->OriginAttributesRef())) { 2967 continue; 2968 } 2969 2970 for (const auto& permEntry : entry.GetPermissions()) { 2971 if (aTypeExceptions.Contains(mTypeArray[permEntry.mType])) { 2972 continue; 2973 } 2974 if (!aTypeInclusions.IsEmpty() && 2975 !aTypeInclusions.Contains(mTypeArray[permEntry.mType])) { 2976 continue; 2977 } 2978 if (!permissions.emplaceBack(principal, mTypeArray[permEntry.mType], 2979 entry.GetKey()->mOrigin)) { 2980 continue; 2981 } 2982 } 2983 } 2984 2985 for (auto& i : permissions) { 2986 AddInternal( 2987 std::get<0>(i), std::get<1>(i), nsIPermissionManager::UNKNOWN_ACTION, 0, 2988 nsIPermissionManager::EXPIRE_NEVER, 0, 0, PermissionManager::eNotify, 2989 PermissionManager::eWriteToDB, &std::get<2>(i)); 2990 } 2991 2992 return NS_OK; 2993 } 2994 2995 nsresult PermissionManager::GetStripPermsForPrincipal( 2996 nsIPrincipal* aPrincipal, bool aSiteScopePermissions, 2997 nsTArray<PermissionEntry>& aResult) { 2998 aResult.Clear(); 2999 aResult.SetCapacity(kStripOAPermissions.size()); 3000 3001 #ifdef __clang__ 3002 # pragma clang diagnostic push 3003 # pragma clang diagnostic ignored "-Wunreachable-code-return" 3004 #endif 3005 // No special strip permissions 3006 if (kStripOAPermissions.empty()) { 3007 return NS_OK; 3008 } 3009 #ifdef __clang__ 3010 # pragma clang diagnostic pop 3011 #endif 3012 3013 nsresult rv; 3014 // Create a key for the principal, but strip any origin attributes. 3015 // The key must be created aware of whether or not we are scoping to site. 3016 RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal( 3017 aPrincipal, true, aSiteScopePermissions, rv); 3018 if (!key) { 3019 MOZ_ASSERT(NS_FAILED(rv)); 3020 return rv; 3021 } 3022 3023 PermissionHashKey* hashKey = mPermissionTable.GetEntry(key); 3024 if (!hashKey) { 3025 return NS_OK; 3026 } 3027 3028 for (const auto& permType : kStripOAPermissions) { 3029 // if the permission type's site scoping does not match this function call, 3030 // we don't care about it, so continue. 3031 // As of time of writing, this never happens when aSiteScopePermissions 3032 // is true because there is no common permission between kStripOAPermissions 3033 // and kSiteScopedPermissions 3034 if (aSiteScopePermissions != IsSiteScopedPermission(permType)) { 3035 continue; 3036 } 3037 int32_t index = GetTypeIndex(permType, false); 3038 if (index == -1) { 3039 continue; 3040 } 3041 PermissionEntry perm = hashKey->GetPermission(index); 3042 if (perm.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { 3043 continue; 3044 } 3045 aResult.AppendElement(perm); 3046 } 3047 3048 return NS_OK; 3049 } 3050 3051 int32_t PermissionManager::GetTypeIndex(const nsACString& aType, bool aAdd) { 3052 for (uint32_t i = 0; i < mTypeArray.length(); ++i) { 3053 if (mTypeArray[i].Equals(aType)) { 3054 return i; 3055 } 3056 } 3057 3058 if (!aAdd) { 3059 // Not found, but that is ok - we were just looking. 3060 return -1; 3061 } 3062 3063 // This type was not registered before. 3064 // append it to the array, without copy-constructing the string 3065 if (!mTypeArray.emplaceBack(aType)) { 3066 return -1; 3067 } 3068 3069 return mTypeArray.length() - 1; 3070 } 3071 3072 PermissionManager::PermissionHashKey* PermissionManager::GetPermissionHashKey( 3073 nsIPrincipal* aPrincipal, uint32_t aType, bool aExactHostMatch) { 3074 EnsureReadCompleted(); 3075 3076 MOZ_ASSERT(PermissionAvailableInternal(aPrincipal, mTypeArray[aType])); 3077 3078 nsresult rv; 3079 RefPtr<PermissionKey> key = PermissionKey::CreateFromPrincipal( 3080 aPrincipal, IsOAForceStripPermission(mTypeArray[aType]), 3081 IsSiteScopedPermission(mTypeArray[aType]), rv); 3082 if (!key) { 3083 return nullptr; 3084 } 3085 3086 PermissionHashKey* entry = mPermissionTable.GetEntry(key); 3087 3088 if (entry) { 3089 PermissionEntry permEntry = entry->GetPermission(aType); 3090 3091 // if the entry is expired, remove and keep looking for others. 3092 if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) { 3093 entry = nullptr; 3094 RemoveFromPrincipalInternal(aPrincipal, mTypeArray[aType]); 3095 } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { 3096 entry = nullptr; 3097 } 3098 } 3099 3100 if (entry) { 3101 return entry; 3102 } 3103 3104 // If aExactHostMatch wasn't true, we can check if the base domain has a 3105 // permission entry. 3106 if (!aExactHostMatch) { 3107 nsCOMPtr<nsIPrincipal> principal = aPrincipal->GetNextSubDomainPrincipal(); 3108 if (principal) { 3109 return GetPermissionHashKey(principal, aType, aExactHostMatch); 3110 } 3111 } 3112 3113 // No entry, really... 3114 return nullptr; 3115 } 3116 3117 PermissionManager::PermissionHashKey* PermissionManager::GetPermissionHashKey( 3118 nsIURI* aURI, const OriginAttributes* aOriginAttributes, uint32_t aType, 3119 bool aExactHostMatch) { 3120 MOZ_ASSERT(aURI); 3121 3122 #ifdef DEBUG 3123 { 3124 nsCOMPtr<nsIPrincipal> principal; 3125 nsresult rv = NS_OK; 3126 if (aURI) { 3127 rv = GetPrincipal(aURI, getter_AddRefs(principal)); 3128 } 3129 MOZ_ASSERT_IF(NS_SUCCEEDED(rv), 3130 PermissionAvailableInternal(principal, mTypeArray[aType])); 3131 } 3132 #endif 3133 3134 nsresult rv; 3135 RefPtr<PermissionKey> key; 3136 3137 if (aOriginAttributes) { 3138 key = PermissionKey::CreateFromURIAndOriginAttributes( 3139 aURI, aOriginAttributes, IsOAForceStripPermission(mTypeArray[aType]), 3140 rv); 3141 } else { 3142 key = PermissionKey::CreateFromURI(aURI, rv); 3143 } 3144 3145 if (!key) { 3146 return nullptr; 3147 } 3148 3149 PermissionHashKey* entry = mPermissionTable.GetEntry(key); 3150 3151 if (entry) { 3152 PermissionEntry permEntry = entry->GetPermission(aType); 3153 3154 // if the entry is expired, remove and keep looking for others. 3155 if (HasExpired(permEntry.mExpireType, permEntry.mExpireTime)) { 3156 entry = nullptr; 3157 // If we need to remove a permission we mint a principal. This is a bit 3158 // inefficient, but hopefully this code path isn't super common. 3159 nsCOMPtr<nsIPrincipal> principal; 3160 if (aURI) { 3161 nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); 3162 if (NS_WARN_IF(NS_FAILED(rv))) { 3163 return nullptr; 3164 } 3165 } 3166 RemoveFromPrincipal(principal, mTypeArray[aType]); 3167 } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { 3168 entry = nullptr; 3169 } 3170 } 3171 3172 if (entry) { 3173 return entry; 3174 } 3175 3176 // If aExactHostMatch wasn't true, we can check if the base domain has a 3177 // permission entry. 3178 if (!aExactHostMatch) { 3179 nsCOMPtr<nsIURI> uri; 3180 if (aURI) { 3181 uri = GetNextSubDomainURI(aURI); 3182 } 3183 if (uri) { 3184 return GetPermissionHashKey(uri, aOriginAttributes, aType, 3185 aExactHostMatch); 3186 } 3187 } 3188 3189 // No entry, really... 3190 return nullptr; 3191 } 3192 3193 nsresult PermissionManager::RemoveAllFromMemory() { 3194 mLargestID = 0; 3195 mTypeArray.clear(); 3196 mPermissionTable.Clear(); 3197 3198 return NS_OK; 3199 } 3200 3201 // wrapper function for mangling (host,type,perm,expireType,expireTime) 3202 // set into an nsIPermission. 3203 void PermissionManager::NotifyObserversWithPermission( 3204 nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t aPermission, 3205 uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime, 3206 const nsString& aData) { 3207 nsCOMPtr<nsIPermission> permission = 3208 Permission::Create(aPrincipal, aType, aPermission, aExpireType, 3209 aExpireTime, aModificationTime); 3210 if (permission) { 3211 NotifyObservers(permission, aData); 3212 } 3213 } 3214 3215 // notify observers that the permission list changed. there are four possible 3216 // values for aData: 3217 // "deleted" means a permission was deleted. aPermission is the deleted 3218 // permission. "added" means a permission was added. aPermission is the added 3219 // permission. "changed" means a permission was altered. aPermission is the new 3220 // permission. "cleared" means the entire permission list was cleared. 3221 // aPermission is null. 3222 void PermissionManager::NotifyObservers( 3223 const nsCOMPtr<nsIPermission>& aPermission, const nsString& aData) { 3224 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 3225 if (observerService) { 3226 // we need to release the monitor here because the observers for the below 3227 // notification can call back in to permission manager and try to lock the 3228 // monitor again. 3229 MonitorAutoUnlock unlock{mMonitor}; 3230 observerService->NotifyObservers(aPermission, kPermissionChangeNotification, 3231 aData.Data()); 3232 } 3233 } 3234 3235 nsresult PermissionManager::Read(const MonitorAutoLock& aProofOfLock) { 3236 ENSURE_NOT_CHILD_PROCESS; 3237 3238 MOZ_ASSERT(!NS_IsMainThread()); 3239 auto data = mThreadBoundData.Access(); 3240 3241 nsresult rv; 3242 bool hasResult; 3243 nsCOMPtr<mozIStorageStatement> stmt; 3244 3245 // Let's retrieve the last used ID. 3246 rv = data->mDBConn->CreateStatement( 3247 nsLiteralCString("SELECT MAX(id) FROM moz_perms"), getter_AddRefs(stmt)); 3248 NS_ENSURE_SUCCESS(rv, rv); 3249 3250 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 3251 int64_t id = stmt->AsInt64(0); 3252 mLargestID = id; 3253 } 3254 3255 rv = data->mDBConn->CreateStatement( 3256 nsLiteralCString( 3257 "SELECT id, origin, type, permission, expireType, " 3258 "expireTime, modificationTime " 3259 "FROM moz_perms WHERE expireType != ?1 OR expireTime > ?2"), 3260 getter_AddRefs(stmt)); 3261 NS_ENSURE_SUCCESS(rv, rv); 3262 3263 rv = stmt->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME); 3264 NS_ENSURE_SUCCESS(rv, rv); 3265 3266 rv = stmt->BindInt64ByIndex(1, EXPIRY_NOW); 3267 NS_ENSURE_SUCCESS(rv, rv); 3268 3269 bool readError = false; 3270 3271 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 3272 ReadEntry entry; 3273 3274 // explicitly set our entry id counter for use in AddInternal(), 3275 // and keep track of the largest id so we know where to pick up. 3276 entry.mId = stmt->AsInt64(0); 3277 MOZ_ASSERT(entry.mId <= mLargestID); 3278 3279 rv = stmt->GetUTF8String(1, entry.mOrigin); 3280 if (NS_FAILED(rv)) { 3281 readError = true; 3282 continue; 3283 } 3284 3285 rv = stmt->GetUTF8String(2, entry.mType); 3286 if (NS_FAILED(rv)) { 3287 readError = true; 3288 continue; 3289 } 3290 3291 entry.mPermission = stmt->AsInt32(3); 3292 entry.mExpireType = stmt->AsInt32(4); 3293 3294 // convert into int64_t values (milliseconds) 3295 entry.mExpireTime = stmt->AsInt64(5); 3296 entry.mModificationTime = stmt->AsInt64(6); 3297 3298 entry.mFromMigration = false; 3299 3300 mReadEntries.AppendElement(entry); 3301 } 3302 3303 if (readError) { 3304 NS_ERROR("Error occured while reading the permissions database!"); 3305 return NS_ERROR_FAILURE; 3306 } 3307 3308 return NS_OK; 3309 } 3310 3311 void PermissionManager::CompleteMigrations() { 3312 MOZ_ASSERT(NS_IsMainThread()); 3313 MOZ_ASSERT(mState == eReady); 3314 3315 nsresult rv; 3316 3317 nsTArray<MigrationEntry> entries = std::move(mMigrationEntries); 3318 3319 for (const MigrationEntry& entry : entries) { 3320 rv = UpgradeHostToOriginAndInsert( 3321 entry.mHost, entry.mType, entry.mPermission, entry.mExpireType, 3322 entry.mExpireTime, entry.mModificationTime, 3323 [&](const nsACString& aOrigin, const nsCString& aType, 3324 uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, 3325 int64_t aModificationTime) MOZ_REQUIRES(mMonitor) { 3326 MaybeAddReadEntryFromMigration(aOrigin, aType, aPermission, 3327 aExpireType, aExpireTime, 3328 aModificationTime, entry.mId); 3329 return NS_OK; 3330 }); 3331 (void)NS_WARN_IF(NS_FAILED(rv)); 3332 } 3333 } 3334 3335 void PermissionManager::CompleteRead() { 3336 MOZ_ASSERT(NS_IsMainThread()); 3337 MOZ_ASSERT(mState == eReady); 3338 3339 nsresult rv; 3340 3341 nsTArray<ReadEntry> entries = std::move(mReadEntries); 3342 3343 for (const ReadEntry& entry : entries) { 3344 nsCOMPtr<nsIPrincipal> principal; 3345 rv = GetPrincipalFromOrigin(entry.mOrigin, 3346 IsOAForceStripPermission(entry.mType), 3347 getter_AddRefs(principal)); 3348 if (NS_WARN_IF(NS_FAILED(rv))) { 3349 continue; 3350 } 3351 3352 DBOperationType op = entry.mFromMigration ? eWriteToDB : eNoDBOperation; 3353 3354 rv = AddInternal(principal, entry.mType, entry.mPermission, entry.mId, 3355 entry.mExpireType, entry.mExpireTime, 3356 entry.mModificationTime, eDontNotify, op, &entry.mOrigin); 3357 (void)NS_WARN_IF(NS_FAILED(rv)); 3358 } 3359 } 3360 3361 void PermissionManager::MaybeAddReadEntryFromMigration( 3362 const nsACString& aOrigin, const nsCString& aType, uint32_t aPermission, 3363 uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime, 3364 int64_t aId) { 3365 // We convert a migration to a ReadEntry only if we don't have an existing 3366 // ReadEntry for the same origin + type. 3367 for (const ReadEntry& entry : mReadEntries) { 3368 if (entry.mOrigin == aOrigin && entry.mType == aType) { 3369 return; 3370 } 3371 } 3372 3373 ReadEntry entry; 3374 entry.mId = aId; 3375 entry.mOrigin = aOrigin; 3376 entry.mType = aType; 3377 entry.mPermission = aPermission; 3378 entry.mExpireType = aExpireType; 3379 entry.mExpireTime = aExpireTime; 3380 entry.mModificationTime = aModificationTime; 3381 entry.mFromMigration = true; 3382 3383 mReadEntries.AppendElement(entry); 3384 } 3385 3386 void PermissionManager::UpdateDB(OperationType aOp, int64_t aID, 3387 const nsACString& aOrigin, 3388 const nsACString& aType, uint32_t aPermission, 3389 uint32_t aExpireType, int64_t aExpireTime, 3390 int64_t aModificationTime) { 3391 ENSURE_NOT_CHILD_PROCESS_NORET; 3392 3393 MOZ_ASSERT(NS_IsMainThread()); 3394 EnsureReadCompleted(); 3395 3396 nsCString origin(aOrigin); 3397 nsCString type(aType); 3398 3399 RefPtr<PermissionManager> self = this; 3400 mThread->Dispatch(NS_NewRunnableFunction( 3401 "PermissionManager::UpdateDB", 3402 [self, aOp, aID, origin, type, aPermission, aExpireType, aExpireTime, 3403 aModificationTime] { 3404 nsresult rv; 3405 3406 auto data = self->mThreadBoundData.Access(); 3407 3408 if (self->mState == eClosed || !data->mDBConn) { 3409 // no statement is ok - just means we don't have a profile 3410 return; 3411 } 3412 3413 mozIStorageStatement* stmt = nullptr; 3414 switch (aOp) { 3415 case eOperationAdding: { 3416 stmt = data->mStmtInsert; 3417 3418 rv = stmt->BindInt64ByIndex(0, aID); 3419 if (NS_FAILED(rv)) break; 3420 3421 rv = stmt->BindUTF8StringByIndex(1, origin); 3422 if (NS_FAILED(rv)) break; 3423 3424 rv = stmt->BindUTF8StringByIndex(2, type); 3425 if (NS_FAILED(rv)) break; 3426 3427 rv = stmt->BindInt32ByIndex(3, aPermission); 3428 if (NS_FAILED(rv)) break; 3429 3430 rv = stmt->BindInt32ByIndex(4, aExpireType); 3431 if (NS_FAILED(rv)) break; 3432 3433 rv = stmt->BindInt64ByIndex(5, aExpireTime); 3434 if (NS_FAILED(rv)) break; 3435 3436 rv = stmt->BindInt64ByIndex(6, aModificationTime); 3437 break; 3438 } 3439 3440 case eOperationRemoving: { 3441 stmt = data->mStmtDelete; 3442 rv = stmt->BindInt64ByIndex(0, aID); 3443 break; 3444 } 3445 3446 case eOperationChanging: { 3447 stmt = data->mStmtUpdate; 3448 3449 rv = stmt->BindInt64ByIndex(0, aID); 3450 if (NS_FAILED(rv)) break; 3451 3452 rv = stmt->BindInt32ByIndex(1, aPermission); 3453 if (NS_FAILED(rv)) break; 3454 3455 rv = stmt->BindInt32ByIndex(2, aExpireType); 3456 if (NS_FAILED(rv)) break; 3457 3458 rv = stmt->BindInt64ByIndex(3, aExpireTime); 3459 if (NS_FAILED(rv)) break; 3460 3461 rv = stmt->BindInt64ByIndex(4, aModificationTime); 3462 break; 3463 } 3464 3465 default: { 3466 MOZ_ASSERT_UNREACHABLE("need a valid operation in UpdateDB()!"); 3467 rv = NS_ERROR_UNEXPECTED; 3468 break; 3469 } 3470 } 3471 3472 if (NS_FAILED(rv)) { 3473 NS_WARNING("db change failed!"); 3474 return; 3475 } 3476 3477 rv = stmt->Execute(); 3478 MOZ_ASSERT(NS_SUCCEEDED(rv)); 3479 })); 3480 } 3481 3482 bool PermissionManager::GetPermissionsFromOriginOrKey( 3483 const nsACString& aOrigin, const nsACString& aKey, 3484 nsTArray<IPC::Permission>& aPerms) { 3485 MonitorAutoLock lock{mMonitor}; 3486 3487 EnsureReadCompleted(); 3488 3489 aPerms.Clear(); 3490 if (NS_WARN_IF(XRE_IsContentProcess())) { 3491 return false; 3492 } 3493 3494 for (const PermissionHashKey& entry : mPermissionTable) { 3495 nsAutoCString permissionKey; 3496 if (aOrigin.IsEmpty()) { 3497 // We can't check for individual OA strip perms here. 3498 // Don't force strip origin attributes. 3499 GetKeyForOrigin(entry.GetKey()->mOrigin, false, false, permissionKey); 3500 3501 // If the keys don't match, and we aren't getting the default "" key, then 3502 // we can exit early. We have to keep looking if we're getting the default 3503 // key, as we may see a preload permission which should be transmitted. 3504 if (aKey != permissionKey && !aKey.IsEmpty()) { 3505 continue; 3506 } 3507 } else if (aOrigin != entry.GetKey()->mOrigin) { 3508 // If the origins don't match, then we can exit early. We have to keep 3509 // looking if we're getting the default origin, as we may see a preload 3510 // permission which should be transmitted. 3511 continue; 3512 } 3513 3514 for (const auto& permEntry : entry.GetPermissions()) { 3515 // Given how "default" permissions work and the possibility of them 3516 // being overridden with UNKNOWN_ACTION, we might see this value here - 3517 // but we do not want to send it to the content process. 3518 if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { 3519 continue; 3520 } 3521 3522 bool isPreload = IsPreloadPermission(mTypeArray[permEntry.mType]); 3523 bool shouldAppend; 3524 if (aOrigin.IsEmpty()) { 3525 shouldAppend = (isPreload && aKey.IsEmpty()) || 3526 (!isPreload && aKey == permissionKey); 3527 } else { 3528 shouldAppend = (!isPreload && aOrigin == entry.GetKey()->mOrigin); 3529 } 3530 if (shouldAppend) { 3531 aPerms.AppendElement( 3532 IPC::Permission(entry.GetKey()->mOrigin, 3533 mTypeArray[permEntry.mType], permEntry.mPermission, 3534 permEntry.mExpireType, permEntry.mExpireTime)); 3535 } 3536 } 3537 } 3538 3539 return true; 3540 } 3541 3542 void PermissionManager::SetPermissionsWithKey( 3543 const nsACString& aPermissionKey, nsTArray<IPC::Permission>& aPerms) { 3544 if (NS_WARN_IF(XRE_IsParentProcess())) { 3545 return; 3546 } 3547 3548 MonitorAutoLock lock{mMonitor}; 3549 3550 RefPtr<GenericNonExclusivePromise::Private> promise; 3551 bool foundKey = 3552 mPermissionKeyPromiseMap.Get(aPermissionKey, getter_AddRefs(promise)); 3553 if (promise) { 3554 MOZ_ASSERT(foundKey); 3555 // NOTE: This will resolve asynchronously, so we can mark it as resolved 3556 // now, and be confident that we will have filled in the database before any 3557 // callbacks run. 3558 promise->Resolve(true, __func__); 3559 } else if (foundKey) { 3560 // NOTE: We shouldn't be sent two InitializePermissionsWithKey for the same 3561 // key, but it's possible. 3562 return; 3563 } 3564 mPermissionKeyPromiseMap.InsertOrUpdate( 3565 aPermissionKey, RefPtr<GenericNonExclusivePromise::Private>{}); 3566 3567 // Add the permissions locally to our process 3568 for (IPC::Permission& perm : aPerms) { 3569 nsCOMPtr<nsIPrincipal> principal; 3570 nsresult rv = 3571 GetPrincipalFromOrigin(perm.origin, IsOAForceStripPermission(perm.type), 3572 getter_AddRefs(principal)); 3573 if (NS_WARN_IF(NS_FAILED(rv))) { 3574 continue; 3575 } 3576 3577 #ifdef DEBUG 3578 nsAutoCString permissionKey; 3579 GetKeyForPermission(principal, perm.type, permissionKey); 3580 MOZ_ASSERT(permissionKey == aPermissionKey, 3581 "The permission keys which were sent over should match!"); 3582 #endif 3583 3584 // The child process doesn't care about modification times - it neither 3585 // reads nor writes, nor removes them based on the date - so 0 (which 3586 // will end up as now()) is fine. 3587 uint64_t modificationTime = 0; 3588 AddInternal(principal, perm.type, perm.capability, 0, perm.expireType, 3589 perm.expireTime, modificationTime, eDontNotify, eNoDBOperation); 3590 } 3591 } 3592 3593 /* static */ 3594 nsresult PermissionManager::GetKeyForOrigin(const nsACString& aOrigin, 3595 bool aForceStripOA, 3596 bool aSiteScopePermissions, 3597 nsACString& aKey) { 3598 aKey.Truncate(); 3599 3600 // We only key origins for http, https URIs. All origins begin with 3601 // the URL which they apply to, which means that they should begin with their 3602 // scheme in the case where they are one of these interesting URIs. We don't 3603 // want to actually parse the URL here however, because this can be called on 3604 // hot paths. 3605 if (!StringBeginsWith(aOrigin, "http:"_ns) && 3606 !StringBeginsWith(aOrigin, "https:"_ns)) { 3607 return NS_OK; 3608 } 3609 3610 // We need to look at the originAttributes if they are present, to make sure 3611 // to remove any which we don't want. We put the rest of the origin, not 3612 // including the attributes, into the key. 3613 OriginAttributes attrs; 3614 if (!attrs.PopulateFromOrigin(aOrigin, aKey)) { 3615 aKey.Truncate(); 3616 return NS_OK; 3617 } 3618 3619 MaybeStripOriginAttributes(aForceStripOA, attrs); 3620 3621 #ifdef DEBUG 3622 // Parse the origin string into a principal, and extract some useful 3623 // information from it for assertions. 3624 nsCOMPtr<nsIPrincipal> dbgPrincipal; 3625 MOZ_ALWAYS_SUCCEEDS(GetPrincipalFromOrigin(aOrigin, aForceStripOA, 3626 getter_AddRefs(dbgPrincipal))); 3627 MOZ_ASSERT(dbgPrincipal->SchemeIs("http") || dbgPrincipal->SchemeIs("https")); 3628 MOZ_ASSERT(dbgPrincipal->OriginAttributesRef() == attrs); 3629 #endif 3630 3631 // If it is needed, turn the origin into its site-origin 3632 if (aSiteScopePermissions) { 3633 nsCOMPtr<nsIURI> uri; 3634 nsresult rv = NS_NewURI(getter_AddRefs(uri), aKey); 3635 if (!NS_WARN_IF(NS_FAILED(rv))) { 3636 nsCString site; 3637 nsCOMPtr<nsIEffectiveTLDService> etld = 3638 mozilla::components::EffectiveTLD::Service(); 3639 rv = etld->GetSite(uri, site); 3640 if (!NS_WARN_IF(NS_FAILED(rv))) { 3641 aKey = site; 3642 } 3643 } 3644 } 3645 3646 // Append the stripped suffix to the output origin key. 3647 nsAutoCString suffix; 3648 attrs.CreateSuffix(suffix); 3649 aKey.Append(suffix); 3650 3651 return NS_OK; 3652 } 3653 3654 /* static */ 3655 nsresult PermissionManager::GetKeyForPrincipal(nsIPrincipal* aPrincipal, 3656 bool aForceStripOA, 3657 bool aSiteScopePermissions, 3658 nsACString& aKey) { 3659 nsAutoCString origin; 3660 nsresult rv = aPrincipal->GetOrigin(origin); 3661 if (NS_WARN_IF(NS_FAILED(rv))) { 3662 aKey.Truncate(); 3663 return rv; 3664 } 3665 return GetKeyForOrigin(origin, aForceStripOA, aSiteScopePermissions, aKey); 3666 } 3667 3668 /* static */ 3669 nsresult PermissionManager::GetKeyForPermission(nsIPrincipal* aPrincipal, 3670 const nsACString& aType, 3671 nsACString& aKey) { 3672 // Preload permissions have the "" key. 3673 if (IsPreloadPermission(aType)) { 3674 aKey.Truncate(); 3675 return NS_OK; 3676 } 3677 3678 return GetKeyForPrincipal(aPrincipal, IsOAForceStripPermission(aType), 3679 IsSiteScopedPermission(aType), aKey); 3680 } 3681 3682 /* static */ 3683 nsTArray<std::pair<nsCString, nsCString>> 3684 PermissionManager::GetAllKeysForPrincipal(nsIPrincipal* aPrincipal) { 3685 MOZ_ASSERT(aPrincipal); 3686 3687 nsTArray<std::pair<nsCString, nsCString>> pairs; 3688 nsCOMPtr<nsIPrincipal> prin = aPrincipal; 3689 3690 while (prin) { 3691 // Add the pair to the list 3692 std::pair<nsCString, nsCString>* pair = 3693 pairs.AppendElement(std::make_pair(""_ns, ""_ns)); 3694 // We can't check for individual OA strip perms here. 3695 // Don't force strip origin attributes. 3696 GetKeyForPrincipal(prin, false, false, pair->first); 3697 3698 // On origins with a derived key set to an empty string 3699 // (basically any non-web URI scheme), we want to make sure 3700 // to return earlier, and leave [("", "")] as the resulting 3701 // pairs (but still run the same debug assertions near the 3702 // end of this method). 3703 if (pair->first.IsEmpty()) { 3704 break; 3705 } 3706 3707 (void)GetOriginFromPrincipal(prin, false, pair->second); 3708 prin = prin->GetNextSubDomainPrincipal(); 3709 // Get the next subdomain principal and loop back around. 3710 } 3711 3712 MOZ_ASSERT(pairs.Length() >= 1, 3713 "Every principal should have at least one pair item."); 3714 return pairs; 3715 } 3716 3717 bool PermissionManager::PermissionAvailable(nsIPrincipal* aPrincipal, 3718 const nsACString& aType) { 3719 MonitorAutoLock lock{mMonitor}; 3720 return PermissionAvailableInternal(aPrincipal, aType); 3721 } 3722 3723 bool PermissionManager::PermissionAvailableInternal(nsIPrincipal* aPrincipal, 3724 const nsACString& aType) { 3725 EnsureReadCompleted(); 3726 3727 if (XRE_IsContentProcess()) { 3728 nsAutoCString permissionKey; 3729 // NOTE: GetKeyForPermission accepts a null aType. 3730 GetKeyForPermission(aPrincipal, aType, permissionKey); 3731 3732 // If we have a pending promise for the permission key in question, we don't 3733 // have the permission available, so report a warning and return false. 3734 RefPtr<GenericNonExclusivePromise::Private> promise; 3735 if (!mPermissionKeyPromiseMap.Get(permissionKey, getter_AddRefs(promise)) || 3736 promise) { 3737 // Emit a useful diagnostic warning with the permissionKey for the process 3738 // which hasn't received permissions yet. 3739 NS_WARNING(nsPrintfCString("This content process hasn't received the " 3740 "permissions for %s yet", 3741 permissionKey.get()) 3742 .get()); 3743 return false; 3744 } 3745 } 3746 return true; 3747 } 3748 3749 void PermissionManager::WhenPermissionsAvailable(nsIPrincipal* aPrincipal, 3750 nsIRunnable* aRunnable) { 3751 MOZ_ASSERT(aRunnable); 3752 3753 if (!XRE_IsContentProcess()) { 3754 aRunnable->Run(); 3755 return; 3756 } 3757 3758 MonitorAutoLock lock{mMonitor}; 3759 3760 nsTArray<RefPtr<GenericNonExclusivePromise>> promises; 3761 for (auto& pair : GetAllKeysForPrincipal(aPrincipal)) { 3762 RefPtr<GenericNonExclusivePromise::Private> promise; 3763 if (!mPermissionKeyPromiseMap.Get(pair.first, getter_AddRefs(promise))) { 3764 // In this case we have found a permission which isn't available in the 3765 // content process and hasn't been requested yet. We need to create a new 3766 // promise, and send the request to the parent (if we have not already 3767 // done so). 3768 promise = new GenericNonExclusivePromise::Private(__func__); 3769 mPermissionKeyPromiseMap.InsertOrUpdate(pair.first, RefPtr{promise}); 3770 } 3771 3772 if (promise) { 3773 promises.AppendElement(std::move(promise)); 3774 } 3775 } 3776 3777 // If all of our permissions are available, immediately run the runnable. This 3778 // avoids any extra overhead during fetch interception which is performance 3779 // sensitive. 3780 if (promises.IsEmpty()) { 3781 aRunnable->Run(); 3782 return; 3783 } 3784 3785 auto* thread = AbstractThread::MainThread(); 3786 3787 RefPtr<nsIRunnable> runnable = aRunnable; 3788 GenericNonExclusivePromise::All(thread, promises) 3789 ->Then( 3790 thread, __func__, [runnable]() { runnable->Run(); }, 3791 []() { 3792 NS_WARNING( 3793 "PermissionManager permission promise rejected. We're " 3794 "probably shutting down."); 3795 }); 3796 } 3797 3798 void PermissionManager::EnsureReadCompleted() { 3799 if (mState == eInitializing) { 3800 while (mState == eInitializing) { 3801 mMonitor.AssertCurrentThreadOwns(); 3802 mMonitor.Wait(); 3803 } 3804 } 3805 3806 switch (mState) { 3807 case eInitializing: 3808 MOZ_CRASH("This state is impossible!"); 3809 3810 case eDBInitialized: 3811 // child processes transitions from eInitializing -> eReady 3812 ENSURE_NOT_CHILD_PROCESS_NORET; 3813 3814 mState = eReady; 3815 3816 CompleteMigrations(); 3817 ImportLatestDefaults(); 3818 CompleteRead(); 3819 3820 [[fallthrough]]; 3821 3822 case eReady: 3823 [[fallthrough]]; 3824 3825 case eClosed: 3826 break; 3827 3828 default: 3829 MOZ_CRASH("Invalid state"); 3830 } 3831 } 3832 3833 already_AddRefed<nsIInputStream> PermissionManager::GetDefaultsInputStream() { 3834 MOZ_ASSERT(NS_IsMainThread()); 3835 3836 nsAutoCString defaultsURL; 3837 Preferences::GetCString(kDefaultsUrlPrefName, defaultsURL); 3838 if (defaultsURL.IsEmpty()) { // == Don't use built-in permissions. 3839 return nullptr; 3840 } 3841 3842 nsCOMPtr<nsIURI> defaultsURI; 3843 nsresult rv = NS_NewURI(getter_AddRefs(defaultsURI), defaultsURL); 3844 NS_ENSURE_SUCCESS(rv, nullptr); 3845 3846 nsCOMPtr<nsIChannel> channel; 3847 rv = NS_NewChannel(getter_AddRefs(channel), defaultsURI, 3848 nsContentUtils::GetSystemPrincipal(), 3849 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 3850 nsIContentPolicy::TYPE_OTHER); 3851 NS_ENSURE_SUCCESS(rv, nullptr); 3852 3853 nsCOMPtr<nsIInputStream> inputStream; 3854 rv = channel->Open(getter_AddRefs(inputStream)); 3855 NS_ENSURE_SUCCESS(rv, nullptr); 3856 3857 return inputStream.forget(); 3858 } 3859 3860 void PermissionManager::ConsumeDefaultsInputStream( 3861 nsIInputStream* aInputStream, const MonitorAutoLock& aProofOfLock) { 3862 MOZ_ASSERT(!NS_IsMainThread()); 3863 3864 constexpr char kMatchTypeHost[] = "host"; 3865 constexpr char kMatchTypeOrigin[] = "origin"; 3866 3867 mDefaultEntriesForImport.Clear(); 3868 3869 if (!aInputStream) { 3870 return; 3871 } 3872 3873 nsresult rv; 3874 3875 /* format is: 3876 * matchtype \t type \t permission \t host 3877 * Only "host" is supported for matchtype 3878 * type is a string that identifies the type of permission (e.g. "cookie") 3879 * permission is an integer between 1 and 15 3880 */ 3881 3882 // Ideally we'd do this with nsILineInputString, but this is called with an 3883 // nsIInputStream that comes from a resource:// URI, which doesn't support 3884 // that interface. So NS_ReadLine to the rescue... 3885 nsLineBuffer<char> lineBuffer; 3886 nsCString line; 3887 bool isMore = true; 3888 do { 3889 rv = NS_ReadLine(aInputStream, &lineBuffer, line, &isMore); 3890 NS_ENSURE_SUCCESS_VOID(rv); 3891 3892 if (line.IsEmpty() || line.First() == '#') { 3893 continue; 3894 } 3895 3896 nsTArray<nsCString> lineArray; 3897 3898 // Split the line at tabs 3899 ParseString(line, '\t', lineArray); 3900 3901 if (lineArray.Length() != 4) { 3902 continue; 3903 } 3904 3905 nsresult error = NS_OK; 3906 uint32_t permission = lineArray[2].ToInteger(&error); 3907 if (NS_FAILED(error)) { 3908 continue; 3909 } 3910 3911 const nsCString& hostOrOrigin = lineArray[3]; 3912 const nsCString& type = lineArray[1]; 3913 3914 if (lineArray[0].EqualsLiteral(kMatchTypeHost)) { 3915 UpgradeHostToOriginAndInsert( 3916 hostOrOrigin, type, permission, nsIPermissionManager::EXPIRE_NEVER, 0, 3917 0, 3918 [&](const nsACString& aOrigin, const nsCString& aType, 3919 uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, 3920 int64_t aModificationTime) MOZ_REQUIRES(mMonitor) { 3921 AddDefaultEntryForImport(aOrigin, aType, aPermission, aProofOfLock); 3922 return NS_OK; 3923 }); 3924 } else if (lineArray[0].EqualsLiteral(kMatchTypeOrigin)) { 3925 AddDefaultEntryForImport(hostOrOrigin, type, permission, aProofOfLock); 3926 } else { 3927 continue; 3928 } 3929 3930 } while (isMore); 3931 } 3932 3933 void PermissionManager::AddDefaultEntryForImport( 3934 const nsACString& aOrigin, const nsCString& aType, uint32_t aPermission, 3935 const MonitorAutoLock& aProofOfLock) { 3936 DefaultEntry* entry = mDefaultEntriesForImport.AppendElement(); 3937 MOZ_ASSERT(entry); 3938 3939 entry->mPermission = aPermission; 3940 entry->mOrigin = aOrigin; 3941 entry->mType = aType; 3942 } 3943 3944 nsresult PermissionManager::ImportDefaultEntry( 3945 const DefaultEntry& aDefaultEntry) { 3946 nsCOMPtr<nsIPrincipal> principal; 3947 nsresult rv = GetPrincipalFromOrigin( 3948 aDefaultEntry.mOrigin, IsOAForceStripPermission(aDefaultEntry.mType), 3949 getter_AddRefs(principal)); 3950 if (NS_FAILED(rv)) { 3951 NS_WARNING("Couldn't import an origin permission - malformed origin"); 3952 return rv; 3953 } 3954 3955 // the import file format doesn't handle modification times, so we use 3956 // 0, which AddInternal will convert to now() 3957 int64_t modificationTime = 0; 3958 3959 rv = AddInternal(principal, aDefaultEntry.mType, aDefaultEntry.mPermission, 3960 cIDPermissionIsDefault, nsIPermissionManager::EXPIRE_NEVER, 3961 0, modificationTime, eDontNotify, eNoDBOperation); 3962 if (NS_FAILED(rv)) { 3963 NS_WARNING("There was a problem importing an origin permission"); 3964 return rv; 3965 } 3966 3967 if (StaticPrefs::permissions_isolateBy_privateBrowsing() && 3968 !IsOAForceStripPermission(aDefaultEntry.mType)) { 3969 // Also import the permission for private browsing. 3970 OriginAttributes attrs = OriginAttributes(principal->OriginAttributesRef()); 3971 attrs.mPrivateBrowsingId = 1; 3972 nsCOMPtr<nsIPrincipal> pbPrincipal = 3973 BasePrincipal::Cast(principal)->CloneForcingOriginAttributes(attrs); 3974 // May return nullptr if clone fails. 3975 if (NS_WARN_IF(NS_FAILED(rv))) { 3976 return rv; 3977 } 3978 3979 rv = 3980 AddInternal(pbPrincipal, aDefaultEntry.mType, aDefaultEntry.mPermission, 3981 cIDPermissionIsDefault, nsIPermissionManager::EXPIRE_NEVER, 3982 0, modificationTime, eDontNotify, eNoDBOperation); 3983 if (NS_FAILED(rv)) { 3984 NS_WARNING( 3985 "There was a problem importing an origin permission for private " 3986 "browsing"); 3987 return rv; 3988 } 3989 } 3990 3991 return NS_OK; 3992 } 3993 3994 // ImportLatestDefaults will import the latest default permissions read during 3995 // the last DB initialization. 3996 nsresult PermissionManager::ImportLatestDefaults() { 3997 MOZ_ASSERT(NS_IsMainThread()); 3998 MOZ_ASSERT(mState == eReady); 3999 4000 for (const DefaultEntry& entry : mDefaultEntriesForImport) { 4001 (void)ImportDefaultEntry(entry); 4002 } 4003 4004 return NS_OK; 4005 } 4006 4007 /** 4008 * Perform the early steps of a permission check and determine whether we need 4009 * to call CommonTestPermissionInternal() for the actual permission check. 4010 * 4011 * @param aPrincipal optional principal argument to check the permission for, 4012 * can be nullptr if we aren't performing a principal-based 4013 * check. 4014 * @param aTypeIndex if the caller isn't sure what the index of the permission 4015 * type to check for is in the mTypeArray member variable, 4016 * it should pass -1, otherwise this would be the index of 4017 * the type inside mTypeArray. This would only be something 4018 * other than -1 in recursive invocations of this function. 4019 * @param aType the permission type to test. 4020 * @param aPermission out argument which will be a permission type that we 4021 * will return from this function once the function is 4022 * done. 4023 * @param aDefaultPermission the default permission to be used if we can't 4024 * determine the result of the permission check. 4025 * @param aDefaultPermissionIsValid whether the previous argument contains a 4026 * valid value. 4027 * @param aExactHostMatch whether to look for the exact host name or also for 4028 * subdomains that can have the same permission. 4029 * @param aIncludingSession whether to include session permissions when 4030 * testing for the permission. 4031 */ 4032 PermissionManager::TestPreparationResult 4033 PermissionManager::CommonPrepareToTestPermission( 4034 nsIPrincipal* aPrincipal, int32_t aTypeIndex, const nsACString& aType, 4035 uint32_t* aPermission, uint32_t aDefaultPermission, 4036 bool aDefaultPermissionIsValid, bool aExactHostMatch, 4037 bool aIncludingSession) { 4038 auto* basePrin = BasePrincipal::Cast(aPrincipal); 4039 if (basePrin && basePrin->IsSystemPrincipal()) { 4040 *aPermission = ALLOW_ACTION; 4041 return AsVariant(NS_OK); 4042 } 4043 4044 EnsureReadCompleted(); 4045 4046 // For some permissions, query the default from a pref. We want to avoid 4047 // doing this for all permissions so that permissions can opt into having 4048 // the pref lookup overhead on each call. 4049 int32_t defaultPermission = 4050 aDefaultPermissionIsValid ? aDefaultPermission : UNKNOWN_ACTION; 4051 if (!aDefaultPermissionIsValid && HasDefaultPref(aType)) { 4052 (void)mDefaultPrefBranch->GetIntPref(PromiseFlatCString(aType).get(), 4053 &defaultPermission); 4054 if (defaultPermission < 0 || 4055 defaultPermission > nsIPermissionManager::MAX_VALID_ACTION) { 4056 defaultPermission = nsIPermissionManager::UNKNOWN_ACTION; 4057 } 4058 } 4059 4060 // Set the default. 4061 *aPermission = defaultPermission; 4062 4063 int32_t typeIndex = 4064 aTypeIndex == -1 ? GetTypeIndex(aType, false) : aTypeIndex; 4065 4066 // For expanded principals, we want to iterate over the allowlist and see 4067 // if the permission is granted for any of them. 4068 if (basePrin && basePrin->Is<ExpandedPrincipal>()) { 4069 auto* ep = basePrin->As<ExpandedPrincipal>(); 4070 for (const auto& prin : ep->AllowList()) { 4071 uint32_t perm; 4072 nsresult rv = 4073 CommonTestPermission(prin, typeIndex, aType, &perm, defaultPermission, 4074 true, aExactHostMatch, aIncludingSession); 4075 if (NS_WARN_IF(NS_FAILED(rv))) { 4076 return AsVariant(rv); 4077 } 4078 4079 if (perm == nsIPermissionManager::ALLOW_ACTION) { 4080 *aPermission = perm; 4081 return AsVariant(NS_OK); 4082 } 4083 if (perm == nsIPermissionManager::PROMPT_ACTION) { 4084 // Store it, but keep going to see if we can do better. 4085 *aPermission = perm; 4086 } 4087 } 4088 4089 return AsVariant(NS_OK); 4090 } 4091 4092 // If type == -1, the type isn't known, just signal that we are done. 4093 if (typeIndex == -1) { 4094 return AsVariant(NS_OK); 4095 } 4096 4097 return AsVariant(typeIndex); 4098 } 4099 4100 // If aTypeIndex is passed -1, we try to inder the type index from aType. 4101 nsresult PermissionManager::CommonTestPermission( 4102 nsIPrincipal* aPrincipal, int32_t aTypeIndex, const nsACString& aType, 4103 uint32_t* aPermission, uint32_t aDefaultPermission, 4104 bool aDefaultPermissionIsValid, bool aExactHostMatch, 4105 bool aIncludingSession) { 4106 auto preparationResult = CommonPrepareToTestPermission( 4107 aPrincipal, aTypeIndex, aType, aPermission, aDefaultPermission, 4108 aDefaultPermissionIsValid, aExactHostMatch, aIncludingSession); 4109 if (preparationResult.is<nsresult>()) { 4110 return preparationResult.as<nsresult>(); 4111 } 4112 4113 return CommonTestPermissionInternal( 4114 aPrincipal, nullptr, nullptr, preparationResult.as<int32_t>(), aType, 4115 aPermission, aExactHostMatch, aIncludingSession); 4116 } 4117 4118 // If aTypeIndex is passed -1, we try to inder the type index from aType. 4119 nsresult PermissionManager::CommonTestPermission( 4120 nsIURI* aURI, int32_t aTypeIndex, const nsACString& aType, 4121 uint32_t* aPermission, uint32_t aDefaultPermission, 4122 bool aDefaultPermissionIsValid, bool aExactHostMatch, 4123 bool aIncludingSession) { 4124 auto preparationResult = CommonPrepareToTestPermission( 4125 nullptr, aTypeIndex, aType, aPermission, aDefaultPermission, 4126 aDefaultPermissionIsValid, aExactHostMatch, aIncludingSession); 4127 if (preparationResult.is<nsresult>()) { 4128 return preparationResult.as<nsresult>(); 4129 } 4130 4131 return CommonTestPermissionInternal( 4132 nullptr, aURI, nullptr, preparationResult.as<int32_t>(), aType, 4133 aPermission, aExactHostMatch, aIncludingSession); 4134 } 4135 4136 nsresult PermissionManager::CommonTestPermission( 4137 nsIURI* aURI, const OriginAttributes* aOriginAttributes, int32_t aTypeIndex, 4138 const nsACString& aType, uint32_t* aPermission, uint32_t aDefaultPermission, 4139 bool aDefaultPermissionIsValid, bool aExactHostMatch, 4140 bool aIncludingSession) { 4141 auto preparationResult = CommonPrepareToTestPermission( 4142 nullptr, aTypeIndex, aType, aPermission, aDefaultPermission, 4143 aDefaultPermissionIsValid, aExactHostMatch, aIncludingSession); 4144 if (preparationResult.is<nsresult>()) { 4145 return preparationResult.as<nsresult>(); 4146 } 4147 4148 return CommonTestPermissionInternal( 4149 nullptr, aURI, aOriginAttributes, preparationResult.as<int32_t>(), aType, 4150 aPermission, aExactHostMatch, aIncludingSession); 4151 } 4152 4153 nsresult PermissionManager::TestPermissionWithoutDefaultsFromPrincipal( 4154 nsIPrincipal* aPrincipal, const nsACString& aType, uint32_t* aPermission) { 4155 MOZ_ASSERT(!HasDefaultPref(aType)); 4156 4157 MonitorAutoLock lock{mMonitor}; 4158 return CommonTestPermission(aPrincipal, -1, aType, aPermission, 4159 nsIPermissionManager::UNKNOWN_ACTION, true, false, 4160 true); 4161 } 4162 4163 void PermissionManager::FinishAsyncShutdown() { 4164 MOZ_ASSERT(NS_IsMainThread()); 4165 4166 nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier(); 4167 MOZ_ASSERT(asc); 4168 4169 DebugOnly<nsresult> rv = asc->RemoveBlocker(this); 4170 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4171 4172 // Now we can safely release our holder. 4173 StaticMutexAutoLock lock(sCreationMutex); 4174 MOZ_ASSERT(sInstanceDead); 4175 if (sInstanceHolder) { 4176 sInstanceHolder = nullptr; 4177 } 4178 } 4179 4180 // Async shutdown blocker methods 4181 4182 NS_IMETHODIMP PermissionManager::GetName(nsAString& aName) { 4183 aName = u"PermissionManager: Flushing data"_ns; 4184 return NS_OK; 4185 } 4186 4187 NS_IMETHODIMP PermissionManager::BlockShutdown( 4188 nsIAsyncShutdownClient* aClient) { 4189 { 4190 // From now on we do not allow to capture new references to our singleton. 4191 StaticMutexAutoLock lock(sCreationMutex); 4192 sInstanceDead = true; 4193 } 4194 4195 MonitorAutoLock lock{mMonitor}; 4196 4197 RemoveIdleDailyMaintenanceJob(); 4198 RemoveAllFromMemory(); 4199 // CloseDB does async work and will call FinishAsyncShutdown once done. 4200 CloseDB(eShutdown); 4201 return NS_OK; 4202 } 4203 4204 NS_IMETHODIMP 4205 PermissionManager::GetState(nsIPropertyBag** aBagOut) { 4206 nsCOMPtr<nsIWritablePropertyBag2> propertyBag = 4207 do_CreateInstance("@mozilla.org/hash-property-bag;1"); 4208 4209 nsresult rv = propertyBag->SetPropertyAsInt32(u"state"_ns, mState); 4210 if (NS_WARN_IF(NS_FAILED(rv))) { 4211 return rv; 4212 } 4213 4214 propertyBag.forget(aBagOut); 4215 4216 return NS_OK; 4217 } 4218 4219 nsCOMPtr<nsIAsyncShutdownClient> PermissionManager::GetAsyncShutdownBarrier() 4220 const { 4221 nsresult rv; 4222 nsCOMPtr<nsIAsyncShutdownService> svc = 4223 do_GetService("@mozilla.org/async-shutdown-service;1", &rv); 4224 if (NS_FAILED(rv)) { 4225 return nullptr; 4226 } 4227 4228 nsCOMPtr<nsIAsyncShutdownClient> client; 4229 // This feels very late but there seem to be other services that rely on 4230 // us later than "profile-before-change". 4231 rv = svc->GetXpcomWillShutdown(getter_AddRefs(client)); 4232 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 4233 4234 return client; 4235 } 4236 4237 void PermissionManager::MaybeStripOriginAttributes( 4238 bool aForceStrip, OriginAttributes& aOriginAttributes) { 4239 uint32_t flags = 0; 4240 4241 if (aForceStrip || !StaticPrefs::permissions_isolateBy_privateBrowsing()) { 4242 flags |= OriginAttributes::STRIP_PRIVATE_BROWSING_ID; 4243 } 4244 4245 if (aForceStrip || !StaticPrefs::permissions_isolateBy_userContext()) { 4246 flags |= OriginAttributes::STRIP_USER_CONTEXT_ID; 4247 } 4248 4249 if (flags != 0) { 4250 aOriginAttributes.StripAttributes(flags); 4251 } 4252 } 4253 4254 } // namespace mozilla