tor-browser

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

UrlClassifierFeatureHarmfulAddonProtection.cpp (9716B)


      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 "UrlClassifierFeatureHarmfulAddonProtection.h"
      8 
      9 #include "mozilla/AntiTrackingUtils.h"
     10 #include "mozilla/extensions/WebExtensionPolicy.h"
     11 #include "mozilla/glean/NetwerkMetrics.h"
     12 #include "mozilla/net/UrlClassifierCommon.h"
     13 #include "ChannelClassifierService.h"
     14 #include "mozilla/StaticPrefs_privacy.h"
     15 #include "nsNetUtil.h"
     16 #include "mozilla/StaticPtr.h"
     17 #include "nsIChannel.h"
     18 #include "nsIEffectiveTLDService.h"
     19 #include "nsIHttpChannelInternal.h"
     20 #include "nsIWebProgressListener.h"
     21 #include "nsIWritablePropertyBag2.h"
     22 
     23 namespace mozilla {
     24 namespace net {
     25 
     26 namespace {
     27 
     28 #define HARMFULADDON_FEATURE_NAME "harmfuladdon-protection"
     29 
     30 #define URLCLASSIFIER_HARMFULADDON_BLOCKLIST \
     31  "urlclassifier.features.harmfuladdon.blocklistTables"
     32 #define URLCLASSIFIER_HARMFULADDON_BLOCKLIST_TEST_ENTRIES \
     33  "urlclassifier.features.harmfuladdon.blocklistHosts"
     34 #define URLCLASSIFIER_HARMFULADDON_ENTITYLIST \
     35  "urlclassifier.features.harmfuladdon.entitylistTables"
     36 #define URLCLASSIFIER_HARMFULADDON_ENTITYLIST_TEST_ENTRIES \
     37  "urlclassifier.features.harmfuladdon.entitylistHosts"
     38 #define URLCLASSIFIER_HARMFULADDON_EXCEPTION_URLS \
     39  "urlclassifier.features.harmfuladdon.skipURLs"
     40 #define TABLE_HARMFULADDON_BLOCKLIST_PREF "harmfuladdon-blocklist-pref"
     41 #define TABLE_HARMFULADDON_ENTITYLIST_PREF "harmfuladdon-entitylist-pref"
     42 
     43 StaticRefPtr<UrlClassifierFeatureHarmfulAddonProtection>
     44    gFeatureHarmfulAddonProtection;
     45 
     46 extensions::WebExtensionPolicy* GetAddonPolicy(nsIChannel* aChannel) {
     47  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
     48 
     49  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     50  if (NS_FAILED(loadInfo->GetTriggeringPrincipal(
     51          getter_AddRefs(triggeringPrincipal)))) {
     52    return nullptr;
     53  }
     54 
     55  nsCOMPtr<nsIPrincipal> loadingPrincipal;
     56  if (NS_FAILED(
     57          loadInfo->GetLoadingPrincipal(getter_AddRefs(loadingPrincipal)))) {
     58    return nullptr;
     59  }
     60 
     61  extensions::WebExtensionPolicy* policy = nullptr;
     62 
     63  if (triggeringPrincipal) {
     64    policy = BasePrincipal::Cast(triggeringPrincipal)->AddonPolicy();
     65 
     66    if (!policy) {
     67      policy =
     68          BasePrincipal::Cast(triggeringPrincipal)->ContentScriptAddonPolicy();
     69    }
     70  }
     71 
     72  if (!policy && loadingPrincipal) {
     73    policy = BasePrincipal::Cast(loadingPrincipal)->AddonPolicy();
     74 
     75    if (!policy) {
     76      policy =
     77          BasePrincipal::Cast(loadingPrincipal)->ContentScriptAddonPolicy();
     78    }
     79  }
     80 
     81  return policy;
     82 }
     83 
     84 bool GetAddonId(nsIChannel* aChannel, nsACString& aAddonID) {
     85  extensions::WebExtensionPolicy* policy = GetAddonPolicy(aChannel);
     86  if (!policy) {
     87    return false;
     88  }
     89 
     90  CopyUTF16toUTF8(nsDependentAtomString(policy->Id()), aAddonID);
     91  return true;
     92 }
     93 
     94 bool GetAddonName(nsIChannel* aChannel, nsACString& aAddonName) {
     95  extensions::WebExtensionPolicy* policy = GetAddonPolicy(aChannel);
     96  if (!policy) {
     97    return false;
     98  }
     99 
    100  CopyUTF16toUTF8(policy->Name(), aAddonName);
    101  return true;
    102 }
    103 
    104 bool GetETLD(nsIChannel* aChannel, nsACString& aETLD) {
    105  nsCOMPtr<nsIURI> uri;
    106  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
    107  if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
    108    return false;
    109  }
    110 
    111  nsCOMPtr<nsIEffectiveTLDService> etld(
    112      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID));
    113  if (NS_WARN_IF(!etld)) {
    114    return false;
    115  }
    116 
    117  rv = etld->GetBaseDomain(uri, 0, aETLD);
    118  if (NS_WARN_IF(NS_FAILED(rv))) {
    119    return false;
    120  }
    121 
    122  return !aETLD.IsEmpty();
    123 }
    124 
    125 void RecordGleanAddonBlocked(nsIChannel* aChannel,
    126                             const nsACString& aTableStr) {
    127  nsAutoCString etld;
    128  if (!GetETLD(aChannel, etld)) {
    129    return;
    130  }
    131 
    132  nsAutoCString addonId;
    133  if (!GetAddonId(aChannel, addonId)) {
    134    return;
    135  }
    136 
    137  glean::network::urlclassifier_addon_block.Record(
    138      Some(glean::network::UrlclassifierAddonBlockExtra{
    139          mozilla::Some(addonId), mozilla::Some(etld),
    140          mozilla::Some(nsCString(aTableStr))}));
    141 }
    142 
    143 }  // namespace
    144 
    145 UrlClassifierFeatureHarmfulAddonProtection::
    146    UrlClassifierFeatureHarmfulAddonProtection()
    147    : UrlClassifierFeatureAntiTrackingBase(
    148          nsLiteralCString(HARMFULADDON_FEATURE_NAME),
    149          nsLiteralCString(URLCLASSIFIER_HARMFULADDON_BLOCKLIST),
    150          nsLiteralCString(URLCLASSIFIER_HARMFULADDON_ENTITYLIST),
    151          nsLiteralCString(URLCLASSIFIER_HARMFULADDON_BLOCKLIST_TEST_ENTRIES),
    152          nsLiteralCString(URLCLASSIFIER_HARMFULADDON_ENTITYLIST_TEST_ENTRIES),
    153          nsLiteralCString(TABLE_HARMFULADDON_BLOCKLIST_PREF),
    154          nsLiteralCString(TABLE_HARMFULADDON_ENTITYLIST_PREF),
    155          nsLiteralCString(URLCLASSIFIER_HARMFULADDON_EXCEPTION_URLS)) {}
    156 
    157 /* static */ const char* UrlClassifierFeatureHarmfulAddonProtection::Name() {
    158  return HARMFULADDON_FEATURE_NAME;
    159 }
    160 
    161 /* static */
    162 void UrlClassifierFeatureHarmfulAddonProtection::MaybeInitialize() {
    163  UC_LOG_LEAK(("UrlClassifierFeatureHarmfulAddonProtection::MaybeInitialize"));
    164 
    165  if (!gFeatureHarmfulAddonProtection) {
    166    gFeatureHarmfulAddonProtection =
    167        new UrlClassifierFeatureHarmfulAddonProtection();
    168    gFeatureHarmfulAddonProtection->InitializePreferences();
    169  }
    170 }
    171 
    172 /* static */
    173 void UrlClassifierFeatureHarmfulAddonProtection::MaybeShutdown() {
    174  UC_LOG_LEAK(("UrlClassifierFeatureHarmfulAddonProtection::MaybeShutdown"));
    175 
    176  if (gFeatureHarmfulAddonProtection) {
    177    gFeatureHarmfulAddonProtection->ShutdownPreferences();
    178    gFeatureHarmfulAddonProtection = nullptr;
    179  }
    180 }
    181 
    182 /* static */
    183 already_AddRefed<UrlClassifierFeatureHarmfulAddonProtection>
    184 UrlClassifierFeatureHarmfulAddonProtection::MaybeCreate(nsIChannel* aChannel) {
    185  MOZ_ASSERT(aChannel);
    186 
    187  UC_LOG_LEAK(
    188      ("UrlClassifierFeatureHarmfulAddonProtection::MaybeCreate - channel %p",
    189       aChannel));
    190 
    191  if (!StaticPrefs::privacy_trackingprotection_harmfuladdon_enabled()) {
    192    return nullptr;
    193  }
    194 
    195  RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    196  nsIPrincipal* triggeringPrincipal = loadInfo->TriggeringPrincipal();
    197  bool addonTriggeringPrincipal =
    198      triggeringPrincipal &&
    199      triggeringPrincipal->GetIsAddonOrExpandedAddonPrincipal();
    200 
    201  nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal();
    202  bool addonLoadingPrincipal =
    203      loadingPrincipal &&
    204      loadingPrincipal->GetIsAddonOrExpandedAddonPrincipal();
    205 
    206  if (!addonTriggeringPrincipal && !addonLoadingPrincipal) {
    207    return nullptr;
    208  }
    209 
    210  // Recommended add-ons are exempt.
    211  extensions::WebExtensionPolicy* policy = GetAddonPolicy(aChannel);
    212  if (policy && policy->HasRecommendedState()) {
    213    return nullptr;
    214  }
    215 
    216  MaybeInitialize();
    217  MOZ_ASSERT(gFeatureHarmfulAddonProtection);
    218 
    219  RefPtr<UrlClassifierFeatureHarmfulAddonProtection> self =
    220      gFeatureHarmfulAddonProtection;
    221  return self.forget();
    222 }
    223 
    224 /* static */
    225 already_AddRefed<nsIUrlClassifierFeature>
    226 UrlClassifierFeatureHarmfulAddonProtection::GetIfNameMatches(
    227    const nsACString& aName) {
    228  if (!aName.EqualsLiteral(HARMFULADDON_FEATURE_NAME)) {
    229    return nullptr;
    230  }
    231 
    232  MaybeInitialize();
    233  MOZ_ASSERT(gFeatureHarmfulAddonProtection);
    234 
    235  RefPtr<UrlClassifierFeatureHarmfulAddonProtection> self =
    236      gFeatureHarmfulAddonProtection;
    237  return self.forget();
    238 }
    239 
    240 NS_IMETHODIMP
    241 UrlClassifierFeatureHarmfulAddonProtection::ProcessChannel(
    242    nsIChannel* aChannel, const nsTArray<nsCString>& aList,
    243    const nsTArray<nsCString>& aHashes, bool* aShouldContinue) {
    244  NS_ENSURE_ARG_POINTER(aChannel);
    245  NS_ENSURE_ARG_POINTER(aShouldContinue);
    246 
    247  bool isAllowListed = UrlClassifierCommon::IsAllowListed(aChannel);
    248 
    249  // This is a blocking feature.
    250  *aShouldContinue = isAllowListed;
    251 
    252  if (isAllowListed) {
    253    return NS_OK;
    254  }
    255 
    256  bool ShouldProcessByProtectionFeature =
    257      UrlClassifierCommon::ShouldProcessWithProtectionFeature(aChannel);
    258 
    259  *aShouldContinue = !ShouldProcessByProtectionFeature;
    260 
    261  if (!ShouldProcessByProtectionFeature) {
    262    return NS_OK;
    263  }
    264 
    265  nsAutoCString list;
    266  UrlClassifierCommon::TablesToString(aList, list);
    267 
    268  ChannelBlockDecision decision =
    269      ChannelClassifierService::OnBeforeBlockChannel(aChannel, mName, list);
    270  if (decision != ChannelBlockDecision::Blocked) {
    271    *aShouldContinue = true;
    272    return NS_OK;
    273  }
    274 
    275  RecordGleanAddonBlocked(aChannel, list);
    276 
    277  nsAutoCString addonName;
    278  if (GetAddonName(aChannel, addonName)) {
    279    nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
    280    if (props) {
    281      props->SetPropertyAsACString(u"blockedExtension"_ns, addonName);
    282    }
    283  }
    284 
    285  UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_HARMFULADDON_URI,
    286                                         list, ""_ns, ""_ns);
    287 
    288  UC_LOG(
    289      ("UrlClassifierFeatureHarmfulAddonProtection::ProcessChannel - "
    290       "cancelling channel %p",
    291       aChannel));
    292 
    293  (void)aChannel->Cancel(NS_ERROR_HARMFULADDON_URI);
    294  return NS_OK;
    295 }
    296 
    297 NS_IMETHODIMP
    298 UrlClassifierFeatureHarmfulAddonProtection::GetURIByListType(
    299    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
    300    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
    301  NS_ENSURE_ARG_POINTER(aChannel);
    302  NS_ENSURE_ARG_POINTER(aURIType);
    303  NS_ENSURE_ARG_POINTER(aURI);
    304 
    305  if (aListType == nsIUrlClassifierFeature::blocklist) {
    306    *aURIType = nsIUrlClassifierFeature::blocklistURI;
    307    return aChannel->GetURI(aURI);
    308  }
    309 
    310  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::entitylist);
    311 
    312  *aURIType = nsIUrlClassifierFeature::pairwiseEntitylistURI;
    313  return aChannel->GetURI(aURI);
    314 }
    315 
    316 }  // namespace net
    317 }  // namespace mozilla