diff options
Diffstat (limited to 'caps/BasePrincipal.cpp')
-rw-r--r-- | caps/BasePrincipal.cpp | 1625 |
1 files changed, 1625 insertions, 0 deletions
diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp new file mode 100644 index 0000000000..97924b23c5 --- /dev/null +++ b/caps/BasePrincipal.cpp @@ -0,0 +1,1625 @@ +/* -*- 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 "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 "json/json.h" +#include "nsSerializationHelper.h" + +namespace mozilla { + +const char* BasePrincipal::JSONEnumKeyStrings[4] = { + "0", + "1", + "2", + "3", +}; + +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::GetAsciiOrigin(nsACString& aOrigin) { + aOrigin.Truncate(); + nsCOMPtr<nsIURI> prinURI; + nsresult rv = GetURI(getter_AddRefs(prinURI)); + if (NS_FAILED(rv) || !prinURI) { + return NS_ERROR_NOT_AVAILABLE; + } + return nsContentUtils::GetASCIIOrigin(prinURI, aOrigin); +} + +NS_IMETHODIMP +BasePrincipal::GetHostPort(nsACString& aRes) { + aRes.Truncate(); + nsCOMPtr<nsIURI> 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<nsIURI> 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); +} + +// Returns the inner Json::value of the serialized principal +// Example input and return values: +// Null principal: +// {"0":{"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}} -> +// {"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"} +// +// Content principal: +// {"1":{"0":"https://mozilla.com"}} -> {"0":"https://mozilla.com"} +// +// Expanded principal: +// {"2":{"0":"<base64principal1>,<base64principal2>"}} -> +// {"0":"<base64principal1>,<base64principal2>"} +// +// System principal: +// {"3":{}} -> {} +// The aKey passed in also returns the corresponding PrincipalKind enum +// +// Warning: The Json::Value* pointer is into the aRoot object +static const Json::Value* GetPrincipalObject(const Json::Value& aRoot, + int& aOutPrincipalKind) { + const Json::Value::Members members = aRoot.getMemberNames(); + // We only support one top level key in the object + if (members.size() != 1) { + return nullptr; + } + // members[0] here is the "0", "1", "2", "3" principalKind + // that is the top level of the serialized JSON principal + const std::string stringPrincipalKind = members[0]; + + // Next we take the string value from the JSON + // and convert it into the int for the BasePrincipal::PrincipalKind enum + + // Verify that the key is within the valid range + int principalKind = std::stoi(stringPrincipalKind); + MOZ_ASSERT(BasePrincipal::eNullPrincipal == 0, + "We need to rely on 0 being a bounds check for the first " + "principal kind."); + if (principalKind < 0 || principalKind > BasePrincipal::eKindMax) { + return nullptr; + } + MOZ_ASSERT(principalKind == BasePrincipal::eNullPrincipal || + principalKind == BasePrincipal::eContentPrincipal || + principalKind == BasePrincipal::eExpandedPrincipal || + principalKind == BasePrincipal::eSystemPrincipal); + aOutPrincipalKind = principalKind; + + if (!aRoot[stringPrincipalKind].isObject()) { + return nullptr; + } + + // Return the inner value of the principal object + return &aRoot[stringPrincipalKind]; +} + +// Accepts the JSON inner object without the wrapping principalKind +// (See GetPrincipalObject for the inner object response examples) +// Creates an array of KeyVal objects that are all defined on the principal +// Each principal type (null, content, expanded) has a KeyVal that stores the +// fields of the JSON +// +// This simplifies deserializing elsewhere as we do the checking for presence +// and string values here for the complete set of serializable keys that the +// corresponding principal supports. +// +// The KeyVal object has the following fields: +// - valueWasSerialized: is true if the deserialized JSON contained a string +// value +// - value: The string that was serialized for this key +// - key: an SerializableKeys enum value specific to the principal. +// For example content principal is an enum of: eURI, eDomain, +// eSuffix, eCSP +// +// +// Given an inner content principal: +// {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"} +// | | | | +// ----------------------------- | +// | | | +// Key ---------------------- +// | +// Value +// +// They Key "0" corresponds to ContentPrincipal::eURI +// They Key "1" corresponds to ContentPrincipal::eSuffix +template <typename T> +static nsTArray<typename T::KeyVal> GetJSONKeys(const Json::Value* aInput) { + int size = T::eMax + 1; + nsTArray<typename T::KeyVal> fields; + for (int i = 0; i != size; i++) { + typename T::KeyVal* field = fields.AppendElement(); + // field->valueWasSerialized returns if the field was found in the + // deserialized code. This simplifies the consumers from having to check + // length. + field->valueWasSerialized = false; + field->key = static_cast<typename T::SerializableKeys>(i); + const std::string key = std::to_string(field->key); + if (aInput->isMember(key)) { + const Json::Value& val = (*aInput)[key]; + if (val.isString()) { + field->value.Append(nsDependentCString(val.asCString())); + field->valueWasSerialized = true; + } + } + } + return fields; +} + +// 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 +// +// The string is first deserialized with jsoncpp to get the Json::Value of the +// object. The inner JSON object is parsed with GetPrincipalObject which returns +// a KeyVal array of the inner object's fields. PrincipalKind is returned by +// GetPrincipalObject which is then used to decide which principal +// implementation of FromProperties to call. The corresponding FromProperties +// call takes the KeyVal fields and turns it into a principal. +already_AddRefed<BasePrincipal> BasePrincipal::FromJSON( + const nsACString& aJSON) { + Json::Value root; + Json::CharReaderBuilder builder; + std::unique_ptr<Json::CharReader> const reader(builder.newCharReader()); + bool parseSuccess = + reader->parse(aJSON.BeginReading(), aJSON.EndReading(), &root, nullptr); + if (!parseSuccess) { + MOZ_ASSERT(false, + "Unable to parse string as JSON to deserialize as a principal"); + return nullptr; + } + + return FromJSON(root); +} + +// Checks if an ExpandedPrincipal is using the legacy format, where +// sub-principals are Base64 encoded. +// +// Given a legacy expanded principal: +// +// * +// {"2": {"0": "eyIxIjp7IjAiOiJodHRwczovL2EuY29tLyJ9fQ=="}} +// | | | +// | ---------- Value +// | | +// PrincipalKind | +// | +// SerializableKeys +// +// The value is a CSV list of Base64 encoded prinipcals. The new format for this +// principal is: +// +// Subsumed principals +// | +// ------------------------------------ +// * | | +// {"2": {"0": [{"1": {"0": https://mozilla.com"}}]}} +// | | | +// -------------- Value +// | +// PrincipalKind +// +// It is possible to tell these apart by checking the type of the property noted +// in both diagrams with an asterisk. In the legacy format the type will be a +// string and in the new format it will be an array. +static bool IsLegacyFormat(const Json::Value& aValue) { + const auto& specs = std::to_string(ExpandedPrincipal::eSpecs); + return aValue.isMember(specs) && aValue[specs].isString(); +} + +/* static */ +already_AddRefed<BasePrincipal> BasePrincipal::FromJSON( + const Json::Value& aJSON) { + int principalKind = -1; + const Json::Value* value = GetPrincipalObject(aJSON, principalKind); + if (!value) { +#ifdef DEBUG + fprintf(stderr, "Unexpected JSON principal %s\n", + aJSON.toStyledString().c_str()); +#endif + MOZ_ASSERT(false, "Unexpected JSON to deserialize as a principal"); + + return nullptr; + } + MOZ_ASSERT(principalKind != -1, + "PrincipalKind should always be >=0 by this point"); + + if (principalKind == eSystemPrincipal) { + RefPtr<BasePrincipal> principal = + BasePrincipal::Cast(nsContentUtils::GetSystemPrincipal()); + return principal.forget(); + } + + if (principalKind == eNullPrincipal) { + nsTArray<NullPrincipal::KeyVal> res = GetJSONKeys<NullPrincipal>(value); + return NullPrincipal::FromProperties(res); + } + + if (principalKind == eContentPrincipal) { + nsTArray<ContentPrincipal::KeyVal> res = + GetJSONKeys<ContentPrincipal>(value); + return ContentPrincipal::FromProperties(res); + } + + if (principalKind == eExpandedPrincipal) { + // Check if expanded principals is stored in the new or the old format. See + // comment for `IsLegacyFormat`. + if (IsLegacyFormat(*value)) { + nsTArray<ExpandedPrincipal::KeyVal> res = + GetJSONKeys<ExpandedPrincipal>(value); + return ExpandedPrincipal::FromProperties(res); + } + + return ExpandedPrincipal::FromProperties(*value); + } + + MOZ_RELEASE_ASSERT(false, "Unexpected enum to deserialize as a principal"); +} + +nsresult BasePrincipal::PopulateJSONObject(Json::Value& aObject) { + return NS_OK; +} + +// 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(); + + Json::Value root = Json::objectValue; + nsresult rv = ToJSON(root); + NS_ENSURE_SUCCESS(rv, rv); + + static StaticAutoPtr<Json::StreamWriterBuilder> sJSONBuilderForPrincipals; + if (!sJSONBuilderForPrincipals) { + sJSONBuilderForPrincipals = new Json::StreamWriterBuilder(); + (*sJSONBuilderForPrincipals)["indentation"] = ""; + (*sJSONBuilderForPrincipals)["emitUTF8"] = true; + ClearOnShutdown(&sJSONBuilderForPrincipals); + } + std::string result = Json::writeString(*sJSONBuilderForPrincipals, root); + aJSON.Append(result); + if (aJSON.Length() == 0) { + MOZ_ASSERT(false, "JSON writer failed to output a principal serialization"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +nsresult BasePrincipal::ToJSON(Json::Value& aObject) { + static_assert(eKindMax < ArrayLength(JSONEnumKeyStrings)); + nsresult rv = PopulateJSONObject( + (aObject[Json::StaticString(JSONEnumKeyStrings[Kind()])] = + Json::objectValue)); + NS_ENSURE_SUCCESS(rv, rv); + + 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<nsIURI> 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<nsIURI> 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<nsIEffectiveTLDService> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> prinURI; + nsresult rv = GetURI(getter_AddRefs(prinURI)); + if (NS_FAILED(rv) || !prinURI) { + return NS_OK; + } + + nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(prinURI); + return exposableURI->GetDisplayPrePath(aPrepath); +} + +NS_IMETHODIMP +BasePrincipal::GetExposableSpec(nsACString& aSpec) { + aSpec.Truncate(); + nsCOMPtr<nsIURI> prinURI; + nsresult rv = GetURI(getter_AddRefs(prinURI)); + if (NS_FAILED(rv) || !prinURI) { + return NS_OK; + } + nsCOMPtr<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> prinURI; + nsresult rv = GetURI(getter_AddRefs(prinURI)); + if (NS_FAILED(rv) || !prinURI) { + return NS_OK; + } + + nsCOMPtr<nsIIOService> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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. + NS_ENSURE_SUCCESS(rv, NS_OK); + + *aRes = nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host); + return NS_OK; +} + +NS_IMETHODIMP +BasePrincipal::GetAboutModuleFlags(uint32_t* flags) { + AssertIsOnMainThread(); + *flags = 0; + nsCOMPtr<nsIURI> 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<nsIAboutModule> 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<JS::Value> 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; +} + +NS_IMETHODIMP +BasePrincipal::GetIsInIsolatedMozBrowserElement( + bool* aIsInIsolatedMozBrowserElement) { + *aIsInIsolatedMozBrowserElement = IsInIsolatedMozBrowserElement(); + return NS_OK; +} + +nsresult BasePrincipal::GetAddonPolicy( + extensions::WebExtensionPolicy** aResult) { + AssertIsOnMainThread(); + RefPtr<extensions::WebExtensionPolicy> policy(AddonPolicy()); + policy.forget(aResult); + return NS_OK; +} + +nsresult BasePrincipal::GetContentScriptAddonPolicy( + extensions::WebExtensionPolicy** aResult) { + RefPtr<extensions::WebExtensionPolicy> policy(ContentScriptAddonPolicy()); + policy.forget(aResult); + return NS_OK; +} + +extensions::WebExtensionPolicy* BasePrincipal::AddonPolicy() { + AssertIsOnMainThread(); + RefPtr<extensions::WebExtensionPolicyCore> core = AddonPolicyCore(); + return core ? core->GetMainThreadPolicy() : nullptr; +} + +RefPtr<extensions::WebExtensionPolicyCore> BasePrincipal::AddonPolicyCore() { + if (Is<ContentPrincipal>()) { + return As<ContentPrincipal>()->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<ExpandedPrincipal>()) { + return As<ExpandedPrincipal>()->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> 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> 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<nsIURIWithSpecialOrigin> uriWithSpecialOrigin = + do_QueryInterface(aURI); + if (uriWithSpecialOrigin) { + nsCOMPtr<nsIURI> origin; + rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + MOZ_ASSERT(origin); + OriginAttributes attrs; + RefPtr<BasePrincipal> principal = + CreateContentPrincipal(origin, attrs, aInitialDomain); + return principal.forget(); + } +#endif + + nsCOMPtr<nsIPrincipal> 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<BasePrincipal> principal = Cast(blobPrincipal); + return principal.forget(); + } + + // Mint a content principal. + RefPtr<ContentPrincipal> principal = + new ContentPrincipal(aURI, aAttrs, aOriginNoSuffix, aInitialDomain); + return principal.forget(); +} + +already_AddRefed<BasePrincipal> 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<nsIURI> uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); + NS_ENSURE_SUCCESS(rv, nullptr); + + return BasePrincipal::CreateContentPrincipal(uri, attrs); +} + +already_AddRefed<BasePrincipal> BasePrincipal::CloneForcingOriginAttributes( + const OriginAttributes& aOriginAttributes) { + if (NS_WARN_IF(!IsContentPrincipal())) { + return nullptr; + } + + nsAutoCString originNoSuffix; + nsresult rv = GetOriginNoSuffix(originNoSuffix); + NS_ENSURE_SUCCESS(rv, nullptr); + + nsCOMPtr<nsIURI> uri; + MOZ_ALWAYS_SUCCEEDS(GetURI(getter_AddRefs(uri))); + + // XXX: This does not copy over the domain. Should it? + RefPtr<ContentPrincipal> copy = + new ContentPrincipal(uri, aOriginAttributes, originNoSuffix, nullptr); + return copy.forget(); +} + +extensions::WebExtensionPolicy* BasePrincipal::ContentScriptAddonPolicy() { + AssertIsOnMainThread(); + RefPtr<extensions::WebExtensionPolicyCore> core = + ContentScriptAddonPolicyCore(); + return core ? core->GetMainThreadPolicy() : nullptr; +} + +RefPtr<extensions::WebExtensionPolicyCore> +BasePrincipal::ContentScriptAddonPolicyCore() { + if (!Is<ExpandedPrincipal>()) { + return nullptr; + } + + auto* expanded = As<ExpandedPrincipal>(); + for (const auto& prin : expanded->AllowList()) { + if (RefPtr<extensions::WebExtensionPolicyCore> policy = + BasePrincipal::Cast(prin)->AddonPolicyCore()) { + return policy; + } + } + + return nullptr; +} + +bool BasePrincipal::AddonAllowsLoad(nsIURI* aURI, + bool aExplicit /* = false */) { + if (Is<ExpandedPrincipal>()) { + return As<ExpandedPrincipal>()->AddonAllowsLoad(aURI, aExplicit); + } + if (auto policy = AddonPolicyCore()) { + return policy->CanAccessURI(aURI, aExplicit); + } + return false; +} + +NS_IMETHODIMP +BasePrincipal::GetLocalStorageQuotaKey(nsACString& aKey) { + aKey.Truncate(); + + nsCOMPtr<nsIURI> 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<nsIURL> url = do_QueryInterface(uri, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = url->GetDirectory(baseDomain); + NS_ENSURE_SUCCESS(rv, rv); + } else { + nsCOMPtr<nsIEffectiveTLDService> 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<nsIURI> 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<nsIURI> 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<nsIPrincipal> 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<nsIURI> 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<nsIURL> 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<nsIURI> 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<nsIURI> prinURI; + RefPtr<dom::ReferrerInfo> 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::SetJSONValue(Json::Value& aObject, const char* aKey, + const nsCString& aValue) { + aObject[Json::StaticString(aKey)] = + Json::Value(aValue.BeginReading(), aValue.EndReading()); +} + +} // namespace mozilla |