/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/BasePrincipal.h" #include "nsDocShell.h" #include "ExpandedPrincipal.h" #include "nsNetUtil.h" #include "nsContentUtils.h" #include "nsIOService.h" #include "nsIURIWithSpecialOrigin.h" #include "nsScriptSecurityManager.h" #include "nsServiceManagerUtils.h" #include "nsAboutProtocolUtils.h" #include "ThirdPartyUtil.h" #include "mozilla/ContentPrincipal.h" #include "mozilla/ExtensionPolicyService.h" #include "mozilla/NullPrincipal.h" #include "mozilla/dom/BlobURLProtocolHandler.h" #include "mozilla/dom/ChromeUtils.h" #include "mozilla/dom/ReferrerInfo.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/nsMixedContentBlocker.h" #include "mozilla/Components.h" #include "mozilla/dom/StorageUtils.h" #include "mozilla/dom/StorageUtils.h" #include "mozilla/JSONStringWriteFuncs.h" #include "mozilla/JSONWriter.h" #include "nsIURL.h" #include "nsEffectiveTLDService.h" #include "nsIURIMutator.h" #include "mozilla/StaticPrefs_permissions.h" #include "nsIURIMutator.h" #include "nsMixedContentBlocker.h" #include "prnetdb.h" #include "nsIURIFixup.h" #include "mozilla/dom/StorageUtils.h" #include "mozilla/StorageAccess.h" #include "nsPIDOMWindow.h" #include "nsIURIMutator.h" #include "mozilla/PermissionManager.h" #include "nsSerializationHelper.h" #include "js/JSON.h" #include "ContentPrincipalJSONHandler.h" #include "ExpandedPrincipalJSONHandler.h" #include "NullPrincipalJSONHandler.h" #include "PrincipalJSONHandler.h" #include "SubsumedPrincipalJSONHandler.h" namespace mozilla { BasePrincipal::BasePrincipal(PrincipalKind aKind, const nsACString& aOriginNoSuffix, const OriginAttributes& aOriginAttributes) : mOriginNoSuffix(NS_Atomize(aOriginNoSuffix)), mOriginSuffix(aOriginAttributes.CreateSuffixAtom()), mOriginAttributes(aOriginAttributes), mKind(aKind), mHasExplicitDomain(false) {} BasePrincipal::BasePrincipal(BasePrincipal* aOther, const OriginAttributes& aOriginAttributes) : mOriginNoSuffix(aOther->mOriginNoSuffix), mOriginSuffix(aOriginAttributes.CreateSuffixAtom()), mOriginAttributes(aOriginAttributes), mKind(aOther->mKind), mHasExplicitDomain(aOther->mHasExplicitDomain.load()) {} BasePrincipal::~BasePrincipal() = default; NS_IMETHODIMP BasePrincipal::GetOrigin(nsACString& aOrigin) { nsresult rv = GetOriginNoSuffix(aOrigin); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString suffix; rv = GetOriginSuffix(suffix); NS_ENSURE_SUCCESS(rv, rv); aOrigin.Append(suffix); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetWebExposedOriginSerialization(nsACString& aOrigin) { aOrigin.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_ERROR_NOT_AVAILABLE; } return nsContentUtils::GetWebExposedOriginSerialization(prinURI, aOrigin); } NS_IMETHODIMP BasePrincipal::GetHostPort(nsACString& aRes) { aRes.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetHostPort(aRes); } NS_IMETHODIMP BasePrincipal::GetHost(nsACString& aRes) { aRes.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetHost(aRes); } NS_IMETHODIMP BasePrincipal::GetOriginNoSuffix(nsACString& aOrigin) { mOriginNoSuffix->ToUTF8String(aOrigin); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetSiteOrigin(nsACString& aSiteOrigin) { nsresult rv = GetSiteOriginNoSuffix(aSiteOrigin); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString suffix; rv = GetOriginSuffix(suffix); NS_ENSURE_SUCCESS(rv, rv); aSiteOrigin.Append(suffix); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetSiteOriginNoSuffix(nsACString& aSiteOrigin) { return GetOriginNoSuffix(aSiteOrigin); } template bool ContainerPrincipalJSONHandler::ProcessInnerResult( bool aResult) { if (!aResult) { NS_WARNING("Failed to parse inner object"); mState = State::Error; return false; } return true; } template bool ContainerPrincipalJSONHandler::startObject() { if (mInnerHandler.isSome()) { return CallOnInner([&](auto& aInner) { return aInner.startObject(); }); } switch (mState) { case State::Init: mState = State::StartObject; break; case State::SystemPrincipal_Key: mState = State::SystemPrincipal_StartObject; break; default: NS_WARNING("Unexpected object value"); mState = State::Error; return false; } return true; } template bool ContainerPrincipalJSONHandler::propertyName( const JS::Latin1Char* name, size_t length) { if (mInnerHandler.isSome()) { return CallOnInner( [&](auto& aInner) { return aInner.propertyName(name, length); }); } switch (mState) { case State::StartObject: { if (length != 1) { NS_WARNING( nsPrintfCString("Unexpected property name length: %zu", length) .get()); mState = State::Error; return false; } char key = char(name[0]); switch (key) { case BasePrincipal::NullPrincipalKey: mState = State::NullPrincipal_Inner; mInnerHandler.emplace(VariantType()); break; case BasePrincipal::ContentPrincipalKey: mState = State::ContentPrincipal_Inner; mInnerHandler.emplace(VariantType()); break; case BasePrincipal::SystemPrincipalKey: mState = State::SystemPrincipal_Key; break; default: if constexpr (CanContainExpandedPrincipal) { if (key == BasePrincipal::ExpandedPrincipalKey) { mState = State::ExpandedPrincipal_Inner; mInnerHandler.emplace( VariantType()); break; } } NS_WARNING( nsPrintfCString("Unexpected property name: '%c'", key).get()); mState = State::Error; return false; } break; } default: NS_WARNING("Unexpected property name"); mState = State::Error; return false; } return true; } template bool ContainerPrincipalJSONHandler::endObject() { if (mInnerHandler.isSome()) { return CallOnInner([&](auto& aInner) { if (!aInner.endObject()) { return false; } if (aInner.HasAccepted()) { this->mPrincipal = aInner.mPrincipal.forget(); MOZ_ASSERT(this->mPrincipal); mInnerHandler.reset(); } return true; }); } switch (mState) { case State::SystemPrincipal_StartObject: mState = State::SystemPrincipal_EndObject; break; case State::SystemPrincipal_EndObject: this->mPrincipal = BasePrincipal::Cast(nsContentUtils::GetSystemPrincipal()); mState = State::EndObject; break; case State::NullPrincipal_Inner: mState = State::EndObject; break; case State::ContentPrincipal_Inner: mState = State::EndObject; break; default: if constexpr (CanContainExpandedPrincipal) { if (mState == State::ExpandedPrincipal_Inner) { mState = State::EndObject; break; } } NS_WARNING("Unexpected end of object"); mState = State::Error; return false; } return true; } template bool ContainerPrincipalJSONHandler::startArray() { if constexpr (CanContainExpandedPrincipal) { if (mInnerHandler.isSome()) { return CallOnInner([&](auto& aInner) { return aInner.startArray(); }); } } NS_WARNING("Unexpected array value"); mState = State::Error; return false; } template bool ContainerPrincipalJSONHandler::endArray() { if constexpr (CanContainExpandedPrincipal) { if (mInnerHandler.isSome()) { return CallOnInner([&](auto& aInner) { return aInner.endArray(); }); } } NS_WARNING("Unexpected array value"); mState = State::Error; return false; } template bool ContainerPrincipalJSONHandler::stringValue( const JS::Latin1Char* str, size_t length) { if (mInnerHandler.isSome()) { return CallOnInner( [&](auto& aInner) { return aInner.stringValue(str, length); }); } NS_WARNING("Unexpected string value"); mState = State::Error; return false; } template class ContainerPrincipalJSONHandler; template class ContainerPrincipalJSONHandler; // Takes a JSON string and parses it turning it into a principal of the // corresponding type // // Given a content principal: // // inner JSON object // | // --------------------------------------------------------- // | | // {"1": {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}} // | | | | | // | ----------------------------- | // | | | | // PrincipalKind | | | // | ---------------------------- // SerializableKeys | // Value // already_AddRefed BasePrincipal::FromJSON( const nsACString& aJSON) { PrincipalJSONHandler handler; if (!JS::ParseJSONWithHandler( reinterpret_cast(aJSON.BeginReading()), aJSON.Length(), &handler)) { NS_WARNING( nsPrintfCString("Unable to parse: %s", aJSON.BeginReading()).get()); MOZ_ASSERT(false, "Unable to parse string as JSON to deserialize as a principal"); return nullptr; } return handler.Get(); } // Returns a JSON representation of the principal. // Calling BasePrincipal::FromJSON will deserialize the JSON into // the corresponding principal type. nsresult BasePrincipal::ToJSON(nsACString& aJSON) { MOZ_ASSERT(aJSON.IsEmpty(), "ToJSON only supports an empty result input"); aJSON.Truncate(); // NOTE: JSONWriter emits raw UTF-8 code units for non-ASCII range. JSONStringRefWriteFunc func(aJSON); JSONWriter writer(func, JSONWriter::CollectionStyle::SingleLineStyle); nsresult rv = ToJSON(writer); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult BasePrincipal::ToJSON(JSONWriter& aWriter) { static_assert(eKindMax < ArrayLength(JSONEnumKeyStrings)); aWriter.Start(JSONWriter::CollectionStyle::SingleLineStyle); nsresult rv = WriteJSONProperties(aWriter); NS_ENSURE_SUCCESS(rv, rv); aWriter.End(); return NS_OK; } nsresult BasePrincipal::WriteJSONProperties(JSONWriter& aWriter) { aWriter.StartObjectProperty(JSONEnumKeyStrings[Kind()], JSONWriter::CollectionStyle::SingleLineStyle); nsresult rv = WriteJSONInnerProperties(aWriter); NS_ENSURE_SUCCESS(rv, rv); aWriter.EndObject(); return NS_OK; } nsresult BasePrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) { return NS_OK; } bool BasePrincipal::FastSubsumesIgnoringFPD( nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) { MOZ_ASSERT(aOther); if (Kind() == eContentPrincipal && !dom::ChromeUtils::IsOriginAttributesEqualIgnoringFPD( mOriginAttributes, Cast(aOther)->mOriginAttributes)) { return false; } return SubsumesInternal(aOther, aConsideration); } bool BasePrincipal::Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) { MOZ_ASSERT(aOther); MOZ_ASSERT_IF(Kind() == eContentPrincipal, mOriginSuffix); // Expanded principals handle origin attributes for each of their // sub-principals individually, null principals do only simple checks for // pointer equality, and system principals are immune to origin attributes // checks, so only do this check for content principals. if (Kind() == eContentPrincipal && mOriginSuffix != Cast(aOther)->mOriginSuffix) { return false; } return SubsumesInternal(aOther, aConsideration); } NS_IMETHODIMP BasePrincipal::Equals(nsIPrincipal* aOther, bool* aResult) { NS_ENSURE_ARG_POINTER(aOther); *aResult = FastEquals(aOther); return NS_OK; } NS_IMETHODIMP BasePrincipal::EqualsForPermission(nsIPrincipal* aOther, bool aExactHost, bool* aResult) { *aResult = false; NS_ENSURE_ARG_POINTER(aOther); NS_ENSURE_ARG_POINTER(aResult); auto* other = Cast(aOther); if (Kind() != other->Kind()) { // Principals of different kinds can't be equal. return NS_OK; } if (Kind() == eSystemPrincipal) { *aResult = this == other; return NS_OK; } if (Kind() == eNullPrincipal) { // We don't store permissions for NullPrincipals. return NS_OK; } MOZ_ASSERT(Kind() == eExpandedPrincipal || Kind() == eContentPrincipal); // Certain origin attributes should not be used to isolate permissions. // Create a stripped copy of both OA sets to compare. mozilla::OriginAttributes ourAttrs = mOriginAttributes; PermissionManager::MaybeStripOriginAttributes(false, ourAttrs); mozilla::OriginAttributes theirAttrs = aOther->OriginAttributesRef(); PermissionManager::MaybeStripOriginAttributes(false, theirAttrs); if (ourAttrs != theirAttrs) { return NS_OK; } if (mOriginNoSuffix == other->mOriginNoSuffix) { *aResult = true; return NS_OK; } // If we are matching with an exact host, we're done now - the permissions // don't match otherwise, we need to start comparing subdomains! if (aExactHost) { return NS_OK; } nsCOMPtr ourURI; nsresult rv = GetURI(getter_AddRefs(ourURI)); NS_ENSURE_SUCCESS(rv, rv); // Some principal types may indicate success, but still return nullptr for // URI. NS_ENSURE_TRUE(ourURI, NS_ERROR_FAILURE); nsCOMPtr otherURI; rv = other->GetURI(getter_AddRefs(otherURI)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(otherURI, NS_ERROR_FAILURE); // Compare schemes nsAutoCString otherScheme; rv = otherURI->GetScheme(otherScheme); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString ourScheme; rv = ourURI->GetScheme(ourScheme); NS_ENSURE_SUCCESS(rv, rv); if (otherScheme != ourScheme) { return NS_OK; } // Compare ports int32_t otherPort; rv = otherURI->GetPort(&otherPort); NS_ENSURE_SUCCESS(rv, rv); int32_t ourPort; rv = ourURI->GetPort(&ourPort); NS_ENSURE_SUCCESS(rv, rv); if (otherPort != ourPort) { return NS_OK; } // Check if the host or any subdomain of their host matches. nsAutoCString otherHost; rv = otherURI->GetHost(otherHost); if (NS_FAILED(rv) || otherHost.IsEmpty()) { return NS_OK; } nsAutoCString ourHost; rv = ourURI->GetHost(ourHost); if (NS_FAILED(rv) || ourHost.IsEmpty()) { return NS_OK; } nsCOMPtr tldService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); if (!tldService) { NS_ERROR("Should have a tld service!"); return NS_ERROR_FAILURE; } // This loop will not loop forever, as GetNextSubDomain will eventually fail // with NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS. while (otherHost != ourHost) { rv = tldService->GetNextSubDomain(otherHost, otherHost); if (NS_FAILED(rv)) { if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { return NS_OK; } return rv; } } *aResult = true; return NS_OK; } NS_IMETHODIMP BasePrincipal::EqualsConsideringDomain(nsIPrincipal* aOther, bool* aResult) { NS_ENSURE_ARG_POINTER(aOther); *aResult = FastEqualsConsideringDomain(aOther); return NS_OK; } NS_IMETHODIMP BasePrincipal::EqualsURI(nsIURI* aOtherURI, bool* aResult) { *aResult = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->EqualsExceptRef(aOtherURI, aResult); } NS_IMETHODIMP BasePrincipal::Subsumes(nsIPrincipal* aOther, bool* aResult) { NS_ENSURE_ARG_POINTER(aOther); *aResult = FastSubsumes(aOther); return NS_OK; } NS_IMETHODIMP BasePrincipal::SubsumesConsideringDomain(nsIPrincipal* aOther, bool* aResult) { NS_ENSURE_ARG_POINTER(aOther); *aResult = FastSubsumesConsideringDomain(aOther); return NS_OK; } NS_IMETHODIMP BasePrincipal::SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther, bool* aResult) { NS_ENSURE_ARG_POINTER(aOther); *aResult = FastSubsumesConsideringDomainIgnoringFPD(aOther); return NS_OK; } NS_IMETHODIMP BasePrincipal::CheckMayLoad(nsIURI* aURI, bool aAllowIfInheritsPrincipal) { AssertIsOnMainThread(); return CheckMayLoadHelper(aURI, aAllowIfInheritsPrincipal, false, 0); } NS_IMETHODIMP BasePrincipal::CheckMayLoadWithReporting(nsIURI* aURI, bool aAllowIfInheritsPrincipal, uint64_t aInnerWindowID) { AssertIsOnMainThread(); return CheckMayLoadHelper(aURI, aAllowIfInheritsPrincipal, true, aInnerWindowID); } nsresult BasePrincipal::CheckMayLoadHelper(nsIURI* aURI, bool aAllowIfInheritsPrincipal, bool aReport, uint64_t aInnerWindowID) { AssertIsOnMainThread(); // Accesses non-threadsafe URI flags and the // non-threadsafe ExtensionPolicyService NS_ENSURE_ARG_POINTER(aURI); MOZ_ASSERT( aReport || aInnerWindowID == 0, "Why do we have an inner window id if we're not supposed to report?"); // Check the internal method first, which allows us to quickly approve loads // for the System Principal. if (MayLoadInternal(aURI)) { return NS_OK; } nsresult rv; if (aAllowIfInheritsPrincipal) { // If the caller specified to allow loads of URIs that inherit // our principal, allow the load if this URI inherits its principal. bool doesInheritSecurityContext; rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &doesInheritSecurityContext); if (NS_SUCCEEDED(rv) && doesInheritSecurityContext) { return NS_OK; } } // Web Accessible Resources in MV2 Extensions are marked with // URI_FETCHABLE_BY_ANYONE bool fetchableByAnyone; rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FETCHABLE_BY_ANYONE, &fetchableByAnyone); if (NS_SUCCEEDED(rv) && fetchableByAnyone) { return NS_OK; } // Get the principal uri for the last flag check or error. nsCOMPtr prinURI; rv = GetURI(getter_AddRefs(prinURI)); if (!(NS_SUCCEEDED(rv) && prinURI)) { return NS_ERROR_DOM_BAD_URI; } // If MV3 Extension uris are web accessible by this principal it is allowed to // load. bool maybeWebAccessible = false; NS_URIChainHasFlags(aURI, nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE, &maybeWebAccessible); NS_ENSURE_SUCCESS(rv, rv); if (maybeWebAccessible) { bool isWebAccessible = false; rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI( prinURI, aURI, &isWebAccessible); if (NS_SUCCEEDED(rv) && isWebAccessible) { return NS_OK; } } if (aReport) { nsScriptSecurityManager::ReportError( "CheckSameOriginError", prinURI, aURI, mOriginAttributes.mPrivateBrowsingId > 0, aInnerWindowID); } return NS_ERROR_DOM_BAD_URI; } NS_IMETHODIMP BasePrincipal::IsThirdPartyURI(nsIURI* aURI, bool* aRes) { if (IsSystemPrincipal() || (AddonPolicyCore() && AddonAllowsLoad(aURI))) { *aRes = false; return NS_OK; } *aRes = true; // If we do not have a URI its always 3rd party. nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance(); return thirdPartyUtil->IsThirdPartyURI(prinURI, aURI, aRes); } NS_IMETHODIMP BasePrincipal::IsThirdPartyPrincipal(nsIPrincipal* aPrin, bool* aRes) { *aRes = true; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return aPrin->IsThirdPartyURI(prinURI, aRes); } NS_IMETHODIMP BasePrincipal::IsThirdPartyChannel(nsIChannel* aChan, bool* aRes) { AssertIsOnMainThread(); if (IsSystemPrincipal()) { // Nothing is 3rd party to the system principal. *aRes = false; return NS_OK; } nsCOMPtr prinURI; GetURI(getter_AddRefs(prinURI)); ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance(); return thirdPartyUtil->IsThirdPartyChannel(aChan, prinURI, aRes); } NS_IMETHODIMP BasePrincipal::IsSameOrigin(nsIURI* aURI, bool* aRes) { *aRes = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { // Note that expanded and system principals return here, because they have // no URI. return NS_OK; } *aRes = nsScriptSecurityManager::SecurityCompareURIs(prinURI, aURI); return NS_OK; } NS_IMETHODIMP BasePrincipal::IsL10nAllowed(nsIURI* aURI, bool* aRes) { AssertIsOnMainThread(); // URI_DANGEROUS_TO_LOAD is not threadsafe to query. *aRes = false; if (nsContentUtils::IsErrorPage(aURI)) { *aRes = true; return NS_OK; } // The system principal is always allowed. if (IsSystemPrincipal()) { *aRes = true; return NS_OK; } nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, NS_OK); bool hasFlags; // Allow access to uris that cannot be loaded by web content. rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DANGEROUS_TO_LOAD, &hasFlags); NS_ENSURE_SUCCESS(rv, NS_OK); if (hasFlags) { *aRes = true; return NS_OK; } // UI resources also get access. rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &hasFlags); NS_ENSURE_SUCCESS(rv, NS_OK); if (hasFlags) { *aRes = true; return NS_OK; } auto policy = AddonPolicyCore(); *aRes = (policy && policy->IsPrivileged()); return NS_OK; } NS_IMETHODIMP BasePrincipal::AllowsRelaxStrictFileOriginPolicy(nsIURI* aURI, bool* aRes) { *aRes = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } *aRes = NS_RelaxStrictFileOriginPolicy(aURI, prinURI); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetPrefLightCacheKey(nsIURI* aURI, bool aWithCredentials, const OriginAttributes& aOriginAttributes, nsACString& _retval) { _retval.Truncate(); constexpr auto space = " "_ns; nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString scheme, host, port; if (uri) { uri->GetScheme(scheme); uri->GetHost(host); port.AppendInt(NS_GetRealPort(uri)); } if (aWithCredentials) { _retval.AssignLiteral("cred"); } else { _retval.AssignLiteral("nocred"); } nsAutoCString spec; rv = aURI->GetSpec(spec); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString originAttributesSuffix; aOriginAttributes.CreateSuffix(originAttributesSuffix); _retval.Append(space + scheme + space + host + space + port + space + spec + space + originAttributesSuffix); return NS_OK; } NS_IMETHODIMP BasePrincipal::HasFirstpartyStorageAccess(mozIDOMWindow* aCheckWindow, uint32_t* aRejectedReason, bool* aOutAllowed) { AssertIsOnMainThread(); *aRejectedReason = 0; *aOutAllowed = false; nsPIDOMWindowInner* win = nsPIDOMWindowInner::From(aCheckWindow); nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv)) { return rv; } *aOutAllowed = ShouldAllowAccessFor(win, uri, aRejectedReason); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsNullPrincipal(bool* aResult) { *aResult = Kind() == eNullPrincipal; return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsContentPrincipal(bool* aResult) { *aResult = Kind() == eContentPrincipal; return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsExpandedPrincipal(bool* aResult) { *aResult = Kind() == eExpandedPrincipal; return NS_OK; } NS_IMETHODIMP BasePrincipal::GetAsciiSpec(nsACString& aSpec) { aSpec.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetAsciiSpec(aSpec); } NS_IMETHODIMP BasePrincipal::GetSpec(nsACString& aSpec) { aSpec.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetSpec(aSpec); } NS_IMETHODIMP BasePrincipal::GetAsciiHost(nsACString& aHost) { aHost.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetAsciiHost(aHost); } NS_IMETHODIMP BasePrincipal::GetExposablePrePath(nsACString& aPrepath) { aPrepath.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } nsCOMPtr exposableURI = net::nsIOService::CreateExposableURI(prinURI); return exposableURI->GetDisplayPrePath(aPrepath); } NS_IMETHODIMP BasePrincipal::GetExposableSpec(nsACString& aSpec) { aSpec.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } nsCOMPtr clone; rv = NS_MutateURI(prinURI) .SetQuery(""_ns) .SetRef(""_ns) .SetUserPass(""_ns) .Finalize(clone); NS_ENSURE_SUCCESS(rv, rv); return clone->GetAsciiSpec(aSpec); } NS_IMETHODIMP BasePrincipal::GetPrePath(nsACString& aPath) { aPath.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetPrePath(aPath); } NS_IMETHODIMP BasePrincipal::GetFilePath(nsACString& aPath) { aPath.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetFilePath(aPath); } NS_IMETHODIMP BasePrincipal::GetIsSystemPrincipal(bool* aResult) { *aResult = IsSystemPrincipal(); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsAddonOrExpandedAddonPrincipal(bool* aResult) { *aResult = AddonPolicyCore() || ContentScriptAddonPolicyCore(); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsOnion(bool* aIsOnion) { *aIsOnion = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } nsAutoCString host; rv = prinURI->GetHost(host); if (NS_FAILED(rv)) { return NS_OK; } *aIsOnion = StringEndsWith(host, ".onion"_ns); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsIpAddress(bool* aIsIpAddress) { *aIsIpAddress = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } nsAutoCString host; rv = prinURI->GetHost(host); if (NS_FAILED(rv)) { return NS_OK; } PRNetAddr prAddr; memset(&prAddr, 0, sizeof(prAddr)); if (PR_StringToNetAddr(host.get(), &prAddr) == PR_SUCCESS) { *aIsIpAddress = true; } return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsLocalIpAddress(bool* aIsIpAddress) { *aIsIpAddress = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } nsCOMPtr ioService = do_GetIOService(&rv); if (NS_FAILED(rv) || !ioService) { return NS_OK; } rv = ioService->HostnameIsLocalIPAddress(prinURI, aIsIpAddress); if (NS_FAILED(rv)) { *aIsIpAddress = false; } return NS_OK; } NS_IMETHODIMP BasePrincipal::GetScheme(nsACString& aScheme) { aScheme.Truncate(); nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } return prinURI->GetScheme(aScheme); } NS_IMETHODIMP BasePrincipal::SchemeIs(const char* aScheme, bool* aResult) { *aResult = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_WARN_IF(NS_FAILED(rv)) || !prinURI) { return NS_OK; } *aResult = prinURI->SchemeIs(aScheme); return NS_OK; } NS_IMETHODIMP BasePrincipal::IsURIInPrefList(const char* aPref, bool* aResult) { AssertIsOnMainThread(); *aResult = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } *aResult = nsContentUtils::IsURIInPrefList(prinURI, aPref); return NS_OK; } NS_IMETHODIMP BasePrincipal::IsURIInList(const nsACString& aList, bool* aResult) { *aResult = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } *aResult = nsContentUtils::IsURIInList(prinURI, nsCString(aList)); return NS_OK; } NS_IMETHODIMP BasePrincipal::IsContentAccessibleAboutURI(bool* aResult) { *aResult = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } if (!prinURI->SchemeIs("about")) { return NS_OK; } *aResult = NS_IsContentAccessibleAboutURI(prinURI); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsOriginPotentiallyTrustworthy(bool* aResult) { AssertIsOnMainThread(); *aResult = false; nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv) || !uri) { return NS_OK; } *aResult = nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(uri); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsLoopbackHost(bool* aRes) { AssertIsOnMainThread(); *aRes = false; nsAutoCString host; nsresult rv = GetHost(host); // Swallow potential failure as this method is infallible. if (NS_FAILED(rv)) { return NS_OK; } *aRes = nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetAboutModuleFlags(uint32_t* flags) { AssertIsOnMainThread(); *flags = 0; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_ERROR_NOT_AVAILABLE; } if (!prinURI->SchemeIs("about")) { return NS_OK; } nsCOMPtr aboutModule; rv = NS_GetAboutModule(prinURI, getter_AddRefs(aboutModule)); if (NS_FAILED(rv) || !aboutModule) { return rv; } return aboutModule->GetURIFlags(prinURI, flags); } NS_IMETHODIMP BasePrincipal::GetOriginAttributes(JSContext* aCx, JS::MutableHandle aVal) { if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aVal))) { return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP BasePrincipal::GetOriginSuffix(nsACString& aOriginAttributes) { MOZ_ASSERT(mOriginSuffix); mOriginSuffix->ToUTF8String(aOriginAttributes); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetUserContextId(uint32_t* aUserContextId) { *aUserContextId = UserContextId(); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) { *aPrivateBrowsingId = PrivateBrowsingId(); return NS_OK; } nsresult BasePrincipal::GetAddonPolicy( extensions::WebExtensionPolicy** aResult) { AssertIsOnMainThread(); RefPtr policy(AddonPolicy()); policy.forget(aResult); return NS_OK; } nsresult BasePrincipal::GetContentScriptAddonPolicy( extensions::WebExtensionPolicy** aResult) { RefPtr policy(ContentScriptAddonPolicy()); policy.forget(aResult); return NS_OK; } extensions::WebExtensionPolicy* BasePrincipal::AddonPolicy() { AssertIsOnMainThread(); RefPtr core = AddonPolicyCore(); return core ? core->GetMainThreadPolicy() : nullptr; } RefPtr BasePrincipal::AddonPolicyCore() { if (Is()) { return As()->AddonPolicyCore(); } return nullptr; } bool BasePrincipal::AddonHasPermission(const nsAtom* aPerm) { if (auto policy = AddonPolicyCore()) { return policy->HasPermission(aPerm); } return false; } nsIPrincipal* BasePrincipal::PrincipalToInherit(nsIURI* aRequestedURI) { if (Is()) { return As()->PrincipalToInherit(aRequestedURI); } return this; } bool BasePrincipal::OverridesCSP(nsIPrincipal* aDocumentPrincipal) { MOZ_ASSERT(aDocumentPrincipal); // Expanded principals override CSP if and only if they subsume the document // principal. if (mKind == eExpandedPrincipal) { return FastSubsumes(aDocumentPrincipal); } // Extension principals always override the CSP of non-extension principals. // This is primarily for the sake of their stylesheets, which are usually // loaded from channels and cannot have expanded principals. return (AddonPolicyCore() && !BasePrincipal::Cast(aDocumentPrincipal)->AddonPolicyCore()); } already_AddRefed BasePrincipal::CreateContentPrincipal( nsIURI* aURI, const OriginAttributes& aAttrs, nsIURI* aInitialDomain) { MOZ_ASSERT(aURI); nsAutoCString originNoSuffix; nsresult rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, originNoSuffix); if (NS_FAILED(rv)) { // If the generation of the origin fails, we still want to have a valid // principal. Better to return a null principal here. return NullPrincipal::Create(aAttrs); } return CreateContentPrincipal(aURI, aAttrs, originNoSuffix, aInitialDomain); } already_AddRefed BasePrincipal::CreateContentPrincipal( nsIURI* aURI, const OriginAttributes& aAttrs, const nsACString& aOriginNoSuffix, nsIURI* aInitialDomain) { MOZ_ASSERT(aURI); MOZ_ASSERT(!aOriginNoSuffix.IsEmpty()); // If the URI is supposed to inherit the security context of whoever loads it, // we shouldn't make a content principal for it. bool inheritsPrincipal; nsresult rv = NS_URIChainHasFlags( aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &inheritsPrincipal); if (NS_FAILED(rv) || inheritsPrincipal) { return NullPrincipal::Create(aAttrs); } // Check whether the URI knows what its principal is supposed to be. #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE) nsCOMPtr uriWithSpecialOrigin = do_QueryInterface(aURI); if (uriWithSpecialOrigin) { nsCOMPtr origin; rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin)); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } MOZ_ASSERT(origin); OriginAttributes attrs; RefPtr principal = CreateContentPrincipal(origin, attrs, aInitialDomain); return principal.forget(); } #endif nsCOMPtr blobPrincipal; if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal( aURI, getter_AddRefs(blobPrincipal))) { MOZ_ASSERT(blobPrincipal); MOZ_ASSERT(!aInitialDomain, "an initial domain for a blob URI makes no sense"); RefPtr principal = Cast(blobPrincipal); return principal.forget(); } // Mint a content principal. RefPtr principal = new ContentPrincipal(aURI, aAttrs, aOriginNoSuffix, aInitialDomain); return principal.forget(); } already_AddRefed BasePrincipal::CreateContentPrincipal( const nsACString& aOrigin) { MOZ_ASSERT(!StringBeginsWith(aOrigin, "["_ns), "CreateContentPrincipal does not support System and Expanded " "principals"); MOZ_ASSERT( !StringBeginsWith(aOrigin, nsLiteralCString(NS_NULLPRINCIPAL_SCHEME ":")), "CreateContentPrincipal does not support NullPrincipal"); nsAutoCString originNoSuffix; OriginAttributes attrs; if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) { return nullptr; } nsCOMPtr uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); NS_ENSURE_SUCCESS(rv, nullptr); return BasePrincipal::CreateContentPrincipal(uri, attrs); } already_AddRefed BasePrincipal::CloneForcingOriginAttributes( const OriginAttributes& aOriginAttributes) { if (NS_WARN_IF(!IsContentPrincipal())) { return nullptr; } nsAutoCString originNoSuffix; nsresult rv = GetOriginNoSuffix(originNoSuffix); NS_ENSURE_SUCCESS(rv, nullptr); nsCOMPtr uri; MOZ_ALWAYS_SUCCEEDS(GetURI(getter_AddRefs(uri))); // XXX: This does not copy over the domain. Should it? RefPtr copy = new ContentPrincipal(uri, aOriginAttributes, originNoSuffix, nullptr); return copy.forget(); } extensions::WebExtensionPolicy* BasePrincipal::ContentScriptAddonPolicy() { AssertIsOnMainThread(); RefPtr core = ContentScriptAddonPolicyCore(); return core ? core->GetMainThreadPolicy() : nullptr; } RefPtr BasePrincipal::ContentScriptAddonPolicyCore() { if (!Is()) { return nullptr; } auto* expanded = As(); for (const auto& prin : expanded->AllowList()) { if (RefPtr policy = BasePrincipal::Cast(prin)->AddonPolicyCore()) { return policy; } } return nullptr; } bool BasePrincipal::AddonAllowsLoad(nsIURI* aURI, bool aExplicit /* = false */) { if (Is()) { return As()->AddonAllowsLoad(aURI, aExplicit); } if (auto policy = AddonPolicyCore()) { return policy->CanAccessURI(aURI, aExplicit); } return false; } NS_IMETHODIMP BasePrincipal::GetLocalStorageQuotaKey(nsACString& aKey) { aKey.Truncate(); nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); // The special handling of the file scheme should be consistent with // GetStorageOriginKey. nsAutoCString baseDomain; rv = uri->GetAsciiHost(baseDomain); NS_ENSURE_SUCCESS(rv, rv); if (baseDomain.IsEmpty() && uri->SchemeIs("file")) { nsCOMPtr url = do_QueryInterface(uri, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = url->GetDirectory(baseDomain); NS_ENSURE_SUCCESS(rv, rv); } else { nsCOMPtr eTLDService( do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString eTLDplusOne; rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne); if (NS_SUCCEEDED(rv)) { baseDomain = eTLDplusOne; } else if (rv == NS_ERROR_HOST_IS_IP_ADDRESS || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { rv = NS_OK; } NS_ENSURE_SUCCESS(rv, rv); } OriginAttributesRef().CreateSuffix(aKey); nsAutoCString subdomainsDBKey; rv = dom::StorageUtils::CreateReversedDomain(baseDomain, subdomainsDBKey); NS_ENSURE_SUCCESS(rv, rv); aKey.Append(':'); aKey.Append(subdomainsDBKey); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetNextSubDomainPrincipal( nsIPrincipal** aNextSubDomainPrincipal) { nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv) || !uri) { return NS_OK; } nsAutoCString host; rv = uri->GetHost(host); if (NS_FAILED(rv) || host.IsEmpty()) { return NS_OK; } nsCString subDomain; rv = nsEffectiveTLDService::GetInstance()->GetNextSubDomain(host, subDomain); if (NS_FAILED(rv) || subDomain.IsEmpty()) { return NS_OK; } nsCOMPtr subDomainURI; rv = NS_MutateURI(uri).SetHost(subDomain).Finalize(subDomainURI); if (NS_FAILED(rv) || !subDomainURI) { return NS_OK; } // Copy the attributes over mozilla::OriginAttributes attrs = OriginAttributesRef(); if (!StaticPrefs::permissions_isolateBy_userContext()) { // Disable userContext for permissions. attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID); } RefPtr principal = mozilla::BasePrincipal::CreateContentPrincipal(subDomainURI, attrs); if (!principal) { return NS_OK; } principal.forget(aNextSubDomainPrincipal); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetStorageOriginKey(nsACString& aOriginKey) { aOriginKey.Truncate(); nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); // The special handling of the file scheme should be consistent with // GetLocalStorageQuotaKey. nsAutoCString domainOrigin; rv = uri->GetAsciiHost(domainOrigin); NS_ENSURE_SUCCESS(rv, rv); if (domainOrigin.IsEmpty()) { // For the file:/// protocol use the exact directory as domain. if (uri->SchemeIs("file")) { nsCOMPtr url = do_QueryInterface(uri, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = url->GetDirectory(domainOrigin); NS_ENSURE_SUCCESS(rv, rv); } } // Append reversed domain nsAutoCString reverseDomain; rv = dom::StorageUtils::CreateReversedDomain(domainOrigin, reverseDomain); NS_ENSURE_SUCCESS(rv, rv); aOriginKey.Append(reverseDomain); // Append scheme nsAutoCString scheme; rv = uri->GetScheme(scheme); NS_ENSURE_SUCCESS(rv, rv); aOriginKey.Append(':'); aOriginKey.Append(scheme); // Append port if any int32_t port = NS_GetRealPort(uri); if (port != -1) { aOriginKey.Append(nsPrintfCString(":%d", port)); } return NS_OK; } NS_IMETHODIMP BasePrincipal::GetIsScriptAllowedByPolicy(bool* aIsScriptAllowedByPolicy) { AssertIsOnMainThread(); *aIsScriptAllowedByPolicy = false; nsCOMPtr prinURI; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { return NS_OK; } nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); if (!ssm) { return NS_ERROR_UNEXPECTED; } return ssm->PolicyAllowsScript(prinURI, aIsScriptAllowedByPolicy); } bool SiteIdentifier::Equals(const SiteIdentifier& aOther) const { MOZ_ASSERT(IsInitialized()); MOZ_ASSERT(aOther.IsInitialized()); return mPrincipal->FastEquals(aOther.mPrincipal); } NS_IMETHODIMP BasePrincipal::CreateReferrerInfo(mozilla::dom::ReferrerPolicy aReferrerPolicy, nsIReferrerInfo** _retval) { nsCOMPtr prinURI; RefPtr info; nsresult rv = GetURI(getter_AddRefs(prinURI)); if (NS_FAILED(rv) || !prinURI) { info = new dom::ReferrerInfo(nullptr); info.forget(_retval); return NS_OK; } info = new dom::ReferrerInfo(prinURI, aReferrerPolicy); info.forget(_retval); return NS_OK; } NS_IMETHODIMP BasePrincipal::GetPrecursorPrincipal(nsIPrincipal** aPrecursor) { *aPrecursor = nullptr; return NS_OK; } NS_IMPL_ADDREF(BasePrincipal::Deserializer) NS_IMPL_RELEASE(BasePrincipal::Deserializer) NS_INTERFACE_MAP_BEGIN(BasePrincipal::Deserializer) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(nsISerializable) if (mPrincipal) { return mPrincipal->QueryInterface(aIID, aInstancePtr); } else NS_INTERFACE_MAP_END NS_IMETHODIMP BasePrincipal::Deserializer::Write(nsIObjectOutputStream* aStream) { // Read is used still for legacy principals MOZ_RELEASE_ASSERT(false, "Old style serialization is removed"); return NS_OK; } /* static */ void BasePrincipal::WriteJSONProperty(JSONWriter& aWriter, const Span& aKey, const nsCString& aValue) { aWriter.StringProperty(aKey, aValue); } } // namespace mozilla