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 "mozilla/BasePrincipal.h"
9 #include "nsDocShell.h"
10 #include "nsIObjectInputStream.h"
11 #include "nsIObjectOutputStream.h"
12 #include "nsIStandardURL.h"
14 #include "ExpandedPrincipal.h"
15 #include "nsNetUtil.h"
16 #include "nsContentUtils.h"
17 #include "nsIURIWithSpecialOrigin.h"
18 #include "nsScriptSecurityManager.h"
19 #include "nsServiceManagerUtils.h"
21 #include "mozilla/ContentPrincipal.h"
22 #include "mozilla/NullPrincipal.h"
23 #include "mozilla/dom/BlobURLProtocolHandler.h"
24 #include "mozilla/dom/ChromeUtils.h"
25 #include "mozilla/dom/ToJSValue.h"
27 #include "json/json.h"
28 #include "nsSerializationHelper.h"
32 BasePrincipal::BasePrincipal(PrincipalKind aKind
)
33 : mKind(aKind
), mHasExplicitDomain(false), mInitialized(false) {}
35 BasePrincipal::~BasePrincipal() {}
38 BasePrincipal::GetOrigin(nsACString
& aOrigin
) {
39 MOZ_ASSERT(mInitialized
);
41 nsresult rv
= GetOriginNoSuffix(aOrigin
);
42 NS_ENSURE_SUCCESS(rv
, rv
);
45 rv
= GetOriginSuffix(suffix
);
46 NS_ENSURE_SUCCESS(rv
, rv
);
47 aOrigin
.Append(suffix
);
52 BasePrincipal::GetOriginNoSuffix(nsACString
& aOrigin
) {
53 MOZ_ASSERT(mInitialized
);
54 mOriginNoSuffix
->ToUTF8String(aOrigin
);
59 BasePrincipal::GetSiteOrigin(nsACString
& aSiteOrigin
) {
60 MOZ_ASSERT(mInitialized
);
61 return GetOrigin(aSiteOrigin
);
64 // Returns the inner Json::value of the serialized principal
65 // Example input and return values:
67 // {"0":{"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}} ->
68 // {"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}
71 // {"1":{"0":"https://mozilla.com"}} -> {"0":"https://mozilla.com"}
73 // Expanded principal:
74 // {"2":{"0":"<base64principal1>,<base64principal2>"}} ->
75 // {"0":"<base64principal1>,<base64principal2>"}
79 // The aKey passed in also returns the corresponding PrincipalKind enum
81 // Warning: The Json::Value* pointer is into the aRoot object
82 static const Json::Value
* GetPrincipalObject(const Json::Value
& aRoot
,
83 int& aOutPrincipalKind
) {
84 const Json::Value::Members members
= aRoot
.getMemberNames();
85 // We only support one top level key in the object
86 if (members
.size() != 1) {
89 // members[0] here is the "0", "1", "2", "3" principalKind
90 // that is the top level of the serialized JSON principal
91 const std::string stringPrincipalKind
= members
[0];
93 // Next we take the string value from the JSON
94 // and convert it into the int for the BasePrincipal::PrincipalKind enum
96 // Verify that the key is within the valid range
97 int principalKind
= std::stoi(stringPrincipalKind
);
98 MOZ_ASSERT(BasePrincipal::eNullPrincipal
== 0,
99 "We need to rely on 0 being a bounds check for the first "
101 if (principalKind
< 0 || principalKind
> BasePrincipal::eKindMax
) {
104 MOZ_ASSERT(principalKind
== BasePrincipal::eNullPrincipal
||
105 principalKind
== BasePrincipal::eContentPrincipal
||
106 principalKind
== BasePrincipal::eExpandedPrincipal
||
107 principalKind
== BasePrincipal::eSystemPrincipal
);
108 aOutPrincipalKind
= principalKind
;
110 if (!aRoot
[stringPrincipalKind
].isObject()) {
114 // Return the inner value of the principal object
115 return &aRoot
[stringPrincipalKind
];
118 // Accepts the JSON inner object without the wrapping principalKind
119 // (See GetPrincipalObject for the inner object response examples)
120 // Creates an array of KeyVal objects that are all defined on the principal
121 // Each principal type (null, content, expanded) has a KeyVal that stores the
122 // fields of the JSON
124 // This simplifies deserializing elsewhere as we do the checking for presence
125 // and string values here for the complete set of serializable keys that the
126 // corresponding principal supports.
128 // The KeyVal object has the following fields:
129 // - valueWasSerialized: is true if the deserialized JSON contained a string
131 // - value: The string that was serialized for this key
132 // - key: an SerializableKeys enum value specific to the principal.
133 // For example content principal is an enum of: eURI, eDomain,
137 // Given an inner content principal:
138 // {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}
140 // ----------------------------- |
142 // Key ----------------------
146 // They Key "0" corresponds to ContentPrincipal::eURI
147 // They Key "1" corresponds to ContentPrincipal::eSuffix
148 template <typename T
>
149 static nsTArray
<typename
T::KeyVal
> GetJSONKeys(const Json::Value
* aInput
) {
150 int size
= T::eMax
+ 1;
151 nsTArray
<typename
T::KeyVal
> fields
;
152 for (int i
= 0; i
!= size
; i
++) {
153 typename
T::KeyVal field
;
154 // field.valueWasSerialized returns if the field was found in the
155 // deserialized code. This simplifies the consumers from having to check
157 field
.valueWasSerialized
= false;
158 field
.key
= static_cast<typename
T::SerializableKeys
>(i
);
159 const std::string key
= std::to_string(field
.key
);
160 if (aInput
->isMember(key
) && (*aInput
)[key
].isString()) {
161 field
.value
.Append(nsDependentCString((*aInput
)[key
].asCString()));
162 field
.valueWasSerialized
= true;
164 fields
.AppendElement(field
);
169 // Takes a JSON string and parses it turning it into a principal of the
170 // corresponding type
172 // Given a content principal:
176 // ---------------------------------------------------------
178 // {"1": {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}}
180 // | ----------------------------- |
182 // PrincipalKind | | |
183 // | ----------------------------
184 // SerializableKeys |
187 // The string is first deserialized with jsoncpp to get the Json::Value of the
188 // object. The inner JSON object is parsed with GetPrincipalObject which returns
189 // a KeyVal array of the inner object's fields. PrincipalKind is returned by
190 // GetPrincipalObject which is then used to decide which principal
191 // implementation of FromProperties to call. The corresponding FromProperties
192 // call takes the KeyVal fields and turns it into a principal.
193 already_AddRefed
<BasePrincipal
> BasePrincipal::FromJSON(
194 const nsACString
& aJSON
) {
196 Json::CharReaderBuilder builder
;
197 std::unique_ptr
<Json::CharReader
> const reader(builder
.newCharReader());
199 reader
->parse(aJSON
.BeginReading(), aJSON
.EndReading(), &root
, nullptr);
202 "Unable to parse string as JSON to deserialize as a principal");
206 int principalKind
= -1;
207 const Json::Value
* value
= GetPrincipalObject(root
, principalKind
);
210 fprintf(stderr
, "Unexpected JSON principal %s\n",
211 root
.toStyledString().c_str());
213 MOZ_ASSERT(false, "Unexpected JSON to deserialize as a principal");
217 MOZ_ASSERT(principalKind
!= -1,
218 "PrincipalKind should always be >=0 by this point");
220 if (principalKind
== eSystemPrincipal
) {
221 RefPtr
<BasePrincipal
> principal
=
222 BasePrincipal::Cast(nsContentUtils::GetSystemPrincipal());
223 return principal
.forget();
226 if (principalKind
== eNullPrincipal
) {
227 nsTArray
<NullPrincipal::KeyVal
> res
= GetJSONKeys
<NullPrincipal
>(value
);
228 return NullPrincipal::FromProperties(res
);
231 if (principalKind
== eContentPrincipal
) {
232 nsTArray
<ContentPrincipal::KeyVal
> res
=
233 GetJSONKeys
<ContentPrincipal
>(value
);
234 return ContentPrincipal::FromProperties(res
);
237 if (principalKind
== eExpandedPrincipal
) {
238 nsTArray
<ExpandedPrincipal::KeyVal
> res
=
239 GetJSONKeys
<ExpandedPrincipal
>(value
);
240 return ExpandedPrincipal::FromProperties(res
);
243 MOZ_RELEASE_ASSERT(false, "Unexpected enum to deserialize as a principal");
246 nsresult
BasePrincipal::PopulateJSONObject(Json::Value
& aObject
) {
250 // Returns a JSON representation of the principal.
251 // Calling BasePrincipal::FromJSON will deserialize the JSON into
252 // the corresponding principal type.
253 nsresult
BasePrincipal::ToJSON(nsACString
& aResult
) {
254 MOZ_ASSERT(aResult
.IsEmpty(), "ToJSON only supports an empty result input");
257 Json::StreamWriterBuilder builder
;
258 builder
["indentation"] = "";
259 Json::Value innerJSONObject
= Json::objectValue
;
261 nsresult rv
= PopulateJSONObject(innerJSONObject
);
262 NS_ENSURE_SUCCESS(rv
, rv
);
264 Json::Value root
= Json::objectValue
;
265 std::string key
= std::to_string(Kind());
266 root
[key
] = innerJSONObject
;
267 std::string result
= Json::writeString(builder
, root
);
268 aResult
.Append(result
);
269 if (aResult
.Length() == 0) {
270 MOZ_ASSERT(false, "JSON writer failed to output a principal serialization");
271 return NS_ERROR_UNEXPECTED
;
276 bool BasePrincipal::Subsumes(nsIPrincipal
* aOther
,
277 DocumentDomainConsideration aConsideration
) {
279 MOZ_ASSERT_IF(Kind() == eContentPrincipal
, mOriginSuffix
);
281 // Expanded principals handle origin attributes for each of their
282 // sub-principals individually, null principals do only simple checks for
283 // pointer equality, and system principals are immune to origin attributes
284 // checks, so only do this check for content principals.
285 if (Kind() == eContentPrincipal
&&
286 mOriginSuffix
!= Cast(aOther
)->mOriginSuffix
) {
290 return SubsumesInternal(aOther
, aConsideration
);
294 BasePrincipal::Equals(nsIPrincipal
* aOther
, bool* aResult
) {
295 NS_ENSURE_TRUE(aOther
, NS_ERROR_INVALID_ARG
);
297 *aResult
= FastEquals(aOther
);
303 BasePrincipal::EqualsConsideringDomain(nsIPrincipal
* aOther
, bool* aResult
) {
304 NS_ENSURE_TRUE(aOther
, NS_ERROR_INVALID_ARG
);
306 *aResult
= FastEqualsConsideringDomain(aOther
);
312 BasePrincipal::Subsumes(nsIPrincipal
* aOther
, bool* aResult
) {
313 NS_ENSURE_TRUE(aOther
, NS_ERROR_INVALID_ARG
);
315 *aResult
= FastSubsumes(aOther
);
321 BasePrincipal::SubsumesConsideringDomain(nsIPrincipal
* aOther
, bool* aResult
) {
322 NS_ENSURE_TRUE(aOther
, NS_ERROR_INVALID_ARG
);
324 *aResult
= FastSubsumesConsideringDomain(aOther
);
330 BasePrincipal::SubsumesConsideringDomainIgnoringFPD(nsIPrincipal
* aOther
,
332 NS_ENSURE_TRUE(aOther
, NS_ERROR_INVALID_ARG
);
334 *aResult
= FastSubsumesConsideringDomainIgnoringFPD(aOther
);
340 BasePrincipal::CheckMayLoad(nsIURI
* aURI
, bool aReport
,
341 bool aAllowIfInheritsPrincipal
) {
342 // Check the internal method first, which allows us to quickly approve loads
343 // for the System Principal.
344 if (MayLoadInternal(aURI
)) {
349 if (aAllowIfInheritsPrincipal
) {
350 // If the caller specified to allow loads of URIs that inherit
351 // our principal, allow the load if this URI inherits its principal.
352 bool doesInheritSecurityContext
;
353 rv
= NS_URIChainHasFlags(aURI
,
354 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT
,
355 &doesInheritSecurityContext
);
356 if (NS_SUCCEEDED(rv
) && doesInheritSecurityContext
) {
361 bool fetchableByAnyone
;
362 rv
= NS_URIChainHasFlags(aURI
, nsIProtocolHandler::URI_FETCHABLE_BY_ANYONE
,
364 if (NS_SUCCEEDED(rv
) && fetchableByAnyone
) {
369 nsCOMPtr
<nsIURI
> prinURI
;
370 rv
= GetURI(getter_AddRefs(prinURI
));
371 if (NS_SUCCEEDED(rv
) && prinURI
) {
372 nsScriptSecurityManager::ReportError(
373 "CheckSameOriginError", prinURI
, aURI
,
374 mOriginAttributes
.mPrivateBrowsingId
> 0);
378 return NS_ERROR_DOM_BAD_URI
;
382 BasePrincipal::GetIsNullPrincipal(bool* aResult
) {
383 *aResult
= Kind() == eNullPrincipal
;
388 BasePrincipal::GetIsContentPrincipal(bool* aResult
) {
389 *aResult
= Kind() == eContentPrincipal
;
394 BasePrincipal::GetIsExpandedPrincipal(bool* aResult
) {
395 *aResult
= Kind() == eExpandedPrincipal
;
400 BasePrincipal::GetIsSystemPrincipal(bool* aResult
) {
401 *aResult
= IsSystemPrincipal();
406 BasePrincipal::GetIsAddonOrExpandedAddonPrincipal(bool* aResult
) {
407 *aResult
= AddonPolicy() || ContentScriptAddonPolicy();
412 BasePrincipal::GetOriginAttributes(JSContext
* aCx
,
413 JS::MutableHandle
<JS::Value
> aVal
) {
414 if (NS_WARN_IF(!ToJSValue(aCx
, mOriginAttributes
, aVal
))) {
415 return NS_ERROR_FAILURE
;
421 BasePrincipal::GetOriginSuffix(nsACString
& aOriginAttributes
) {
422 MOZ_ASSERT(mOriginSuffix
);
423 mOriginSuffix
->ToUTF8String(aOriginAttributes
);
428 BasePrincipal::GetUserContextId(uint32_t* aUserContextId
) {
429 *aUserContextId
= UserContextId();
434 BasePrincipal::GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId
) {
435 *aPrivateBrowsingId
= PrivateBrowsingId();
440 BasePrincipal::GetIsInIsolatedMozBrowserElement(
441 bool* aIsInIsolatedMozBrowserElement
) {
442 *aIsInIsolatedMozBrowserElement
= IsInIsolatedMozBrowserElement();
446 nsresult
BasePrincipal::GetAddonPolicy(nsISupports
** aResult
) {
447 RefPtr
<extensions::WebExtensionPolicy
> policy(AddonPolicy());
448 policy
.forget(aResult
);
452 extensions::WebExtensionPolicy
* BasePrincipal::AddonPolicy() {
453 if (Is
<ContentPrincipal
>()) {
454 return As
<ContentPrincipal
>()->AddonPolicy();
459 bool BasePrincipal::AddonHasPermission(const nsAtom
* aPerm
) {
460 if (auto policy
= AddonPolicy()) {
461 return policy
->HasPermission(aPerm
);
466 nsIPrincipal
* BasePrincipal::PrincipalToInherit(nsIURI
* aRequestedURI
) {
467 if (Is
<ExpandedPrincipal
>()) {
468 return As
<ExpandedPrincipal
>()->PrincipalToInherit(aRequestedURI
);
473 already_AddRefed
<BasePrincipal
> BasePrincipal::CreateContentPrincipal(
474 nsIURI
* aURI
, const OriginAttributes
& aAttrs
) {
477 nsAutoCString originNoSuffix
;
479 ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI
, originNoSuffix
);
481 // If the generation of the origin fails, we still want to have a valid
482 // principal. Better to return a null principal here.
483 return NullPrincipal::Create(aAttrs
);
486 return CreateContentPrincipal(aURI
, aAttrs
, originNoSuffix
);
489 already_AddRefed
<BasePrincipal
> BasePrincipal::CreateContentPrincipal(
490 nsIURI
* aURI
, const OriginAttributes
& aAttrs
,
491 const nsACString
& aOriginNoSuffix
) {
493 MOZ_ASSERT(!aOriginNoSuffix
.IsEmpty());
495 // If the URI is supposed to inherit the security context of whoever loads it,
496 // we shouldn't make a content principal for it.
497 bool inheritsPrincipal
;
498 nsresult rv
= NS_URIChainHasFlags(
499 aURI
, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT
,
501 if (NS_FAILED(rv
) || inheritsPrincipal
) {
502 return NullPrincipal::Create(aAttrs
);
505 // Check whether the URI knows what its principal is supposed to be.
506 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
507 nsCOMPtr
<nsIURIWithSpecialOrigin
> uriWithSpecialOrigin
=
508 do_QueryInterface(aURI
);
509 if (uriWithSpecialOrigin
) {
510 nsCOMPtr
<nsIURI
> origin
;
511 rv
= uriWithSpecialOrigin
->GetOrigin(getter_AddRefs(origin
));
512 if (NS_WARN_IF(NS_FAILED(rv
))) {
516 OriginAttributes attrs
;
517 RefPtr
<BasePrincipal
> principal
= CreateContentPrincipal(origin
, attrs
);
518 return principal
.forget();
522 nsCOMPtr
<nsIPrincipal
> blobPrincipal
;
523 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
524 aURI
, getter_AddRefs(blobPrincipal
))) {
525 MOZ_ASSERT(blobPrincipal
);
526 RefPtr
<BasePrincipal
> principal
= Cast(blobPrincipal
);
527 return principal
.forget();
530 // Mint a content principal.
531 RefPtr
<ContentPrincipal
> principal
= new ContentPrincipal();
532 rv
= principal
->Init(aURI
, aAttrs
, aOriginNoSuffix
);
533 NS_ENSURE_SUCCESS(rv
, nullptr);
534 return principal
.forget();
537 already_AddRefed
<BasePrincipal
> BasePrincipal::CreateContentPrincipal(
538 const nsACString
& aOrigin
) {
539 MOZ_ASSERT(!StringBeginsWith(aOrigin
, NS_LITERAL_CSTRING("[")),
540 "CreateContentPrincipal does not support System and Expanded "
543 MOZ_ASSERT(!StringBeginsWith(aOrigin
,
544 NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME
":")),
545 "CreateContentPrincipal does not support NullPrincipal");
547 nsAutoCString originNoSuffix
;
548 OriginAttributes attrs
;
549 if (!attrs
.PopulateFromOrigin(aOrigin
, originNoSuffix
)) {
553 nsCOMPtr
<nsIURI
> uri
;
554 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), originNoSuffix
);
555 NS_ENSURE_SUCCESS(rv
, nullptr);
557 return BasePrincipal::CreateContentPrincipal(uri
, attrs
);
560 already_AddRefed
<BasePrincipal
> BasePrincipal::CloneForcingOriginAttributes(
561 const OriginAttributes
& aOriginAttributes
) {
562 if (NS_WARN_IF(!IsContentPrincipal())) {
566 nsAutoCString originNoSuffix
;
567 nsresult rv
= GetOriginNoSuffix(originNoSuffix
);
568 NS_ENSURE_SUCCESS(rv
, nullptr);
570 nsIURI
* uri
= static_cast<ContentPrincipal
*>(this)->mURI
;
571 RefPtr
<ContentPrincipal
> copy
= new ContentPrincipal();
572 rv
= copy
->Init(uri
, aOriginAttributes
, originNoSuffix
);
573 NS_ENSURE_SUCCESS(rv
, nullptr);
575 return copy
.forget();
578 extensions::WebExtensionPolicy
* BasePrincipal::ContentScriptAddonPolicy() {
579 if (!Is
<ExpandedPrincipal
>()) {
583 auto expanded
= As
<ExpandedPrincipal
>();
584 for (auto& prin
: expanded
->AllowList()) {
585 if (auto policy
= BasePrincipal::Cast(prin
)->AddonPolicy()) {
593 bool BasePrincipal::AddonAllowsLoad(nsIURI
* aURI
,
594 bool aExplicit
/* = false */) {
595 if (Is
<ExpandedPrincipal
>()) {
596 return As
<ExpandedPrincipal
>()->AddonAllowsLoad(aURI
, aExplicit
);
598 if (auto policy
= AddonPolicy()) {
599 return policy
->CanAccessURI(aURI
, aExplicit
);
604 void BasePrincipal::FinishInit(const nsACString
& aOriginNoSuffix
,
605 const OriginAttributes
& aOriginAttributes
) {
607 mOriginAttributes
= aOriginAttributes
;
609 // First compute the origin suffix since it's infallible.
610 nsAutoCString originSuffix
;
611 mOriginAttributes
.CreateSuffix(originSuffix
);
612 mOriginSuffix
= NS_Atomize(originSuffix
);
614 MOZ_ASSERT(!aOriginNoSuffix
.IsEmpty());
615 mOriginNoSuffix
= NS_Atomize(aOriginNoSuffix
);
618 void BasePrincipal::FinishInit(BasePrincipal
* aOther
,
619 const OriginAttributes
& aOriginAttributes
) {
621 mOriginAttributes
= aOriginAttributes
;
623 // First compute the origin suffix since it's infallible.
624 nsAutoCString originSuffix
;
625 mOriginAttributes
.CreateSuffix(originSuffix
);
626 mOriginSuffix
= NS_Atomize(originSuffix
);
628 mOriginNoSuffix
= aOther
->mOriginNoSuffix
;
629 mHasExplicitDomain
= aOther
->mHasExplicitDomain
;
632 bool SiteIdentifier::Equals(const SiteIdentifier
& aOther
) const {
633 MOZ_ASSERT(IsInitialized());
634 MOZ_ASSERT(aOther
.IsInitialized());
635 return mPrincipal
->FastEquals(aOther
.mPrincipal
);
638 } // namespace mozilla