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 static void 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_OnlyNonASCII
| esc_Forced
,
86 aPrecursorQuery
.Assign(std::move(modified
));
90 static void UnescapePrecursorQuery(nsACString
& aPrecursorQuery
) {
92 if (NS_UnescapeURL(aPrecursorQuery
.BeginReading(), aPrecursorQuery
.Length(),
93 /* aFlags */ 0, modified
)) {
94 aPrecursorQuery
.Assign(std::move(modified
));
98 already_AddRefed
<nsIURI
> NullPrincipal::CreateURI(
99 nsIPrincipal
* aPrecursor
, const nsID
* aNullPrincipalID
) {
100 nsCOMPtr
<nsIURIMutator
> iMutator
;
101 if (StaticPrefs::network_url_useDefaultURI()) {
102 iMutator
= new mozilla::net::DefaultURI::Mutator();
104 iMutator
= new mozilla::net::nsSimpleURI::Mutator();
107 nsID uuid
= aNullPrincipalID
? *aNullPrincipalID
: nsID::GenerateUUID();
109 NS_MutateURI
mutator(iMutator
);
110 mutator
.SetSpec(NS_NULLPRINCIPAL_SCHEME
":"_ns
+
111 nsDependentCString(nsIDToCString(uuid
).get()));
113 // If there's a precursor URI, encode it in the null principal URI's query.
115 nsAutoCString precursorOrigin
;
116 switch (BasePrincipal::Cast(aPrecursor
)->Kind()) {
117 case eNullPrincipal
: {
118 // If the precursor null principal has a precursor, inherit it.
119 if (nsCOMPtr
<nsIURI
> nullPrecursorURI
= aPrecursor
->GetURI()) {
120 MOZ_ALWAYS_SUCCEEDS(nullPrecursorURI
->GetQuery(precursorOrigin
));
124 case eContentPrincipal
: {
125 MOZ_ALWAYS_SUCCEEDS(aPrecursor
->GetOriginNoSuffix(precursorOrigin
));
127 nsAutoCString
original(precursorOrigin
);
129 EscapePrecursorQuery(precursorOrigin
);
131 nsAutoCString
unescaped(precursorOrigin
);
132 UnescapePrecursorQuery(unescaped
);
133 MOZ_ASSERT(unescaped
== original
,
134 "cannot recover original precursor origin after escape");
139 // For now, we won't track expanded or system principal precursors. We may
140 // want to track expanded principal precursors in the future, but it's
141 // unlikely we'll want to track system principal precursors.
142 case eExpandedPrincipal
:
143 case eSystemPrincipal
:
146 if (!precursorOrigin
.IsEmpty()) {
147 mutator
.SetQuery(precursorOrigin
);
151 nsCOMPtr
<nsIURI
> uri
;
152 MOZ_ALWAYS_SUCCEEDS(mutator
.Finalize(getter_AddRefs(uri
)));
156 already_AddRefed
<NullPrincipal
> NullPrincipal::CreateInternal(
157 const OriginAttributes
& aOriginAttributes
, bool aIsFirstParty
, nsIURI
* aURI
,
158 nsIPrincipal
* aPrecursor
) {
159 MOZ_ASSERT_IF(aPrecursor
, !aURI
);
160 nsCOMPtr
<nsIURI
> uri
= aURI
;
162 uri
= NullPrincipal::CreateURI(aPrecursor
);
165 MOZ_RELEASE_ASSERT(uri
->SchemeIs(NS_NULLPRINCIPAL_SCHEME
));
167 nsAutoCString originNoSuffix
;
168 DebugOnly
<nsresult
> rv
= uri
->GetSpec(originNoSuffix
);
169 MOZ_ASSERT(NS_SUCCEEDED(rv
));
171 OriginAttributes
attrs(aOriginAttributes
);
173 // The FirstPartyDomain attribute will not include information about the
176 rv
= uri
->GetFilePath(path
);
177 MOZ_ASSERT(NS_SUCCEEDED(rv
));
179 // remove the '{}' characters from both ends.
180 path
.Mid(path
, 1, path
.Length() - 2);
181 path
.AppendLiteral(".mozilla");
182 attrs
.SetFirstPartyDomain(true, path
);
185 RefPtr
<NullPrincipal
> nullPrin
=
186 new NullPrincipal(uri
, originNoSuffix
, attrs
);
187 return nullPrin
.forget();
190 nsresult
NullPrincipal::GetScriptLocation(nsACString
& aStr
) {
191 return mURI
->GetSpec(aStr
);
195 * nsIPrincipal implementation
198 uint32_t NullPrincipal::GetHashValue() { return (NS_PTR_TO_INT32(this) >> 2); }
201 NullPrincipal::GetURI(nsIURI
** aURI
) {
202 nsCOMPtr
<nsIURI
> uri
= mURI
;
207 NullPrincipal::GetIsOriginPotentiallyTrustworthy(bool* aResult
) {
213 NullPrincipal::GetDomain(nsIURI
** aDomain
) {
214 nsCOMPtr
<nsIURI
> uri
= mURI
;
220 NullPrincipal::SetDomain(nsIURI
* aDomain
) {
221 // I think the right thing to do here is to just throw... Silently failing
222 // seems counterproductive.
223 return NS_ERROR_NOT_AVAILABLE
;
226 bool NullPrincipal::MayLoadInternal(nsIURI
* aURI
) {
227 // Also allow the load if we are the principal of the URI being checked.
228 nsCOMPtr
<nsIPrincipal
> blobPrincipal
;
229 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
230 aURI
, getter_AddRefs(blobPrincipal
))) {
231 MOZ_ASSERT(blobPrincipal
);
232 return SubsumesInternal(blobPrincipal
,
233 BasePrincipal::ConsiderDocumentDomain
);
240 NullPrincipal::GetBaseDomain(nsACString
& aBaseDomain
) {
241 // For a null principal, we use our unique uuid as the base domain.
242 return mURI
->GetPathQueryRef(aBaseDomain
);
246 NullPrincipal::GetAddonId(nsAString
& aAddonId
) {
252 * nsISerializable implementation
255 NullPrincipal::Deserializer::Read(nsIObjectInputStream
* aStream
) {
257 nsresult rv
= aStream
->ReadCString(spec
);
258 NS_ENSURE_SUCCESS(rv
, rv
);
260 nsCOMPtr
<nsIURI
> uri
;
261 rv
= NS_NewURI(getter_AddRefs(uri
), spec
);
262 NS_ENSURE_SUCCESS(rv
, rv
);
264 nsAutoCString suffix
;
265 rv
= aStream
->ReadCString(suffix
);
266 NS_ENSURE_SUCCESS(rv
, rv
);
268 OriginAttributes attrs
;
269 bool ok
= attrs
.PopulateFromSuffix(suffix
);
270 NS_ENSURE_TRUE(ok
, NS_ERROR_FAILURE
);
272 mPrincipal
= NullPrincipal::Create(attrs
, uri
);
273 NS_ENSURE_TRUE(mPrincipal
, NS_ERROR_FAILURE
);
278 nsresult
NullPrincipal::PopulateJSONObject(Json::Value
& aObject
) {
279 nsAutoCString principalURI
;
280 nsresult rv
= mURI
->GetSpec(principalURI
);
281 NS_ENSURE_SUCCESS(rv
, rv
);
282 aObject
[std::to_string(eSpec
)] = principalURI
.get();
284 nsAutoCString suffix
;
285 OriginAttributesRef().CreateSuffix(suffix
);
286 if (suffix
.Length() > 0) {
287 aObject
[std::to_string(eSuffix
)] = suffix
.get();
293 already_AddRefed
<BasePrincipal
> NullPrincipal::FromProperties(
294 nsTArray
<NullPrincipal::KeyVal
>& aFields
) {
295 MOZ_ASSERT(aFields
.Length() == eMax
+ 1, "Must have all the keys");
297 nsCOMPtr
<nsIURI
> uri
;
298 OriginAttributes attrs
;
300 // The odd structure here is to make the code to not compile
301 // if all the switch enum cases haven't been codified
302 for (const auto& field
: aFields
) {
304 case NullPrincipal::eSpec
:
305 if (!field
.valueWasSerialized
) {
307 "Null principals require a spec URI in serialized JSON");
310 rv
= NS_NewURI(getter_AddRefs(uri
), field
.value
);
311 NS_ENSURE_SUCCESS(rv
, nullptr);
313 case NullPrincipal::eSuffix
:
314 bool ok
= attrs
.PopulateFromSuffix(field
.value
);
323 MOZ_ASSERT(false, "No URI deserialized");
327 return NullPrincipal::Create(attrs
, uri
);
331 NullPrincipal::GetPrecursorPrincipal(nsIPrincipal
** aPrincipal
) {
332 *aPrincipal
= nullptr;
335 if (NS_FAILED(mURI
->GetQuery(query
)) || query
.IsEmpty()) {
338 UnescapePrecursorQuery(query
);
340 nsCOMPtr
<nsIURI
> precursorURI
;
341 if (NS_FAILED(NS_NewURI(getter_AddRefs(precursorURI
), query
))) {
342 MOZ_ASSERT_UNREACHABLE(
343 "Failed to parse precursor from nullprincipal query");
347 // If our precursor is another null principal, re-construct it. This can
348 // happen if a null principal without a precursor causes another principal to
350 if (precursorURI
->SchemeIs(NS_NULLPRINCIPAL_SCHEME
)) {
352 nsAutoCString precursorQuery
;
353 precursorURI
->GetQuery(precursorQuery
);
354 MOZ_ASSERT(precursorQuery
.IsEmpty(),
355 "Null principal with nested precursors?");
358 NullPrincipal::Create(OriginAttributesRef(), precursorURI
).take();
362 RefPtr
<BasePrincipal
> contentPrincipal
=
363 BasePrincipal::CreateContentPrincipal(precursorURI
,
364 OriginAttributesRef());
365 // If `CreateContentPrincipal` failed, it will create a new NullPrincipal and
366 // return that instead. We only want to return real content principals here.
367 if (!contentPrincipal
|| !contentPrincipal
->Is
<ContentPrincipal
>()) {
370 contentPrincipal
.forget(aPrincipal
);