Bug 1826982 - Provide a better error message when servers fail to start, r=Sasha
[gecko.git] / caps / NullPrincipal.cpp
blobf0a0ad4708776e1e4c539a0f6314beb3fc019424
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/. */
7 /**
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"
21 #include "nsIClassInfoImpl.h"
22 #include "nsNetCID.h"
23 #include "nsError.h"
24 #include "nsEscape.h"
25 #include "ContentPrincipal.h"
26 #include "nsScriptSecurityManager.h"
27 #include "pratom.h"
28 #include "nsIObjectInputStream.h"
30 #include "json/json.h"
32 using namespace mozilla;
34 NS_IMPL_CLASSINFO(NullPrincipal, nullptr, 0, NS_NULLPRINCIPAL_CID)
35 NS_IMPL_QUERY_INTERFACE_CI(NullPrincipal, nsIPrincipal)
36 NS_IMPL_CI_INTERFACE_GETTER(NullPrincipal, nsIPrincipal)
38 NullPrincipal::NullPrincipal(nsIURI* aURI, const nsACString& aOriginNoSuffix,
39 const OriginAttributes& aOriginAttributes)
40 : BasePrincipal(eNullPrincipal, aOriginNoSuffix, aOriginAttributes),
41 mURI(aURI) {}
43 /* static */
44 already_AddRefed<NullPrincipal> NullPrincipal::CreateWithInheritedAttributes(
45 nsIPrincipal* aInheritFrom) {
46 MOZ_ASSERT(aInheritFrom);
47 nsCOMPtr<nsIURI> uri = CreateURI(aInheritFrom);
48 return Create(Cast(aInheritFrom)->OriginAttributesRef(), uri);
51 /* static */
52 already_AddRefed<NullPrincipal> NullPrincipal::Create(
53 const OriginAttributes& aOriginAttributes, nsIURI* aNullPrincipalURI) {
54 nsCOMPtr<nsIURI> uri = aNullPrincipalURI;
55 if (!uri) {
56 uri = NullPrincipal::CreateURI(nullptr);
59 MOZ_RELEASE_ASSERT(uri->SchemeIs(NS_NULLPRINCIPAL_SCHEME));
61 nsAutoCString originNoSuffix;
62 DebugOnly<nsresult> rv = uri->GetSpec(originNoSuffix);
63 MOZ_ASSERT(NS_SUCCEEDED(rv));
65 RefPtr<NullPrincipal> nullPrin =
66 new NullPrincipal(uri, originNoSuffix, aOriginAttributes);
67 return nullPrin.forget();
70 /* static */
71 already_AddRefed<NullPrincipal> NullPrincipal::CreateWithoutOriginAttributes() {
72 return NullPrincipal::Create(OriginAttributes(), nullptr);
75 void NullPrincipal::EscapePrecursorQuery(nsACString& aPrecursorQuery) {
76 // origins should not contain existing escape sequences, so set `esc_Forced`
77 // to force any `%` in the input to be escaped in addition to non-ascii,
78 // control characters and DEL.
79 nsCString modified;
80 if (NS_EscapeURLSpan(aPrecursorQuery, esc_Query | esc_Forced, modified)) {
81 aPrecursorQuery.Assign(std::move(modified));
85 void NullPrincipal::UnescapePrecursorQuery(nsACString& aPrecursorQuery) {
86 nsCString modified;
87 if (NS_UnescapeURL(aPrecursorQuery.BeginReading(), aPrecursorQuery.Length(),
88 /* aFlags */ 0, modified)) {
89 aPrecursorQuery.Assign(std::move(modified));
93 already_AddRefed<nsIURI> NullPrincipal::CreateURI(
94 nsIPrincipal* aPrecursor, const nsID* aNullPrincipalID) {
95 nsCOMPtr<nsIURIMutator> iMutator;
96 if (StaticPrefs::network_url_useDefaultURI()) {
97 iMutator = new mozilla::net::DefaultURI::Mutator();
98 } else {
99 iMutator = new mozilla::net::nsSimpleURI::Mutator();
102 nsID uuid = aNullPrincipalID ? *aNullPrincipalID : nsID::GenerateUUID();
104 NS_MutateURI mutator(iMutator);
105 mutator.SetSpec(NS_NULLPRINCIPAL_SCHEME ":"_ns +
106 nsDependentCString(nsIDToCString(uuid).get()));
108 // If there's a precursor URI, encode it in the null principal URI's query.
109 if (aPrecursor) {
110 nsAutoCString precursorOrigin;
111 switch (BasePrincipal::Cast(aPrecursor)->Kind()) {
112 case eNullPrincipal: {
113 // If the precursor null principal has a precursor, inherit it.
114 if (nsCOMPtr<nsIURI> nullPrecursorURI = aPrecursor->GetURI()) {
115 MOZ_ALWAYS_SUCCEEDS(nullPrecursorURI->GetQuery(precursorOrigin));
117 break;
119 case eContentPrincipal: {
120 MOZ_ALWAYS_SUCCEEDS(aPrecursor->GetOriginNoSuffix(precursorOrigin));
121 #ifdef DEBUG
122 nsAutoCString original(precursorOrigin);
123 #endif
124 EscapePrecursorQuery(precursorOrigin);
125 #ifdef DEBUG
126 nsAutoCString unescaped(precursorOrigin);
127 UnescapePrecursorQuery(unescaped);
128 MOZ_ASSERT(unescaped == original,
129 "cannot recover original precursor origin after escape");
130 #endif
131 break;
134 // For now, we won't track expanded or system principal precursors. We may
135 // want to track expanded principal precursors in the future, but it's
136 // unlikely we'll want to track system principal precursors.
137 case eExpandedPrincipal:
138 case eSystemPrincipal:
139 break;
141 if (!precursorOrigin.IsEmpty()) {
142 mutator.SetQuery(precursorOrigin);
146 nsCOMPtr<nsIURI> uri;
147 MOZ_ALWAYS_SUCCEEDS(mutator.Finalize(getter_AddRefs(uri)));
148 return uri.forget();
151 nsresult NullPrincipal::GetScriptLocation(nsACString& aStr) {
152 return mURI->GetSpec(aStr);
156 * nsIPrincipal implementation
159 uint32_t NullPrincipal::GetHashValue() { return (NS_PTR_TO_INT32(this) >> 2); }
161 NS_IMETHODIMP
162 NullPrincipal::GetURI(nsIURI** aURI) {
163 nsCOMPtr<nsIURI> uri = mURI;
164 uri.forget(aURI);
165 return NS_OK;
167 NS_IMETHODIMP
168 NullPrincipal::GetIsOriginPotentiallyTrustworthy(bool* aResult) {
169 *aResult = false;
170 return NS_OK;
173 NS_IMETHODIMP
174 NullPrincipal::GetDomain(nsIURI** aDomain) {
175 nsCOMPtr<nsIURI> uri = mURI;
176 uri.forget(aDomain);
177 return NS_OK;
180 NS_IMETHODIMP
181 NullPrincipal::SetDomain(nsIURI* aDomain) {
182 // I think the right thing to do here is to just throw... Silently failing
183 // seems counterproductive.
184 return NS_ERROR_NOT_AVAILABLE;
187 bool NullPrincipal::MayLoadInternal(nsIURI* aURI) {
188 // Also allow the load if we are the principal of the URI being checked.
189 nsCOMPtr<nsIPrincipal> blobPrincipal;
190 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
191 aURI, getter_AddRefs(blobPrincipal))) {
192 MOZ_ASSERT(blobPrincipal);
193 return SubsumesInternal(blobPrincipal,
194 BasePrincipal::ConsiderDocumentDomain);
197 return false;
200 NS_IMETHODIMP
201 NullPrincipal::GetBaseDomain(nsACString& aBaseDomain) {
202 // For a null principal, we use our unique uuid as the base domain.
203 return mURI->GetPathQueryRef(aBaseDomain);
206 NS_IMETHODIMP
207 NullPrincipal::GetAddonId(nsAString& aAddonId) {
208 aAddonId.Truncate();
209 return NS_OK;
213 * nsISerializable implementation
215 NS_IMETHODIMP
216 NullPrincipal::Deserializer::Read(nsIObjectInputStream* aStream) {
217 nsAutoCString spec;
218 nsresult rv = aStream->ReadCString(spec);
219 NS_ENSURE_SUCCESS(rv, rv);
221 nsCOMPtr<nsIURI> uri;
222 rv = NS_NewURI(getter_AddRefs(uri), spec);
223 NS_ENSURE_SUCCESS(rv, rv);
225 nsAutoCString suffix;
226 rv = aStream->ReadCString(suffix);
227 NS_ENSURE_SUCCESS(rv, rv);
229 OriginAttributes attrs;
230 bool ok = attrs.PopulateFromSuffix(suffix);
231 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
233 mPrincipal = NullPrincipal::Create(attrs, uri);
234 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE);
236 return NS_OK;
239 nsresult NullPrincipal::PopulateJSONObject(Json::Value& aObject) {
240 nsAutoCString principalURI;
241 nsresult rv = mURI->GetSpec(principalURI);
242 NS_ENSURE_SUCCESS(rv, rv);
243 SetJSONValue<eSpec>(aObject, principalURI);
245 nsAutoCString suffix;
246 OriginAttributesRef().CreateSuffix(suffix);
247 if (suffix.Length() > 0) {
248 SetJSONValue<eSuffix>(aObject, suffix);
251 return NS_OK;
254 already_AddRefed<BasePrincipal> NullPrincipal::FromProperties(
255 nsTArray<NullPrincipal::KeyVal>& aFields) {
256 MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
257 nsresult rv;
258 nsCOMPtr<nsIURI> uri;
259 OriginAttributes attrs;
261 // The odd structure here is to make the code to not compile
262 // if all the switch enum cases haven't been codified
263 for (const auto& field : aFields) {
264 switch (field.key) {
265 case NullPrincipal::eSpec:
266 if (!field.valueWasSerialized) {
267 MOZ_ASSERT(false,
268 "Null principals require a spec URI in serialized JSON");
269 return nullptr;
271 rv = NS_NewURI(getter_AddRefs(uri), field.value);
272 NS_ENSURE_SUCCESS(rv, nullptr);
273 break;
274 case NullPrincipal::eSuffix:
275 bool ok = attrs.PopulateFromSuffix(field.value);
276 if (!ok) {
277 return nullptr;
279 break;
283 if (!uri) {
284 MOZ_ASSERT(false, "No URI deserialized");
285 return nullptr;
288 return NullPrincipal::Create(attrs, uri);
291 NS_IMETHODIMP
292 NullPrincipal::GetPrecursorPrincipal(nsIPrincipal** aPrincipal) {
293 *aPrincipal = nullptr;
295 nsAutoCString query;
296 if (NS_FAILED(mURI->GetQuery(query)) || query.IsEmpty()) {
297 return NS_OK;
299 UnescapePrecursorQuery(query);
301 nsCOMPtr<nsIURI> precursorURI;
302 if (NS_FAILED(NS_NewURI(getter_AddRefs(precursorURI), query))) {
303 MOZ_ASSERT_UNREACHABLE(
304 "Failed to parse precursor from nullprincipal query");
305 return NS_OK;
308 // If our precursor is another null principal, re-construct it. This can
309 // happen if a null principal without a precursor causes another principal to
310 // be created.
311 if (precursorURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME)) {
312 #ifdef DEBUG
313 nsAutoCString precursorQuery;
314 precursorURI->GetQuery(precursorQuery);
315 MOZ_ASSERT(precursorQuery.IsEmpty(),
316 "Null principal with nested precursors?");
317 #endif
318 *aPrincipal =
319 NullPrincipal::Create(OriginAttributesRef(), precursorURI).take();
320 return NS_OK;
323 RefPtr<BasePrincipal> contentPrincipal =
324 BasePrincipal::CreateContentPrincipal(precursorURI,
325 OriginAttributesRef());
326 // If `CreateContentPrincipal` failed, it will create a new NullPrincipal and
327 // return that instead. We only want to return real content principals here.
328 if (!contentPrincipal || !contentPrincipal->Is<ContentPrincipal>()) {
329 return NS_OK;
331 contentPrincipal.forget(aPrincipal);
332 return NS_OK;