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/. */
7 #include "ExpandedPrincipal.h"
8 #include "nsIClassInfoImpl.h"
9 #include "nsIObjectInputStream.h"
10 #include "nsReadableUtils.h"
11 #include "mozilla/Base64.h"
12 #include "mozilla/extensions/WebExtensionPolicy.h"
13 #include "mozilla/JSONWriter.h"
16 #include "ExpandedPrincipalJSONHandler.h"
17 #include "SubsumedPrincipalJSONHandler.h"
19 using namespace mozilla
;
21 NS_IMPL_CLASSINFO(ExpandedPrincipal
, nullptr, 0, NS_EXPANDEDPRINCIPAL_CID
)
22 NS_IMPL_QUERY_INTERFACE_CI(ExpandedPrincipal
, nsIPrincipal
,
24 NS_IMPL_CI_INTERFACE_GETTER(ExpandedPrincipal
, nsIPrincipal
,
27 ExpandedPrincipal::ExpandedPrincipal(
28 nsTArray
<nsCOMPtr
<nsIPrincipal
>>&& aPrincipals
,
29 const nsACString
& aOriginNoSuffix
, const OriginAttributes
& aAttrs
)
30 : BasePrincipal(eExpandedPrincipal
, aOriginNoSuffix
, aAttrs
),
31 mPrincipals(std::move(aPrincipals
)) {}
33 ExpandedPrincipal::~ExpandedPrincipal() = default;
35 already_AddRefed
<ExpandedPrincipal
> ExpandedPrincipal::Create(
36 const nsTArray
<nsCOMPtr
<nsIPrincipal
>>& aAllowList
,
37 const OriginAttributes
& aAttrs
) {
38 nsTArray
<nsCOMPtr
<nsIPrincipal
>> principals
;
39 for (size_t i
= 0; i
< aAllowList
.Length(); ++i
) {
40 principals
.AppendElement(aAllowList
[i
]);
44 origin
.AssignLiteral("[Expanded Principal [");
46 origin
, ", "_ns
, principals
,
47 [](nsACString
& dest
, const nsCOMPtr
<nsIPrincipal
>& principal
) {
48 nsAutoCString subOrigin
;
49 DebugOnly
<nsresult
> rv
= principal
->GetOrigin(subOrigin
);
50 MOZ_ASSERT(NS_SUCCEEDED(rv
));
51 dest
.Append(subOrigin
);
53 origin
.AppendLiteral("]]");
55 RefPtr
<ExpandedPrincipal
> ep
=
56 new ExpandedPrincipal(std::move(principals
), origin
, aAttrs
);
61 ExpandedPrincipal::GetDomain(nsIURI
** aDomain
) {
67 ExpandedPrincipal::SetDomain(nsIURI
* aDomain
) { return NS_OK
; }
69 bool ExpandedPrincipal::SubsumesInternal(
71 BasePrincipal::DocumentDomainConsideration aConsideration
) {
72 // If aOther is an ExpandedPrincipal too, we break it down into its component
73 // nsIPrincipals, and check subsumes on each one.
74 if (Cast(aOther
)->Is
<ExpandedPrincipal
>()) {
75 auto* expanded
= Cast(aOther
)->As
<ExpandedPrincipal
>();
77 for (auto& other
: expanded
->AllowList()) {
78 // Use SubsumesInternal rather than Subsumes here, since OriginAttribute
79 // checks are only done between non-expanded sub-principals, and we don't
80 // need to incur the extra virtual call overhead.
81 if (!SubsumesInternal(other
, aConsideration
)) {
88 // We're dealing with a regular principal. One of our principals must subsume
90 for (uint32_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
91 if (Cast(mPrincipals
[i
])->Subsumes(aOther
, aConsideration
)) {
99 bool ExpandedPrincipal::MayLoadInternal(nsIURI
* uri
) {
100 for (uint32_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
101 if (BasePrincipal::Cast(mPrincipals
[i
])->MayLoadInternal(uri
)) {
109 uint32_t ExpandedPrincipal::GetHashValue() {
110 MOZ_CRASH("extended principal should never be used as key in a hash map");
114 ExpandedPrincipal::GetURI(nsIURI
** aURI
) {
119 const nsTArray
<nsCOMPtr
<nsIPrincipal
>>& ExpandedPrincipal::AllowList() {
124 ExpandedPrincipal::GetBaseDomain(nsACString
& aBaseDomain
) {
125 return NS_ERROR_NOT_AVAILABLE
;
129 ExpandedPrincipal::GetAddonId(nsAString
& aAddonId
) {
134 bool ExpandedPrincipal::AddonHasPermission(const nsAtom
* aPerm
) {
135 for (size_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
136 if (BasePrincipal::Cast(mPrincipals
[i
])->AddonHasPermission(aPerm
)) {
143 bool ExpandedPrincipal::AddonAllowsLoad(nsIURI
* aURI
,
144 bool aExplicit
/* = false */) {
145 for (const auto& principal
: mPrincipals
) {
146 if (Cast(principal
)->AddonAllowsLoad(aURI
, aExplicit
)) {
153 void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy
* aCSP
) {
154 AssertIsOnMainThread();
155 mCSP
= new nsMainThreadPtrHolder
<nsIContentSecurityPolicy
>(
156 "ExpandedPrincipal::mCSP", aCSP
);
160 ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy
** aCsp
) {
161 AssertIsOnMainThread();
162 NS_IF_ADDREF(*aCsp
= mCSP
);
166 nsIPrincipal
* ExpandedPrincipal::PrincipalToInherit(nsIURI
* aRequestedURI
) {
168 // If a given sub-principal subsumes the given URI, use that principal for
169 // inheritance. In general, this only happens with certain CORS modes, loads
170 // with forced principal inheritance, and creation of XML documents from
171 // XMLHttpRequests or fetch requests. For URIs that normally inherit a
172 // principal (such as data: URIs), we fall back to the last principal in the
174 for (const auto& principal
: mPrincipals
) {
175 if (Cast(principal
)->MayLoadInternal(aRequestedURI
)) {
180 return mPrincipals
.LastElement();
183 nsresult
ExpandedPrincipal::GetScriptLocation(nsACString
& aStr
) {
184 aStr
.AssignLiteral("[Expanded Principal [");
185 for (size_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
187 aStr
.AppendLiteral(", ");
192 nsJSPrincipals::get(mPrincipals
.ElementAt(i
))->GetScriptLocation(spec
);
193 NS_ENSURE_SUCCESS(rv
, rv
);
197 aStr
.AppendLiteral("]]");
201 //////////////////////////////////////////
202 // Methods implementing nsISerializable //
203 //////////////////////////////////////////
205 // We've had way too many issues with unversioned serializations, so
206 // explicitly version this one.
207 static const uint32_t kSerializationVersion
= 1;
210 ExpandedPrincipal::Deserializer::Read(nsIObjectInputStream
* aStream
) {
212 nsresult rv
= aStream
->Read32(&version
);
213 if (version
!= kSerializationVersion
) {
215 "We really need to add handling of the old(?) version here");
216 return NS_ERROR_UNEXPECTED
;
220 rv
= aStream
->Read32(&count
);
225 nsTArray
<nsCOMPtr
<nsIPrincipal
>> principals
;
226 if (!principals
.SetCapacity(count
, fallible
)) {
227 return NS_ERROR_OUT_OF_MEMORY
;
230 for (uint32_t i
= 0; i
< count
; ++i
) {
231 nsCOMPtr
<nsISupports
> read
;
232 rv
= aStream
->ReadObject(true, getter_AddRefs(read
));
237 nsCOMPtr
<nsIPrincipal
> principal
= do_QueryInterface(read
);
239 return NS_ERROR_UNEXPECTED
;
242 principals
.AppendElement(std::move(principal
));
245 mPrincipal
= ExpandedPrincipal::Create(principals
, OriginAttributes());
249 nsresult
ExpandedPrincipal::GetSiteIdentifier(SiteIdentifier
& aSite
) {
250 // Call GetSiteIdentifier on each of our principals and return a new
251 // ExpandedPrincipal.
253 nsTArray
<nsCOMPtr
<nsIPrincipal
>> allowlist
;
254 for (const auto& principal
: mPrincipals
) {
256 nsresult rv
= Cast(principal
)->GetSiteIdentifier(site
);
257 NS_ENSURE_SUCCESS(rv
, rv
);
258 allowlist
.AppendElement(site
.GetPrincipal());
261 RefPtr
<ExpandedPrincipal
> expandedPrincipal
=
262 ExpandedPrincipal::Create(allowlist
, OriginAttributesRef());
263 MOZ_ASSERT(expandedPrincipal
, "ExpandedPrincipal::Create returned nullptr?");
265 aSite
.Init(expandedPrincipal
);
269 nsresult
ExpandedPrincipal::WriteJSONInnerProperties(JSONWriter
& aWriter
) {
270 aWriter
.StartArrayProperty(JSONEnumKeyString
<eSpecs
>(),
271 JSONWriter::CollectionStyle::SingleLineStyle
);
273 for (const auto& principal
: mPrincipals
) {
274 aWriter
.StartObjectElement(JSONWriter::CollectionStyle::SingleLineStyle
);
276 nsresult rv
= BasePrincipal::Cast(principal
)->WriteJSONProperties(aWriter
);
277 NS_ENSURE_SUCCESS(rv
, rv
);
284 nsAutoCString suffix
;
285 OriginAttributesRef().CreateSuffix(suffix
);
286 if (suffix
.Length() > 0) {
287 WriteJSONProperty
<eSuffix
>(aWriter
, suffix
);
293 bool ExpandedPrincipalJSONHandler::ProcessSubsumedResult(bool aResult
) {
295 NS_WARNING("Failed to parse subsumed principal");
296 mState
= State::Error
;
302 bool ExpandedPrincipalJSONHandler::startObject() {
303 if (mSubsumedHandler
.isSome()) {
304 return ProcessSubsumedResult(mSubsumedHandler
->startObject());
309 mState
= State::StartObject
;
311 case State::StartArray
:
312 mState
= State::SubsumedPrincipal
;
314 case State::SubsumedPrincipal
:
315 mSubsumedHandler
.emplace();
317 return ProcessSubsumedResult(mSubsumedHandler
->startObject());
319 NS_WARNING("Unexpected object value");
320 mState
= State::Error
;
327 bool ExpandedPrincipalJSONHandler::propertyName(const JS::Latin1Char
* name
,
329 if (mSubsumedHandler
.isSome()) {
330 return ProcessSubsumedResult(mSubsumedHandler
->propertyName(name
, length
));
334 case State::StartObject
:
335 case State::AfterPropertyValue
: {
338 nsPrintfCString("Unexpected property name length: %zu", length
)
340 mState
= State::Error
;
344 char key
= char(name
[0]);
346 case ExpandedPrincipal::SpecsKey
:
347 mState
= State::SpecsKey
;
349 case ExpandedPrincipal::SuffixKey
:
350 mState
= State::SuffixKey
;
354 nsPrintfCString("Unexpected property name: '%c'", key
).get());
355 mState
= State::Error
;
361 NS_WARNING("Unexpected property name");
362 mState
= State::Error
;
369 bool ExpandedPrincipalJSONHandler::endObject() {
370 if (mSubsumedHandler
.isSome()) {
371 if (!ProcessSubsumedResult(mSubsumedHandler
->endObject())) {
374 if (mSubsumedHandler
->HasAccepted()) {
375 nsCOMPtr
<nsIPrincipal
> principal
= mSubsumedHandler
->mPrincipal
.forget();
376 mSubsumedHandler
.reset();
377 mAllowList
.AppendElement(principal
);
383 case State::AfterPropertyValue
:
384 mPrincipal
= ExpandedPrincipal::Create(mAllowList
, mAttrs
);
385 MOZ_ASSERT(mPrincipal
);
387 mState
= State::EndObject
;
390 NS_WARNING("Unexpected end of object");
391 mState
= State::Error
;
398 bool ExpandedPrincipalJSONHandler::startArray() {
400 case State::SpecsKey
:
401 mState
= State::StartArray
;
404 NS_WARNING("Unexpected array value");
405 mState
= State::Error
;
412 bool ExpandedPrincipalJSONHandler::endArray() {
414 case State::SubsumedPrincipal
: {
415 mState
= State::AfterPropertyValue
;
419 NS_WARNING("Unexpected end of array");
420 mState
= State::Error
;
427 bool ExpandedPrincipalJSONHandler::stringValue(const JS::Latin1Char
* str
,
429 if (mSubsumedHandler
.isSome()) {
430 return ProcessSubsumedResult(mSubsumedHandler
->stringValue(str
, length
));
434 case State::SpecsKey
: {
435 nsDependentCSubstring
specs(reinterpret_cast<const char*>(str
), length
);
437 for (const nsACString
& each
: specs
.Split(',')) {
438 nsAutoCString result
;
439 nsresult rv
= Base64Decode(each
, result
);
440 MOZ_ASSERT(NS_SUCCEEDED(rv
), "failed to decode");
442 mState
= State::Error
;
446 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::FromJSON(result
);
448 mState
= State::Error
;
451 mAllowList
.AppendElement(principal
);
454 mState
= State::AfterPropertyValue
;
457 case State::SuffixKey
: {
458 nsDependentCSubstring
attrs(reinterpret_cast<const char*>(str
), length
);
459 if (!mAttrs
.PopulateFromSuffix(attrs
)) {
460 mState
= State::Error
;
464 mState
= State::AfterPropertyValue
;
468 NS_WARNING("Unexpected string value");
469 mState
= State::Error
;
477 ExpandedPrincipal::IsThirdPartyURI(nsIURI
* aURI
, bool* aRes
) {
478 // ExpandedPrincipal for extension content scripts consist of two principals,
479 // the document's principal and the extension's principal.
480 // To make sure that the third-party check behaves like the web page on which
481 // the content script is running, ignore the extension's principal.
483 for (const auto& principal
: mPrincipals
) {
484 if (!Cast(principal
)->AddonPolicyCore()) {
485 return Cast(principal
)->IsThirdPartyURI(aURI
, aRes
);
489 if (mPrincipals
.IsEmpty()) {
494 return Cast(mPrincipals
[0])->IsThirdPartyURI(aURI
, aRes
);