Bug 1576631 - include 'variables' in generated actions.json r=nalexander
[gecko.git] / caps / BasePrincipal.cpp
blob5b2a7b67702703f292bba4471c7d165a3ef3e4d4
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"
30 namespace mozilla {
32 BasePrincipal::BasePrincipal(PrincipalKind aKind)
33 : mKind(aKind), mHasExplicitDomain(false), mInitialized(false) {}
35 BasePrincipal::~BasePrincipal() {}
37 NS_IMETHODIMP
38 BasePrincipal::GetOrigin(nsACString& aOrigin) {
39 MOZ_ASSERT(mInitialized);
41 nsresult rv = GetOriginNoSuffix(aOrigin);
42 NS_ENSURE_SUCCESS(rv, rv);
44 nsAutoCString suffix;
45 rv = GetOriginSuffix(suffix);
46 NS_ENSURE_SUCCESS(rv, rv);
47 aOrigin.Append(suffix);
48 return NS_OK;
51 NS_IMETHODIMP
52 BasePrincipal::GetOriginNoSuffix(nsACString& aOrigin) {
53 MOZ_ASSERT(mInitialized);
54 mOriginNoSuffix->ToUTF8String(aOrigin);
55 return NS_OK;
58 NS_IMETHODIMP
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:
66 // Null principal:
67 // {"0":{"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}} ->
68 // {"0":"moz-nullprincipal:{56cac540-864d-47e7-8e25-1614eab5155e}"}
70 // Content principal:
71 // {"1":{"0":"https://mozilla.com"}} -> {"0":"https://mozilla.com"}
73 // Expanded principal:
74 // {"2":{"0":"<base64principal1>,<base64principal2>"}} ->
75 // {"0":"<base64principal1>,<base64principal2>"}
77 // System principal:
78 // {"3":{}} -> {}
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) {
87 return nullptr;
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 "
100 "principal kind.");
101 if (principalKind < 0 || principalKind > BasePrincipal::eKindMax) {
102 return nullptr;
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()) {
111 return nullptr;
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
130 // value
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,
134 // eSuffix, eCSP
137 // Given an inner content principal:
138 // {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}
139 // | | | |
140 // ----------------------------- |
141 // | | |
142 // Key ----------------------
143 // |
144 // Value
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
156 // length.
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);
166 return fields;
169 // Takes a JSON string and parses it turning it into a principal of the
170 // corresponding type
172 // Given a content principal:
174 // inner JSON object
175 // |
176 // ---------------------------------------------------------
177 // | |
178 // {"1": {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}}
179 // | | | | |
180 // | ----------------------------- |
181 // | | | |
182 // PrincipalKind | | |
183 // | ----------------------------
184 // SerializableKeys |
185 // Value
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) {
195 Json::Value root;
196 Json::CharReaderBuilder builder;
197 std::unique_ptr<Json::CharReader> const reader(builder.newCharReader());
198 bool parseSuccess =
199 reader->parse(aJSON.BeginReading(), aJSON.EndReading(), &root, nullptr);
200 if (!parseSuccess) {
201 MOZ_ASSERT(false,
202 "Unable to parse string as JSON to deserialize as a principal");
203 return nullptr;
206 int principalKind = -1;
207 const Json::Value* value = GetPrincipalObject(root, principalKind);
208 if (!value) {
209 #ifdef DEBUG
210 fprintf(stderr, "Unexpected JSON principal %s\n",
211 root.toStyledString().c_str());
212 #endif
213 MOZ_ASSERT(false, "Unexpected JSON to deserialize as a principal");
215 return nullptr;
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) {
247 return NS_OK;
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");
255 aResult.Truncate();
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;
273 return NS_OK;
276 bool BasePrincipal::Subsumes(nsIPrincipal* aOther,
277 DocumentDomainConsideration aConsideration) {
278 MOZ_ASSERT(aOther);
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) {
287 return false;
290 return SubsumesInternal(aOther, aConsideration);
293 NS_IMETHODIMP
294 BasePrincipal::Equals(nsIPrincipal* aOther, bool* aResult) {
295 NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
297 *aResult = FastEquals(aOther);
299 return NS_OK;
302 NS_IMETHODIMP
303 BasePrincipal::EqualsConsideringDomain(nsIPrincipal* aOther, bool* aResult) {
304 NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
306 *aResult = FastEqualsConsideringDomain(aOther);
308 return NS_OK;
311 NS_IMETHODIMP
312 BasePrincipal::Subsumes(nsIPrincipal* aOther, bool* aResult) {
313 NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
315 *aResult = FastSubsumes(aOther);
317 return NS_OK;
320 NS_IMETHODIMP
321 BasePrincipal::SubsumesConsideringDomain(nsIPrincipal* aOther, bool* aResult) {
322 NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
324 *aResult = FastSubsumesConsideringDomain(aOther);
326 return NS_OK;
329 NS_IMETHODIMP
330 BasePrincipal::SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther,
331 bool* aResult) {
332 NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
334 *aResult = FastSubsumesConsideringDomainIgnoringFPD(aOther);
336 return NS_OK;
339 NS_IMETHODIMP
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)) {
345 return NS_OK;
348 nsresult rv;
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) {
357 return NS_OK;
361 bool fetchableByAnyone;
362 rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FETCHABLE_BY_ANYONE,
363 &fetchableByAnyone);
364 if (NS_SUCCEEDED(rv) && fetchableByAnyone) {
365 return NS_OK;
368 if (aReport) {
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;
381 NS_IMETHODIMP
382 BasePrincipal::GetIsNullPrincipal(bool* aResult) {
383 *aResult = Kind() == eNullPrincipal;
384 return NS_OK;
387 NS_IMETHODIMP
388 BasePrincipal::GetIsContentPrincipal(bool* aResult) {
389 *aResult = Kind() == eContentPrincipal;
390 return NS_OK;
393 NS_IMETHODIMP
394 BasePrincipal::GetIsExpandedPrincipal(bool* aResult) {
395 *aResult = Kind() == eExpandedPrincipal;
396 return NS_OK;
399 NS_IMETHODIMP
400 BasePrincipal::GetIsSystemPrincipal(bool* aResult) {
401 *aResult = IsSystemPrincipal();
402 return NS_OK;
405 NS_IMETHODIMP
406 BasePrincipal::GetIsAddonOrExpandedAddonPrincipal(bool* aResult) {
407 *aResult = AddonPolicy() || ContentScriptAddonPolicy();
408 return NS_OK;
411 NS_IMETHODIMP
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;
417 return NS_OK;
420 NS_IMETHODIMP
421 BasePrincipal::GetOriginSuffix(nsACString& aOriginAttributes) {
422 MOZ_ASSERT(mOriginSuffix);
423 mOriginSuffix->ToUTF8String(aOriginAttributes);
424 return NS_OK;
427 NS_IMETHODIMP
428 BasePrincipal::GetUserContextId(uint32_t* aUserContextId) {
429 *aUserContextId = UserContextId();
430 return NS_OK;
433 NS_IMETHODIMP
434 BasePrincipal::GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) {
435 *aPrivateBrowsingId = PrivateBrowsingId();
436 return NS_OK;
439 NS_IMETHODIMP
440 BasePrincipal::GetIsInIsolatedMozBrowserElement(
441 bool* aIsInIsolatedMozBrowserElement) {
442 *aIsInIsolatedMozBrowserElement = IsInIsolatedMozBrowserElement();
443 return NS_OK;
446 nsresult BasePrincipal::GetAddonPolicy(nsISupports** aResult) {
447 RefPtr<extensions::WebExtensionPolicy> policy(AddonPolicy());
448 policy.forget(aResult);
449 return NS_OK;
452 extensions::WebExtensionPolicy* BasePrincipal::AddonPolicy() {
453 if (Is<ContentPrincipal>()) {
454 return As<ContentPrincipal>()->AddonPolicy();
456 return nullptr;
459 bool BasePrincipal::AddonHasPermission(const nsAtom* aPerm) {
460 if (auto policy = AddonPolicy()) {
461 return policy->HasPermission(aPerm);
463 return false;
466 nsIPrincipal* BasePrincipal::PrincipalToInherit(nsIURI* aRequestedURI) {
467 if (Is<ExpandedPrincipal>()) {
468 return As<ExpandedPrincipal>()->PrincipalToInherit(aRequestedURI);
470 return this;
473 already_AddRefed<BasePrincipal> BasePrincipal::CreateContentPrincipal(
474 nsIURI* aURI, const OriginAttributes& aAttrs) {
475 MOZ_ASSERT(aURI);
477 nsAutoCString originNoSuffix;
478 nsresult rv =
479 ContentPrincipal::GenerateOriginNoSuffixFromURI(aURI, originNoSuffix);
480 if (NS_FAILED(rv)) {
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) {
492 MOZ_ASSERT(aURI);
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,
500 &inheritsPrincipal);
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))) {
513 return nullptr;
515 MOZ_ASSERT(origin);
516 OriginAttributes attrs;
517 RefPtr<BasePrincipal> principal = CreateContentPrincipal(origin, attrs);
518 return principal.forget();
520 #endif
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 "
541 "principals");
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)) {
550 return nullptr;
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())) {
563 return nullptr;
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>()) {
580 return nullptr;
583 auto expanded = As<ExpandedPrincipal>();
584 for (auto& prin : expanded->AllowList()) {
585 if (auto policy = BasePrincipal::Cast(prin)->AddonPolicy()) {
586 return policy;
590 return nullptr;
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);
601 return false;
604 void BasePrincipal::FinishInit(const nsACString& aOriginNoSuffix,
605 const OriginAttributes& aOriginAttributes) {
606 mInitialized = true;
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) {
620 mInitialized = true;
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