tor-browser

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

AboutRedirector.cpp (15550B)


      1 /* -*- Mode: C++; tab-width: 2; 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 // See also: docshell/base/nsAboutRedirector.cpp
      7 
      8 #include "AboutRedirector.h"
      9 #include "nsNetUtil.h"
     10 #include "nsIAppStartup.h"
     11 #include "nsIChannel.h"
     12 #include "nsIURI.h"
     13 #include "nsIProtocolHandler.h"
     14 #include "nsServiceManagerUtils.h"
     15 #include "mozilla/Components.h"
     16 #include "mozilla/StaticPrefs_browser.h"
     17 #include "mozilla/dom/ContentChild.h"
     18 #include "mozilla/browser/NimbusFeatures.h"
     19 
     20 // For Tor Browser manual
     21 #include "nsTHashSet.h"
     22 #include "mozilla/intl/LocaleService.h"
     23 #include "mozilla/Omnijar.h"
     24 
     25 #define PROFILES_ENABLED_PREF "browser.profiles.enabled"
     26 #define ABOUT_WELCOME_CHROME_URL \
     27  "chrome://browser/content/aboutwelcome/aboutwelcome.html"
     28 #define ABOUT_HOME_URL "about:home"
     29 // NOTE: We return "about:tor" rather than the "chrome:" path
     30 // "chrome://browser/content/abouttor/aboutTor.html"
     31 // The result is that the channel created in NewChannel in will have its
     32 // resultPrincipalURI set to "about:tor".
     33 // What this means in practice is that the loaded document's documentURI and
     34 // currentURI will be "about:tor" rather than "about:newtab", "about:home",
     35 // "about:welcome" or "about:privatebrowsing".
     36 // The disadvantage of this is that we often need to add "about:tor" to places
     37 // where "about:newtab" or other URIs appear.
     38 // The advantage is that we maintain more control against changes in
     39 // mozilla-central.
     40 #define BASE_BROWSER_HOME_PAGE_URL "about:tor"
     41 
     42 namespace mozilla {
     43 namespace browser {
     44 
     45 NS_IMPL_ISUPPORTS(AboutRedirector, nsIAboutModule)
     46 
     47 struct RedirEntry {
     48  const char* id;
     49  const char* url;
     50  uint32_t flags;
     51 };
     52 
     53 static const uint32_t BASE_BROWSER_HOME_PAGE_FLAGS =
     54    nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     55    nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
     56    nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     57    nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI;
     58 
     59 /*
     60  Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome
     61  privileges. This is potentially dangerous. Please use
     62  URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below
     63  unless your about: page really needs chrome privileges. Security review is
     64  required before adding new map entries without
     65  URI_SAFE_FOR_UNTRUSTED_CONTENT.
     66 
     67  NOTE: changes to this redir map need to be accompanied with changes to
     68    browser/components/about/components.conf
     69 */
     70 static const RedirEntry kRedirMap[] = {
     71    {"aichatcontent", "chrome://browser/content/aiwindow/aiChatContent.html",
     72     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     73         nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     74         nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     75    {"asrouter", "chrome://browser/content/asrouter/asrouter-admin.html",
     76     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     77         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
     78         nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
     79         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     80    {"blocked", "chrome://browser/content/blockedSite.xhtml",
     81     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     82         nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
     83         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     84    {"certerror", "chrome://global/content/aboutNetError.html",
     85     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     86         nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
     87         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     88    {"unloads", "chrome://browser/content/tabunloader/aboutUnloads.html",
     89     nsIAboutModule::ALLOW_SCRIPT},
     90    {"framecrashed", "chrome://browser/content/aboutFrameCrashed.html",
     91     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     92         nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
     93         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     94    {"logins", "chrome://browser/content/aboutlogins/aboutLogins.html",
     95     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     96         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
     97         nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     98         nsIAboutModule::IS_SECURE_CHROME_UI},
     99    {"loginsimportreport",
    100     "chrome://browser/content/aboutlogins/aboutLoginsImportReport.html",
    101     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    102         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
    103         nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    104         nsIAboutModule::IS_SECURE_CHROME_UI},
    105    {"opentabs", "chrome://browser/content/tabbrowser/opentabs.html",
    106     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI |
    107         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    108    {"policies", "chrome://browser/content/policies/aboutPolicies.html",
    109     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI},
    110    {"privatebrowsing", "about:blank", BASE_BROWSER_HOME_PAGE_FLAGS},
    111    {"profiling",
    112     "chrome://devtools/content/performance-new/aboutprofiling/index.html",
    113     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI},
    114 // Drop about:rights. tor-browser#43901.
    115 #ifndef BASE_BROWSER_VERSION
    116    {"robots", "chrome://browser/content/aboutRobots.xhtml",
    117     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    118         nsIAboutModule::ALLOW_SCRIPT},
    119 #endif
    120    {"rulesets", "chrome://browser/content/rulesets/aboutRulesets.html",
    121     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    122         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
    123         nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    124         nsIAboutModule::IS_SECURE_CHROME_UI},
    125    {"sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
    126     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT |
    127         nsIAboutModule::IS_SECURE_CHROME_UI},
    128    {"tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
    129     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    130         nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    131    {"welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",
    132     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT |
    133         nsIAboutModule::IS_SECURE_CHROME_UI},
    134    {"welcome", "about:blank", BASE_BROWSER_HOME_PAGE_FLAGS},
    135    {"home", "about:blank", BASE_BROWSER_HOME_PAGE_FLAGS},
    136    {"newtab", "chrome://browser/content/blanktab.html",
    137     BASE_BROWSER_HOME_PAGE_FLAGS},
    138    {"messagepreview",
    139     "chrome://browser/content/messagepreview/messagepreview.html",
    140     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    141         nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    142         nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    143    {"settings", "chrome://browser/content/preferences/preferences.xhtml",
    144     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI |
    145         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    146    {"preferences", "chrome://browser/content/preferences/preferences.xhtml",
    147     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI},
    148    {"downloads",
    149     "chrome://browser/content/downloads/contentAreaDownloadsView.xhtml",
    150     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI},
    151    {"reader", "chrome://global/content/reader/aboutReader.html",
    152     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    153         nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    154         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    155    {"restartrequired", "chrome://browser/content/aboutRestartRequired.xhtml",
    156     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    157 #ifdef MOZ_SELECTABLE_PROFILES
    158    {"profilemanager", "chrome://browser/content/profiles/profiles.html",
    159     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI |
    160         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    161    {"editprofile", "chrome://browser/content/profiles/edit-profile.html",
    162     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    163         nsIAboutModule::IS_SECURE_CHROME_UI | nsIAboutModule::ALLOW_SCRIPT |
    164         nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    165         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
    166         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    167    {"deleteprofile", "chrome://browser/content/profiles/delete-profile.html",
    168     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    169         nsIAboutModule::IS_SECURE_CHROME_UI |
    170         nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
    171         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
    172         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    173    {"newprofile", "chrome://browser/content/profiles/new-profile.html",
    174     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    175         nsIAboutModule::IS_SECURE_CHROME_UI | nsIAboutModule::ALLOW_SCRIPT |
    176         nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    177         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
    178         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
    179 #endif
    180    {"keyboard", "chrome://browser/content/customkeys/customkeys.html",
    181     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    182         nsIAboutModule::IS_SECURE_CHROME_UI | nsIAboutModule::ALLOW_SCRIPT |
    183         nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    184         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS},
    185    {"tor", "chrome://browser/content/abouttor/aboutTor.html",
    186     BASE_BROWSER_HOME_PAGE_FLAGS},
    187    // The correct URI must be obtained by GetManualChromeURI
    188    {"manual", "about:blank",
    189     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    190         nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
    191         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
    192         nsIAboutModule::IS_SECURE_CHROME_UI},
    193 };
    194 
    195 static nsAutoCString GetAboutModuleName(nsIURI* aURI) {
    196  nsAutoCString path;
    197  aURI->GetPathQueryRef(path);
    198 
    199  int32_t f = path.FindChar('#');
    200  if (f >= 0) path.SetLength(f);
    201 
    202  f = path.FindChar('?');
    203  if (f >= 0) path.SetLength(f);
    204 
    205  ToLowerCase(path);
    206  return path;
    207 }
    208 
    209 static nsTHashSet<nsCStringHashKey> GetManualLocales() {
    210  nsTHashSet<nsCStringHashKey> locales;
    211  RefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::APP);
    212  if (!zip) {
    213    // Probably a local build started with ./mach run
    214    return locales;
    215  }
    216  UniquePtr<nsZipFind> find;
    217  const nsAutoCString prefix("chrome/browser/content/browser/manual/");
    218  nsAutoCString needle = prefix;
    219  needle.Append("*.html");
    220  if (NS_SUCCEEDED(zip->FindInit(needle.get(), getter_Transfers(find)))) {
    221    const char* entryName;
    222    uint16_t entryNameLen;
    223    while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
    224      // 5 is to remove the final `.html`
    225      const size_t length = entryNameLen - prefix.Length() - 5;
    226      locales.Insert(nsAutoCString(entryName + prefix.Length(), length));
    227    }
    228  }
    229  return locales;
    230 }
    231 
    232 static nsAutoCString GetManualChromeURI() {
    233  static nsTHashSet<nsCStringHashKey> locales = GetManualLocales();
    234 
    235  nsAutoCString reqLocale;
    236  intl::LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale);
    237  // Check every time the URL is needed in case the locale has changed.
    238  // It might help also if we start allowing to change language, e.g., with a
    239  // get parameter (see tor-browser#42675).
    240  if (!locales.Contains(reqLocale) && reqLocale.Length() > 2 &&
    241      reqLocale[2] == '-') {
    242    // At the moment, codes in our manual output are either 2 letters (en) or
    243    // 5 letters (pt-BR)
    244    reqLocale.SetLength(2);
    245  }
    246  if (!locales.Contains(reqLocale)) {
    247    reqLocale = "en";
    248  }
    249 
    250  // %s is the language
    251  constexpr char model[] = "chrome://browser/content/manual/%s.html";
    252  nsAutoCString url;
    253  url.AppendPrintf(model, reqLocale.get());
    254  return url;
    255 }
    256 
    257 NS_IMETHODIMP
    258 AboutRedirector::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
    259                            nsIChannel** result) {
    260  NS_ENSURE_ARG_POINTER(aURI);
    261  NS_ENSURE_ARG_POINTER(aLoadInfo);
    262 
    263  NS_ASSERTION(result, "must not be null");
    264 
    265  nsAutoCString path = GetAboutModuleName(aURI);
    266 
    267  if ((path.EqualsASCII("editprofile") || path.EqualsASCII("deleteprofile") ||
    268       path.EqualsASCII("newprofile")) &&
    269      !mozilla::Preferences::GetBool(PROFILES_ENABLED_PREF, false)) {
    270    return NS_ERROR_NOT_AVAILABLE;
    271  }
    272 
    273  if (path.EqualsASCII("profilemanager") &&
    274      !mozilla::Preferences::GetBool(PROFILES_ENABLED_PREF, false)) {
    275    bool startingUp;
    276    nsCOMPtr<nsIAppStartup> appStartup(
    277        mozilla::components::AppStartup::Service());
    278    if (NS_FAILED(appStartup->GetStartingUp(&startingUp)) || !startingUp) {
    279      return NS_ERROR_NOT_AVAILABLE;
    280    }
    281  }
    282 
    283  for (auto& redir : kRedirMap) {
    284    if (!strcmp(path.get(), redir.id)) {
    285      nsAutoCString url;
    286 
    287      if (path.EqualsLiteral("welcome") || path.EqualsLiteral("home") ||
    288          path.EqualsLiteral("privatebrowsing") ||
    289          (path.EqualsLiteral("newtab") &&
    290           StaticPrefs::browser_newtabpage_enabled())) {
    291        url.AssignASCII(BASE_BROWSER_HOME_PAGE_URL);
    292      }
    293 
    294      if (path.EqualsLiteral("manual")) {
    295        url = GetManualChromeURI();
    296      }
    297 
    298      // fall back to the specified url in the map
    299      if (url.IsEmpty()) {
    300        url.AssignASCII(redir.url);
    301      }
    302 
    303      nsCOMPtr<nsIChannel> tempChannel;
    304      nsCOMPtr<nsIURI> tempURI;
    305      nsresult rv = NS_NewURI(getter_AddRefs(tempURI), url);
    306      NS_ENSURE_SUCCESS(rv, rv);
    307 
    308      // If tempURI links to an external URI (i.e. something other than
    309      // chrome:// or resource://) then set the result principal URI on the
    310      // load info which forces the channel prncipal to reflect the displayed
    311      // URL rather then being the systemPrincipal.
    312      bool isUIResource = false;
    313      rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
    314                               &isUIResource);
    315      NS_ENSURE_SUCCESS(rv, rv);
    316 
    317      rv = NS_NewChannelInternal(getter_AddRefs(tempChannel), tempURI,
    318                                 aLoadInfo);
    319      NS_ENSURE_SUCCESS(rv, rv);
    320 
    321      if (!isUIResource) {
    322        aLoadInfo->SetResultPrincipalURI(tempURI);
    323      }
    324      tempChannel->SetOriginalURI(aURI);
    325 
    326      NS_ADDREF(*result = tempChannel);
    327      return rv;
    328    }
    329  }
    330 
    331  return NS_ERROR_ILLEGAL_VALUE;
    332 }
    333 
    334 NS_IMETHODIMP
    335 AboutRedirector::GetURIFlags(nsIURI* aURI, uint32_t* result) {
    336  NS_ENSURE_ARG_POINTER(aURI);
    337 
    338  nsAutoCString name = GetAboutModuleName(aURI);
    339 
    340  for (auto& redir : kRedirMap) {
    341    if (name.Equals(redir.id)) {
    342      *result = redir.flags;
    343      return NS_OK;
    344    }
    345  }
    346 
    347  return NS_ERROR_ILLEGAL_VALUE;
    348 }
    349 
    350 NS_IMETHODIMP
    351 AboutRedirector::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) {
    352  NS_ENSURE_ARG_POINTER(aURI);
    353 
    354  nsAutoCString name = GetAboutModuleName(aURI);
    355 
    356  if (name.EqualsLiteral("manual")) {
    357    return NS_NewURI(chromeURI, GetManualChromeURI());
    358  }
    359 
    360  for (const auto& redir : kRedirMap) {
    361    if (name.Equals(redir.id)) {
    362      return NS_NewURI(chromeURI, redir.url);
    363    }
    364  }
    365 
    366  return NS_ERROR_ILLEGAL_VALUE;
    367 }
    368 
    369 nsresult AboutRedirector::Create(REFNSIID aIID, void** result) {
    370  AboutRedirector* about = new AboutRedirector();
    371  if (about == nullptr) return NS_ERROR_OUT_OF_MEMORY;
    372  NS_ADDREF(about);
    373  nsresult rv = about->QueryInterface(aIID, result);
    374  NS_RELEASE(about);
    375  return rv;
    376 }
    377 
    378 }  // namespace browser
    379 }  // namespace mozilla