tor-browser

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

AccessCheck.cpp (5784B)


      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 #include "AccessCheck.h"
      8 
      9 #include "nsJSPrincipals.h"
     10 
     11 #include "XPCWrapper.h"
     12 #include "XrayWrapper.h"
     13 #include "FilteringWrapper.h"
     14 
     15 #include "jsfriendapi.h"
     16 #include "js/Object.h"  // JS::GetClass, JS::GetCompartment
     17 #include "mozilla/BasePrincipal.h"
     18 #include "mozilla/ErrorResult.h"
     19 #include "mozilla/dom/BindingUtils.h"
     20 #include "mozilla/dom/LocationBinding.h"
     21 #include "mozilla/dom/WindowBinding.h"
     22 #include "nsJSUtils.h"
     23 #include "xpcprivate.h"
     24 
     25 using namespace mozilla;
     26 using namespace JS;
     27 using namespace js;
     28 
     29 namespace xpc {
     30 
     31 BasePrincipal* GetRealmPrincipal(JS::Realm* realm) {
     32  return BasePrincipal::Cast(
     33      nsJSPrincipals::get(JS::GetRealmPrincipals(realm)));
     34 }
     35 
     36 nsIPrincipal* GetObjectPrincipal(JSObject* obj) {
     37  return GetRealmPrincipal(js::GetNonCCWObjectRealm(obj));
     38 }
     39 
     40 bool AccessCheck::subsumes(JSObject* a, JSObject* b) {
     41  return CompartmentOriginInfo::Subsumes(JS::GetCompartment(a),
     42                                         JS::GetCompartment(b));
     43 }
     44 
     45 // Same as above, but considering document.domain.
     46 bool AccessCheck::subsumesConsideringDomain(JS::Realm* a, JS::Realm* b) {
     47  MOZ_ASSERT(OriginAttributes::IsRestrictOpenerAccessForFPI());
     48  BasePrincipal* aprin = GetRealmPrincipal(a);
     49  BasePrincipal* bprin = GetRealmPrincipal(b);
     50  return aprin->FastSubsumesConsideringDomain(bprin);
     51 }
     52 
     53 bool AccessCheck::subsumesConsideringDomainIgnoringFPD(JS::Realm* a,
     54                                                       JS::Realm* b) {
     55  MOZ_ASSERT(!OriginAttributes::IsRestrictOpenerAccessForFPI());
     56  BasePrincipal* aprin = GetRealmPrincipal(a);
     57  BasePrincipal* bprin = GetRealmPrincipal(b);
     58  return aprin->FastSubsumesConsideringDomainIgnoringFPD(bprin);
     59 }
     60 
     61 // Does the compartment of the wrapper subsumes the compartment of the wrappee?
     62 bool AccessCheck::wrapperSubsumes(JSObject* wrapper) {
     63  MOZ_ASSERT(js::IsWrapper(wrapper));
     64  JSObject* wrapped = js::UncheckedUnwrap(wrapper);
     65  return CompartmentOriginInfo::Subsumes(JS::GetCompartment(wrapper),
     66                                         JS::GetCompartment(wrapped));
     67 }
     68 
     69 bool AccessCheck::isChrome(JS::Compartment* compartment) {
     70  return js::IsSystemCompartment(compartment);
     71 }
     72 
     73 bool AccessCheck::isChrome(JS::Realm* realm) {
     74  return isChrome(JS::GetCompartmentForRealm(realm));
     75 }
     76 
     77 bool AccessCheck::isChrome(JSObject* obj) {
     78  return isChrome(JS::GetCompartment(obj));
     79 }
     80 
     81 bool IsCrossOriginAccessibleObject(JSObject* obj) {
     82  obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
     83  const JSClass* clasp = JS::GetClass(obj);
     84 
     85  return (clasp->name[0] == 'L' && !strcmp(clasp->name, "Location")) ||
     86         (clasp->name[0] == 'W' && !strcmp(clasp->name, "Window"));
     87 }
     88 
     89 bool AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper,
     90                                            HandleValue v) {
     91  // Primitives are fine.
     92  if (!v.isObject()) {
     93    return true;
     94  }
     95  RootedObject obj(cx, &v.toObject());
     96 
     97  // Non-wrappers are fine.
     98  if (!js::IsWrapper(obj)) {
     99    return true;
    100  }
    101 
    102  // Same-origin wrappers are fine.
    103  if (AccessCheck::wrapperSubsumes(obj)) {
    104    return true;
    105  }
    106 
    107  // Badness.
    108  JS_ReportErrorASCII(cx,
    109                      "Permission denied to pass object to privileged code");
    110  return false;
    111 }
    112 
    113 bool AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper,
    114                                            const CallArgs& args) {
    115  if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv())) {
    116    return false;
    117  }
    118  for (size_t i = 0; i < args.length(); ++i) {
    119    if (!checkPassToPrivilegedCode(cx, wrapper, args[i])) {
    120      return false;
    121    }
    122  }
    123  return true;
    124 }
    125 
    126 void AccessCheck::reportCrossOriginDenial(JSContext* cx, JS::HandleId id,
    127                                          const nsACString& accessType) {
    128  // This function exists because we want to report DOM SecurityErrors, not JS
    129  // Errors, when denying access on cross-origin DOM objects.  It's
    130  // conceptually pretty similar to
    131  // AutoEnterPolicy::reportErrorIfExceptionIsNotPending.
    132  if (JS_IsExceptionPending(cx)) {
    133    return;
    134  }
    135 
    136  nsAutoCString message;
    137  if (id.isVoid()) {
    138    message = "Permission denied to access object"_ns;
    139  } else {
    140    // We want to use JS_ValueToSource here, because that most closely
    141    // matches what AutoEnterPolicy::reportErrorIfExceptionIsNotPending
    142    // does.
    143    JS::RootedValue idVal(cx, js::IdToValue(id));
    144    nsAutoJSString propName;
    145    JS::RootedString idStr(cx, JS_ValueToSource(cx, idVal));
    146    if (!idStr || !propName.init(cx, idStr)) {
    147      return;
    148    }
    149    message = "Permission denied to "_ns + accessType + " property "_ns +
    150              NS_ConvertUTF16toUTF8(propName) + " on cross-origin object"_ns;
    151  }
    152  ErrorResult rv;
    153  rv.ThrowSecurityError(message);
    154  MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(cx));
    155 }
    156 
    157 bool OpaqueWithSilentFailing::deny(JSContext* cx, js::Wrapper::Action act,
    158                                   HandleId id, bool mayThrow) {
    159  // Fail silently for GET, ENUMERATE, and GET_PROPERTY_DESCRIPTOR.
    160  if (act == js::Wrapper::GET || act == js::Wrapper::ENUMERATE ||
    161      act == js::Wrapper::GET_PROPERTY_DESCRIPTOR) {
    162    // Note that ReportWrapperDenial doesn't do any _exception_ reporting,
    163    // so we want to do this regardless of the value of mayThrow.
    164    return ReportWrapperDenial(cx, id, WrapperDenialForCOW,
    165                               "Access to privileged JS object not permitted");
    166  }
    167 
    168  return false;
    169 }
    170 
    171 }  // namespace xpc