tor-browser

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

WorkerLoadInfo.cpp (16809B)


      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 "WorkerLoadInfo.h"
      8 
      9 #include "WorkerPrivate.h"
     10 #include "mozilla/BasePrincipal.h"
     11 #include "mozilla/LoadContext.h"
     12 #include "mozilla/StorageAccess.h"
     13 #include "mozilla/StoragePrincipalHelper.h"
     14 #include "mozilla/dom/BrowserChild.h"
     15 #include "mozilla/dom/PolicyContainer.h"
     16 #include "mozilla/dom/ReferrerInfo.h"
     17 #include "mozilla/dom/nsCSPUtils.h"
     18 #include "mozilla/ipc/BackgroundUtils.h"
     19 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     20 #include "nsContentUtils.h"
     21 #include "nsIBrowserChild.h"
     22 #include "nsIContentSecurityPolicy.h"
     23 #include "nsICookieJarSettings.h"
     24 #include "nsINetworkInterceptController.h"
     25 #include "nsIProtocolHandler.h"
     26 #include "nsIReferrerInfo.h"
     27 #include "nsNetUtil.h"
     28 #include "nsScriptSecurityManager.h"
     29 
     30 namespace mozilla {
     31 
     32 using namespace ipc;
     33 
     34 namespace dom {
     35 
     36 namespace {
     37 
     38 class MainThreadReleaseRunnable final : public Runnable {
     39  nsTArray<nsCOMPtr<nsISupports>> mDoomed;
     40  nsCOMPtr<nsILoadGroup> mLoadGroupToCancel;
     41 
     42 public:
     43  MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>&& aDoomed,
     44                            nsCOMPtr<nsILoadGroup>&& aLoadGroupToCancel)
     45      : mozilla::Runnable("MainThreadReleaseRunnable"),
     46        mDoomed(std::move(aDoomed)),
     47        mLoadGroupToCancel(std::move(aLoadGroupToCancel)) {}
     48 
     49  NS_INLINE_DECL_REFCOUNTING_INHERITED(MainThreadReleaseRunnable, Runnable)
     50 
     51  NS_IMETHOD
     52  Run() override {
     53    if (mLoadGroupToCancel) {
     54      mLoadGroupToCancel->CancelWithReason(
     55          NS_BINDING_ABORTED, "WorkerLoadInfo::MainThreadReleaseRunnable"_ns);
     56      mLoadGroupToCancel = nullptr;
     57    }
     58 
     59    mDoomed.Clear();
     60    return NS_OK;
     61  }
     62 
     63 private:
     64  ~MainThreadReleaseRunnable() = default;
     65 };
     66 
     67 // Specialize this if there's some class that has multiple nsISupports bases.
     68 template <class T>
     69 struct ISupportsBaseInfo {
     70  using ISupportsBase = T;
     71 };
     72 
     73 template <template <class> class SmartPtr, class T>
     74 inline void SwapToISupportsArray(SmartPtr<T>& aSrc,
     75                                 nsTArray<nsCOMPtr<nsISupports>>& aDest) {
     76  nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
     77 
     78  T* raw = nullptr;
     79  aSrc.swap(raw);
     80 
     81  nsISupports* rawSupports =
     82      static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
     83  dest->swap(rawSupports);
     84 }
     85 
     86 }  // namespace
     87 
     88 WorkerLoadInfoData::WorkerLoadInfoData()
     89    : mLoadFlags(nsIRequest::LOAD_NORMAL),
     90      mWindowID(UINT64_MAX),
     91      mAssociatedBrowsingContextID(0),
     92      mReferrerInfo(new ReferrerInfo(nullptr)),
     93      mFromWindow(false),
     94      mXHRParamsAllowed(false),
     95      mWatchedByDevTools(false),
     96      mStorageAccess(StorageAccess::eDeny),
     97      mUseRegularPrincipal(false),
     98      mUsingStorageAccess(false),
     99      mServiceWorkersTestingInWindow(false),
    100      mShouldResistFingerprinting(false),
    101      mIsThirdPartyContext(true),
    102      mSecureContext(eNotSet) {}
    103 
    104 nsresult WorkerLoadInfo::SetPrincipalsAndCSPOnMainThread(
    105    nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
    106    nsILoadGroup* aLoadGroup, nsIContentSecurityPolicy* aCsp) {
    107  AssertIsOnMainThread();
    108  MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
    109 
    110  mPrincipal = aPrincipal;
    111  mPartitionedPrincipal = aPartitionedPrincipal;
    112 
    113  mCSP = aCsp;
    114 
    115  if (mCSP) {
    116    Result<UniquePtr<WorkerCSPContext>, nsresult> ctx =
    117        WorkerCSPContext::CreateFromCSP(aCsp);
    118    if (NS_WARN_IF(ctx.isErr())) {
    119      return ctx.unwrapErr();
    120    }
    121    mCSPContext = ctx.unwrap();
    122  }
    123 
    124  mLoadGroup = aLoadGroup;
    125 
    126  mPrincipalInfo = MakeUnique<PrincipalInfo>();
    127  mPartitionedPrincipalInfo = MakeUnique<PrincipalInfo>();
    128  StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(
    129      aLoadGroup, mOriginAttributes);
    130 
    131  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo.get());
    132  NS_ENSURE_SUCCESS(rv, rv);
    133 
    134  if (aPrincipal->Equals(aPartitionedPrincipal)) {
    135    *mPartitionedPrincipalInfo = *mPrincipalInfo;
    136  } else {
    137    mPartitionedPrincipalInfo = MakeUnique<PrincipalInfo>();
    138    rv = PrincipalToPrincipalInfo(aPartitionedPrincipal,
    139                                  mPartitionedPrincipalInfo.get());
    140    NS_ENSURE_SUCCESS(rv, rv);
    141  }
    142  return NS_OK;
    143 }
    144 
    145 nsresult WorkerLoadInfo::GetPrincipalsAndLoadGroupFromChannel(
    146    nsIChannel* aChannel, nsIPrincipal** aPrincipalOut,
    147    nsIPrincipal** aPartitionedPrincipalOut, nsILoadGroup** aLoadGroupOut) {
    148  AssertIsOnMainThread();
    149  MOZ_DIAGNOSTIC_ASSERT(aChannel);
    150  MOZ_DIAGNOSTIC_ASSERT(aPrincipalOut);
    151  MOZ_DIAGNOSTIC_ASSERT(aPartitionedPrincipalOut);
    152  MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut);
    153 
    154  // Initial triggering principal should be set
    155  NS_ENSURE_TRUE(mLoadingPrincipal, NS_ERROR_DOM_INVALID_STATE_ERR);
    156 
    157  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
    158  MOZ_DIAGNOSTIC_ASSERT(ssm);
    159 
    160  nsCOMPtr<nsIPrincipal> channelPrincipal;
    161  nsCOMPtr<nsIPrincipal> channelPartitionedPrincipal;
    162  nsresult rv = ssm->GetChannelResultPrincipals(
    163      aChannel, getter_AddRefs(channelPrincipal),
    164      getter_AddRefs(channelPartitionedPrincipal));
    165  NS_ENSURE_SUCCESS(rv, rv);
    166 
    167  // Every time we call GetChannelResultPrincipal() it will return a different
    168  // null principal for a data URL.  We don't want to change the worker's
    169  // principal again, though.  Instead just keep the original null principal we
    170  // first got from the channel.
    171  //
    172  // Note, we don't do this by setting principalToInherit on the channel's
    173  // load info because we don't yet have the first null principal when we
    174  // create the channel.
    175  if (mPrincipal && mPrincipal->GetIsNullPrincipal() &&
    176      channelPrincipal->GetIsNullPrincipal()) {
    177    channelPrincipal = mPrincipal;
    178    channelPartitionedPrincipal = mPrincipal;
    179  }
    180 
    181  nsCOMPtr<nsILoadGroup> channelLoadGroup;
    182  rv = aChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
    183  NS_ENSURE_SUCCESS(rv, rv);
    184  MOZ_ASSERT(channelLoadGroup);
    185 
    186  // If the loading principal is the system principal then the channel
    187  // principal must also be the system principal (we do not allow chrome
    188  // code to create workers with non-chrome scripts, and if we ever decide
    189  // to change this we need to make sure we don't always set
    190  // mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
    191  // this channel principal must be same origin with the load principal (we
    192  // check again here in case redirects changed the location of the script).
    193  if (mLoadingPrincipal->IsSystemPrincipal()) {
    194    if (!channelPrincipal->IsSystemPrincipal()) {
    195      nsCOMPtr<nsIURI> finalURI;
    196      rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
    197      NS_ENSURE_SUCCESS(rv, rv);
    198 
    199      // See if this is a resource URI. Since JSMs usually come from
    200      // resource:// URIs we're currently considering all URIs with the
    201      // URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
    202      bool isResource;
    203      rv = NS_URIChainHasFlags(finalURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
    204                               &isResource);
    205      NS_ENSURE_SUCCESS(rv, rv);
    206 
    207      if (isResource) {
    208        // Assign the system principal to the resource:// worker only if it
    209        // was loaded from code using the system principal.
    210        channelPrincipal = mLoadingPrincipal;
    211        channelPartitionedPrincipal = mLoadingPrincipal;
    212      } else {
    213        return NS_ERROR_DOM_BAD_URI;
    214      }
    215    }
    216  }
    217 
    218  // The principal can change, but it should still match the original
    219  // load group's browser element flag.
    220  MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup, channelPrincipal));
    221 
    222  channelPrincipal.forget(aPrincipalOut);
    223  channelPartitionedPrincipal.forget(aPartitionedPrincipalOut);
    224  channelLoadGroup.forget(aLoadGroupOut);
    225 
    226  return NS_OK;
    227 }
    228 
    229 nsresult WorkerLoadInfo::SetPrincipalsAndCSPFromChannel(nsIChannel* aChannel) {
    230  AssertIsOnMainThread();
    231 
    232  nsCOMPtr<nsIPrincipal> principal;
    233  nsCOMPtr<nsIPrincipal> partitionedPrincipal;
    234  nsCOMPtr<nsILoadGroup> loadGroup;
    235  nsresult rv = GetPrincipalsAndLoadGroupFromChannel(
    236      aChannel, getter_AddRefs(principal), getter_AddRefs(partitionedPrincipal),
    237      getter_AddRefs(loadGroup));
    238  NS_ENSURE_SUCCESS(rv, rv);
    239 
    240  // Workers themselves can have their own CSP - Workers of an opaque origin
    241  // however inherit the CSP of the document that spawned the worker.
    242  nsCOMPtr<nsIContentSecurityPolicy> csp;
    243  if (CSP_ShouldResponseInheritCSP(aChannel)) {
    244    nsCOMPtr<nsILoadInfo> loadinfo = aChannel->LoadInfo();
    245    nsCOMPtr<nsIPolicyContainer> policyContainer =
    246        loadinfo->GetPolicyContainer();
    247    csp = PolicyContainer::GetCSP(policyContainer);
    248  }
    249  return SetPrincipalsAndCSPOnMainThread(principal, partitionedPrincipal,
    250                                         loadGroup, csp);
    251 }
    252 
    253 bool WorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel* aChannel) {
    254  AssertIsOnMainThread();
    255 
    256  nsCOMPtr<nsIPrincipal> principal;
    257  nsCOMPtr<nsIPrincipal> partitionedPrincipal;
    258  nsCOMPtr<nsILoadGroup> loadGroup;
    259  nsresult rv = GetPrincipalsAndLoadGroupFromChannel(
    260      aChannel, getter_AddRefs(principal), getter_AddRefs(partitionedPrincipal),
    261      getter_AddRefs(loadGroup));
    262  NS_ENSURE_SUCCESS(rv, false);
    263 
    264  // Verify that the channel is still a null principal.  We don't care
    265  // if these are the exact same null principal object, though.  From
    266  // the worker's perspective its the same effect.
    267  if (principal->GetIsNullPrincipal() && mPrincipal->GetIsNullPrincipal()) {
    268    return true;
    269  }
    270 
    271  // Otherwise we require exact equality.  Redirects can happen, but they
    272  // are not allowed to change our principal.
    273  if (principal->Equals(mPrincipal)) {
    274    return true;
    275  }
    276 
    277  return false;
    278 }
    279 
    280 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    281 bool WorkerLoadInfo::PrincipalIsValid() const {
    282  return mPrincipal && mPrincipalInfo &&
    283         mPrincipalInfo->type() != PrincipalInfo::T__None &&
    284         mPrincipalInfo->type() <= PrincipalInfo::T__Last &&
    285         mPartitionedPrincipal && mPartitionedPrincipalInfo &&
    286         mPartitionedPrincipalInfo->type() != PrincipalInfo::T__None &&
    287         mPartitionedPrincipalInfo->type() <= PrincipalInfo::T__Last;
    288 }
    289 
    290 bool WorkerLoadInfo::PrincipalURIMatchesScriptURL() {
    291  AssertIsOnMainThread();
    292 
    293  nsAutoCString scheme;
    294  nsresult rv = mBaseURI->GetScheme(scheme);
    295  NS_ENSURE_SUCCESS(rv, false);
    296 
    297  // A system principal must either be a blob URL or a resource JSM.
    298  if (mPrincipal->IsSystemPrincipal()) {
    299    if (scheme == "blob"_ns) {
    300      return true;
    301    }
    302 
    303    bool isResource = false;
    304    nsresult rv = NS_URIChainHasFlags(
    305        mBaseURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isResource);
    306    NS_ENSURE_SUCCESS(rv, false);
    307 
    308    return isResource;
    309  }
    310 
    311  // A null principal can occur for a data URL worker script or a blob URL
    312  // worker script from a sandboxed iframe.
    313  if (mPrincipal->GetIsNullPrincipal()) {
    314    return scheme == "data"_ns || scheme == "blob"_ns;
    315  }
    316 
    317  // The principal for a blob: URL worker script does not have a matching URL.
    318  // This is likely a bug in our referer setting logic, but exempt it for now.
    319  // This is another reason we should fix bug 1340694 so that referer does not
    320  // depend on the principal URI.
    321  if (scheme == "blob"_ns) {
    322    return true;
    323  }
    324 
    325  if (mPrincipal->IsSameOrigin(mBaseURI)) {
    326    return true;
    327  }
    328 
    329  return false;
    330 }
    331 #endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED
    332 
    333 bool WorkerLoadInfo::ProxyReleaseMainThreadObjects(
    334    WorkerPrivate* aWorkerPrivate) {
    335  nsCOMPtr<nsILoadGroup> nullLoadGroup;
    336  return ProxyReleaseMainThreadObjects(aWorkerPrivate,
    337                                       std::move(nullLoadGroup));
    338 }
    339 
    340 bool WorkerLoadInfo::ProxyReleaseMainThreadObjects(
    341    WorkerPrivate* aWorkerPrivate,
    342    nsCOMPtr<nsILoadGroup>&& aLoadGroupToCancel) {
    343  static const uint32_t kDoomedCount = 11;
    344  nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
    345 
    346  SwapToISupportsArray(mWindow, doomed);
    347  SwapToISupportsArray(mScriptContext, doomed);
    348  SwapToISupportsArray(mBaseURI, doomed);
    349  SwapToISupportsArray(mResolvedScriptURI, doomed);
    350  SwapToISupportsArray(mPrincipal, doomed);
    351  SwapToISupportsArray(mPartitionedPrincipal, doomed);
    352  SwapToISupportsArray(mLoadingPrincipal, doomed);
    353  SwapToISupportsArray(mChannel, doomed);
    354  SwapToISupportsArray(mCSP, doomed);
    355  SwapToISupportsArray(mLoadGroup, doomed);
    356  SwapToISupportsArray(mInterfaceRequestor, doomed);
    357  // Before adding anything here update kDoomedCount above!
    358 
    359  MOZ_ASSERT(doomed.Length() == kDoomedCount);
    360 
    361  RefPtr<MainThreadReleaseRunnable> runnable = new MainThreadReleaseRunnable(
    362      std::move(doomed), std::move(aLoadGroupToCancel));
    363  return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
    364 }
    365 
    366 WorkerLoadInfo::InterfaceRequestor::InterfaceRequestor(
    367    nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup) {
    368  MOZ_ASSERT(NS_IsMainThread());
    369  MOZ_ASSERT(aPrincipal);
    370 
    371  // Look for an existing LoadContext.  This is optional and it's ok if
    372  // we don't find one.
    373  nsCOMPtr<nsILoadContext> baseContext;
    374  if (aLoadGroup) {
    375    nsCOMPtr<nsIInterfaceRequestor> callbacks;
    376    aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
    377    if (callbacks) {
    378      callbacks->GetInterface(NS_GET_IID(nsILoadContext),
    379                              getter_AddRefs(baseContext));
    380    }
    381    mOuterRequestor = callbacks;
    382  }
    383 
    384  mLoadContext = new LoadContext(aPrincipal, baseContext);
    385 }
    386 
    387 void WorkerLoadInfo::InterfaceRequestor::MaybeAddBrowserChild(
    388    nsILoadGroup* aLoadGroup) {
    389  MOZ_ASSERT(NS_IsMainThread());
    390 
    391  if (!aLoadGroup) {
    392    return;
    393  }
    394 
    395  nsCOMPtr<nsIInterfaceRequestor> callbacks;
    396  aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
    397  if (!callbacks) {
    398    return;
    399  }
    400 
    401  nsCOMPtr<nsIBrowserChild> browserChild;
    402  callbacks->GetInterface(NS_GET_IID(nsIBrowserChild),
    403                          getter_AddRefs(browserChild));
    404  if (!browserChild) {
    405    return;
    406  }
    407 
    408  // Use weak references to the tab child.  Holding a strong reference will
    409  // not prevent an ActorDestroy() from being called on the BrowserChild.
    410  // Therefore, we should let the BrowserChild destroy itself as soon as
    411  // possible.
    412  mBrowserChildList.AppendElement(do_GetWeakReference(browserChild));
    413 }
    414 
    415 NS_IMETHODIMP
    416 WorkerLoadInfo::InterfaceRequestor::GetInterface(const nsIID& aIID,
    417                                                 void** aSink) {
    418  MOZ_ASSERT(NS_IsMainThread());
    419  MOZ_ASSERT(mLoadContext);
    420 
    421  if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
    422    nsCOMPtr<nsILoadContext> ref = mLoadContext;
    423    ref.forget(aSink);
    424    return NS_OK;
    425  }
    426 
    427  // If we still have an active nsIBrowserChild, then return it.  Its possible,
    428  // though, that all of the BrowserChild objects have been destroyed.  In that
    429  // case we return NS_NOINTERFACE.
    430  if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
    431    nsCOMPtr<nsIBrowserChild> browserChild = GetAnyLiveBrowserChild();
    432    if (!browserChild) {
    433      return NS_NOINTERFACE;
    434    }
    435    browserChild.forget(aSink);
    436    return NS_OK;
    437  }
    438 
    439  if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
    440      mOuterRequestor) {
    441    // If asked for the network intercept controller, ask the outer requestor,
    442    // which could be the docshell.
    443    return mOuterRequestor->GetInterface(aIID, aSink);
    444  }
    445 
    446  return NS_NOINTERFACE;
    447 }
    448 
    449 already_AddRefed<nsIBrowserChild>
    450 WorkerLoadInfo::InterfaceRequestor::GetAnyLiveBrowserChild() {
    451  MOZ_ASSERT(NS_IsMainThread());
    452 
    453  // Search our list of known BrowserChild objects for one that still exists.
    454  while (!mBrowserChildList.IsEmpty()) {
    455    nsCOMPtr<nsIBrowserChild> browserChild =
    456        do_QueryReferent(mBrowserChildList.LastElement());
    457 
    458    // Does this tab child still exist?  If so, return it.  We are done.  If the
    459    // PBrowser actor is no longer useful, don't bother returning this tab.
    460    if (browserChild &&
    461        !static_cast<BrowserChild*>(browserChild.get())->IsDestroyed()) {
    462      return browserChild.forget();
    463    }
    464 
    465    // Otherwise remove the stale weak reference and check the next one
    466    mBrowserChildList.RemoveLastElement();
    467  }
    468 
    469  return nullptr;
    470 }
    471 
    472 NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)
    473 NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)
    474 NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor,
    475                        nsIInterfaceRequestor)
    476 
    477 WorkerLoadInfo::WorkerLoadInfo() { MOZ_COUNT_CTOR(WorkerLoadInfo); }
    478 
    479 WorkerLoadInfo::WorkerLoadInfo(WorkerLoadInfo&& aOther) noexcept
    480    : WorkerLoadInfoData(std::move(aOther)) {
    481  MOZ_COUNT_CTOR(WorkerLoadInfo);
    482 }
    483 
    484 WorkerLoadInfo::~WorkerLoadInfo() { MOZ_COUNT_DTOR(WorkerLoadInfo); }
    485 
    486 }  // namespace dom
    487 }  // namespace mozilla