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 "nsReadableUtils.h"
10 #include "mozilla/Base64.h"
12 using namespace mozilla
;
14 NS_IMPL_CLASSINFO(ExpandedPrincipal
, nullptr, nsIClassInfo::MAIN_THREAD_ONLY
,
15 NS_EXPANDEDPRINCIPAL_CID
)
16 NS_IMPL_QUERY_INTERFACE_CI(ExpandedPrincipal
, nsIPrincipal
,
17 nsIExpandedPrincipal
, nsISerializable
)
18 NS_IMPL_CI_INTERFACE_GETTER(ExpandedPrincipal
, nsIPrincipal
,
19 nsIExpandedPrincipal
, nsISerializable
)
21 struct OriginComparator
{
22 bool LessThan(nsIPrincipal
* a
, nsIPrincipal
* b
) const {
23 nsAutoCString originA
;
24 DebugOnly
<nsresult
> rv
= a
->GetOrigin(originA
);
25 MOZ_ASSERT(NS_SUCCEEDED(rv
));
26 nsAutoCString originB
;
27 rv
= b
->GetOrigin(originB
);
28 MOZ_ASSERT(NS_SUCCEEDED(rv
));
29 return originA
< originB
;
32 bool Equals(nsIPrincipal
* a
, nsIPrincipal
* b
) const {
33 nsAutoCString originA
;
34 DebugOnly
<nsresult
> rv
= a
->GetOrigin(originA
);
35 MOZ_ASSERT(NS_SUCCEEDED(rv
));
36 nsAutoCString originB
;
37 rv
= b
->GetOrigin(originB
);
38 MOZ_ASSERT(NS_SUCCEEDED(rv
));
43 ExpandedPrincipal::ExpandedPrincipal(
44 nsTArray
<nsCOMPtr
<nsIPrincipal
>>& aAllowList
)
45 : BasePrincipal(eExpandedPrincipal
) {
46 // We force the principals to be sorted by origin so that ExpandedPrincipal
47 // origins can have a canonical form.
49 for (size_t i
= 0; i
< aAllowList
.Length(); ++i
) {
50 mPrincipals
.InsertElementSorted(aAllowList
[i
], c
);
54 ExpandedPrincipal::ExpandedPrincipal() : BasePrincipal(eExpandedPrincipal
) {}
56 ExpandedPrincipal::~ExpandedPrincipal() {}
58 already_AddRefed
<ExpandedPrincipal
> ExpandedPrincipal::Create(
59 nsTArray
<nsCOMPtr
<nsIPrincipal
>>& aAllowList
,
60 const OriginAttributes
& aAttrs
) {
61 RefPtr
<ExpandedPrincipal
> ep
= new ExpandedPrincipal(aAllowList
);
64 origin
.AssignLiteral("[Expanded Principal [");
66 origin
, ", "_ns
, ep
->mPrincipals
,
67 [](nsACString
& dest
, const nsCOMPtr
<nsIPrincipal
>& principal
) {
68 nsAutoCString subOrigin
;
69 DebugOnly
<nsresult
> rv
= principal
->GetOrigin(subOrigin
);
70 MOZ_ASSERT(NS_SUCCEEDED(rv
));
71 dest
.Append(subOrigin
);
73 origin
.AppendLiteral("]]");
75 ep
->FinishInit(origin
, aAttrs
);
80 ExpandedPrincipal::GetDomain(nsIURI
** aDomain
) {
86 ExpandedPrincipal::SetDomain(nsIURI
* aDomain
) { return NS_OK
; }
88 bool ExpandedPrincipal::SubsumesInternal(
90 BasePrincipal::DocumentDomainConsideration aConsideration
) {
91 // If aOther is an ExpandedPrincipal too, we break it down into its component
92 // nsIPrincipals, and check subsumes on each one.
93 if (Cast(aOther
)->Is
<ExpandedPrincipal
>()) {
94 auto* expanded
= Cast(aOther
)->As
<ExpandedPrincipal
>();
96 for (auto& other
: expanded
->AllowList()) {
97 // Use SubsumesInternal rather than Subsumes here, since OriginAttribute
98 // checks are only done between non-expanded sub-principals, and we don't
99 // need to incur the extra virtual call overhead.
100 if (!SubsumesInternal(other
, aConsideration
)) {
107 // We're dealing with a regular principal. One of our principals must subsume
109 for (uint32_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
110 if (Cast(mPrincipals
[i
])->Subsumes(aOther
, aConsideration
)) {
118 bool ExpandedPrincipal::MayLoadInternal(nsIURI
* uri
) {
119 for (uint32_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
120 if (BasePrincipal::Cast(mPrincipals
[i
])->MayLoadInternal(uri
)) {
128 uint32_t ExpandedPrincipal::GetHashValue() {
129 MOZ_CRASH("extended principal should never be used as key in a hash map");
133 ExpandedPrincipal::GetURI(nsIURI
** aURI
) {
138 const nsTArray
<nsCOMPtr
<nsIPrincipal
>>& ExpandedPrincipal::AllowList() {
143 ExpandedPrincipal::GetBaseDomain(nsACString
& aBaseDomain
) {
144 return NS_ERROR_NOT_AVAILABLE
;
148 ExpandedPrincipal::GetAddonId(nsAString
& aAddonId
) {
153 bool ExpandedPrincipal::AddonHasPermission(const nsAtom
* aPerm
) {
154 for (size_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
155 if (BasePrincipal::Cast(mPrincipals
[i
])->AddonHasPermission(aPerm
)) {
162 bool ExpandedPrincipal::AddonAllowsLoad(nsIURI
* aURI
,
163 bool aExplicit
/* = false */) {
164 for (const auto& principal
: mPrincipals
) {
165 if (Cast(principal
)->AddonAllowsLoad(aURI
, aExplicit
)) {
172 void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy
* aCSP
) { mCSP
= aCSP
; }
175 ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy
** aCsp
) {
176 NS_IF_ADDREF(*aCsp
= mCSP
);
180 nsIPrincipal
* ExpandedPrincipal::PrincipalToInherit(nsIURI
* aRequestedURI
) {
182 // If a given sub-principal subsumes the given URI, use that principal for
183 // inheritance. In general, this only happens with certain CORS modes, loads
184 // with forced principal inheritance, and creation of XML documents from
185 // XMLHttpRequests or fetch requests. For URIs that normally inherit a
186 // principal (such as data: URIs), we fall back to the last principal in the
188 for (const auto& principal
: mPrincipals
) {
189 if (Cast(principal
)->MayLoadInternal(aRequestedURI
)) {
194 return mPrincipals
.LastElement();
197 nsresult
ExpandedPrincipal::GetScriptLocation(nsACString
& aStr
) {
198 aStr
.AssignLiteral("[Expanded Principal [");
199 for (size_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
201 aStr
.AppendLiteral(", ");
206 nsJSPrincipals::get(mPrincipals
.ElementAt(i
))->GetScriptLocation(spec
);
207 NS_ENSURE_SUCCESS(rv
, rv
);
211 aStr
.AppendLiteral("]]");
215 //////////////////////////////////////////
216 // Methods implementing nsISerializable //
217 //////////////////////////////////////////
219 // We've had way too many issues with unversioned serializations, so
220 // explicitly version this one.
221 static const uint32_t kSerializationVersion
= 1;
224 ExpandedPrincipal::Read(nsIObjectInputStream
* aStream
) {
226 nsresult rv
= aStream
->Read32(&version
);
227 if (version
!= kSerializationVersion
) {
229 "We really need to add handling of the old(?) version here");
230 return NS_ERROR_UNEXPECTED
;
234 rv
= aStream
->Read32(&count
);
239 if (!mPrincipals
.SetCapacity(count
, fallible
)) {
240 return NS_ERROR_OUT_OF_MEMORY
;
244 for (uint32_t i
= 0; i
< count
; ++i
) {
245 nsCOMPtr
<nsISupports
> read
;
246 rv
= aStream
->ReadObject(true, getter_AddRefs(read
));
251 nsCOMPtr
<nsIPrincipal
> principal
= do_QueryInterface(read
);
253 return NS_ERROR_UNEXPECTED
;
256 // Play it safe and InsertElementSorted, in case the sort order
257 // changed for some bizarre reason.
258 mPrincipals
.InsertElementSorted(std::move(principal
), c
);
265 ExpandedPrincipal::Write(nsIObjectOutputStream
* aStream
) {
266 // Read is used still for legacy principals
267 MOZ_RELEASE_ASSERT(false, "Old style serialization is removed");
271 nsresult
ExpandedPrincipal::GetSiteIdentifier(SiteIdentifier
& aSite
) {
272 // Call GetSiteIdentifier on each of our principals and return a new
273 // ExpandedPrincipal.
275 nsTArray
<nsCOMPtr
<nsIPrincipal
>> allowlist
;
276 for (const auto& principal
: mPrincipals
) {
278 nsresult rv
= Cast(principal
)->GetSiteIdentifier(site
);
279 NS_ENSURE_SUCCESS(rv
, rv
);
280 allowlist
.AppendElement(site
.GetPrincipal());
283 RefPtr
<ExpandedPrincipal
> expandedPrincipal
=
284 ExpandedPrincipal::Create(allowlist
, OriginAttributesRef());
285 MOZ_ASSERT(expandedPrincipal
, "ExpandedPrincipal::Create returned nullptr?");
287 aSite
.Init(expandedPrincipal
);
291 nsresult
ExpandedPrincipal::PopulateJSONObject(Json::Value
& aObject
) {
292 nsAutoCString principalList
;
293 // First item through we have a blank separator and append the next result
295 for (auto& principal
: mPrincipals
) {
297 BasePrincipal::Cast(principal
)->ToJSON(JSON
);
298 // This is blank for the first run through so the last in the list doesn't
300 principalList
.Append(sep
);
302 // Values currently only copes with strings so encode into base64 to allow a
305 rv
= Base64EncodeAppend(JSON
, principalList
);
306 NS_ENSURE_SUCCESS(rv
, rv
);
308 aObject
[std::to_string(eSpecs
)] = principalList
.get();
310 nsAutoCString suffix
;
311 OriginAttributesRef().CreateSuffix(suffix
);
312 if (suffix
.Length() > 0) {
313 aObject
[std::to_string(eSuffix
)] = suffix
.get();
319 already_AddRefed
<BasePrincipal
> ExpandedPrincipal::FromProperties(
320 nsTArray
<ExpandedPrincipal::KeyVal
>& aFields
) {
321 MOZ_ASSERT(aFields
.Length() == eMax
+ 1, "Must have all the keys");
322 nsTArray
<nsCOMPtr
<nsIPrincipal
>> allowList
;
323 OriginAttributes attrs
;
324 // The odd structure here is to make the code to not compile
325 // if all the switch enum cases haven't been codified
326 for (const auto& field
: aFields
) {
328 case ExpandedPrincipal::eSpecs
:
329 if (!field
.valueWasSerialized
) {
331 "Expanded principals require specs in serialized JSON");
334 for (const nsACString
& each
: field
.value
.Split(',')) {
335 nsAutoCString result
;
337 rv
= Base64Decode(each
, result
);
338 MOZ_ASSERT(NS_SUCCEEDED(rv
), "failed to decode");
340 NS_ENSURE_SUCCESS(rv
, nullptr);
341 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::FromJSON(result
);
342 allowList
.AppendElement(principal
);
345 case ExpandedPrincipal::eSuffix
:
346 if (field
.valueWasSerialized
) {
347 bool ok
= attrs
.PopulateFromSuffix(field
.value
);
356 if (allowList
.Length() == 0) {
360 RefPtr
<ExpandedPrincipal
> expandedPrincipal
=
361 ExpandedPrincipal::Create(allowList
, attrs
);
363 return expandedPrincipal
.forget();
367 ExpandedPrincipal::IsThirdPartyURI(nsIURI
* aURI
, bool* aRes
) {
368 // ExpandedPrincipal for extension content scripts consist of two principals,
369 // the document's principal and the extension's principal.
370 // To make sure that the third-party check behaves like the web page on which
371 // the content script is running, ignore the extension's principal.
373 for (const auto& principal
: mPrincipals
) {
374 if (!Cast(principal
)->AddonPolicy()) {
375 return Cast(principal
)->IsThirdPartyURI(aURI
, aRes
);
379 if (mPrincipals
.IsEmpty()) {
384 return Cast(mPrincipals
[0])->IsThirdPartyURI(aURI
, aRes
);