tor-browser

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

NullPrincipal.cpp (11627B)


      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/. */
      6 
      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.
     11 */
     12 
     13 #include "mozilla/dom/BlobURLProtocolHandler.h"
     14 #include "mozilla/StaticPrefs_network.h"
     15 #include "nsDocShell.h"
     16 #include "NullPrincipal.h"
     17 #include "DefaultURI.h"
     18 #include "nsSimpleURI.h"
     19 #include "nsIClassInfoImpl.h"
     20 #include "nsNetCID.h"
     21 #include "nsError.h"
     22 #include "nsEscape.h"
     23 #include "ContentPrincipal.h"
     24 #include "nsScriptSecurityManager.h"
     25 #include "pratom.h"
     26 #include "nsIObjectInputStream.h"
     27 
     28 #include "js/JSON.h"
     29 #include "NullPrincipalJSONHandler.h"
     30 
     31 using namespace mozilla;
     32 
     33 NS_IMPL_CLASSINFO(NullPrincipal, nullptr, 0, NS_NULLPRINCIPAL_CID)
     34 NS_IMPL_QUERY_INTERFACE_CI(NullPrincipal, nsIPrincipal)
     35 NS_IMPL_CI_INTERFACE_GETTER(NullPrincipal, nsIPrincipal)
     36 
     37 NullPrincipal::NullPrincipal(nsIURI* aURI, const nsACString& aOriginNoSuffix,
     38                             const OriginAttributes& aOriginAttributes)
     39    : BasePrincipal(eNullPrincipal, aOriginNoSuffix, aOriginAttributes),
     40      mURI(aURI) {}
     41 
     42 /* static */
     43 already_AddRefed<NullPrincipal> NullPrincipal::CreateWithInheritedAttributes(
     44    nsIPrincipal* aInheritFrom) {
     45  MOZ_ASSERT(aInheritFrom);
     46  nsCOMPtr<nsIURI> uri = CreateURI(aInheritFrom);
     47  return Create(Cast(aInheritFrom)->OriginAttributesRef(), uri);
     48 }
     49 
     50 /* static */
     51 already_AddRefed<NullPrincipal> NullPrincipal::Create(
     52    const OriginAttributes& aOriginAttributes, nsIURI* aNullPrincipalURI) {
     53  nsCOMPtr<nsIURI> uri = aNullPrincipalURI;
     54  if (!uri) {
     55    uri = NullPrincipal::CreateURI(nullptr);
     56  }
     57 
     58  MOZ_RELEASE_ASSERT(uri->SchemeIs(NS_NULLPRINCIPAL_SCHEME));
     59 
     60  nsAutoCString originNoSuffix;
     61  DebugOnly<nsresult> rv = uri->GetSpec(originNoSuffix);
     62  MOZ_ASSERT(NS_SUCCEEDED(rv));
     63 
     64  RefPtr<NullPrincipal> nullPrin =
     65      new NullPrincipal(uri, originNoSuffix, aOriginAttributes);
     66  return nullPrin.forget();
     67 }
     68 
     69 /* static */
     70 already_AddRefed<NullPrincipal> NullPrincipal::CreateWithoutOriginAttributes() {
     71  return NullPrincipal::Create(OriginAttributes(), nullptr);
     72 }
     73 
     74 void NullPrincipal::EscapePrecursorQuery(nsACString& aPrecursorQuery) {
     75  // origins should not contain existing escape sequences, so set `esc_Forced`
     76  // to force any `%` in the input to be escaped in addition to non-ascii,
     77  // control characters and DEL.
     78  nsCString modified;
     79  if (NS_EscapeURLSpan(aPrecursorQuery, esc_Query | esc_Forced, modified)) {
     80    aPrecursorQuery.Assign(std::move(modified));
     81  }
     82 }
     83 
     84 void NullPrincipal::UnescapePrecursorQuery(nsACString& aPrecursorQuery) {
     85  nsCString modified;
     86  if (NS_UnescapeURL(aPrecursorQuery.BeginReading(), aPrecursorQuery.Length(),
     87                     /* aFlags */ 0, modified)) {
     88    aPrecursorQuery.Assign(std::move(modified));
     89  }
     90 }
     91 
     92 already_AddRefed<nsIURI> NullPrincipal::CreateURI(
     93    nsIPrincipal* aPrecursor, const nsID* aNullPrincipalID) {
     94  nsCOMPtr<nsIURIMutator> iMutator;
     95  if (StaticPrefs::network_url_useDefaultURI()) {
     96    iMutator = new mozilla::net::DefaultURI::Mutator();
     97  } else {
     98    iMutator = new mozilla::net::nsSimpleURI::Mutator();
     99  }
    100 
    101  nsID uuid = aNullPrincipalID ? *aNullPrincipalID : nsID::GenerateUUID();
    102 
    103  NS_MutateURI mutator(iMutator);
    104  mutator.SetSpec(NS_NULLPRINCIPAL_SCHEME ":"_ns +
    105                  nsDependentCString(nsIDToCString(uuid).get()));
    106 
    107  // If there's a precursor URI, encode it in the null principal URI's query.
    108  if (aPrecursor) {
    109    nsAutoCString precursorOrigin;
    110    switch (BasePrincipal::Cast(aPrecursor)->Kind()) {
    111      case eNullPrincipal: {
    112        // If the precursor null principal has a precursor, inherit it.
    113        if (nsCOMPtr<nsIURI> nullPrecursorURI = aPrecursor->GetURI()) {
    114          MOZ_ALWAYS_SUCCEEDS(nullPrecursorURI->GetQuery(precursorOrigin));
    115        }
    116        break;
    117      }
    118      case eContentPrincipal: {
    119        MOZ_ALWAYS_SUCCEEDS(aPrecursor->GetOriginNoSuffix(precursorOrigin));
    120 #ifdef DEBUG
    121        nsAutoCString original(precursorOrigin);
    122 #endif
    123        EscapePrecursorQuery(precursorOrigin);
    124 #ifdef DEBUG
    125        nsAutoCString unescaped(precursorOrigin);
    126        UnescapePrecursorQuery(unescaped);
    127        MOZ_ASSERT(unescaped == original,
    128                   "cannot recover original precursor origin after escape");
    129 #endif
    130        break;
    131      }
    132 
    133      // For now, we won't track expanded or system principal precursors. We may
    134      // want to track expanded principal precursors in the future, but it's
    135      // unlikely we'll want to track system principal precursors.
    136      case eExpandedPrincipal:
    137      case eSystemPrincipal:
    138        break;
    139    }
    140    if (!precursorOrigin.IsEmpty()) {
    141      mutator.SetQuery(precursorOrigin);
    142    }
    143  }
    144 
    145  nsCOMPtr<nsIURI> uri;
    146  MOZ_ALWAYS_SUCCEEDS(mutator.Finalize(getter_AddRefs(uri)));
    147  return uri.forget();
    148 }
    149 
    150 nsresult NullPrincipal::GetScriptLocation(nsACString& aStr) {
    151  return mURI->GetSpec(aStr);
    152 }
    153 
    154 /**
    155 * nsIPrincipal implementation
    156 */
    157 
    158 NS_IMETHODIMP
    159 NullPrincipal::GetURI(nsIURI** aURI) {
    160  nsCOMPtr<nsIURI> uri = mURI;
    161  uri.forget(aURI);
    162  return NS_OK;
    163 }
    164 NS_IMETHODIMP
    165 NullPrincipal::GetIsOriginPotentiallyTrustworthy(bool* aResult) {
    166  *aResult = false;
    167  return NS_OK;
    168 }
    169 
    170 NS_IMETHODIMP
    171 NullPrincipal::GetDomain(nsIURI** aDomain) {
    172  nsCOMPtr<nsIURI> uri = mURI;
    173  uri.forget(aDomain);
    174  return NS_OK;
    175 }
    176 
    177 NS_IMETHODIMP
    178 NullPrincipal::SetDomain(nsIURI* aDomain) {
    179  // I think the right thing to do here is to just throw...  Silently failing
    180  // seems counterproductive.
    181  return NS_ERROR_NOT_AVAILABLE;
    182 }
    183 
    184 bool NullPrincipal::MayLoadInternal(nsIURI* aURI) {
    185  // Also allow the load if we are the principal of the URI being checked.
    186  nsCOMPtr<nsIPrincipal> blobPrincipal;
    187  if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
    188          aURI, getter_AddRefs(blobPrincipal))) {
    189    MOZ_ASSERT(blobPrincipal);
    190    return SubsumesInternal(blobPrincipal,
    191                            BasePrincipal::ConsiderDocumentDomain);
    192  }
    193 
    194  return false;
    195 }
    196 
    197 NS_IMETHODIMP
    198 NullPrincipal::GetBaseDomain(nsACString& aBaseDomain) {
    199  // For a null principal, we use our unique uuid as the base domain.
    200  return mURI->GetPathQueryRef(aBaseDomain);
    201 }
    202 
    203 NS_IMETHODIMP
    204 NullPrincipal::GetAddonId(nsAString& aAddonId) {
    205  aAddonId.Truncate();
    206  return NS_OK;
    207 };
    208 
    209 /**
    210 * nsISerializable implementation
    211 */
    212 NS_IMETHODIMP
    213 NullPrincipal::Deserializer::Read(nsIObjectInputStream* aStream) {
    214  nsAutoCString spec;
    215  nsresult rv = aStream->ReadCString(spec);
    216  NS_ENSURE_SUCCESS(rv, rv);
    217 
    218  nsCOMPtr<nsIURI> uri;
    219  rv = NS_NewURI(getter_AddRefs(uri), spec);
    220  NS_ENSURE_SUCCESS(rv, rv);
    221 
    222  nsAutoCString suffix;
    223  rv = aStream->ReadCString(suffix);
    224  NS_ENSURE_SUCCESS(rv, rv);
    225 
    226  OriginAttributes attrs;
    227  bool ok = attrs.PopulateFromSuffix(suffix);
    228  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
    229 
    230  mPrincipal = NullPrincipal::Create(attrs, uri);
    231  NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE);
    232 
    233  return NS_OK;
    234 }
    235 
    236 nsresult NullPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) {
    237  nsAutoCString principalURI;
    238  nsresult rv = mURI->GetSpec(principalURI);
    239  NS_ENSURE_SUCCESS(rv, rv);
    240  WriteJSONProperty<eSpec>(aWriter, principalURI);
    241 
    242  nsAutoCString suffix;
    243  OriginAttributesRef().CreateSuffix(suffix);
    244  if (suffix.Length() > 0) {
    245    WriteJSONProperty<eSuffix>(aWriter, suffix);
    246  }
    247 
    248  return NS_OK;
    249 }
    250 
    251 NS_IMETHODIMP
    252 NullPrincipal::GetPrecursorPrincipal(nsIPrincipal** aPrincipal) {
    253  *aPrincipal = nullptr;
    254 
    255  nsAutoCString query;
    256  if (NS_FAILED(mURI->GetQuery(query)) || query.IsEmpty()) {
    257    return NS_OK;
    258  }
    259  UnescapePrecursorQuery(query);
    260 
    261  nsCOMPtr<nsIURI> precursorURI;
    262  if (NS_FAILED(NS_NewURI(getter_AddRefs(precursorURI), query))) {
    263    MOZ_ASSERT_UNREACHABLE(
    264        "Failed to parse precursor from nullprincipal query");
    265    return NS_OK;
    266  }
    267 
    268  // If our precursor is another null principal, re-construct it. This can
    269  // happen if a null principal without a precursor causes another principal to
    270  // be created.
    271  if (precursorURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME)) {
    272 #ifdef DEBUG
    273    nsAutoCString precursorQuery;
    274    precursorURI->GetQuery(precursorQuery);
    275    MOZ_ASSERT(precursorQuery.IsEmpty(),
    276               "Null principal with nested precursors?");
    277 #endif
    278    *aPrincipal =
    279        NullPrincipal::Create(OriginAttributesRef(), precursorURI).take();
    280    return NS_OK;
    281  }
    282 
    283  RefPtr<BasePrincipal> contentPrincipal =
    284      BasePrincipal::CreateContentPrincipal(precursorURI,
    285                                            OriginAttributesRef());
    286  // If `CreateContentPrincipal` failed, it will create a new NullPrincipal and
    287  // return that instead. We only want to return real content principals here.
    288  if (!contentPrincipal || !contentPrincipal->Is<ContentPrincipal>()) {
    289    return NS_OK;
    290  }
    291  contentPrincipal.forget(aPrincipal);
    292  return NS_OK;
    293 }
    294 
    295 bool NullPrincipalJSONHandler::startObject() {
    296  switch (mState) {
    297    case State::Init:
    298      mState = State::StartObject;
    299      break;
    300    default:
    301      NS_WARNING("Unexpected object value");
    302      mState = State::Error;
    303      return false;
    304  }
    305 
    306  return true;
    307 }
    308 
    309 bool NullPrincipalJSONHandler::propertyName(const JS::Latin1Char* name,
    310                                            size_t length) {
    311  switch (mState) {
    312    case State::StartObject:
    313    case State::AfterPropertyValue: {
    314      if (length != 1) {
    315        NS_WARNING(
    316            nsPrintfCString("Unexpected property name length: %zu", length)
    317                .get());
    318        mState = State::Error;
    319        return false;
    320      }
    321 
    322      char key = char(name[0]);
    323      switch (key) {
    324        case NullPrincipal::SpecKey:
    325          mState = State::SpecKey;
    326          break;
    327        case NullPrincipal::SuffixKey:
    328          mState = State::SuffixKey;
    329          break;
    330        default:
    331          NS_WARNING(
    332              nsPrintfCString("Unexpected property name: '%c'", key).get());
    333          mState = State::Error;
    334          return false;
    335      }
    336      break;
    337    }
    338    default:
    339      NS_WARNING("Unexpected property name");
    340      mState = State::Error;
    341      return false;
    342  }
    343 
    344  return true;
    345 }
    346 
    347 bool NullPrincipalJSONHandler::endObject() {
    348  switch (mState) {
    349    case State::AfterPropertyValue:
    350      MOZ_ASSERT(mUri);
    351 
    352      mPrincipal = NullPrincipal::Create(mAttrs, mUri);
    353      MOZ_ASSERT(mPrincipal);
    354 
    355      mState = State::EndObject;
    356      break;
    357    default:
    358      NS_WARNING("Unexpected end of object");
    359      mState = State::Error;
    360      return false;
    361  }
    362 
    363  return true;
    364 }
    365 
    366 bool NullPrincipalJSONHandler::stringValue(const JS::Latin1Char* str,
    367                                           size_t length) {
    368  switch (mState) {
    369    case State::SpecKey: {
    370      nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);
    371      nsresult rv = NS_NewURI(getter_AddRefs(mUri), spec);
    372      if (NS_FAILED(rv)) {
    373        mState = State::Error;
    374        return false;
    375      }
    376 
    377      mState = State::AfterPropertyValue;
    378      break;
    379    }
    380    case State::SuffixKey: {
    381      nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length);
    382      if (!mAttrs.PopulateFromSuffix(attrs)) {
    383        mState = State::Error;
    384        return false;
    385      }
    386 
    387      mState = State::AfterPropertyValue;
    388      break;
    389    }
    390    default:
    391      NS_WARNING("Unexpected string value");
    392      mState = State::Error;
    393      return false;
    394  }
    395 
    396  return true;
    397 }