ContentPrincipal.cpp (24019B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 sw=2 et 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 "ContentPrincipal.h" 8 9 #include "mozIThirdPartyUtil.h" 10 #include "nsContentUtils.h" 11 #include "nscore.h" 12 #include "nsScriptSecurityManager.h" 13 #include "nsString.h" 14 #include "nsReadableUtils.h" 15 #include "pratom.h" 16 #include "nsIURI.h" 17 #include "nsIURL.h" 18 #include "nsIStandardURL.h" 19 #include "nsIURIWithSpecialOrigin.h" 20 #include "nsIURIMutator.h" 21 #include "nsJSPrincipals.h" 22 #include "nsIEffectiveTLDService.h" 23 #include "nsIClassInfoImpl.h" 24 #include "nsIObjectInputStream.h" 25 #include "nsIObjectOutputStream.h" 26 #include "nsIProtocolHandler.h" 27 #include "nsError.h" 28 #include "nsIContentSecurityPolicy.h" 29 #include "nsNetCID.h" 30 #include "js/RealmIterators.h" 31 #include "js/Wrapper.h" 32 33 #include "mozilla/dom/BlobURLProtocolHandler.h" 34 #include "mozilla/dom/ScriptSettings.h" 35 #include "mozilla/ClearOnShutdown.h" 36 #include "mozilla/ExtensionPolicyService.h" 37 #include "mozilla/Preferences.h" 38 39 #include "nsSerializationHelper.h" 40 41 #include "js/JSON.h" 42 #include "ContentPrincipalJSONHandler.h" 43 44 using namespace mozilla; 45 46 NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, 0, NS_PRINCIPAL_CID) 47 NS_IMPL_QUERY_INTERFACE_CI(ContentPrincipal, nsIPrincipal) 48 NS_IMPL_CI_INTERFACE_GETTER(ContentPrincipal, nsIPrincipal) 49 50 ContentPrincipal::ContentPrincipal(nsIURI* aURI, 51 const OriginAttributes& aOriginAttributes, 52 const nsACString& aOriginNoSuffix, 53 nsIURI* aInitialDomain) 54 : BasePrincipal(eContentPrincipal, aOriginNoSuffix, aOriginAttributes), 55 mURI(aURI), 56 mDomain(aInitialDomain) { 57 if (mDomain) { 58 // We're just creating the principal, so no need to re-compute wrappers. 59 SetHasExplicitDomain(); 60 } 61 62 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 63 // Assert that the URI we get here isn't any of the schemes that we know we 64 // should not get here. These schemes always either inherit their principal 65 // or fall back to a null principal. These are schemes which return 66 // URI_INHERITS_SECURITY_CONTEXT from their protocol handler's 67 // GetProtocolFlags function. 68 bool hasFlag = false; 69 MOZ_DIAGNOSTIC_ASSERT( 70 NS_SUCCEEDED(NS_URIChainHasFlags( 71 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &hasFlag)) && 72 !hasFlag); 73 #endif 74 } 75 76 ContentPrincipal::ContentPrincipal(ContentPrincipal* aOther, 77 const OriginAttributes& aOriginAttributes) 78 : BasePrincipal(aOther, aOriginAttributes), 79 mURI(aOther->mURI), 80 mDomain(aOther->mDomain), 81 mAddon(aOther->mAddon) {} 82 83 ContentPrincipal::~ContentPrincipal() = default; 84 85 nsresult ContentPrincipal::GetScriptLocation(nsACString& aStr) { 86 return mURI->GetSpec(aStr); 87 } 88 89 /* static */ 90 nsresult ContentPrincipal::GenerateOriginNoSuffixFromURI( 91 nsIURI* aURI, nsACString& aOriginNoSuffix) { 92 if (!aURI) { 93 return NS_ERROR_FAILURE; 94 } 95 96 nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(aURI); 97 if (!origin) { 98 return NS_ERROR_FAILURE; 99 } 100 101 MOZ_ASSERT(!NS_IsAboutBlankAllowQueryAndFragment(origin), 102 "The inner URI for about:blank must be moz-safe-about:blank"); 103 104 // Handle non-strict file:// uris. 105 if (!nsScriptSecurityManager::GetStrictFileOriginPolicy() && 106 NS_URIIsLocalFile(origin)) { 107 // If strict file origin policy is not in effect, all local files are 108 // considered to be same-origin, so return a known dummy origin here. 109 aOriginNoSuffix.AssignLiteral("file://UNIVERSAL_FILE_URI_ORIGIN"); 110 return NS_OK; 111 } 112 113 nsresult rv; 114 // NB: This is only compiled for Thunderbird/Suite. 115 #if IS_ORIGIN_IS_FULL_SPEC_DEFINED 116 bool fullSpec = false; 117 rv = NS_URIChainHasFlags(origin, nsIProtocolHandler::ORIGIN_IS_FULL_SPEC, 118 &fullSpec); 119 NS_ENSURE_SUCCESS(rv, rv); 120 if (fullSpec) { 121 return origin->GetAsciiSpec(aOriginNoSuffix); 122 } 123 #endif 124 125 // We want the invariant that prinA.origin == prinB.origin i.f.f. 126 // prinA.equals(prinB). However, this requires that we impose certain 127 // constraints on the behavior and origin semantics of principals, and in 128 // particular, forbid creating origin strings for principals whose equality 129 // constraints are not expressible as strings (i.e. object equality). 130 // Moreover, we want to forbid URIs containing the magic "^" we use as a 131 // separating character for origin attributes. 132 // 133 // These constraints can generally be achieved by restricting .origin to 134 // nsIStandardURL-based URIs, but there are a few other URI schemes that we 135 // need to handle. 136 if (origin->SchemeIs("about") || 137 (origin->SchemeIs("moz-safe-about") && 138 // We generally consider two about:foo origins to be same-origin, but 139 // about:blank is special since it can be generated from different 140 // sources. We check for moz-safe-about:blank since origin is an 141 // innermost URI. 142 !StringBeginsWith(origin->GetSpecOrDefault(), 143 "moz-safe-about:blank"_ns))) { 144 rv = origin->GetAsciiSpec(aOriginNoSuffix); 145 NS_ENSURE_SUCCESS(rv, rv); 146 147 int32_t pos = aOriginNoSuffix.FindChar('?'); 148 int32_t hashPos = aOriginNoSuffix.FindChar('#'); 149 150 if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) { 151 pos = hashPos; 152 } 153 154 if (pos != kNotFound) { 155 aOriginNoSuffix.Truncate(pos); 156 } 157 158 // These URIs could technically contain a '^', but they never should. 159 if (NS_WARN_IF(aOriginNoSuffix.FindChar('^', 0) != -1)) { 160 aOriginNoSuffix.Truncate(); 161 return NS_ERROR_FAILURE; 162 } 163 return NS_OK; 164 } 165 166 // This URL can be a blobURL. In this case, we should use the 'parent' 167 // principal instead. 168 nsCOMPtr<nsIPrincipal> blobPrincipal; 169 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal( 170 origin, getter_AddRefs(blobPrincipal))) { 171 MOZ_ASSERT(blobPrincipal); 172 return blobPrincipal->GetOriginNoSuffix(aOriginNoSuffix); 173 } 174 175 // If we reached this branch, we can only create an origin if we have a 176 // nsIStandardURL. So, we query to a nsIStandardURL, and fail if we aren't 177 // an instance of an nsIStandardURL nsIStandardURLs have the good property 178 // of escaping the '^' character in their specs, which means that we can be 179 // sure that the caret character (which is reserved for delimiting the end 180 // of the spec, and the beginning of the origin attributes) is not present 181 // in the origin string 182 nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin); 183 if (!standardURL) { 184 return NS_ERROR_FAILURE; 185 } 186 187 // See whether we have a useful hostPort. If we do, use that. 188 nsAutoCString hostPort; 189 if (!origin->SchemeIs("chrome")) { 190 rv = origin->GetAsciiHostPort(hostPort); 191 NS_ENSURE_SUCCESS(rv, rv); 192 } 193 if (!hostPort.IsEmpty()) { 194 rv = origin->GetScheme(aOriginNoSuffix); 195 NS_ENSURE_SUCCESS(rv, rv); 196 aOriginNoSuffix.AppendLiteral("://"); 197 aOriginNoSuffix.Append(hostPort); 198 return NS_OK; 199 } 200 201 rv = aURI->GetAsciiSpec(aOriginNoSuffix); 202 NS_ENSURE_SUCCESS(rv, rv); 203 204 // The origin, when taken from the spec, should not contain the ref part of 205 // the URL. 206 207 int32_t pos = aOriginNoSuffix.FindChar('?'); 208 int32_t hashPos = aOriginNoSuffix.FindChar('#'); 209 210 if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) { 211 pos = hashPos; 212 } 213 214 if (pos != kNotFound) { 215 aOriginNoSuffix.Truncate(pos); 216 } 217 218 return NS_OK; 219 } 220 221 bool ContentPrincipal::SubsumesInternal( 222 nsIPrincipal* aOther, 223 BasePrincipal::DocumentDomainConsideration aConsideration) { 224 MOZ_ASSERT(aOther); 225 226 // For ContentPrincipal, Subsumes is equivalent to Equals. 227 if (aOther == this) { 228 return true; 229 } 230 231 // If either the subject or the object has changed its principal by 232 // explicitly setting document.domain then the other must also have 233 // done so in order to be considered the same origin. This prevents 234 // DNS spoofing based on document.domain (154930) 235 if (aConsideration == ConsiderDocumentDomain) { 236 // Get .domain on each principal. 237 nsCOMPtr<nsIURI> thisDomain, otherDomain; 238 GetDomain(getter_AddRefs(thisDomain)); 239 aOther->GetDomain(getter_AddRefs(otherDomain)); 240 241 // If either has .domain set, we have equality i.f.f. the domains match. 242 // Otherwise, we fall through to the non-document-domain-considering case. 243 if (thisDomain || otherDomain) { 244 bool isMatch = 245 nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain); 246 #ifdef DEBUG 247 if (isMatch) { 248 nsAutoCString thisSiteOrigin, otherSiteOrigin; 249 MOZ_ALWAYS_SUCCEEDS(GetSiteOrigin(thisSiteOrigin)); 250 MOZ_ALWAYS_SUCCEEDS(aOther->GetSiteOrigin(otherSiteOrigin)); 251 MOZ_ASSERT( 252 thisSiteOrigin == otherSiteOrigin, 253 "SubsumesConsideringDomain passed with mismatched siteOrigin!"); 254 } 255 #endif 256 return isMatch; 257 } 258 } 259 260 // Do a fast check (including origin attributes) or a slow uri comparison. 261 return FastEquals(aOther) || aOther->IsSameOrigin(mURI); 262 } 263 264 NS_IMETHODIMP 265 ContentPrincipal::GetURI(nsIURI** aURI) { 266 *aURI = do_AddRef(mURI).take(); 267 return NS_OK; 268 } 269 270 bool ContentPrincipal::MayLoadInternal(nsIURI* aURI) { 271 MOZ_ASSERT(aURI); 272 273 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE) 274 nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin = 275 do_QueryInterface(aURI); 276 if (uriWithSpecialOrigin) { 277 nsCOMPtr<nsIURI> origin; 278 nsresult rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin)); 279 if (NS_WARN_IF(NS_FAILED(rv))) { 280 return false; 281 } 282 MOZ_ASSERT(origin); 283 OriginAttributes attrs; 284 RefPtr<BasePrincipal> principal = 285 BasePrincipal::CreateContentPrincipal(origin, attrs); 286 return nsIPrincipal::Subsumes(principal); 287 } 288 #endif 289 290 nsCOMPtr<nsIPrincipal> blobPrincipal; 291 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal( 292 aURI, getter_AddRefs(blobPrincipal))) { 293 MOZ_ASSERT(blobPrincipal); 294 return nsIPrincipal::Subsumes(blobPrincipal); 295 } 296 297 // If this principal is associated with an addon, check whether that addon 298 // has been given permission to load from this domain. 299 if (AddonAllowsLoad(aURI)) { 300 return true; 301 } 302 303 if (nsScriptSecurityManager::SecurityCompareURIs(mURI, aURI)) { 304 return true; 305 } 306 307 return false; 308 } 309 310 NS_IMETHODIMP 311 ContentPrincipal::GetDomain(nsIURI** aDomain) { 312 if (!GetHasExplicitDomain()) { 313 *aDomain = nullptr; 314 return NS_OK; 315 } 316 317 mozilla::MutexAutoLock lock(mMutex); 318 NS_ADDREF(*aDomain = mDomain); 319 return NS_OK; 320 } 321 322 NS_IMETHODIMP 323 ContentPrincipal::SetDomain(nsIURI* aDomain) { 324 AssertIsOnMainThread(); 325 MOZ_ASSERT(aDomain); 326 327 { 328 mozilla::MutexAutoLock lock(mMutex); 329 mDomain = aDomain; 330 SetHasExplicitDomain(); 331 } 332 333 // Set the changed-document-domain flag on compartments containing realms 334 // using this principal. 335 auto cb = [](JSContext*, void*, JS::Realm* aRealm, 336 const JS::AutoRequireNoGC& nogc) { 337 JS::Compartment* comp = JS::GetCompartmentForRealm(aRealm); 338 xpc::SetCompartmentChangedDocumentDomain(comp); 339 }; 340 JSPrincipals* principals = 341 nsJSPrincipals::get(static_cast<nsIPrincipal*>(this)); 342 343 dom::AutoJSAPI jsapi; 344 jsapi.Init(); 345 JS::IterateRealmsWithPrincipals(jsapi.cx(), principals, nullptr, cb); 346 347 return NS_OK; 348 } 349 350 static nsresult GetSpecialBaseDomain(const nsCOMPtr<nsIURI>& aURI, 351 bool* aHandled, nsACString& aBaseDomain) { 352 *aHandled = false; 353 354 // Special handling for a file URI. 355 if (NS_URIIsLocalFile(aURI)) { 356 // If strict file origin policy is not in effect, all local files are 357 // considered to be same-origin, so return a known dummy domain here. 358 if (!nsScriptSecurityManager::GetStrictFileOriginPolicy()) { 359 *aHandled = true; 360 aBaseDomain.AssignLiteral("UNIVERSAL_FILE_URI_ORIGIN"); 361 return NS_OK; 362 } 363 364 // Otherwise, we return the file path. 365 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI); 366 367 if (url) { 368 *aHandled = true; 369 return url->GetFilePath(aBaseDomain); 370 } 371 } 372 373 bool hasNoRelativeFlag; 374 nsresult rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_NORELATIVE, 375 &hasNoRelativeFlag); 376 if (NS_WARN_IF(NS_FAILED(rv))) { 377 return rv; 378 } 379 380 // In case of FTP we want to get base domain via TLD service even if FTP 381 // protocol handler is disabled and the scheme is handled by external protocol 382 // handler which returns URI_NORELATIVE flag. 383 if (hasNoRelativeFlag && !aURI->SchemeIs("ftp")) { 384 *aHandled = true; 385 return aURI->GetSpec(aBaseDomain); 386 } 387 388 // For local resources we can't get a meaningful base domain. 389 bool isUIResource = false; 390 if (NS_SUCCEEDED(NS_URIChainHasFlags( 391 aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isUIResource)) && 392 isUIResource) { 393 *aHandled = true; 394 return aURI->GetPrePath(aBaseDomain); 395 } 396 397 if (aURI->SchemeIs("indexeddb")) { 398 *aHandled = true; 399 return aURI->GetSpec(aBaseDomain); 400 } 401 402 return NS_OK; 403 } 404 405 NS_IMETHODIMP 406 ContentPrincipal::GetBaseDomain(nsACString& aBaseDomain) { 407 // Handle some special URIs first. 408 bool handled; 409 nsresult rv = GetSpecialBaseDomain(mURI, &handled, aBaseDomain); 410 NS_ENSURE_SUCCESS(rv, rv); 411 412 if (handled) { 413 return NS_OK; 414 } 415 416 // For everything else, we ask the TLD service via the ThirdPartyUtil. 417 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = 418 do_GetService(THIRDPARTYUTIL_CONTRACTID); 419 if (!thirdPartyUtil) { 420 return NS_ERROR_FAILURE; 421 } 422 423 return thirdPartyUtil->GetBaseDomain(mURI, aBaseDomain); 424 } 425 426 NS_IMETHODIMP 427 ContentPrincipal::GetSiteOriginNoSuffix(nsACString& aSiteOrigin) { 428 nsresult rv = GetOriginNoSuffix(aSiteOrigin); 429 NS_ENSURE_SUCCESS(rv, rv); 430 431 // The originNoSuffix is already normalized when being generated by 432 // GenerateOriginNoSuffixFromURI. Avoid re-parsing the URI here. 433 // 434 // For all URIs which are not http(s), the site-origin matches the full 435 // origin, so can immediately return. 436 int32_t schemeEnd = aSiteOrigin.Find("://"); 437 if (schemeEnd == kNotFound) { 438 return NS_OK; 439 } 440 441 // Check for a http(s) scheme. For all URIs which are not http(s), the 442 // site-origin matches the full origin, so we can immediately return. 443 nsDependentCSubstring scheme(aSiteOrigin, 0, schemeEnd); 444 if (scheme != "http"_ns && scheme != "https"_ns) { 445 return NS_OK; 446 } 447 448 // The site-origin never includes a port (:[0-9]*), so remove it. 449 // NOTE: This avoids using RFindChar to avoid false-positives. 450 const char* portStart = aSiteOrigin.EndReading() - 1; 451 while ('0' <= *portStart && *portStart <= '9') { 452 --portStart; 453 } 454 if (*portStart == ':') { 455 aSiteOrigin.Truncate(portStart - aSiteOrigin.BeginReading()); 456 } 457 458 nsCOMPtr<nsIEffectiveTLDService> tldService = 459 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); 460 if (!tldService) { 461 return NS_ERROR_NOT_AVAILABLE; 462 } 463 464 // Get the base-domain from the host. We use the "fromHost" variant to avoid 465 // unnecessary changes to the site-origin. 466 nsAutoCString baseDomain; 467 int32_t hostStart = schemeEnd + 3; 468 nsDependentCSubstring host(aSiteOrigin, hostStart); 469 rv = tldService->GetBaseDomainFromHost(host, 0, baseDomain); 470 if (NS_FAILED(rv)) { 471 // If this is an IP address or something like "localhost", there's nothing 472 // else to be done. 473 if (rv != NS_ERROR_HOST_IS_IP_ADDRESS && 474 rv != NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS && 475 rv != NS_ERROR_INVALID_ARG) { 476 return rv; 477 } 478 return NS_OK; 479 } 480 481 // Replace the host in aSiteOrigin with baseDomain. 482 if (baseDomain != host) { 483 aSiteOrigin.Replace(hostStart, aSiteOrigin.Length() - hostStart, 484 baseDomain); 485 } 486 return NS_OK; 487 } 488 489 nsresult ContentPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) { 490 nsCString siteOrigin; 491 nsresult rv = GetSiteOrigin(siteOrigin); 492 NS_ENSURE_SUCCESS(rv, rv); 493 494 RefPtr<BasePrincipal> principal = CreateContentPrincipal(siteOrigin); 495 if (!principal) { 496 NS_WARNING("could not instantiate content principal"); 497 return NS_ERROR_FAILURE; 498 } 499 500 aSite.Init(principal); 501 return NS_OK; 502 } 503 504 RefPtr<extensions::WebExtensionPolicyCore> ContentPrincipal::AddonPolicyCore() { 505 mozilla::MutexAutoLock lock(mMutex); 506 if (!mAddon.isSome()) { 507 NS_ENSURE_TRUE(mURI, nullptr); 508 509 RefPtr<extensions::WebExtensionPolicyCore> core; 510 if (mURI->SchemeIs("moz-extension")) { 511 nsCString host; 512 NS_ENSURE_SUCCESS(mURI->GetHost(host), nullptr); 513 core = ExtensionPolicyService::GetCoreByHost(host); 514 } 515 516 mAddon.emplace(core); 517 } 518 return *mAddon; 519 } 520 521 NS_IMETHODIMP 522 ContentPrincipal::GetAddonId(nsAString& aAddonId) { 523 if (RefPtr<extensions::WebExtensionPolicyCore> policy = AddonPolicyCore()) { 524 policy->Id()->ToString(aAddonId); 525 } else { 526 aAddonId.Truncate(); 527 } 528 return NS_OK; 529 } 530 531 NS_IMETHODIMP 532 ContentPrincipal::Deserializer::Read(nsIObjectInputStream* aStream) { 533 MOZ_ASSERT(!mPrincipal); 534 535 nsCOMPtr<nsISupports> supports; 536 nsCOMPtr<nsIURI> principalURI; 537 nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports)); 538 if (NS_FAILED(rv)) { 539 return rv; 540 } 541 542 principalURI = do_QueryInterface(supports); 543 // Enforce re-parsing about: URIs so that if they change, we continue to use 544 // their new principals correctly. 545 if (principalURI->SchemeIs("about")) { 546 nsAutoCString spec; 547 principalURI->GetSpec(spec); 548 NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(principalURI), spec), 549 NS_ERROR_FAILURE); 550 } 551 552 nsCOMPtr<nsIURI> domain; 553 rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports)); 554 if (NS_FAILED(rv)) { 555 return rv; 556 } 557 558 domain = do_QueryInterface(supports); 559 560 nsAutoCString suffix; 561 rv = aStream->ReadCString(suffix); 562 NS_ENSURE_SUCCESS(rv, rv); 563 564 OriginAttributes attrs; 565 bool ok = attrs.PopulateFromSuffix(suffix); 566 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); 567 568 // Since Bug 965637 we do not serialize the CSP within the 569 // Principal anymore. Nevertheless there might still be 570 // serialized Principals that do have a serialized CSP. 571 // For now, we just read the CSP here but do not actually 572 // consume it. Please note that we deliberately ignore 573 // the return value to avoid CSP deserialization problems. 574 // After Bug 1508939 we will have a new serialization for 575 // Principals which allows us to update the code here. 576 // Additionally, the format for serialized CSPs changed 577 // within Bug 965637 which also can cause failures within 578 // the CSP deserialization code. 579 (void)NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports)); 580 581 nsAutoCString originNoSuffix; 582 rv = GenerateOriginNoSuffixFromURI(principalURI, originNoSuffix); 583 NS_ENSURE_SUCCESS(rv, rv); 584 585 mPrincipal = 586 new ContentPrincipal(principalURI, attrs, originNoSuffix, domain); 587 return NS_OK; 588 } 589 590 nsresult ContentPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) { 591 nsAutoCString principalURI; 592 nsresult rv = mURI->GetSpec(principalURI); 593 NS_ENSURE_SUCCESS(rv, rv); 594 595 // We turn each int enum field into a JSON string key of the object, aWriter 596 // is set up to be inside of the inner object that has stringified enum keys 597 // An example inner object might be: 598 // 599 // eURI eSuffix 600 // | | 601 // {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"} 602 // | | | | 603 // ----------------------------- | 604 // | | | 605 // Key ---------------------- 606 // | 607 // Value 608 WriteJSONProperty<eURI>(aWriter, principalURI); 609 610 if (GetHasExplicitDomain()) { 611 nsAutoCString domainStr; 612 { 613 MutexAutoLock lock(mMutex); 614 rv = mDomain->GetSpec(domainStr); 615 NS_ENSURE_SUCCESS(rv, rv); 616 } 617 WriteJSONProperty<eDomain>(aWriter, domainStr); 618 } 619 620 nsAutoCString suffix; 621 OriginAttributesRef().CreateSuffix(suffix); 622 if (suffix.Length() > 0) { 623 WriteJSONProperty<eSuffix>(aWriter, suffix); 624 } 625 626 return NS_OK; 627 } 628 629 bool ContentPrincipalJSONHandler::startObject() { 630 switch (mState) { 631 case State::Init: 632 mState = State::StartObject; 633 break; 634 default: 635 NS_WARNING("Unexpected object value"); 636 mState = State::Error; 637 return false; 638 } 639 640 return true; 641 } 642 643 bool ContentPrincipalJSONHandler::propertyName(const JS::Latin1Char* name, 644 size_t length) { 645 switch (mState) { 646 case State::StartObject: 647 case State::AfterPropertyValue: { 648 if (length != 1) { 649 NS_WARNING( 650 nsPrintfCString("Unexpected property name length: %zu", length) 651 .get()); 652 mState = State::Error; 653 return false; 654 } 655 656 char key = char(name[0]); 657 switch (key) { 658 case ContentPrincipal::URIKey: 659 mState = State::URIKey; 660 break; 661 case ContentPrincipal::DomainKey: 662 mState = State::DomainKey; 663 break; 664 case ContentPrincipal::SuffixKey: 665 mState = State::SuffixKey; 666 break; 667 default: 668 NS_WARNING( 669 nsPrintfCString("Unexpected property name: '%c'", key).get()); 670 mState = State::Error; 671 return false; 672 } 673 break; 674 } 675 default: 676 NS_WARNING("Unexpected property name"); 677 mState = State::Error; 678 return false; 679 } 680 681 return true; 682 } 683 684 bool ContentPrincipalJSONHandler::endObject() { 685 switch (mState) { 686 case State::AfterPropertyValue: { 687 MOZ_ASSERT(mPrincipalURI); 688 // NOTE: mDomain is optional. 689 690 nsAutoCString originNoSuffix; 691 nsresult rv = ContentPrincipal::GenerateOriginNoSuffixFromURI( 692 mPrincipalURI, originNoSuffix); 693 if (NS_FAILED(rv)) { 694 mState = State::Error; 695 return false; 696 } 697 698 mPrincipal = 699 new ContentPrincipal(mPrincipalURI, mAttrs, originNoSuffix, mDomain); 700 MOZ_ASSERT(mPrincipal); 701 702 mState = State::EndObject; 703 break; 704 } 705 default: 706 NS_WARNING("Unexpected end of object"); 707 mState = State::Error; 708 return false; 709 } 710 711 return true; 712 } 713 714 bool ContentPrincipalJSONHandler::stringValue(const JS::Latin1Char* str, 715 size_t length) { 716 switch (mState) { 717 case State::URIKey: { 718 nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length); 719 720 nsresult rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec); 721 if (NS_FAILED(rv)) { 722 mState = State::Error; 723 return false; 724 } 725 726 { 727 // Enforce re-parsing about: URIs so that if they change, we 728 // continue to use their new principals correctly. 729 if (mPrincipalURI->SchemeIs("about")) { 730 nsAutoCString spec; 731 mPrincipalURI->GetSpec(spec); 732 rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec); 733 if (NS_FAILED(rv)) { 734 mState = State::Error; 735 return false; 736 } 737 } 738 } 739 740 mState = State::AfterPropertyValue; 741 break; 742 } 743 case State::DomainKey: { 744 nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length); 745 746 nsresult rv = NS_NewURI(getter_AddRefs(mDomain), spec); 747 if (NS_FAILED(rv)) { 748 mState = State::Error; 749 return false; 750 } 751 752 mState = State::AfterPropertyValue; 753 break; 754 } 755 case State::SuffixKey: { 756 nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length); 757 if (!mAttrs.PopulateFromSuffix(attrs)) { 758 mState = State::Error; 759 return false; 760 } 761 762 mState = State::AfterPropertyValue; 763 break; 764 } 765 default: 766 NS_WARNING("Unexpected string value"); 767 mState = State::Error; 768 return false; 769 } 770 771 return true; 772 }