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/. */
6 
7 #include "ContentPrincipal.h"
8 
9 #include "mozIThirdPartyUtil.h"
10 #include "nsContentUtils.h"
11 #include "nscore.h"
12 #include "nsScriptSecurityManager.h"
13 #include "nsString.h"
14 #include "nsReadableUtils.h"
15 #include "pratom.h"
16 #include "nsIURI.h"
17 #include "nsIURL.h"
18 #include "nsIStandardURL.h"
19 #include "nsIURIWithSpecialOrigin.h"
20 #include "nsIURIMutator.h"
21 #include "nsJSPrincipals.h"
22 #include "nsIEffectiveTLDService.h"
23 #include "nsIClassInfoImpl.h"
24 #include "nsIObjectInputStream.h"
25 #include "nsIObjectOutputStream.h"
26 #include "nsIProtocolHandler.h"
27 #include "nsError.h"
28 #include "nsIContentSecurityPolicy.h"
29 #include "nsNetCID.h"
30 #include "js/Wrapper.h"
31 
32 #include "mozilla/dom/BlobURLProtocolHandler.h"
33 #include "mozilla/dom/ScriptSettings.h"
34 #include "mozilla/ClearOnShutdown.h"
35 #include "mozilla/ExtensionPolicyService.h"
36 #include "mozilla/Preferences.h"
37 #include "mozilla/HashFunctions.h"
38 
39 #include "nsSerializationHelper.h"
40 #include "json/json.h"
41 
42 using namespace mozilla;
43 
EPS()44 static inline ExtensionPolicyService& EPS() {
45   return ExtensionPolicyService::GetSingleton();
46 }
47 
NS_IMPL_CLASSINFO(ContentPrincipal,nullptr,nsIClassInfo::MAIN_THREAD_ONLY,NS_PRINCIPAL_CID)48 NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
49                   NS_PRINCIPAL_CID)
50 NS_IMPL_QUERY_INTERFACE_CI(ContentPrincipal, nsIPrincipal, nsISerializable)
51 NS_IMPL_CI_INTERFACE_GETTER(ContentPrincipal, nsIPrincipal, nsISerializable)
52 
53 ContentPrincipal::ContentPrincipal() : BasePrincipal(eContentPrincipal) {}
54 
~ContentPrincipal()55 ContentPrincipal::~ContentPrincipal() {}
56 
Init(nsIURI * aURI,const OriginAttributes & aOriginAttributes,const nsACString & aOriginNoSuffix)57 nsresult ContentPrincipal::Init(nsIURI* aURI,
58                                 const OriginAttributes& aOriginAttributes,
59                                 const nsACString& aOriginNoSuffix) {
60   NS_ENSURE_ARG(aURI);
61 
62   // Assert that the URI we get here isn't any of the schemes that we know we
63   // should not get here.  These schemes always either inherit their principal
64   // or fall back to a null principal.  These are schemes which return
65   // URI_INHERITS_SECURITY_CONTEXT from their protocol handler's
66   // GetProtocolFlags function.
67   bool hasFlag;
68   Unused << hasFlag;  // silence possible compiler warnings.
69   MOZ_DIAGNOSTIC_ASSERT(
70       NS_SUCCEEDED(NS_URIChainHasFlags(
71           aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &hasFlag)) &&
72       !hasFlag);
73 
74   mURI = aURI;
75   FinishInit(aOriginNoSuffix, aOriginAttributes);
76 
77   return NS_OK;
78 }
79 
Init(ContentPrincipal * aOther,const OriginAttributes & aOriginAttributes)80 nsresult ContentPrincipal::Init(ContentPrincipal* aOther,
81                                 const OriginAttributes& aOriginAttributes) {
82   NS_ENSURE_ARG(aOther);
83 
84   mURI = aOther->mURI;
85   FinishInit(aOther, aOriginAttributes);
86 
87   mDomain = aOther->mDomain;
88   mAddon = aOther->mAddon;
89   return NS_OK;
90 }
91 
GetScriptLocation(nsACString & aStr)92 nsresult ContentPrincipal::GetScriptLocation(nsACString& aStr) {
93   return mURI->GetSpec(aStr);
94 }
95 
96 /* static */
GenerateOriginNoSuffixFromURI(nsIURI * aURI,nsACString & aOriginNoSuffix)97 nsresult ContentPrincipal::GenerateOriginNoSuffixFromURI(
98     nsIURI* aURI, nsACString& aOriginNoSuffix) {
99   if (!aURI) {
100     return NS_ERROR_FAILURE;
101   }
102 
103   nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(aURI);
104   if (!origin) {
105     return NS_ERROR_FAILURE;
106   }
107 
108   MOZ_ASSERT(!NS_IsAboutBlank(origin),
109              "The inner URI for about:blank must be moz-safe-about:blank");
110 
111   // Handle non-strict file:// uris.
112   if (!nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
113       NS_URIIsLocalFile(origin)) {
114     // If strict file origin policy is not in effect, all local files are
115     // considered to be same-origin, so return a known dummy origin here.
116     aOriginNoSuffix.AssignLiteral("file://UNIVERSAL_FILE_URI_ORIGIN");
117     return NS_OK;
118   }
119 
120   nsresult rv;
121 // NB: This is only compiled for Thunderbird/Suite.
122 #if IS_ORIGIN_IS_FULL_SPEC_DEFINED
123   bool fullSpec = false;
124   rv = NS_URIChainHasFlags(origin, nsIProtocolHandler::ORIGIN_IS_FULL_SPEC,
125                            &fullSpec);
126   NS_ENSURE_SUCCESS(rv, rv);
127   if (fullSpec) {
128     return origin->GetAsciiSpec(aOriginNoSuffix);
129   }
130 #endif
131 
132   // We want the invariant that prinA.origin == prinB.origin i.f.f.
133   // prinA.equals(prinB). However, this requires that we impose certain
134   // constraints on the behavior and origin semantics of principals, and in
135   // particular, forbid creating origin strings for principals whose equality
136   // constraints are not expressible as strings (i.e. object equality).
137   // Moreover, we want to forbid URIs containing the magic "^" we use as a
138   // separating character for origin attributes.
139   //
140   // These constraints can generally be achieved by restricting .origin to
141   // nsIStandardURL-based URIs, but there are a few other URI schemes that we
142   // need to handle.
143   if (origin->SchemeIs("about") ||
144       (origin->SchemeIs("moz-safe-about") &&
145        // We generally consider two about:foo origins to be same-origin, but
146        // about:blank is special since it can be generated from different
147        // sources. We check for moz-safe-about:blank since origin is an
148        // innermost URI.
149        !StringBeginsWith(origin->GetSpecOrDefault(),
150                          NS_LITERAL_CSTRING("moz-safe-about:blank")))) {
151     rv = origin->GetAsciiSpec(aOriginNoSuffix);
152     NS_ENSURE_SUCCESS(rv, rv);
153 
154     int32_t pos = aOriginNoSuffix.FindChar('?');
155     int32_t hashPos = aOriginNoSuffix.FindChar('#');
156 
157     if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
158       pos = hashPos;
159     }
160 
161     if (pos != kNotFound) {
162       aOriginNoSuffix.Truncate(pos);
163     }
164 
165     // These URIs could technically contain a '^', but they never should.
166     if (NS_WARN_IF(aOriginNoSuffix.FindChar('^', 0) != -1)) {
167       aOriginNoSuffix.Truncate();
168       return NS_ERROR_FAILURE;
169     }
170     return NS_OK;
171   }
172 
173   // This URL can be a blobURL. In this case, we should use the 'parent'
174   // principal instead.
175   nsCOMPtr<nsIPrincipal> blobPrincipal;
176   if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
177           origin, getter_AddRefs(blobPrincipal))) {
178     MOZ_ASSERT(blobPrincipal);
179     return blobPrincipal->GetOriginNoSuffix(aOriginNoSuffix);
180   }
181 
182   // If we reached this branch, we can only create an origin if we have a
183   // nsIStandardURL.  So, we query to a nsIStandardURL, and fail if we aren't
184   // an instance of an nsIStandardURL nsIStandardURLs have the good property
185   // of escaping the '^' character in their specs, which means that we can be
186   // sure that the caret character (which is reserved for delimiting the end
187   // of the spec, and the beginning of the origin attributes) is not present
188   // in the origin string
189   nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin);
190   if (!standardURL) {
191     return NS_ERROR_FAILURE;
192   }
193 
194   // See whether we have a useful hostPort. If we do, use that.
195   nsAutoCString hostPort;
196   if (!origin->SchemeIs("chrome")) {
197     rv = origin->GetAsciiHostPort(hostPort);
198     NS_ENSURE_SUCCESS(rv, rv);
199   }
200   if (!hostPort.IsEmpty()) {
201     rv = origin->GetScheme(aOriginNoSuffix);
202     NS_ENSURE_SUCCESS(rv, rv);
203     aOriginNoSuffix.AppendLiteral("://");
204     aOriginNoSuffix.Append(hostPort);
205     return NS_OK;
206   }
207 
208   rv = aURI->GetAsciiSpec(aOriginNoSuffix);
209   NS_ENSURE_SUCCESS(rv, rv);
210 
211   // The origin, when taken from the spec, should not contain the ref part of
212   // the URL.
213 
214   int32_t pos = aOriginNoSuffix.FindChar('?');
215   int32_t hashPos = aOriginNoSuffix.FindChar('#');
216 
217   if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
218     pos = hashPos;
219   }
220 
221   if (pos != kNotFound) {
222     aOriginNoSuffix.Truncate(pos);
223   }
224 
225   return NS_OK;
226 }
227 
SubsumesInternal(nsIPrincipal * aOther,BasePrincipal::DocumentDomainConsideration aConsideration)228 bool ContentPrincipal::SubsumesInternal(
229     nsIPrincipal* aOther,
230     BasePrincipal::DocumentDomainConsideration aConsideration) {
231   MOZ_ASSERT(aOther);
232 
233   // For ContentPrincipal, Subsumes is equivalent to Equals.
234   if (aOther == this) {
235     return true;
236   }
237 
238   // If either the subject or the object has changed its principal by
239   // explicitly setting document.domain then the other must also have
240   // done so in order to be considered the same origin. This prevents
241   // DNS spoofing based on document.domain (154930)
242   nsresult rv;
243   if (aConsideration == ConsiderDocumentDomain) {
244     // Get .domain on each principal.
245     nsCOMPtr<nsIURI> thisDomain, otherDomain;
246     GetDomain(getter_AddRefs(thisDomain));
247     aOther->GetDomain(getter_AddRefs(otherDomain));
248 
249     // If either has .domain set, we have equality i.f.f. the domains match.
250     // Otherwise, we fall through to the non-document-domain-considering case.
251     if (thisDomain || otherDomain) {
252       bool isMatch =
253           nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain);
254 #ifdef DEBUG
255       if (isMatch) {
256         nsAutoCString thisSiteOrigin, otherSiteOrigin;
257         MOZ_ALWAYS_SUCCEEDS(GetSiteOrigin(thisSiteOrigin));
258         MOZ_ALWAYS_SUCCEEDS(aOther->GetSiteOrigin(otherSiteOrigin));
259         MOZ_ASSERT(
260             thisSiteOrigin == otherSiteOrigin,
261             "SubsumesConsideringDomain passed with mismatched siteOrigin!");
262       }
263 #endif
264       return isMatch;
265     }
266   }
267 
268   // Compare uris.
269   bool isSameOrigin = false;
270   rv = aOther->IsSameOrigin(mURI, false, &isSameOrigin);
271   NS_ENSURE_SUCCESS(rv, false);
272   return isSameOrigin;
273 }
274 
275 NS_IMETHODIMP
GetURI(nsIURI ** aURI)276 ContentPrincipal::GetURI(nsIURI** aURI) {
277   NS_ADDREF(*aURI = mURI);
278   return NS_OK;
279 }
280 
MayLoadInternal(nsIURI * aURI)281 bool ContentPrincipal::MayLoadInternal(nsIURI* aURI) {
282   MOZ_ASSERT(aURI);
283 
284 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
285   nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
286       do_QueryInterface(aURI);
287   if (uriWithSpecialOrigin) {
288     nsCOMPtr<nsIURI> origin;
289     nsresult rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
290     if (NS_WARN_IF(NS_FAILED(rv))) {
291       return false;
292     }
293     MOZ_ASSERT(origin);
294     OriginAttributes attrs;
295     RefPtr<BasePrincipal> principal =
296         BasePrincipal::CreateContentPrincipal(origin, attrs);
297     return nsIPrincipal::Subsumes(principal);
298   }
299 #endif
300 
301   nsCOMPtr<nsIPrincipal> blobPrincipal;
302   if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
303           aURI, getter_AddRefs(blobPrincipal))) {
304     MOZ_ASSERT(blobPrincipal);
305     return nsIPrincipal::Subsumes(blobPrincipal);
306   }
307 
308   // If this principal is associated with an addon, check whether that addon
309   // has been given permission to load from this domain.
310   if (AddonAllowsLoad(aURI)) {
311     return true;
312   }
313 
314   if (nsScriptSecurityManager::SecurityCompareURIs(mURI, aURI)) {
315     return true;
316   }
317 
318   // If strict file origin policy is in effect, local files will always fail
319   // SecurityCompareURIs unless they are identical. Explicitly check file origin
320   // policy, in that case.
321   if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
322       NS_URIIsLocalFile(aURI) && NS_RelaxStrictFileOriginPolicy(aURI, mURI)) {
323     return true;
324   }
325 
326   return false;
327 }
328 
GetHashValue()329 uint32_t ContentPrincipal::GetHashValue() {
330   MOZ_ASSERT(mURI, "Need a principal URI");
331 
332   nsCOMPtr<nsIURI> uri;
333   GetDomain(getter_AddRefs(uri));
334   if (!uri) {
335     GetURI(getter_AddRefs(uri));
336   };
337   return NS_SecurityHashURI(uri);
338 }
339 
340 NS_IMETHODIMP
GetDomain(nsIURI ** aDomain)341 ContentPrincipal::GetDomain(nsIURI** aDomain) {
342   if (!mDomain) {
343     *aDomain = nullptr;
344     return NS_OK;
345   }
346 
347   NS_ADDREF(*aDomain = mDomain);
348   return NS_OK;
349 }
350 
351 NS_IMETHODIMP
SetDomain(nsIURI * aDomain)352 ContentPrincipal::SetDomain(nsIURI* aDomain) {
353   MOZ_ASSERT(aDomain);
354 
355   mDomain = aDomain;
356   SetHasExplicitDomain();
357 
358   // Set the changed-document-domain flag on compartments containing realms
359   // using this principal.
360   auto cb = [](JSContext*, void*, JS::Handle<JS::Realm*> aRealm) {
361     JS::Compartment* comp = JS::GetCompartmentForRealm(aRealm);
362     xpc::SetCompartmentChangedDocumentDomain(comp);
363   };
364   JSPrincipals* principals =
365       nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
366   AutoSafeJSContext cx;
367   JS::IterateRealmsWithPrincipals(cx, principals, nullptr, cb);
368 
369   return NS_OK;
370 }
371 
GetSpecialBaseDomain(const nsCOMPtr<nsIURI> & aURI,bool * aHandled,nsACString & aBaseDomain)372 static nsresult GetSpecialBaseDomain(const nsCOMPtr<nsIURI>& aURI,
373                                      bool* aHandled, nsACString& aBaseDomain) {
374   *aHandled = false;
375 
376   // Special handling for a file URI.
377   if (NS_URIIsLocalFile(aURI)) {
378     // If strict file origin policy is not in effect, all local files are
379     // considered to be same-origin, so return a known dummy domain here.
380     if (!nsScriptSecurityManager::GetStrictFileOriginPolicy()) {
381       *aHandled = true;
382       aBaseDomain.AssignLiteral("UNIVERSAL_FILE_URI_ORIGIN");
383       return NS_OK;
384     }
385 
386     // Otherwise, we return the file path.
387     nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
388 
389     if (url) {
390       *aHandled = true;
391       return url->GetFilePath(aBaseDomain);
392     }
393   }
394 
395   bool hasNoRelativeFlag;
396   nsresult rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_NORELATIVE,
397                                     &hasNoRelativeFlag);
398   if (NS_WARN_IF(NS_FAILED(rv))) {
399     return rv;
400   }
401 
402   // In case of FTP we want to get base domain via TLD service even if FTP
403   // protocol handler is disabled and the scheme is handled by external protocol
404   // handler which returns URI_NORELATIVE flag.
405   if (hasNoRelativeFlag && !aURI->SchemeIs("ftp")) {
406     *aHandled = true;
407     return aURI->GetSpec(aBaseDomain);
408   }
409 
410   if (aURI->SchemeIs("indexeddb")) {
411     *aHandled = true;
412     return aURI->GetSpec(aBaseDomain);
413   }
414 
415   return NS_OK;
416 }
417 
418 NS_IMETHODIMP
GetBaseDomain(nsACString & aBaseDomain)419 ContentPrincipal::GetBaseDomain(nsACString& aBaseDomain) {
420   // Handle some special URIs first.
421   bool handled;
422   nsresult rv = GetSpecialBaseDomain(mURI, &handled, aBaseDomain);
423   NS_ENSURE_SUCCESS(rv, rv);
424 
425   if (handled) {
426     return NS_OK;
427   }
428 
429   // For everything else, we ask the TLD service via the ThirdPartyUtil.
430   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
431       do_GetService(THIRDPARTYUTIL_CONTRACTID);
432   if (!thirdPartyUtil) {
433     return NS_ERROR_FAILURE;
434   }
435 
436   return thirdPartyUtil->GetBaseDomain(mURI, aBaseDomain);
437 }
438 
439 NS_IMETHODIMP
GetSiteOrigin(nsACString & aSiteOrigin)440 ContentPrincipal::GetSiteOrigin(nsACString& aSiteOrigin) {
441   // Handle some special URIs first.
442   nsAutoCString baseDomain;
443   bool handled;
444   nsresult rv = GetSpecialBaseDomain(mURI, &handled, baseDomain);
445   NS_ENSURE_SUCCESS(rv, rv);
446 
447   if (handled) {
448     // This is a special URI ("file:", "about:", "view-source:", etc). Just
449     // return the origin.
450     return GetOrigin(aSiteOrigin);
451   }
452 
453   // For everything else, we ask the TLD service. Note that, unlike in
454   // GetBaseDomain, we don't use ThirdPartyUtil.getBaseDomain because if the
455   // host is an IP address that returns the raw address and we can't use it with
456   // SetHost below because SetHost expects '[' and ']' around IPv6 addresses.
457   // See bug 1491728.
458   nsCOMPtr<nsIEffectiveTLDService> tldService =
459       do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
460   if (!tldService) {
461     return NS_ERROR_FAILURE;
462   }
463 
464   bool gotBaseDomain = false;
465   rv = tldService->GetBaseDomain(mURI, 0, baseDomain);
466   if (NS_SUCCEEDED(rv)) {
467     gotBaseDomain = true;
468   } else {
469     // If this is an IP address or something like "localhost", we just continue
470     // with gotBaseDomain = false.
471     if (rv != NS_ERROR_HOST_IS_IP_ADDRESS &&
472         rv != NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
473       return rv;
474     }
475   }
476 
477   // NOTE: Calling `SetHostPort` with a portless domain is insufficient to clear
478   // the port, so an extra `SetPort` call has to be made.
479   nsCOMPtr<nsIURI> siteUri;
480   NS_MutateURI mutator(mURI);
481   mutator.SetUserPass(EmptyCString()).SetPort(-1);
482   if (gotBaseDomain) {
483     mutator.SetHost(baseDomain);
484   }
485   rv = mutator.Finalize(siteUri);
486   MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to create siteUri");
487   NS_ENSURE_SUCCESS(rv, rv);
488 
489   rv = GenerateOriginNoSuffixFromURI(siteUri, aSiteOrigin);
490   MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to create siteOriginNoSuffix");
491   NS_ENSURE_SUCCESS(rv, rv);
492 
493   nsAutoCString suffix;
494   rv = GetOriginSuffix(suffix);
495   MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to create suffix");
496   NS_ENSURE_SUCCESS(rv, rv);
497 
498   aSiteOrigin.Append(suffix);
499   return NS_OK;
500 }
501 
GetSiteIdentifier(SiteIdentifier & aSite)502 nsresult ContentPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) {
503   nsCString siteOrigin;
504   nsresult rv = GetSiteOrigin(siteOrigin);
505   NS_ENSURE_SUCCESS(rv, rv);
506 
507   RefPtr<BasePrincipal> principal = CreateContentPrincipal(siteOrigin);
508   if (!principal) {
509     NS_WARNING("could not instantiate content principal");
510     return NS_ERROR_FAILURE;
511   }
512 
513   aSite.Init(principal);
514   return NS_OK;
515 }
516 
AddonPolicy()517 WebExtensionPolicy* ContentPrincipal::AddonPolicy() {
518   if (!mAddon.isSome()) {
519     NS_ENSURE_TRUE(mURI, nullptr);
520 
521     if (mURI->SchemeIs("moz-extension")) {
522       mAddon.emplace(EPS().GetByURL(mURI.get()));
523     } else {
524       mAddon.emplace(nullptr);
525     }
526   }
527 
528   return mAddon.value();
529 }
530 
531 NS_IMETHODIMP
GetAddonId(nsAString & aAddonId)532 ContentPrincipal::GetAddonId(nsAString& aAddonId) {
533   auto policy = AddonPolicy();
534   if (policy) {
535     policy->GetId(aAddonId);
536   } else {
537     aAddonId.Truncate();
538   }
539   return NS_OK;
540 }
541 
542 NS_IMETHODIMP
Read(nsIObjectInputStream * aStream)543 ContentPrincipal::Read(nsIObjectInputStream* aStream) {
544   nsCOMPtr<nsISupports> supports;
545   nsCOMPtr<nsIURI> principalURI;
546   nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
547   if (NS_FAILED(rv)) {
548     return rv;
549   }
550 
551   principalURI = do_QueryInterface(supports);
552   // Enforce re-parsing about: URIs so that if they change, we continue to use
553   // their new principals correctly.
554   if (principalURI->SchemeIs("about")) {
555     nsAutoCString spec;
556     principalURI->GetSpec(spec);
557     NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(principalURI), spec),
558                       NS_ERROR_FAILURE);
559   }
560 
561   nsCOMPtr<nsIURI> domain;
562   rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
563   if (NS_FAILED(rv)) {
564     return rv;
565   }
566 
567   domain = do_QueryInterface(supports);
568 
569   nsAutoCString suffix;
570   rv = aStream->ReadCString(suffix);
571   NS_ENSURE_SUCCESS(rv, rv);
572 
573   OriginAttributes attrs;
574   bool ok = attrs.PopulateFromSuffix(suffix);
575   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
576 
577   // Since Bug 965637 we do not serialize the CSP within the
578   // Principal anymore. Nevertheless there might still be
579   // serialized Principals that do have a serialized CSP.
580   // For now, we just read the CSP here but do not actually
581   // consume it. Please note that we deliberately ignore
582   // the return value to avoid CSP deserialization problems.
583   // After Bug 1508939 we will have a new serialization for
584   // Principals which allows us to update the code here.
585   // Additionally, the format for serialized CSPs changed
586   // within Bug 965637 which also can cause failures within
587   // the CSP deserialization code.
588   Unused << NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
589 
590   nsAutoCString originNoSuffix;
591   rv = GenerateOriginNoSuffixFromURI(principalURI, originNoSuffix);
592   NS_ENSURE_SUCCESS(rv, rv);
593 
594   rv = Init(principalURI, attrs, originNoSuffix);
595   NS_ENSURE_SUCCESS(rv, rv);
596 
597   // Note: we don't call SetDomain here because we don't need the wrapper
598   // recomputation code there (we just created this principal).
599   mDomain = domain;
600   if (mDomain) {
601     SetHasExplicitDomain();
602   }
603 
604   return NS_OK;
605 }
606 
607 NS_IMETHODIMP
Write(nsIObjectOutputStream * aStream)608 ContentPrincipal::Write(nsIObjectOutputStream* aStream) {
609   // Read is used still for legacy principals
610   MOZ_RELEASE_ASSERT(false, "Old style serialization is removed");
611   return NS_OK;
612 }
613 
PopulateJSONObject(Json::Value & aObject)614 nsresult ContentPrincipal::PopulateJSONObject(Json::Value& aObject) {
615   nsAutoCString principalURI;
616   nsresult rv = mURI->GetSpec(principalURI);
617   NS_ENSURE_SUCCESS(rv, rv);
618 
619   // We turn each int enum field into a JSON string key of the object
620   // aObject is the inner JSON object that has stringified enum keys
621   // An example aObject might be:
622   //
623   // eURI                   eSuffix
624   //    |                           |
625   //  {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}
626   //    |                |          |         |
627   //    -----------------------------         |
628   //         |           |                    |
629   //        Key          ----------------------
630   //                                |
631   //                              Value
632   aObject[std::to_string(eURI)] = principalURI.get();
633 
634   if (mDomain) {
635     nsAutoCString domainStr;
636     rv = mDomain->GetSpec(domainStr);
637     NS_ENSURE_SUCCESS(rv, rv);
638     aObject[std::to_string(eDomain)] = domainStr.get();
639   }
640 
641   nsAutoCString suffix;
642   OriginAttributesRef().CreateSuffix(suffix);
643   if (suffix.Length() > 0) {
644     aObject[std::to_string(eSuffix)] = suffix.get();
645   }
646 
647   return NS_OK;
648 }
649 
FromProperties(nsTArray<ContentPrincipal::KeyVal> & aFields)650 already_AddRefed<BasePrincipal> ContentPrincipal::FromProperties(
651     nsTArray<ContentPrincipal::KeyVal>& aFields) {
652   MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
653   nsresult rv;
654   nsCOMPtr<nsIURI> principalURI;
655   nsCOMPtr<nsIURI> domain;
656   nsCOMPtr<nsIContentSecurityPolicy> csp;
657   OriginAttributes attrs;
658 
659   // The odd structure here is to make the code to not compile
660   // if all the switch enum cases haven't been codified
661   for (const auto& field : aFields) {
662     switch (field.key) {
663       case ContentPrincipal::eURI:
664         if (!field.valueWasSerialized) {
665           MOZ_ASSERT(
666               false,
667               "Content principals require a principal URI in serialized JSON");
668           return nullptr;
669         }
670         rv = NS_NewURI(getter_AddRefs(principalURI), field.value.get());
671         NS_ENSURE_SUCCESS(rv, nullptr);
672 
673         {
674           // Enforce re-parsing about: URIs so that if they change, we
675           // continue to use their new principals correctly.
676           if (principalURI->SchemeIs("about")) {
677             nsAutoCString spec;
678             principalURI->GetSpec(spec);
679             if (NS_FAILED(NS_NewURI(getter_AddRefs(principalURI), spec))) {
680               return nullptr;
681             }
682           }
683         }
684         break;
685       case ContentPrincipal::eDomain:
686         if (field.valueWasSerialized) {
687           rv = NS_NewURI(getter_AddRefs(domain), field.value.get());
688           NS_ENSURE_SUCCESS(rv, nullptr);
689         }
690         break;
691       case ContentPrincipal::eSuffix:
692         if (field.valueWasSerialized) {
693           bool ok = attrs.PopulateFromSuffix(field.value);
694           if (!ok) {
695             return nullptr;
696           }
697         }
698         break;
699     }
700   }
701   nsAutoCString originNoSuffix;
702   rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(principalURI,
703                                                        originNoSuffix);
704   if (NS_FAILED(rv)) {
705     return nullptr;
706   }
707 
708   RefPtr<ContentPrincipal> principal = new ContentPrincipal();
709   rv = principal->Init(principalURI, attrs, originNoSuffix);
710   if (NS_FAILED(rv)) {
711     return nullptr;
712   }
713 
714   principal->mDomain = domain;
715   if (principal->mDomain) {
716     principal->SetHasExplicitDomain();
717   }
718 
719   return principal.forget();
720 }
721