tor-browser

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

nsJSPrincipals.cpp (11147B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsIPrincipal.h"
      7 #include "xpcpublic.h"
      8 #include "nsString.h"
      9 #include "nsJSPrincipals.h"
     10 #include "nsCOMPtr.h"
     11 #include "mozilla/BasePrincipal.h"
     12 #include "mozilla/StaticPtr.h"
     13 #include "mozilla/dom/StructuredCloneTags.h"
     14 #include "mozilla/ipc/BackgroundUtils.h"
     15 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     16 
     17 using namespace mozilla;
     18 using namespace mozilla::dom;
     19 using namespace mozilla::ipc;
     20 
     21 NS_IMETHODIMP_(MozExternalRefCountType)
     22 nsJSPrincipals::AddRef() {
     23  MOZ_ASSERT(int32_t(refcount) >= 0, "illegal refcnt");
     24  nsrefcnt count = ++refcount;
     25  NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this));
     26  return count;
     27 }
     28 
     29 NS_IMETHODIMP_(MozExternalRefCountType)
     30 nsJSPrincipals::Release() {
     31  MOZ_ASSERT(0 != refcount, "dup release");
     32  nsrefcnt count = --refcount;
     33  NS_LOG_RELEASE(this, count, "nsJSPrincipals");
     34  if (count == 0) {
     35    delete this;
     36  }
     37 
     38  return count;
     39 }
     40 
     41 /* static */
     42 bool nsJSPrincipals::Subsume(JSPrincipals* jsprin, JSPrincipals* other) {
     43  bool result;
     44  nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(
     45      nsJSPrincipals::get(other), &result);
     46  return NS_SUCCEEDED(rv) && result;
     47 }
     48 
     49 /* static */
     50 void nsJSPrincipals::Destroy(JSPrincipals* jsprin) {
     51  // The JS runtime can call this method during the last GC when
     52  // nsScriptSecurityManager is destroyed. So we must not assume here that
     53  // the security manager still exists.
     54 
     55  nsJSPrincipals* nsjsprin = nsJSPrincipals::get(jsprin);
     56 
     57  // We need to destroy the nsIPrincipal. We'll do this by adding
     58  // to the refcount and calling release
     59 
     60 #ifdef NS_BUILD_REFCNT_LOGGING
     61  // The refcount logging considers AddRef-to-1 to indicate creation,
     62  // so trick it into thinking it's otherwise, but balance the
     63  // Release() we do below.
     64  nsjsprin->refcount++;
     65  nsjsprin->AddRef();
     66  nsjsprin->refcount--;
     67 #else
     68  nsjsprin->refcount++;
     69 #endif
     70  nsjsprin->Release();
     71 }
     72 
     73 #ifdef DEBUG
     74 
     75 // Defined here so one can do principals->dump() in the debugger
     76 JS_PUBLIC_API void JSPrincipals::dump() {
     77  if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
     78    nsAutoCString str;
     79    nsresult rv = static_cast<nsJSPrincipals*>(this)->GetScriptLocation(str);
     80    fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this),
     81            NS_SUCCEEDED(rv) ? str.get() : "(unknown)");
     82  } else {
     83    fprintf(stderr,
     84            "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
     85            "actual=0x%x expected=0x%x\n",
     86            this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
     87  }
     88 }
     89 
     90 #endif
     91 
     92 /* static */
     93 bool nsJSPrincipals::ReadPrincipals(JSContext* aCx,
     94                                    JSStructuredCloneReader* aReader,
     95                                    JSPrincipals** aOutPrincipals) {
     96  uint32_t tag;
     97  uint32_t unused;
     98  if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
     99    return false;
    100  }
    101 
    102  if (tag != SCTAG_DOM_NULL_PRINCIPAL && tag != SCTAG_DOM_SYSTEM_PRINCIPAL &&
    103      tag != SCTAG_DOM_CONTENT_PRINCIPAL &&
    104      tag != SCTAG_DOM_EXPANDED_PRINCIPAL) {
    105    xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
    106    return false;
    107  }
    108 
    109  return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
    110 }
    111 
    112 static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
    113                              OriginAttributes& aAttrs, nsACString& aSpec,
    114                              nsACString& aOriginNoSuffix,
    115                              nsACString& aBaseDomain) {
    116  uint32_t suffixLength, specLength;
    117  if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
    118    return false;
    119  }
    120 
    121  nsAutoCString suffix;
    122  if (!suffix.SetLength(suffixLength, fallible)) {
    123    return false;
    124  }
    125 
    126  if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
    127    return false;
    128  }
    129 
    130  if (!aAttrs.PopulateFromSuffix(suffix)) {
    131    return false;
    132  }
    133 
    134  if (!aSpec.SetLength(specLength, fallible)) {
    135    return false;
    136  }
    137 
    138  if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
    139    return false;
    140  }
    141 
    142  uint32_t originNoSuffixLength, dummy;
    143  if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &dummy)) {
    144    return false;
    145  }
    146 
    147  MOZ_ASSERT(dummy == 0);
    148  if (dummy != 0) {
    149    return false;
    150  }
    151 
    152  if (!aOriginNoSuffix.SetLength(originNoSuffixLength, fallible)) {
    153    return false;
    154  }
    155 
    156  if (!JS_ReadBytes(aReader, aOriginNoSuffix.BeginWriting(),
    157                    originNoSuffixLength)) {
    158    return false;
    159  }
    160 
    161  uint32_t baseDomainIsVoid, baseDomainLength;
    162  if (!JS_ReadUint32Pair(aReader, &baseDomainIsVoid, &baseDomainLength)) {
    163    return false;
    164  }
    165 
    166  if (baseDomainIsVoid != 0 && baseDomainIsVoid != 1) {
    167    return false;
    168  }
    169 
    170  if (baseDomainIsVoid) {
    171    if (baseDomainLength != 0) {
    172      return false;
    173    }
    174 
    175    aBaseDomain.SetIsVoid(true);
    176    return true;
    177  }
    178 
    179  if (!aBaseDomain.SetLength(baseDomainLength, fallible)) {
    180    return false;
    181  }
    182 
    183  if (!JS_ReadBytes(aReader, aBaseDomain.BeginWriting(), baseDomainLength)) {
    184    return false;
    185  }
    186 
    187  return true;
    188 }
    189 
    190 static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
    191                              PrincipalInfo& aInfo) {
    192  if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
    193    aInfo = SystemPrincipalInfo();
    194  } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
    195    OriginAttributes attrs;
    196    nsAutoCString spec;
    197    nsAutoCString originNoSuffix;
    198    nsAutoCString baseDomain;
    199    if (!::ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix,
    200                             baseDomain)) {
    201      return false;
    202    }
    203    aInfo = NullPrincipalInfo(attrs, spec);
    204  } else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
    205    uint32_t length, unused;
    206    if (!JS_ReadUint32Pair(aReader, &length, &unused)) {
    207      return false;
    208    }
    209 
    210    ExpandedPrincipalInfo expanded;
    211 
    212    for (uint32_t i = 0; i < length; i++) {
    213      uint32_t tag;
    214      if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
    215        return false;
    216      }
    217 
    218      PrincipalInfo sub;
    219      if (!ReadPrincipalInfo(aReader, tag, sub)) {
    220        return false;
    221      }
    222      expanded.allowlist().AppendElement(sub);
    223    }
    224 
    225    aInfo = expanded;
    226  } else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
    227    OriginAttributes attrs;
    228    nsAutoCString spec;
    229    nsAutoCString originNoSuffix;
    230    nsAutoCString baseDomain;
    231    if (!::ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix,
    232                             baseDomain)) {
    233      return false;
    234    }
    235 
    236 #ifdef FUZZING
    237    if (originNoSuffix.IsEmpty()) {
    238      return false;
    239    }
    240 #endif
    241 
    242    MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty());
    243 
    244    // XXX: Do we care about mDomain for structured clone?
    245    aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(),
    246                                 baseDomain);
    247  } else {
    248 #ifdef FUZZING
    249    return false;
    250 #else
    251    MOZ_CRASH("unexpected principal structured clone tag");
    252 #endif
    253  }
    254 
    255  return true;
    256 }
    257 
    258 /* static */
    259 bool nsJSPrincipals::ReadPrincipalInfo(JSStructuredCloneReader* aReader,
    260                                       PrincipalInfo& aInfo) {
    261  uint32_t tag, unused;
    262  if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
    263    return false;
    264  }
    265  return ::ReadPrincipalInfo(aReader, tag, aInfo);
    266 }
    267 
    268 /* static */
    269 bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
    270                                            JSStructuredCloneReader* aReader,
    271                                            uint32_t aTag,
    272                                            JSPrincipals** aOutPrincipals) {
    273  MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
    274             aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
    275             aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
    276             aTag == SCTAG_DOM_EXPANDED_PRINCIPAL);
    277 
    278  PrincipalInfo info;
    279  if (!::ReadPrincipalInfo(aReader, aTag, info)) {
    280    return false;
    281  }
    282 
    283  auto principalOrErr = PrincipalInfoToPrincipal(info);
    284  if (NS_WARN_IF(principalOrErr.isErr())) {
    285    xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
    286    return false;
    287  }
    288 
    289  nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
    290 
    291  *aOutPrincipals = get(principal.forget().take());
    292  return true;
    293 }
    294 
    295 static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
    296                               const OriginAttributes& aAttrs,
    297                               const nsCString& aSpec,
    298                               const nsCString& aOriginNoSuffix,
    299                               const nsCString& aBaseDomain) {
    300  nsAutoCString suffix;
    301  aAttrs.CreateSuffix(suffix);
    302 
    303  if (!(JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
    304        JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
    305        JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) &&
    306        JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), 0) &&
    307        JS_WriteBytes(aWriter, aOriginNoSuffix.get(),
    308                      aOriginNoSuffix.Length()))) {
    309    return false;
    310  }
    311 
    312  if (aBaseDomain.IsVoid()) {
    313    return JS_WriteUint32Pair(aWriter, 1, 0);
    314  }
    315 
    316  return JS_WriteUint32Pair(aWriter, 0, aBaseDomain.Length()) &&
    317         JS_WriteBytes(aWriter, aBaseDomain.get(), aBaseDomain.Length());
    318 }
    319 
    320 /* static */
    321 bool nsJSPrincipals::WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
    322                                        const PrincipalInfo& aInfo) {
    323  if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
    324    const NullPrincipalInfo& nullInfo = aInfo;
    325    return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) &&
    326           ::WritePrincipalInfo(aWriter, nullInfo.attrs(), nullInfo.spec(),
    327                                ""_ns, ""_ns);
    328  }
    329  if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
    330    return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
    331  }
    332  if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) {
    333    const ExpandedPrincipalInfo& expanded = aInfo;
    334    if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) ||
    335        !JS_WriteUint32Pair(aWriter, expanded.allowlist().Length(), 0)) {
    336      return false;
    337    }
    338 
    339    for (uint32_t i = 0; i < expanded.allowlist().Length(); i++) {
    340      if (!WritePrincipalInfo(aWriter, expanded.allowlist()[i])) {
    341        return false;
    342      }
    343    }
    344    return true;
    345  }
    346 
    347  MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
    348  const ContentPrincipalInfo& cInfo = aInfo;
    349  return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
    350         ::WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(),
    351                              cInfo.originNoSuffix(), cInfo.baseDomain());
    352 }
    353 
    354 bool nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter) {
    355  PrincipalInfo info;
    356  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
    357    xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
    358    return false;
    359  }
    360 
    361  return WritePrincipalInfo(aWriter, info);
    362 }
    363 
    364 bool nsJSPrincipals::isSystemOrAddonPrincipal() {
    365  JS::AutoSuppressGCAnalysis suppress;
    366  return this->IsSystemPrincipal() ||
    367         this->GetIsAddonOrExpandedAddonPrincipal();
    368 }