1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 sts=2 ts=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/. */
8 * This is the principal that has no rights and can't be accessed by
9 * anything other than itself and chrome; null principals are not
10 * same-origin with anything but themselves.
13 #include "mozilla/ArrayUtils.h"
15 #include "mozilla/dom/BlobURLProtocolHandler.h"
16 #include "mozilla/StaticPrefs_network.h"
17 #include "nsDocShell.h"
18 #include "NullPrincipal.h"
19 #include "DefaultURI.h"
20 #include "nsSimpleURI.h"
22 #include "nsIClassInfoImpl.h"
26 #include "ContentPrincipal.h"
27 #include "nsScriptSecurityManager.h"
29 #include "nsIObjectInputStream.h"
31 #include "json/json.h"
33 using namespace mozilla
;
35 NS_IMPL_CLASSINFO(NullPrincipal
, nullptr, nsIClassInfo::MAIN_THREAD_ONLY
,
37 NS_IMPL_QUERY_INTERFACE_CI(NullPrincipal
, nsIPrincipal
)
38 NS_IMPL_CI_INTERFACE_GETTER(NullPrincipal
, nsIPrincipal
)
40 NullPrincipal::NullPrincipal(nsIURI
* aURI
, const nsACString
& aOriginNoSuffix
,
41 const OriginAttributes
& aOriginAttributes
)
42 : BasePrincipal(eNullPrincipal
, aOriginNoSuffix
, aOriginAttributes
),
46 already_AddRefed
<NullPrincipal
> NullPrincipal::CreateWithInheritedAttributes(
47 nsIPrincipal
* aInheritFrom
) {
48 MOZ_ASSERT(aInheritFrom
);
49 return CreateInternal(Cast(aInheritFrom
)->OriginAttributesRef(), false,
50 nullptr, aInheritFrom
);
54 already_AddRefed
<NullPrincipal
> NullPrincipal::CreateWithInheritedAttributes(
55 nsIDocShell
* aDocShell
, bool aIsFirstParty
) {
56 MOZ_ASSERT(aDocShell
);
58 OriginAttributes attrs
= nsDocShell::Cast(aDocShell
)->GetOriginAttributes();
59 return CreateWithInheritedAttributes(attrs
, aIsFirstParty
);
63 already_AddRefed
<NullPrincipal
> NullPrincipal::CreateWithInheritedAttributes(
64 const OriginAttributes
& aOriginAttributes
, bool aIsFirstParty
) {
65 return CreateInternal(aOriginAttributes
, aIsFirstParty
);
69 already_AddRefed
<NullPrincipal
> NullPrincipal::Create(
70 const OriginAttributes
& aOriginAttributes
, nsIURI
* aURI
) {
71 return CreateInternal(aOriginAttributes
, false, aURI
);
75 already_AddRefed
<NullPrincipal
> NullPrincipal::CreateWithoutOriginAttributes() {
76 return NullPrincipal::Create(OriginAttributes(), nullptr);
79 void NullPrincipal::EscapePrecursorQuery(nsACString
& aPrecursorQuery
) {
80 // origins should not contain existing escape sequences, so set `esc_Forced`
81 // to force any `%` in the input to be escaped in addition to non-ascii,
82 // control characters and DEL.
84 if (NS_EscapeURLSpan(aPrecursorQuery
, esc_Query
| esc_Forced
, modified
)) {
85 aPrecursorQuery
.Assign(std::move(modified
));
89 void NullPrincipal::UnescapePrecursorQuery(nsACString
& aPrecursorQuery
) {
91 if (NS_UnescapeURL(aPrecursorQuery
.BeginReading(), aPrecursorQuery
.Length(),
92 /* aFlags */ 0, modified
)) {
93 aPrecursorQuery
.Assign(std::move(modified
));
97 already_AddRefed
<nsIURI
> NullPrincipal::CreateURI(
98 nsIPrincipal
* aPrecursor
, const nsID
* aNullPrincipalID
) {
99 nsCOMPtr
<nsIURIMutator
> iMutator
;
100 if (StaticPrefs::network_url_useDefaultURI()) {
101 iMutator
= new mozilla::net::DefaultURI::Mutator();
103 iMutator
= new mozilla::net::nsSimpleURI::Mutator();
106 nsID uuid
= aNullPrincipalID
? *aNullPrincipalID
: nsID::GenerateUUID();
108 NS_MutateURI
mutator(iMutator
);
109 mutator
.SetSpec(NS_NULLPRINCIPAL_SCHEME
":"_ns
+
110 nsDependentCString(nsIDToCString(uuid
).get()));
112 // If there's a precursor URI, encode it in the null principal URI's query.
114 nsAutoCString precursorOrigin
;
115 switch (BasePrincipal::Cast(aPrecursor
)->Kind()) {
116 case eNullPrincipal
: {
117 // If the precursor null principal has a precursor, inherit it.
118 if (nsCOMPtr
<nsIURI
> nullPrecursorURI
= aPrecursor
->GetURI()) {
119 MOZ_ALWAYS_SUCCEEDS(nullPrecursorURI
->GetQuery(precursorOrigin
));
123 case eContentPrincipal
: {
124 MOZ_ALWAYS_SUCCEEDS(aPrecursor
->GetOriginNoSuffix(precursorOrigin
));
126 nsAutoCString
original(precursorOrigin
);
128 EscapePrecursorQuery(precursorOrigin
);
130 nsAutoCString
unescaped(precursorOrigin
);
131 UnescapePrecursorQuery(unescaped
);
132 MOZ_ASSERT(unescaped
== original
,
133 "cannot recover original precursor origin after escape");
138 // For now, we won't track expanded or system principal precursors. We may
139 // want to track expanded principal precursors in the future, but it's
140 // unlikely we'll want to track system principal precursors.
141 case eExpandedPrincipal
:
142 case eSystemPrincipal
:
145 if (!precursorOrigin
.IsEmpty()) {
146 mutator
.SetQuery(precursorOrigin
);
150 nsCOMPtr
<nsIURI
> uri
;
151 MOZ_ALWAYS_SUCCEEDS(mutator
.Finalize(getter_AddRefs(uri
)));
155 already_AddRefed
<NullPrincipal
> NullPrincipal::CreateInternal(
156 const OriginAttributes
& aOriginAttributes
, bool aIsFirstParty
, nsIURI
* aURI
,
157 nsIPrincipal
* aPrecursor
) {
158 MOZ_ASSERT_IF(aPrecursor
, !aURI
);
159 nsCOMPtr
<nsIURI
> uri
= aURI
;
161 uri
= NullPrincipal::CreateURI(aPrecursor
);
164 MOZ_RELEASE_ASSERT(uri
->SchemeIs(NS_NULLPRINCIPAL_SCHEME
));
166 nsAutoCString originNoSuffix
;
167 DebugOnly
<nsresult
> rv
= uri
->GetSpec(originNoSuffix
);
168 MOZ_ASSERT(NS_SUCCEEDED(rv
));
170 OriginAttributes
attrs(aOriginAttributes
);
172 // The FirstPartyDomain attribute will not include information about the
175 rv
= uri
->GetFilePath(path
);
176 MOZ_ASSERT(NS_SUCCEEDED(rv
));
178 // remove the '{}' characters from both ends.
179 path
.Mid(path
, 1, path
.Length() - 2);
180 path
.AppendLiteral(".mozilla");
181 attrs
.SetFirstPartyDomain(true, path
);
184 RefPtr
<NullPrincipal
> nullPrin
=
185 new NullPrincipal(uri
, originNoSuffix
, attrs
);
186 return nullPrin
.forget();
189 nsresult
NullPrincipal::GetScriptLocation(nsACString
& aStr
) {
190 return mURI
->GetSpec(aStr
);
194 * nsIPrincipal implementation
197 uint32_t NullPrincipal::GetHashValue() { return (NS_PTR_TO_INT32(this) >> 2); }
200 NullPrincipal::GetURI(nsIURI
** aURI
) {
201 nsCOMPtr
<nsIURI
> uri
= mURI
;
206 NullPrincipal::GetIsOriginPotentiallyTrustworthy(bool* aResult
) {
212 NullPrincipal::GetDomain(nsIURI
** aDomain
) {
213 nsCOMPtr
<nsIURI
> uri
= mURI
;
219 NullPrincipal::SetDomain(nsIURI
* aDomain
) {
220 // I think the right thing to do here is to just throw... Silently failing
221 // seems counterproductive.
222 return NS_ERROR_NOT_AVAILABLE
;
225 bool NullPrincipal::MayLoadInternal(nsIURI
* aURI
) {
226 // Also allow the load if we are the principal of the URI being checked.
227 nsCOMPtr
<nsIPrincipal
> blobPrincipal
;
228 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
229 aURI
, getter_AddRefs(blobPrincipal
))) {
230 MOZ_ASSERT(blobPrincipal
);
231 return SubsumesInternal(blobPrincipal
,
232 BasePrincipal::ConsiderDocumentDomain
);
239 NullPrincipal::GetBaseDomain(nsACString
& aBaseDomain
) {
240 // For a null principal, we use our unique uuid as the base domain.
241 return mURI
->GetPathQueryRef(aBaseDomain
);
245 NullPrincipal::GetAddonId(nsAString
& aAddonId
) {
251 * nsISerializable implementation
254 NullPrincipal::Deserializer::Read(nsIObjectInputStream
* aStream
) {
256 nsresult rv
= aStream
->ReadCString(spec
);
257 NS_ENSURE_SUCCESS(rv
, rv
);
259 nsCOMPtr
<nsIURI
> uri
;
260 rv
= NS_NewURI(getter_AddRefs(uri
), spec
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 nsAutoCString suffix
;
264 rv
= aStream
->ReadCString(suffix
);
265 NS_ENSURE_SUCCESS(rv
, rv
);
267 OriginAttributes attrs
;
268 bool ok
= attrs
.PopulateFromSuffix(suffix
);
269 NS_ENSURE_TRUE(ok
, NS_ERROR_FAILURE
);
271 mPrincipal
= NullPrincipal::Create(attrs
, uri
);
272 NS_ENSURE_TRUE(mPrincipal
, NS_ERROR_FAILURE
);
277 nsresult
NullPrincipal::PopulateJSONObject(Json::Value
& aObject
) {
278 nsAutoCString principalURI
;
279 nsresult rv
= mURI
->GetSpec(principalURI
);
280 NS_ENSURE_SUCCESS(rv
, rv
);
281 aObject
[std::to_string(eSpec
)] = principalURI
.get();
283 nsAutoCString suffix
;
284 OriginAttributesRef().CreateSuffix(suffix
);
285 if (suffix
.Length() > 0) {
286 aObject
[std::to_string(eSuffix
)] = suffix
.get();
292 already_AddRefed
<BasePrincipal
> NullPrincipal::FromProperties(
293 nsTArray
<NullPrincipal::KeyVal
>& aFields
) {
294 MOZ_ASSERT(aFields
.Length() == eMax
+ 1, "Must have all the keys");
296 nsCOMPtr
<nsIURI
> uri
;
297 OriginAttributes attrs
;
299 // The odd structure here is to make the code to not compile
300 // if all the switch enum cases haven't been codified
301 for (const auto& field
: aFields
) {
303 case NullPrincipal::eSpec
:
304 if (!field
.valueWasSerialized
) {
306 "Null principals require a spec URI in serialized JSON");
309 rv
= NS_NewURI(getter_AddRefs(uri
), field
.value
);
310 NS_ENSURE_SUCCESS(rv
, nullptr);
312 case NullPrincipal::eSuffix
:
313 bool ok
= attrs
.PopulateFromSuffix(field
.value
);
322 MOZ_ASSERT(false, "No URI deserialized");
326 return NullPrincipal::Create(attrs
, uri
);
330 NullPrincipal::GetPrecursorPrincipal(nsIPrincipal
** aPrincipal
) {
331 *aPrincipal
= nullptr;
334 if (NS_FAILED(mURI
->GetQuery(query
)) || query
.IsEmpty()) {
337 UnescapePrecursorQuery(query
);
339 nsCOMPtr
<nsIURI
> precursorURI
;
340 if (NS_FAILED(NS_NewURI(getter_AddRefs(precursorURI
), query
))) {
341 MOZ_ASSERT_UNREACHABLE(
342 "Failed to parse precursor from nullprincipal query");
346 // If our precursor is another null principal, re-construct it. This can
347 // happen if a null principal without a precursor causes another principal to
349 if (precursorURI
->SchemeIs(NS_NULLPRINCIPAL_SCHEME
)) {
351 nsAutoCString precursorQuery
;
352 precursorURI
->GetQuery(precursorQuery
);
353 MOZ_ASSERT(precursorQuery
.IsEmpty(),
354 "Null principal with nested precursors?");
357 NullPrincipal::Create(OriginAttributesRef(), precursorURI
).take();
361 RefPtr
<BasePrincipal
> contentPrincipal
=
362 BasePrincipal::CreateContentPrincipal(precursorURI
,
363 OriginAttributesRef());
364 // If `CreateContentPrincipal` failed, it will create a new NullPrincipal and
365 // return that instead. We only want to return real content principals here.
366 if (!contentPrincipal
|| !contentPrincipal
->Is
<ContentPrincipal
>()) {
369 contentPrincipal
.forget(aPrincipal
);