tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BasePrincipal.h (18323B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 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 #ifndef mozilla_BasePrincipal_h
      8 #define mozilla_BasePrincipal_h
      9 
     10 #include <stdint.h>
     11 #include "ErrorList.h"
     12 #include "js/TypeDecls.h"
     13 #include "mozilla/AlreadyAddRefed.h"
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/OriginAttributes.h"
     16 #include "mozilla/RefPtr.h"
     17 #include "nsAtom.h"
     18 #include "nsIObjectOutputStream.h"
     19 #include "nsIPrincipal.h"
     20 #include "nsJSPrincipals.h"
     21 #include "nsStringFwd.h"
     22 #include "nscore.h"
     23 
     24 class ExpandedPrincipal;
     25 class mozIDOMWindow;
     26 class nsIChannel;
     27 class nsIReferrerInfo;
     28 class nsISupports;
     29 class nsIURI;
     30 
     31 namespace mozilla {
     32 
     33 class JSONWriter;
     34 
     35 namespace dom {
     36 enum class ReferrerPolicy : uint8_t;
     37 }
     38 
     39 namespace extensions {
     40 class WebExtensionPolicy;
     41 class WebExtensionPolicyCore;
     42 }  // namespace extensions
     43 
     44 class BasePrincipal;
     45 
     46 // Content principals (and content principals embedded within expanded
     47 // principals) stored in SiteIdentifier are guaranteed to contain only the
     48 // eTLD+1 part of the original domain. This is used to determine whether two
     49 // origins are same-site: if it's possible for two origins to access each other
     50 // (maybe after mutating document.domain), then they must have the same site
     51 // identifier.
     52 class SiteIdentifier {
     53 public:
     54  void Init(BasePrincipal* aPrincipal) {
     55    MOZ_ASSERT(aPrincipal);
     56    mPrincipal = aPrincipal;
     57  }
     58 
     59  bool IsInitialized() const { return !!mPrincipal; }
     60 
     61  bool Equals(const SiteIdentifier& aOther) const;
     62 
     63 private:
     64  friend class ::ExpandedPrincipal;
     65 
     66  BasePrincipal* GetPrincipal() const {
     67    MOZ_ASSERT(IsInitialized());
     68    return mPrincipal;
     69  }
     70 
     71  RefPtr<BasePrincipal> mPrincipal;
     72 };
     73 
     74 /*
     75 * Base class from which all nsIPrincipal implementations inherit. Use this for
     76 * default implementations and other commonalities between principal
     77 * implementations.
     78 *
     79 * We should merge nsJSPrincipals into this class at some point.
     80 */
     81 class BasePrincipal : public nsJSPrincipals {
     82 public:
     83  // Warning: this enum impacts Principal serialization into JSON format.
     84  // Only update if you know exactly what you are doing
     85  enum PrincipalKind {
     86    eNullPrincipal = 0,
     87    eContentPrincipal,
     88    eExpandedPrincipal,
     89    eSystemPrincipal,
     90    eKindMax = eSystemPrincipal
     91  };
     92 
     93  static constexpr char NullPrincipalKey = '0';
     94  static_assert(eNullPrincipal == 0);
     95  static constexpr char ContentPrincipalKey = '1';
     96  static_assert(eContentPrincipal == 1);
     97  static constexpr char ExpandedPrincipalKey = '2';
     98  static_assert(eExpandedPrincipal == 2);
     99  static constexpr char SystemPrincipalKey = '3';
    100  static_assert(eSystemPrincipal == 3);
    101 
    102  template <typename T>
    103  bool Is() const {
    104    return mKind == T::Kind();
    105  }
    106 
    107  template <typename T>
    108  T* As() {
    109    MOZ_ASSERT(Is<T>());
    110    return static_cast<T*>(this);
    111  }
    112 
    113  enum DocumentDomainConsideration {
    114    DontConsiderDocumentDomain,
    115    ConsiderDocumentDomain
    116  };
    117  bool Subsumes(nsIPrincipal* aOther,
    118                DocumentDomainConsideration aConsideration);
    119 
    120  NS_IMETHOD GetOrigin(nsACString& aOrigin) final;
    121  NS_IMETHOD GetWebExposedOriginSerialization(nsACString& aOrigin) override;
    122  NS_IMETHOD GetOriginNoSuffix(nsACString& aOrigin) final;
    123  NS_IMETHOD Equals(nsIPrincipal* other, bool* _retval) final;
    124  NS_IMETHOD EqualsConsideringDomain(nsIPrincipal* other, bool* _retval) final;
    125  NS_IMETHOD EqualsURI(nsIURI* aOtherURI, bool* _retval) override;
    126  NS_IMETHOD EqualsForPermission(nsIPrincipal* other, bool aExactHost,
    127                                 bool* _retval) final;
    128  NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval) final;
    129  NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other,
    130                                       bool* _retval) final;
    131  NS_IMETHOD SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* other,
    132                                                  bool* _retval) final;
    133  NS_IMETHOD CheckMayLoad(nsIURI* uri, bool allowIfInheritsPrincipal) final;
    134  NS_IMETHOD CheckMayLoadWithReporting(nsIURI* uri,
    135                                       bool allowIfInheritsPrincipal,
    136                                       uint64_t innerWindowID) final;
    137  NS_IMETHOD GetAddonPolicy(extensions::WebExtensionPolicy** aResult) final;
    138  NS_IMETHOD GetContentScriptAddonPolicy(
    139      extensions::WebExtensionPolicy** aResult) final;
    140  NS_IMETHOD GetIsNullPrincipal(bool* aResult) override;
    141  NS_IMETHOD GetIsContentPrincipal(bool* aResult) override;
    142  NS_IMETHOD GetIsExpandedPrincipal(bool* aResult) override;
    143  NS_IMETHOD GetIsSystemPrincipal(bool* aResult) override;
    144  NS_IMETHOD GetScheme(nsACString& aScheme) override;
    145  NS_IMETHOD SchemeIs(const char* aScheme, bool* aResult) override;
    146  NS_IMETHOD IsURIInPrefList(const char* aPref, bool* aResult) override;
    147  NS_IMETHOD IsURIInList(const nsACString& aList, bool* aResult) override;
    148  NS_IMETHOD IsContentAccessibleAboutURI(bool* aResult) override;
    149  NS_IMETHOD IsL10nAllowed(nsIURI* aURI, bool* aResult) override;
    150  NS_IMETHOD GetAboutModuleFlags(uint32_t* flags) override;
    151  NS_IMETHOD GetIsAddonOrExpandedAddonPrincipal(bool* aResult) override;
    152  NS_IMETHOD GetOriginAttributes(JSContext* aCx,
    153                                 JS::MutableHandle<JS::Value> aVal) final;
    154  NS_IMETHOD GetAsciiSpec(nsACString& aSpec) override;
    155  NS_IMETHOD GetSpec(nsACString& aSpec) override;
    156  NS_IMETHOD GetExposablePrePath(nsACString& aResult) override;
    157  NS_IMETHOD GetExposableSpec(nsACString& aSpec) override;
    158  NS_IMETHOD GetHostPort(nsACString& aRes) override;
    159  NS_IMETHOD GetHost(nsACString& aRes) override;
    160  NS_IMETHOD GetPrePath(nsACString& aResult) override;
    161  NS_IMETHOD GetFilePath(nsACString& aResult) override;
    162  NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
    163  NS_IMETHOD GetIsIpAddress(bool* aIsIpAddress) override;
    164  NS_IMETHOD GetIsLocalIpAddress(bool* aIsIpAddress) override;
    165  NS_IMETHOD GetIsOnion(bool* aIsOnion) override;
    166  NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
    167  NS_IMETHOD GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) final;
    168  NS_IMETHOD GetIsInPrivateBrowsing(bool* aIsInPrivateBrowsing) final;
    169  NS_IMETHOD GetSiteOrigin(nsACString& aSiteOrigin) final;
    170  NS_IMETHOD GetSiteOriginNoSuffix(nsACString& aSiteOrigin) override;
    171  NS_IMETHOD IsThirdPartyURI(nsIURI* uri, bool* aRes) override;
    172  NS_IMETHOD IsThirdPartyPrincipal(nsIPrincipal* uri, bool* aRes) override;
    173  NS_IMETHOD IsThirdPartyChannel(nsIChannel* aChannel, bool* aRes) override;
    174  NS_IMETHOD GetIsOriginPotentiallyTrustworthy(bool* aResult) override;
    175  NS_IMETHOD GetIsLoopbackHost(bool* aResult) override;
    176  NS_IMETHOD IsSameOrigin(nsIURI* aURI, bool* aRes) override;
    177  NS_IMETHOD GetPrefLightCacheKey(nsIURI* aURI, bool aWithCredentials,
    178                                  const OriginAttributes& aOriginAttributes,
    179                                  nsACString& _retval) override;
    180  NS_IMETHOD HasFirstpartyStorageAccess(mozIDOMWindow* aCheckWindow,
    181                                        uint32_t* aRejectedReason,
    182                                        bool* aOutAllowed) override;
    183  NS_IMETHOD GetAsciiHost(nsACString& aAsciiHost) override;
    184  NS_IMETHOD GetLocalStorageQuotaKey(nsACString& aRes) override;
    185  NS_IMETHOD CreateReferrerInfo(mozilla::dom::ReferrerPolicy aReferrerPolicy,
    186                                nsIReferrerInfo** _retval) override;
    187  NS_IMETHOD GetIsScriptAllowedByPolicy(
    188      bool* aIsScriptAllowedByPolicy) override;
    189  NS_IMETHOD GetStorageOriginKey(nsACString& aOriginKey) override;
    190 
    191  NS_IMETHOD GetNextSubDomainPrincipal(
    192      nsIPrincipal** aNextSubDomainPrincipal) override;
    193 
    194  NS_IMETHOD GetPrecursorPrincipal(nsIPrincipal** aPrecursor) override;
    195 
    196  nsresult ToJSON(nsACString& aJSON);
    197  nsresult ToJSON(JSONWriter& aWriter);
    198  nsresult WriteJSONProperties(JSONWriter& aWriter);
    199 
    200  static already_AddRefed<BasePrincipal> FromJSON(const nsACString& aJSON);
    201 
    202  // Method to write serializable fields which represent all of the fields to
    203  // deserialize the principal.
    204  virtual nsresult WriteJSONInnerProperties(JSONWriter& aWriter);
    205 
    206  virtual bool AddonHasPermission(const nsAtom* aPerm);
    207 
    208  virtual bool IsContentPrincipal() const { return false; };
    209 
    210  static BasePrincipal* Cast(nsIPrincipal* aPrin) {
    211    return static_cast<BasePrincipal*>(aPrin);
    212  }
    213 
    214  static BasePrincipal& Cast(nsIPrincipal& aPrin) {
    215    return *static_cast<BasePrincipal*>(&aPrin);
    216  }
    217 
    218  static const BasePrincipal* Cast(const nsIPrincipal* aPrin) {
    219    return static_cast<const BasePrincipal*>(aPrin);
    220  }
    221 
    222  static const BasePrincipal& Cast(const nsIPrincipal& aPrin) {
    223    return *static_cast<const BasePrincipal*>(&aPrin);
    224  }
    225 
    226  static already_AddRefed<BasePrincipal> CreateContentPrincipal(
    227      const nsACString& aOrigin);
    228 
    229  // This method may not create a content principal in case it's not possible to
    230  // generate a correct origin from the passed URI. If this happens, a
    231  // NullPrincipal is returned.
    232  //
    233  // If `aInitialDomain` is specified, and a ContentPrincipal is set, it will
    234  // initially have its domain set to the given value, without re-computing js
    235  // wrappers. Unlike `SetDomain()` this is safe to do off-main-thread.
    236 
    237  static already_AddRefed<BasePrincipal> CreateContentPrincipal(
    238      nsIURI* aURI, const OriginAttributes& aAttrs,
    239      nsIURI* aInitialDomain = nullptr);
    240 
    241  const OriginAttributes& OriginAttributesRef() final {
    242    return mOriginAttributes;
    243  }
    244  extensions::WebExtensionPolicy* AddonPolicy();
    245  RefPtr<extensions::WebExtensionPolicyCore> AddonPolicyCore();
    246  uint32_t UserContextId() const { return mOriginAttributes.mUserContextId; }
    247  uint32_t PrivateBrowsingId() const {
    248    return mOriginAttributes.mPrivateBrowsingId;
    249  }
    250 
    251  PrincipalKind Kind() const { return mKind; }
    252 
    253  already_AddRefed<BasePrincipal> CloneForcingOriginAttributes(
    254      const OriginAttributes& aOriginAttributes);
    255 
    256  // If this is an add-on content script principal, returns its AddonPolicy.
    257  // Otherwise returns null.
    258  extensions::WebExtensionPolicy* ContentScriptAddonPolicy();
    259  RefPtr<extensions::WebExtensionPolicyCore> ContentScriptAddonPolicyCore();
    260 
    261  // Helper to check whether this principal is associated with an addon that
    262  // allows unprivileged code to load aURI.  aExplicit == true will prevent
    263  // use of all_urls permission, requiring the domain in its permissions.
    264  bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false);
    265 
    266  // Call these to avoid the cost of virtual dispatch.
    267  inline bool FastEquals(nsIPrincipal* aOther);
    268  inline bool FastEqualsConsideringDomain(nsIPrincipal* aOther);
    269  inline bool FastSubsumes(nsIPrincipal* aOther);
    270  inline bool FastSubsumesConsideringDomain(nsIPrincipal* aOther);
    271  inline bool FastSubsumesIgnoringFPD(nsIPrincipal* aOther);
    272  inline bool FastSubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther);
    273 
    274  // Fast way to check whether we have a system principal.
    275  inline bool IsSystemPrincipal() const;
    276 
    277  // Returns the principal to inherit when a caller with this principal loads
    278  // the given URI.
    279  //
    280  // For most principal types, this returns the principal itself. For expanded
    281  // principals, it returns the first sub-principal which subsumes the given URI
    282  // (or, if no URI is given, the last allowlist principal).
    283  nsIPrincipal* PrincipalToInherit(nsIURI* aRequestedURI = nullptr);
    284 
    285  /* Returns true if this principal's CSP should override a document's CSP for
    286   * loads that it triggers. Currently true for expanded principals which
    287   * subsume the document principal, and add-on content principals regardless
    288   * of whether they subsume the document principal.
    289   */
    290  bool OverridesCSP(nsIPrincipal* aDocumentPrincipal);
    291 
    292  uint32_t GetOriginNoSuffixHash() const { return mOriginNoSuffix->hash(); }
    293  uint32_t GetOriginSuffixHash() const { return mOriginSuffix->hash(); }
    294 
    295  virtual nsresult GetSiteIdentifier(SiteIdentifier& aSite) = 0;
    296 
    297 protected:
    298  BasePrincipal(PrincipalKind aKind, const nsACString& aOriginNoSuffix,
    299                const OriginAttributes& aOriginAttributes);
    300  BasePrincipal(BasePrincipal* aOther,
    301                const OriginAttributes& aOriginAttributes);
    302 
    303  virtual ~BasePrincipal();
    304 
    305  // Note that this does not check OriginAttributes. Callers that depend on
    306  // those must call Subsumes instead.
    307  virtual bool SubsumesInternal(nsIPrincipal* aOther,
    308                                DocumentDomainConsideration aConsider) = 0;
    309 
    310  // Internal, side-effect-free check to determine whether the concrete
    311  // principal would allow the load ignoring any common behavior implemented in
    312  // BasePrincipal::CheckMayLoad.
    313  //
    314  // Safe to call from any thread, unlike CheckMayLoad.
    315  virtual bool MayLoadInternal(nsIURI* aURI) = 0;
    316  friend class ::ExpandedPrincipal;
    317 
    318  // Helper for implementing CheckMayLoad and CheckMayLoadWithReporting.
    319  nsresult CheckMayLoadHelper(nsIURI* aURI, bool aAllowIfInheritsPrincipal,
    320                              bool aReport, uint64_t aInnerWindowID);
    321 
    322  void SetHasExplicitDomain() { mHasExplicitDomain = true; }
    323  bool GetHasExplicitDomain() { return mHasExplicitDomain; }
    324 
    325  // KeyValT holds a principal subtype-specific key value and the associated
    326  // parsed value after JSON parsing.
    327  template <typename SerializedKey>
    328  struct KeyValT {
    329    static_assert(sizeof(SerializedKey) == 1,
    330                  "SerializedKey should be a uint8_t");
    331    SerializedKey key;
    332    bool valueWasSerialized;
    333    nsCString value;
    334  };
    335 
    336  // Common base class for all Deserializer implementations in concrete
    337  // subclasses. Subclasses will initialize `mPrincipal` in `Read`, and then
    338  // calls to `QueryInterface` will QI on the target object.
    339  class Deserializer : public nsISerializable {
    340   public:
    341    NS_DECL_ISUPPORTS
    342    NS_IMETHOD Write(nsIObjectOutputStream* aStream) override;
    343 
    344   protected:
    345    virtual ~Deserializer() = default;
    346    RefPtr<BasePrincipal> mPrincipal;
    347  };
    348 
    349 private:
    350  static constexpr Span<const char> JSONEnumKeyStrings[4] = {
    351      MakeStringSpan("0"),
    352      MakeStringSpan("1"),
    353      MakeStringSpan("2"),
    354      MakeStringSpan("3"),
    355  };
    356 
    357  static void WriteJSONProperty(JSONWriter& aWriter,
    358                                const Span<const char>& aKey,
    359                                const nsCString& aValue);
    360 
    361 protected:
    362  template <size_t EnumValue>
    363  static inline constexpr const Span<const char>& JSONEnumKeyString() {
    364    static_assert(EnumValue < std::size(JSONEnumKeyStrings));
    365    return JSONEnumKeyStrings[EnumValue];
    366  }
    367  template <size_t EnumValue>
    368  static void WriteJSONProperty(JSONWriter& aWriter, const nsCString& aValue) {
    369    WriteJSONProperty(aWriter, JSONEnumKeyString<EnumValue>(), aValue);
    370  }
    371 
    372 private:
    373  static already_AddRefed<BasePrincipal> CreateContentPrincipal(
    374      nsIURI* aURI, const OriginAttributes& aAttrs,
    375      const nsACString& aOriginNoSuffix, nsIURI* aInitialDomain);
    376 
    377  bool FastSubsumesIgnoringFPD(nsIPrincipal* aOther,
    378                               DocumentDomainConsideration aConsideration);
    379 
    380  const RefPtr<nsAtom> mOriginNoSuffix;
    381  const RefPtr<nsAtom> mOriginSuffix;
    382 
    383  const OriginAttributes mOriginAttributes;
    384  const PrincipalKind mKind;
    385  std::atomic<bool> mHasExplicitDomain;
    386 };
    387 
    388 inline bool BasePrincipal::FastEquals(nsIPrincipal* aOther) {
    389  MOZ_ASSERT(aOther);
    390 
    391  auto other = Cast(aOther);
    392  if (Kind() != other->Kind()) {
    393    // Principals of different kinds can't be equal.
    394    return false;
    395  }
    396 
    397  // Two principals are considered to be equal if their origins are the same.
    398  // If the two principals are content principals, their origin attributes
    399  // (aka the origin suffix) must also match.
    400  if (Kind() == eSystemPrincipal) {
    401    return this == other;
    402  }
    403 
    404  if (Kind() == eContentPrincipal || Kind() == eNullPrincipal) {
    405    return mOriginNoSuffix == other->mOriginNoSuffix &&
    406           mOriginSuffix == other->mOriginSuffix;
    407  }
    408 
    409  MOZ_ASSERT(Kind() == eExpandedPrincipal);
    410  return mOriginNoSuffix == other->mOriginNoSuffix;
    411 }
    412 
    413 inline bool BasePrincipal::FastEqualsConsideringDomain(nsIPrincipal* aOther) {
    414  MOZ_ASSERT(aOther);
    415 
    416  // If neither of the principals have document.domain set, we use the fast path
    417  // in Equals().  Otherwise, we fall back to the slow path below.
    418  auto other = Cast(aOther);
    419  if (!mHasExplicitDomain && !other->mHasExplicitDomain) {
    420    return FastEquals(aOther);
    421  }
    422 
    423  // Principals of different kinds can't be equal.
    424  if (Kind() != other->Kind()) {
    425    return false;
    426  }
    427 
    428  // Only ContentPrincipals should have mHasExplicitDomain set to true, so test
    429  // that we haven't ended up here instead of FastEquals by mistake.
    430  MOZ_ASSERT(IsContentPrincipal(),
    431             "Only content principals can set mHasExplicitDomain");
    432 
    433  return Subsumes(aOther, ConsiderDocumentDomain) &&
    434         other->Subsumes(this, ConsiderDocumentDomain);
    435 }
    436 
    437 inline bool BasePrincipal::FastSubsumes(nsIPrincipal* aOther) {
    438  MOZ_ASSERT(aOther);
    439 
    440  // If two principals are equal, then they both subsume each other.
    441  if (FastEquals(aOther)) {
    442    return true;
    443  }
    444 
    445  // Otherwise, fall back to the slow path.
    446  return Subsumes(aOther, DontConsiderDocumentDomain);
    447 }
    448 
    449 inline bool BasePrincipal::FastSubsumesConsideringDomain(nsIPrincipal* aOther) {
    450  MOZ_ASSERT(aOther);
    451 
    452  // If neither of the principals have document.domain set, we hand off to
    453  // FastSubsumes() which has fast paths for some special cases. Otherwise, we
    454  // fall back to the slow path below.
    455  if (!mHasExplicitDomain && !Cast(aOther)->mHasExplicitDomain) {
    456    return FastSubsumes(aOther);
    457  }
    458 
    459  return Subsumes(aOther, ConsiderDocumentDomain);
    460 }
    461 
    462 inline bool BasePrincipal::FastSubsumesIgnoringFPD(nsIPrincipal* aOther) {
    463  return FastSubsumesIgnoringFPD(aOther, DontConsiderDocumentDomain);
    464 }
    465 
    466 inline bool BasePrincipal::FastSubsumesConsideringDomainIgnoringFPD(
    467    nsIPrincipal* aOther) {
    468  return FastSubsumesIgnoringFPD(aOther, ConsiderDocumentDomain);
    469 }
    470 
    471 inline bool BasePrincipal::IsSystemPrincipal() const {
    472  return Kind() == eSystemPrincipal;
    473 }
    474 
    475 }  // namespace mozilla
    476 
    477 inline bool nsIPrincipal::IsSystemPrincipal() const {
    478  return mozilla::BasePrincipal::Cast(this)->IsSystemPrincipal();
    479 }
    480 
    481 #endif /* mozilla_BasePrincipal_h */