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 "json/json.h"
15 using namespace mozilla
;
17 NS_IMPL_CLASSINFO(ExpandedPrincipal
, nullptr, 0, NS_EXPANDEDPRINCIPAL_CID
)
18 NS_IMPL_QUERY_INTERFACE_CI(ExpandedPrincipal
, nsIPrincipal
,
20 NS_IMPL_CI_INTERFACE_GETTER(ExpandedPrincipal
, nsIPrincipal
,
23 ExpandedPrincipal::ExpandedPrincipal(
24 nsTArray
<nsCOMPtr
<nsIPrincipal
>>&& aPrincipals
,
25 const nsACString
& aOriginNoSuffix
, const OriginAttributes
& aAttrs
)
26 : BasePrincipal(eExpandedPrincipal
, aOriginNoSuffix
, aAttrs
),
27 mPrincipals(std::move(aPrincipals
)) {}
29 ExpandedPrincipal::~ExpandedPrincipal() = default;
31 already_AddRefed
<ExpandedPrincipal
> ExpandedPrincipal::Create(
32 const nsTArray
<nsCOMPtr
<nsIPrincipal
>>& aAllowList
,
33 const OriginAttributes
& aAttrs
) {
34 nsTArray
<nsCOMPtr
<nsIPrincipal
>> principals
;
35 for (size_t i
= 0; i
< aAllowList
.Length(); ++i
) {
36 principals
.AppendElement(aAllowList
[i
]);
40 origin
.AssignLiteral("[Expanded Principal [");
42 origin
, ", "_ns
, principals
,
43 [](nsACString
& dest
, const nsCOMPtr
<nsIPrincipal
>& principal
) {
44 nsAutoCString subOrigin
;
45 DebugOnly
<nsresult
> rv
= principal
->GetOrigin(subOrigin
);
46 MOZ_ASSERT(NS_SUCCEEDED(rv
));
47 dest
.Append(subOrigin
);
49 origin
.AppendLiteral("]]");
51 RefPtr
<ExpandedPrincipal
> ep
=
52 new ExpandedPrincipal(std::move(principals
), origin
, aAttrs
);
57 ExpandedPrincipal::GetDomain(nsIURI
** aDomain
) {
63 ExpandedPrincipal::SetDomain(nsIURI
* aDomain
) { return NS_OK
; }
65 bool ExpandedPrincipal::SubsumesInternal(
67 BasePrincipal::DocumentDomainConsideration aConsideration
) {
68 // If aOther is an ExpandedPrincipal too, we break it down into its component
69 // nsIPrincipals, and check subsumes on each one.
70 if (Cast(aOther
)->Is
<ExpandedPrincipal
>()) {
71 auto* expanded
= Cast(aOther
)->As
<ExpandedPrincipal
>();
73 for (auto& other
: expanded
->AllowList()) {
74 // Use SubsumesInternal rather than Subsumes here, since OriginAttribute
75 // checks are only done between non-expanded sub-principals, and we don't
76 // need to incur the extra virtual call overhead.
77 if (!SubsumesInternal(other
, aConsideration
)) {
84 // We're dealing with a regular principal. One of our principals must subsume
86 for (uint32_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
87 if (Cast(mPrincipals
[i
])->Subsumes(aOther
, aConsideration
)) {
95 bool ExpandedPrincipal::MayLoadInternal(nsIURI
* uri
) {
96 for (uint32_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
97 if (BasePrincipal::Cast(mPrincipals
[i
])->MayLoadInternal(uri
)) {
105 uint32_t ExpandedPrincipal::GetHashValue() {
106 MOZ_CRASH("extended principal should never be used as key in a hash map");
110 ExpandedPrincipal::GetURI(nsIURI
** aURI
) {
115 const nsTArray
<nsCOMPtr
<nsIPrincipal
>>& ExpandedPrincipal::AllowList() {
120 ExpandedPrincipal::GetBaseDomain(nsACString
& aBaseDomain
) {
121 return NS_ERROR_NOT_AVAILABLE
;
125 ExpandedPrincipal::GetAddonId(nsAString
& aAddonId
) {
130 bool ExpandedPrincipal::AddonHasPermission(const nsAtom
* aPerm
) {
131 for (size_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
132 if (BasePrincipal::Cast(mPrincipals
[i
])->AddonHasPermission(aPerm
)) {
139 bool ExpandedPrincipal::AddonAllowsLoad(nsIURI
* aURI
,
140 bool aExplicit
/* = false */) {
141 for (const auto& principal
: mPrincipals
) {
142 if (Cast(principal
)->AddonAllowsLoad(aURI
, aExplicit
)) {
149 void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy
* aCSP
) {
150 AssertIsOnMainThread();
151 mCSP
= new nsMainThreadPtrHolder
<nsIContentSecurityPolicy
>(
152 "ExpandedPrincipal::mCSP", aCSP
);
156 ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy
** aCsp
) {
157 AssertIsOnMainThread();
158 NS_IF_ADDREF(*aCsp
= mCSP
);
162 nsIPrincipal
* ExpandedPrincipal::PrincipalToInherit(nsIURI
* aRequestedURI
) {
164 // If a given sub-principal subsumes the given URI, use that principal for
165 // inheritance. In general, this only happens with certain CORS modes, loads
166 // with forced principal inheritance, and creation of XML documents from
167 // XMLHttpRequests or fetch requests. For URIs that normally inherit a
168 // principal (such as data: URIs), we fall back to the last principal in the
170 for (const auto& principal
: mPrincipals
) {
171 if (Cast(principal
)->MayLoadInternal(aRequestedURI
)) {
176 return mPrincipals
.LastElement();
179 nsresult
ExpandedPrincipal::GetScriptLocation(nsACString
& aStr
) {
180 aStr
.AssignLiteral("[Expanded Principal [");
181 for (size_t i
= 0; i
< mPrincipals
.Length(); ++i
) {
183 aStr
.AppendLiteral(", ");
188 nsJSPrincipals::get(mPrincipals
.ElementAt(i
))->GetScriptLocation(spec
);
189 NS_ENSURE_SUCCESS(rv
, rv
);
193 aStr
.AppendLiteral("]]");
197 //////////////////////////////////////////
198 // Methods implementing nsISerializable //
199 //////////////////////////////////////////
201 // We've had way too many issues with unversioned serializations, so
202 // explicitly version this one.
203 static const uint32_t kSerializationVersion
= 1;
206 ExpandedPrincipal::Deserializer::Read(nsIObjectInputStream
* aStream
) {
208 nsresult rv
= aStream
->Read32(&version
);
209 if (version
!= kSerializationVersion
) {
211 "We really need to add handling of the old(?) version here");
212 return NS_ERROR_UNEXPECTED
;
216 rv
= aStream
->Read32(&count
);
221 nsTArray
<nsCOMPtr
<nsIPrincipal
>> principals
;
222 if (!principals
.SetCapacity(count
, fallible
)) {
223 return NS_ERROR_OUT_OF_MEMORY
;
226 for (uint32_t i
= 0; i
< count
; ++i
) {
227 nsCOMPtr
<nsISupports
> read
;
228 rv
= aStream
->ReadObject(true, getter_AddRefs(read
));
233 nsCOMPtr
<nsIPrincipal
> principal
= do_QueryInterface(read
);
235 return NS_ERROR_UNEXPECTED
;
238 principals
.AppendElement(std::move(principal
));
241 mPrincipal
= ExpandedPrincipal::Create(principals
, OriginAttributes());
245 nsresult
ExpandedPrincipal::GetSiteIdentifier(SiteIdentifier
& aSite
) {
246 // Call GetSiteIdentifier on each of our principals and return a new
247 // ExpandedPrincipal.
249 nsTArray
<nsCOMPtr
<nsIPrincipal
>> allowlist
;
250 for (const auto& principal
: mPrincipals
) {
252 nsresult rv
= Cast(principal
)->GetSiteIdentifier(site
);
253 NS_ENSURE_SUCCESS(rv
, rv
);
254 allowlist
.AppendElement(site
.GetPrincipal());
257 RefPtr
<ExpandedPrincipal
> expandedPrincipal
=
258 ExpandedPrincipal::Create(allowlist
, OriginAttributesRef());
259 MOZ_ASSERT(expandedPrincipal
, "ExpandedPrincipal::Create returned nullptr?");
261 aSite
.Init(expandedPrincipal
);
265 nsresult
ExpandedPrincipal::PopulateJSONObject(Json::Value
& aObject
) {
266 Json::Value
& principalList
=
267 aObject
[Json::StaticString(JSONEnumKeyString
<eSpecs
>())] =
269 for (const auto& principal
: mPrincipals
) {
270 Json::Value object
= Json::objectValue
;
271 nsresult rv
= BasePrincipal::Cast(principal
)->ToJSON(object
);
272 NS_ENSURE_SUCCESS(rv
, rv
);
274 principalList
.append(std::move(object
));
277 nsAutoCString suffix
;
278 OriginAttributesRef().CreateSuffix(suffix
);
279 if (suffix
.Length() > 0) {
280 SetJSONValue
<eSuffix
>(aObject
, suffix
);
286 already_AddRefed
<BasePrincipal
> ExpandedPrincipal::FromProperties(
287 nsTArray
<ExpandedPrincipal::KeyVal
>& aFields
) {
288 MOZ_ASSERT(aFields
.Length() == eMax
+ 1, "Must have all the keys");
289 nsTArray
<nsCOMPtr
<nsIPrincipal
>> allowList
;
290 OriginAttributes attrs
;
291 // The odd structure here is to make the code to not compile
292 // if all the switch enum cases haven't been codified
294 for (const auto& field
: aFields
) {
296 case ExpandedPrincipal::eSpecs
:
297 if (!field
.valueWasSerialized
) {
299 "Expanded principals require specs in serialized JSON");
302 for (const nsACString
& each
: field
.value
.Split(',')) {
303 nsAutoCString result
;
305 rv
= Base64Decode(each
, result
);
306 MOZ_ASSERT(NS_SUCCEEDED(rv
), "failed to decode");
308 NS_ENSURE_SUCCESS(rv
, nullptr);
309 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::FromJSON(result
);
310 allowList
.AppendElement(principal
);
313 case ExpandedPrincipal::eSuffix
:
314 if (field
.valueWasSerialized
) {
315 bool ok
= attrs
.PopulateFromSuffix(field
.value
);
324 if (allowList
.Length() == 0) {
328 RefPtr
<ExpandedPrincipal
> expandedPrincipal
=
329 ExpandedPrincipal::Create(allowList
, attrs
);
331 return expandedPrincipal
.forget();
335 already_AddRefed
<BasePrincipal
> ExpandedPrincipal::FromProperties(
336 const Json::Value
& aJSON
) {
337 MOZ_ASSERT(aJSON
.size() <= eMax
+ 1, "Must have at most, all the properties");
338 const std::string specs
= std::to_string(eSpecs
);
339 const std::string suffix
= std::to_string(eSuffix
);
340 MOZ_ASSERT(aJSON
.isMember(specs
), "The eSpecs member is required");
341 MOZ_ASSERT(aJSON
.size() == 1 || aJSON
.isMember(suffix
),
342 "eSuffix is optional");
344 const auto* specsValue
=
345 aJSON
.find(specs
.c_str(), specs
.c_str() + specs
.length());
347 MOZ_ASSERT(false, "Expanded principals require specs in serialized JSON");
351 nsTArray
<nsCOMPtr
<nsIPrincipal
>> allowList
;
352 for (const auto& principalJSON
: *specsValue
) {
353 if (nsCOMPtr
<nsIPrincipal
> principal
=
354 BasePrincipal::FromJSON(principalJSON
)) {
355 allowList
.AppendElement(principal
);
359 if (allowList
.Length() == 0) {
363 OriginAttributes attrs
;
364 if (aJSON
.isMember(suffix
)) {
365 const auto& value
= aJSON
[suffix
];
366 if (!value
.isString()) {
370 bool ok
= attrs
.PopulateFromSuffix(nsDependentCString(value
.asCString()));
376 RefPtr
<ExpandedPrincipal
> expandedPrincipal
=
377 ExpandedPrincipal::Create(allowList
, attrs
);
379 return expandedPrincipal
.forget();
383 ExpandedPrincipal::IsThirdPartyURI(nsIURI
* aURI
, bool* aRes
) {
384 // ExpandedPrincipal for extension content scripts consist of two principals,
385 // the document's principal and the extension's principal.
386 // To make sure that the third-party check behaves like the web page on which
387 // the content script is running, ignore the extension's principal.
389 for (const auto& principal
: mPrincipals
) {
390 if (!Cast(principal
)->AddonPolicyCore()) {
391 return Cast(principal
)->IsThirdPartyURI(aURI
, aRes
);
395 if (mPrincipals
.IsEmpty()) {
400 return Cast(mPrincipals
[0])->IsThirdPartyURI(aURI
, aRes
);