nsAboutRedirector.cpp (15056B)
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 "nsAboutRedirector.h" 8 #include "nsNetUtil.h" 9 #include "nsAboutProtocolUtils.h" 10 #include "nsBaseChannel.h" 11 #include "nsIProtocolHandler.h" 12 #include "nsXULAppAPI.h" 13 #include "mozilla/Preferences.h" 14 #include "mozilla/dom/ContentParent.h" 15 #include "mozilla/dom/RemoteType.h" 16 #include "mozilla/gfx/GPUProcessManager.h" 17 18 #ifdef MOZ_WIDGET_ANDROID 19 # include "mozilla/java/GeckoAppShellWrappers.h" 20 #endif 21 22 #define ABOUT_CONFIG_ENABLED_PREF "general.aboutConfig.enable" 23 24 NS_IMPL_ISUPPORTS(nsAboutRedirector, nsIAboutModule) 25 26 struct RedirEntry { 27 const char* id; 28 const char* url; 29 uint32_t flags; 30 }; 31 32 class CrashChannel final : public nsBaseChannel { 33 public: 34 explicit CrashChannel(nsIURI* aURI) { SetURI(aURI); } 35 36 nsresult OpenContentStream(bool async, nsIInputStream** stream, 37 nsIChannel** channel) override { 38 nsAutoCString spec; 39 mURI->GetSpec(spec); 40 41 if (spec.EqualsASCII("about:crashparent") && XRE_IsParentProcess()) { 42 MOZ_CRASH("Crash via about:crashparent"); 43 } 44 45 if (spec.EqualsASCII("about:crashgpu") && XRE_IsParentProcess()) { 46 if (auto* gpu = mozilla::gfx::GPUProcessManager::Get()) { 47 gpu->CrashProcess(); 48 } 49 } 50 51 if (spec.EqualsASCII("about:crashcontent") && XRE_IsContentProcess()) { 52 MOZ_CRASH("Crash via about:crashcontent"); 53 } 54 55 #ifdef MOZ_WIDGET_ANDROID 56 if (spec.EqualsASCII("about:crashcontentjava") && XRE_IsContentProcess()) { 57 mozilla::java::GeckoAppShell::CrashByUncaughtException(); 58 } 59 #endif 60 61 if (spec.EqualsASCII("about:crashextensions") && XRE_IsParentProcess()) { 62 using ContentParent = mozilla::dom::ContentParent; 63 nsTArray<RefPtr<ContentParent>> toKill; 64 for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { 65 if (cp->GetRemoteType() == EXTENSION_REMOTE_TYPE) { 66 toKill.AppendElement(cp); 67 } 68 } 69 for (auto& cp : toKill) { 70 cp->KillHard("Killed via about:crashextensions"); 71 } 72 } 73 74 NS_WARNING("Unhandled about:crash* URI or wrong process"); 75 return NS_ERROR_NOT_IMPLEMENTED; 76 } 77 78 protected: 79 virtual ~CrashChannel() = default; 80 }; 81 82 /* 83 Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome 84 privileges. This is potentially dangerous. Please use 85 URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below 86 unless your about: page really needs chrome privileges. Security review is 87 required before adding new map entries without 88 URI_SAFE_FOR_UNTRUSTED_CONTENT. 89 90 URI_SAFE_FOR_UNTRUSTED_CONTENT is not enough to let web pages load that page, 91 for that you need MAKE_LINKABLE. 92 93 NOTE: changes to this redir map need to be accompanied with changes to 94 docshell/build/components.conf 95 */ 96 static const RedirEntry kRedirMap[] = { 97 {"about", "chrome://global/content/aboutAbout.html", 0}, 98 #ifndef MOZ_WIDGET_ANDROID 99 {"addons", "chrome://mozapps/content/extensions/aboutaddons.html", 100 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 101 #endif 102 {"buildconfig", "chrome://global/content/buildconfig.html", 103 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 104 nsIAboutModule::IS_SECURE_CHROME_UI}, 105 {"checkerboard", "chrome://global/content/aboutCheckerboard.html", 106 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 107 nsIAboutModule::ALLOW_SCRIPT}, 108 #ifndef MOZ_WIDGET_ANDROID 109 {"config", "chrome://global/content/aboutconfig/aboutconfig.html", 110 nsIAboutModule::IS_SECURE_CHROME_UI}, 111 #else 112 {"config", "chrome://geckoview/content/config.xhtml", 113 nsIAboutModule::IS_SECURE_CHROME_UI | 114 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 115 #endif 116 #ifdef MOZ_CRASHREPORTER 117 {"crashes", "chrome://global/content/crashes.html", 118 nsIAboutModule::IS_SECURE_CHROME_UI}, 119 #endif 120 {"credits", "https://www.mozilla.org/credits/", 121 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 122 nsIAboutModule::URI_MUST_LOAD_IN_CHILD}, 123 {"fingerprintingprotection", 124 "chrome://global/content/usercharacteristics/usercharacteristics.html", 125 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 126 nsIAboutModule::HIDE_FROM_ABOUTABOUT | nsIAboutModule::ALLOW_SCRIPT | 127 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 128 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS}, 129 #ifdef MOZ_WIDGET_ANDROID 130 {"home", "about:blank", 131 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 132 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 133 nsIAboutModule::URI_MUST_LOAD_IN_CHILD}, 134 #endif 135 {"httpsonlyerror", "chrome://global/content/httpsonlyerror/errorpage.html", 136 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 137 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 138 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 139 #if defined(NIGHTLY_BUILD) && !defined(BASE_BROWSER_VERSION) 140 // Do not include about:inference since "ml" is excluded. tor-browser#44045. 141 {"inference", "chrome://global/content/aboutInference.html", 142 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 143 #endif 144 {"license", "chrome://global/content/license.html", 145 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 146 nsIAboutModule::IS_SECURE_CHROME_UI}, 147 {"logging", "chrome://global/content/aboutLogging/aboutLogging.html", 148 nsIAboutModule::ALLOW_SCRIPT}, 149 // Do not allow web pages to link to about:logo, which varies between 150 // channels. See tor-browser#43308. 151 // Moreover, it exposes firefox-specific branding. 152 {"memory", "chrome://global/content/aboutMemory.xhtml", 153 nsIAboutModule::ALLOW_SCRIPT}, 154 {"certificate", "chrome://global/content/certviewer/certviewer.html", 155 nsIAboutModule::ALLOW_SCRIPT | 156 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 157 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 158 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS | 159 nsIAboutModule::IS_SECURE_CHROME_UI}, 160 #ifndef BASE_BROWSER_VERSION 161 {"mozilla", "chrome://global/content/mozilla.html", 162 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT}, 163 #endif 164 #if !defined(ANDROID) && !defined(XP_WIN) 165 {"webauthn", "chrome://global/content/aboutWebauthn.html", 166 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 167 #endif 168 {"neterror", "chrome://global/content/aboutNetError.html", 169 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 170 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 171 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 172 {"networking", "chrome://global/content/aboutNetworking.html", 173 nsIAboutModule::ALLOW_SCRIPT}, 174 {"performance", "about:processes", 175 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI | 176 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 177 {"processes", "chrome://global/content/aboutProcesses.html", 178 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 179 {"restricted", 180 "chrome://global/content/aboutRestricted/aboutRestricted.html", 181 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 182 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 183 nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 184 // about:serviceworkers always wants to load in the parent process because 185 // the only place nsIServiceWorkerManager has any data is in the parent 186 // process. 187 // 188 // There is overlap without about:debugging, but about:debugging is not 189 // available on mobile at this time, and it's useful to be able to know if 190 // a ServiceWorker is registered directly from the mobile browser without 191 // having to connect the device to a desktop machine and all that entails. 192 {"serviceworkers", "chrome://global/content/aboutServiceWorkers.xhtml", 193 nsIAboutModule::ALLOW_SCRIPT}, 194 #ifndef ANDROID 195 {"profiles", "chrome://global/content/aboutProfiles.xhtml", 196 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 197 #endif 198 // about:srcdoc is unresolvable by specification. It is included here 199 // because the security manager would disallow srcdoc iframes otherwise. 200 {"srcdoc", "about:blank", 201 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 202 nsIAboutModule::HIDE_FROM_ABOUTABOUT | 203 // Needs to be linkable so content can touch its own srcdoc frames 204 nsIAboutModule::MAKE_LINKABLE | nsIAboutModule::URI_CAN_LOAD_IN_CHILD}, 205 {"support", "chrome://global/content/aboutSupport.xhtml", 206 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 207 #ifdef XP_WIN 208 {"third-party", "chrome://global/content/aboutThirdParty.html", 209 nsIAboutModule::ALLOW_SCRIPT}, 210 {"windows-messages", "chrome://global/content/aboutWindowsMessages.html", 211 nsIAboutModule::ALLOW_SCRIPT}, 212 #endif 213 #ifdef MOZ_TELEMETRY_REPORTING 214 # ifndef MOZ_GLEAN_ANDROID 215 {"glean", "chrome://global/content/aboutGlean.html", 216 # if !defined(NIGHTLY_BUILD) && defined(MOZILLA_OFFICIAL) 217 nsIAboutModule::HIDE_FROM_ABOUTABOUT | 218 # endif 219 nsIAboutModule::ALLOW_SCRIPT}, 220 # endif 221 {"telemetry", "chrome://global/content/aboutTelemetry.xhtml", 222 nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI}, 223 #endif 224 {"torconnect", "chrome://global/content/torconnect/aboutTorConnect.html", 225 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 226 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT | 227 nsIAboutModule::HIDE_FROM_ABOUTABOUT | 228 nsIAboutModule::IS_SECURE_CHROME_UI}, 229 #ifndef BASE_BROWSER_VERSION 230 // Remove about:translations since translations are disabled. 231 // See tor-browser#44045 and tor-browser#42872. 232 {"translations", 233 "chrome://global/content/translations/about-translations.html", 234 nsIAboutModule::ALLOW_SCRIPT | 235 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 236 nsIAboutModule::URI_MUST_LOAD_IN_CHILD | 237 nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS}, 238 #endif 239 #ifndef BASE_BROWSER_VERSION 240 // We disable safe browsing and the data update mechanisms. So this page 241 // will be non-functional or at least unreliable. 242 {"url-classifier", "chrome://global/content/aboutUrlClassifier.xhtml", 243 nsIAboutModule::ALLOW_SCRIPT}, 244 #endif 245 #ifdef MOZ_WEBRTC 246 {"webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.html", 247 nsIAboutModule::ALLOW_SCRIPT}, 248 #endif 249 {"crashparent", "about:blank", nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 250 {"crashcontent", "about:blank", 251 nsIAboutModule::HIDE_FROM_ABOUTABOUT | 252 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 253 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | 254 nsIAboutModule::URI_MUST_LOAD_IN_CHILD}, 255 #ifdef MOZ_WIDGET_ANDROID 256 {"crashcontentjava", "about:blank", 257 nsIAboutModule::HIDE_FROM_ABOUTABOUT | 258 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | 259 nsIAboutModule::URI_CAN_LOAD_IN_CHILD | 260 nsIAboutModule::URI_MUST_LOAD_IN_CHILD}, 261 #endif 262 {"crashgpu", "about:blank", nsIAboutModule::HIDE_FROM_ABOUTABOUT}, 263 {"crashextensions", "about:blank", nsIAboutModule::HIDE_FROM_ABOUTABOUT}}; 264 static const int kRedirTotal = std::size(kRedirMap); 265 266 NS_IMETHODIMP 267 nsAboutRedirector::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, 268 nsIChannel** aResult) { 269 NS_ENSURE_ARG_POINTER(aURI); 270 NS_ENSURE_ARG_POINTER(aLoadInfo); 271 NS_ASSERTION(aResult, "must not be null"); 272 273 nsAutoCString path; 274 nsresult rv = NS_GetAboutModuleName(aURI, path); 275 NS_ENSURE_SUCCESS(rv, rv); 276 277 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); 278 NS_ENSURE_SUCCESS(rv, rv); 279 280 if (path.EqualsASCII("crashparent") || path.EqualsASCII("crashcontent") || 281 path.EqualsASCII("crashgpu") || path.EqualsASCII("crashextensions") 282 #ifdef MOZ_WIDGET_ANDROID 283 || path.EqualsASCII("crashcontentjava") 284 #endif 285 ) { 286 bool isExternal; 287 aLoadInfo->GetLoadTriggeredFromExternal(&isExternal); 288 if (isExternal || !aLoadInfo->TriggeringPrincipal() || 289 !aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { 290 return NS_ERROR_NOT_AVAILABLE; 291 } 292 293 nsCOMPtr<nsIChannel> channel = new CrashChannel(aURI); 294 channel->SetLoadInfo(aLoadInfo); 295 channel.forget(aResult); 296 return NS_OK; 297 } 298 299 if (path.EqualsASCII("config") && 300 !mozilla::Preferences::GetBool(ABOUT_CONFIG_ENABLED_PREF, true)) { 301 return NS_ERROR_NOT_AVAILABLE; 302 } 303 304 for (int i = 0; i < kRedirTotal; i++) { 305 if (!strcmp(path.get(), kRedirMap[i].id)) { 306 nsCOMPtr<nsIChannel> tempChannel; 307 nsCOMPtr<nsIURI> tempURI; 308 rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url); 309 NS_ENSURE_SUCCESS(rv, rv); 310 311 rv = NS_NewChannelInternal(getter_AddRefs(tempChannel), tempURI, 312 aLoadInfo); 313 NS_ENSURE_SUCCESS(rv, rv); 314 315 // If tempURI links to an external URI (i.e. something other than 316 // chrome:// or resource://) then set result principal URI on the 317 // load info which forces the channel principal to reflect the displayed 318 // URL rather then being the systemPrincipal. 319 bool isUIResource = false; 320 rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, 321 &isUIResource); 322 NS_ENSURE_SUCCESS(rv, rv); 323 324 bool isAboutBlank = NS_IsAboutBlank(tempURI); 325 326 if (!isUIResource && !isAboutBlank) { 327 aLoadInfo->SetResultPrincipalURI(tempURI); 328 } 329 330 tempChannel->SetOriginalURI(aURI); 331 332 tempChannel.forget(aResult); 333 return rv; 334 } 335 } 336 337 NS_ERROR("nsAboutRedirector called for unknown case"); 338 return NS_ERROR_ILLEGAL_VALUE; 339 } 340 341 NS_IMETHODIMP 342 nsAboutRedirector::GetURIFlags(nsIURI* aURI, uint32_t* aResult) { 343 NS_ENSURE_ARG_POINTER(aURI); 344 345 nsAutoCString name; 346 nsresult rv = NS_GetAboutModuleName(aURI, name); 347 NS_ENSURE_SUCCESS(rv, rv); 348 349 for (int i = 0; i < kRedirTotal; i++) { 350 if (name.EqualsASCII(kRedirMap[i].id)) { 351 *aResult = kRedirMap[i].flags; 352 return NS_OK; 353 } 354 } 355 356 NS_ERROR("nsAboutRedirector called for unknown case"); 357 return NS_ERROR_ILLEGAL_VALUE; 358 } 359 360 NS_IMETHODIMP 361 nsAboutRedirector::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) { 362 NS_ENSURE_ARG_POINTER(aURI); 363 364 nsAutoCString name; 365 nsresult rv = NS_GetAboutModuleName(aURI, name); 366 NS_ENSURE_SUCCESS(rv, rv); 367 368 for (const auto& redir : kRedirMap) { 369 if (name.EqualsASCII(redir.id)) { 370 return NS_NewURI(chromeURI, redir.url); 371 } 372 } 373 374 NS_ERROR("nsAboutRedirector called for unknown case"); 375 return NS_ERROR_ILLEGAL_VALUE; 376 } 377 378 nsresult nsAboutRedirector::Create(REFNSIID aIID, void** aResult) { 379 RefPtr<nsAboutRedirector> about = new nsAboutRedirector(); 380 return about->QueryInterface(aIID, aResult); 381 }