tor-browser

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

FeaturePolicy.cpp (11759B)


      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 "FeaturePolicy.h"
      8 
      9 #include "mozilla/BasePrincipal.h"
     10 #include "mozilla/StaticPrefs_dom.h"
     11 #include "mozilla/dom/BrowsingContext.h"
     12 #include "mozilla/dom/Feature.h"
     13 #include "mozilla/dom/FeaturePolicyBinding.h"
     14 #include "mozilla/dom/FeaturePolicyParser.h"
     15 #include "mozilla/dom/FeaturePolicyUtils.h"
     16 #include "mozilla/dom/HTMLIFrameElement.h"
     17 #include "nsContentUtils.h"
     18 #include "nsNetUtil.h"
     19 
     20 namespace mozilla::dom {
     21 
     22 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FeaturePolicy)
     23 NS_IMPL_CYCLE_COLLECTING_ADDREF(FeaturePolicy)
     24 NS_IMPL_CYCLE_COLLECTING_RELEASE(FeaturePolicy)
     25 
     26 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FeaturePolicy)
     27  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     28  NS_INTERFACE_MAP_ENTRY(nsISupports)
     29 NS_INTERFACE_MAP_END
     30 
     31 FeaturePolicy::FeaturePolicy(nsINode* aNode) : mParentNode(aNode) {}
     32 
     33 void FeaturePolicy::InheritPolicy(FeaturePolicy* aParentPolicy) {
     34  MOZ_ASSERT(aParentPolicy);
     35 
     36  mInheritedDeniedFeatureNames.Clear();
     37 
     38  RefPtr<FeaturePolicy> dest = this;
     39  RefPtr<FeaturePolicy> src = aParentPolicy;
     40 
     41  // Inherit origins which explicitly declared policy in chain
     42  for (const Feature& featureInChain :
     43       aParentPolicy->mDeclaredFeaturesInAncestorChain) {
     44    dest->AppendToDeclaredAllowInAncestorChain(featureInChain);
     45  }
     46 
     47  FeaturePolicyUtils::ForEachFeature([dest, src](const char* aFeatureName) {
     48    nsString featureName;
     49    featureName.AppendASCII(aFeatureName);
     50    // Store unsafe allows all (allow=*)
     51    if (src->HasFeatureUnsafeAllowsAll(featureName)) {
     52      dest->mParentAllowedAllFeatures.AppendElement(featureName);
     53    }
     54 
     55    // If the destination has a declared feature (via the HTTP header or 'allow'
     56    // attribute) we allow the feature if the destination allows it and the
     57    // parent allows its origin or the destinations' one.
     58    if (dest->HasDeclaredFeature(featureName) &&
     59        dest->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
     60      if (!src->AllowsFeatureInternal(featureName, src->mDefaultOrigin) &&
     61          !src->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
     62        dest->SetInheritedDeniedFeature(featureName);
     63      }
     64      return;
     65    }
     66 
     67    // If there was not a declared feature, we allow the feature if the parent
     68    // FeaturePolicy allows the current origin.
     69    if (!src->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
     70      dest->SetInheritedDeniedFeature(featureName);
     71    }
     72  });
     73 }
     74 
     75 void FeaturePolicy::InheritPolicy(
     76    const FeaturePolicyInfo& aContainerFeaturePolicyInfo) {
     77  // We create a temporary FeaturePolicy from the FeaturePolicyInfo to be able
     78  // to re-use the inheriting functionality from FeaturePolicy.
     79  RefPtr<dom::FeaturePolicy> featurePolicy = new dom::FeaturePolicy(nullptr);
     80  featurePolicy->SetDefaultOrigin(aContainerFeaturePolicyInfo.mDefaultOrigin);
     81  featurePolicy->SetInheritedDeniedFeatureNames(
     82      aContainerFeaturePolicyInfo.mInheritedDeniedFeatureNames);
     83 
     84  const auto& declaredString = aContainerFeaturePolicyInfo.mDeclaredString;
     85  if (aContainerFeaturePolicyInfo.mSelfOrigin && !declaredString.IsEmpty()) {
     86    featurePolicy->SetDeclaredPolicy(nullptr, declaredString,
     87                                     aContainerFeaturePolicyInfo.mSelfOrigin,
     88                                     aContainerFeaturePolicyInfo.mSrcOrigin);
     89  }
     90 
     91  for (const auto& featureName :
     92       aContainerFeaturePolicyInfo.mAttributeEnabledFeatureNames) {
     93    featurePolicy->MaybeSetAllowedPolicy(featureName);
     94  }
     95 
     96  InheritPolicy(featurePolicy);
     97 }
     98 
     99 void FeaturePolicy::SetInheritedDeniedFeature(const nsAString& aFeatureName) {
    100  MOZ_ASSERT(!HasInheritedDeniedFeature(aFeatureName));
    101  mInheritedDeniedFeatureNames.AppendElement(aFeatureName);
    102 }
    103 
    104 bool FeaturePolicy::HasInheritedDeniedFeature(
    105    const nsAString& aFeatureName) const {
    106  return mInheritedDeniedFeatureNames.Contains(aFeatureName);
    107 }
    108 
    109 bool FeaturePolicy::HasDeclaredFeature(const nsAString& aFeatureName) const {
    110  for (const Feature& feature : mFeatures) {
    111    if (feature.Name().Equals(aFeatureName)) {
    112      return true;
    113    }
    114  }
    115 
    116  return false;
    117 }
    118 
    119 bool FeaturePolicy::HasFeatureUnsafeAllowsAll(
    120    const nsAString& aFeatureName) const {
    121  for (const Feature& feature : mFeatures) {
    122    if (feature.AllowsAll() && feature.Name().Equals(aFeatureName)) {
    123      return true;
    124    }
    125  }
    126 
    127  // We should look into parent too (for example, document of iframe which
    128  // allows all, would be unsafe)
    129  return mParentAllowedAllFeatures.Contains(aFeatureName);
    130 }
    131 
    132 void FeaturePolicy::AppendToDeclaredAllowInAncestorChain(
    133    const Feature& aFeature) {
    134  for (Feature& featureInChain : mDeclaredFeaturesInAncestorChain) {
    135    if (featureInChain.Name().Equals(aFeature.Name())) {
    136      MOZ_ASSERT(featureInChain.HasAllowList());
    137 
    138      nsTArray<nsCOMPtr<nsIPrincipal>> list;
    139      aFeature.GetAllowList(list);
    140 
    141      for (nsIPrincipal* principal : list) {
    142        featureInChain.AppendToAllowList(principal);
    143      }
    144      continue;
    145    }
    146  }
    147 
    148  mDeclaredFeaturesInAncestorChain.AppendElement(aFeature);
    149 }
    150 
    151 bool FeaturePolicy::IsSameOriginAsSrc(nsIPrincipal* aPrincipal) const {
    152  MOZ_ASSERT(aPrincipal);
    153 
    154  if (!mSrcOrigin) {
    155    return false;
    156  }
    157 
    158  return BasePrincipal::Cast(mSrcOrigin)
    159      ->Subsumes(aPrincipal, BasePrincipal::ConsiderDocumentDomain);
    160 }
    161 
    162 void FeaturePolicy::SetDeclaredPolicy(Document* aDocument,
    163                                      const nsAString& aPolicyString,
    164                                      nsIPrincipal* aSelfOrigin,
    165                                      nsIPrincipal* aSrcOrigin) {
    166  ResetDeclaredPolicy();
    167 
    168  mDeclaredString = aPolicyString;
    169  mSelfOrigin = aSelfOrigin;
    170  mSrcOrigin = aSrcOrigin;
    171 
    172  (void)NS_WARN_IF(!FeaturePolicyParser::ParseString(
    173      aPolicyString, aDocument, aSelfOrigin, aSrcOrigin, mFeatures));
    174 
    175  // Only store explicitly declared allowlist
    176  for (const Feature& feature : mFeatures) {
    177    if (feature.HasAllowList()) {
    178      AppendToDeclaredAllowInAncestorChain(feature);
    179    }
    180  }
    181 }
    182 
    183 void FeaturePolicy::ResetDeclaredPolicy() {
    184  mFeatures.Clear();
    185  mDeclaredString.Truncate();
    186  mSelfOrigin = nullptr;
    187  mSrcOrigin = nullptr;
    188  mDeclaredFeaturesInAncestorChain.Clear();
    189  mAttributeEnabledFeatureNames.Clear();
    190 }
    191 
    192 JSObject* FeaturePolicy::WrapObject(JSContext* aCx,
    193                                    JS::Handle<JSObject*> aGivenProto) {
    194  return FeaturePolicy_Binding::Wrap(aCx, this, aGivenProto);
    195 }
    196 
    197 bool FeaturePolicy::AllowsFeature(const nsAString& aFeatureName,
    198                                  const Optional<nsAString>& aOrigin) const {
    199  nsCOMPtr<nsIPrincipal> origin;
    200  if (aOrigin.WasPassed()) {
    201    nsCOMPtr<nsIURI> uri;
    202    nsresult rv = NS_NewURI(getter_AddRefs(uri), aOrigin.Value());
    203    if (NS_FAILED(rv)) {
    204      return false;
    205    }
    206    origin = BasePrincipal::CreateContentPrincipal(
    207        uri, BasePrincipal::Cast(mDefaultOrigin)->OriginAttributesRef());
    208  } else {
    209    origin = mDefaultOrigin;
    210  }
    211 
    212  if (NS_WARN_IF(!origin)) {
    213    return false;
    214  }
    215 
    216  return AllowsFeatureInternal(aFeatureName, origin);
    217 }
    218 
    219 bool FeaturePolicy::AllowsFeatureExplicitlyInAncestorChain(
    220    const nsAString& aFeatureName, nsIPrincipal* aOrigin) const {
    221  MOZ_ASSERT(aOrigin);
    222 
    223  for (const Feature& feature : mDeclaredFeaturesInAncestorChain) {
    224    if (feature.Name().Equals(aFeatureName)) {
    225      return feature.AllowListContains(aOrigin);
    226    }
    227  }
    228 
    229  return false;
    230 }
    231 
    232 bool FeaturePolicy::AllowsFeatureInternal(const nsAString& aFeatureName,
    233                                          nsIPrincipal* aOrigin) const {
    234  MOZ_ASSERT(aOrigin);
    235 
    236  // Let's see if have to disable this feature because inherited policy.
    237  if (HasInheritedDeniedFeature(aFeatureName)) {
    238    return false;
    239  }
    240 
    241  for (const Feature& feature : mFeatures) {
    242    if (feature.Name().Equals(aFeatureName)) {
    243      return feature.Allows(aOrigin);
    244    }
    245  }
    246 
    247  switch (FeaturePolicyUtils::DefaultAllowListFeature(aFeatureName)) {
    248    case FeaturePolicyUtils::FeaturePolicyValue::eAll:
    249      return true;
    250 
    251    case FeaturePolicyUtils::FeaturePolicyValue::eSelf:
    252      return BasePrincipal::Cast(mDefaultOrigin)
    253          ->Subsumes(aOrigin, BasePrincipal::ConsiderDocumentDomain);
    254 
    255    case FeaturePolicyUtils::FeaturePolicyValue::eNone:
    256      return false;
    257 
    258    default:
    259      MOZ_CRASH("Unknown default value");
    260  }
    261 
    262  return false;
    263 }
    264 
    265 void FeaturePolicy::Features(nsTArray<nsString>& aFeatures) {
    266  RefPtr<FeaturePolicy> self = this;
    267  FeaturePolicyUtils::ForEachFeature(
    268      [self, &aFeatures](const char* aFeatureName) {
    269        nsString featureName;
    270        featureName.AppendASCII(aFeatureName);
    271        aFeatures.AppendElement(featureName);
    272      });
    273 }
    274 
    275 void FeaturePolicy::AllowedFeatures(nsTArray<nsString>& aAllowedFeatures) {
    276  RefPtr<FeaturePolicy> self = this;
    277  FeaturePolicyUtils::ForEachFeature(
    278      [self, &aAllowedFeatures](const char* aFeatureName) {
    279        nsString featureName;
    280        featureName.AppendASCII(aFeatureName);
    281 
    282        if (self->AllowsFeatureInternal(featureName, self->mDefaultOrigin)) {
    283          aAllowedFeatures.AppendElement(featureName);
    284        }
    285      });
    286 }
    287 
    288 void FeaturePolicy::GetAllowlistForFeature(const nsAString& aFeatureName,
    289                                           nsTArray<nsString>& aList) const {
    290  if (!AllowsFeatureInternal(aFeatureName, mDefaultOrigin)) {
    291    return;
    292  }
    293 
    294  for (const Feature& feature : mFeatures) {
    295    if (feature.Name().Equals(aFeatureName)) {
    296      if (feature.AllowsAll()) {
    297        aList.AppendElement(u"*"_ns);
    298        return;
    299      }
    300 
    301      nsTArray<nsCOMPtr<nsIPrincipal>> list;
    302      feature.GetAllowList(list);
    303 
    304      for (nsIPrincipal* principal : list) {
    305        nsAutoCString originNoSuffix;
    306        nsresult rv = principal->GetOriginNoSuffix(originNoSuffix);
    307        if (NS_WARN_IF(NS_FAILED(rv))) {
    308          return;
    309        }
    310 
    311        aList.AppendElement(NS_ConvertUTF8toUTF16(originNoSuffix));
    312      }
    313      return;
    314    }
    315  }
    316 
    317  switch (FeaturePolicyUtils::DefaultAllowListFeature(aFeatureName)) {
    318    case FeaturePolicyUtils::FeaturePolicyValue::eAll:
    319      aList.AppendElement(u"*"_ns);
    320      return;
    321 
    322    case FeaturePolicyUtils::FeaturePolicyValue::eSelf: {
    323      nsAutoCString originNoSuffix;
    324      nsresult rv = mDefaultOrigin->GetOriginNoSuffix(originNoSuffix);
    325      if (NS_WARN_IF(NS_FAILED(rv))) {
    326        return;
    327      }
    328 
    329      aList.AppendElement(NS_ConvertUTF8toUTF16(originNoSuffix));
    330      return;
    331    }
    332 
    333    case FeaturePolicyUtils::FeaturePolicyValue::eNone:
    334      return;
    335 
    336    default:
    337      MOZ_CRASH("Unknown default value");
    338  }
    339 }
    340 
    341 void FeaturePolicy::MaybeSetAllowedPolicy(const nsAString& aFeatureName) {
    342  MOZ_ASSERT(FeaturePolicyUtils::IsSupportedFeature(aFeatureName) ||
    343             FeaturePolicyUtils::IsExperimentalFeature(aFeatureName));
    344  // Skip if feature is in experimental phase
    345  if (!StaticPrefs::dom_security_featurePolicy_experimental_enabled() &&
    346      FeaturePolicyUtils::IsExperimentalFeature(aFeatureName)) {
    347    return;
    348  }
    349 
    350  if (HasDeclaredFeature(aFeatureName)) {
    351    return;
    352  }
    353 
    354  Feature feature(aFeatureName);
    355  feature.SetAllowsAll();
    356 
    357  mFeatures.AppendElement(feature);
    358  mAttributeEnabledFeatureNames.AppendElement(aFeatureName);
    359 }
    360 
    361 FeaturePolicyInfo FeaturePolicy::ToFeaturePolicyInfo() const {
    362  return {mInheritedDeniedFeatureNames.Clone(),
    363          mAttributeEnabledFeatureNames.Clone(),
    364          mDeclaredString,
    365          mDefaultOrigin,
    366          mSelfOrigin,
    367          mSrcOrigin};
    368 }
    369 
    370 }  // namespace mozilla::dom