commit 3aee9ca1bb25121d1914670959f1528ea9f95fdc Author: Richard Pospesel richard@torproject.org Date: Fri May 21 22:18:23 2021 +0200
Bug 40416: Add v2 Onion deprecation warnings
- adds new v2 deprecated warning page (js and styling) that piggy-backs off of the existing added onion service errors - updates identity-icon to onionWarning.svg when visiting a v2 onion site adds warning tooltip; this warning supersedes all other identity states (including mixed-content error) - we determine whether to show the warning page in nsDocShell::DoURILoad() - a new synchonous IPC method is added to ContentChild/ContentParent to determine if the session has loaded the warning page already; worst case scenario, each child process will need to wait on this method to return only once when visiting a v2 onion; nothing is permanently cached with regards to this change - an exception for the new sync method is added to sync-messages.ini (generally, in practice adding new blocking methods is probably bad, but the minimial overhead and frequency this method is called is worth the simpler code) --- browser/base/content/aboutNetError.xhtml | 3 ++ browser/base/content/browser-siteIdentity.js | 12 +++++ browser/base/jar.mn | 2 +- .../content/netError/onionNetError.js | 6 +++ .../content/netError/v2Deprecated.css | 25 +++++++++ .../onionservices/content/netError/v2Deprecated.js | 50 ++++++++++++++++++ browser/components/onionservices/jar.mn | 8 ++- browser/modules/TorStrings.jsm | 8 +++ .../shared/identity-block/identity-block.inc.css | 3 +- browser/themes/shared/onionPattern.inc.xhtml | 4 +- docshell/base/nsDocShell.cpp | 61 ++++++++++++++++++++++ dom/ipc/ContentParent.cpp | 11 ++++ dom/ipc/ContentParent.h | 2 + dom/ipc/PContent.ipdl | 3 ++ ipc/ipdl/sync-messages.ini | 3 ++ js/xpconnect/src/xpc.msg | 1 + xpcom/base/ErrorList.py | 2 + 17 files changed, 200 insertions(+), 4 deletions(-)
diff --git a/browser/base/content/aboutNetError.xhtml b/browser/base/content/aboutNetError.xhtml index 957b6f15a0be..4572eb2024f1 100644 --- a/browser/base/content/aboutNetError.xhtml +++ b/browser/base/content/aboutNetError.xhtml @@ -207,7 +207,10 @@ </div> </div> </div> +<!-- The onion pattern is disabled by default unless the onionPattern.css is also included; we include onionPattern.css programmatically in the v2Deprecation error page, so the onion pattern will not be visible in all error pages --> +#include ../../themes/shared/onionPattern.inc.xhtml </body> + <script src="chrome://browser/content/onionservices/netError/v2Deprecated.js"/> <script src="chrome://browser/content/onionservices/netError/onionNetError.js"/> <script src="chrome://browser/content/aboutNetError.js"/> </html> diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js index 2a3431172886..27fee74cba5b 100644 --- a/browser/base/content/browser-siteIdentity.js +++ b/browser/base/content/browser-siteIdentity.js @@ -135,6 +135,15 @@ var gIdentityHandler = { return this._uriHasHost ? this._uri.host.toLowerCase().endsWith(".onion") : false; },
+ get _uriIsDeprecatedOnionHost() { + const hostIsV2Onion = function(host) { + // matches on v2 onion domains with any number of subdomains + const pattern = /^(.*.)*[a-z2-7]{16}.onion/i; + return pattern.test(host); + }; + + return this._uriHasHost ? hostIsV2Onion(this._uri.host) : false; + }, // smart getters get _identityPopup() { delete this._identityPopup; @@ -685,6 +694,9 @@ var gIdentityHandler = { "identity.extension.label", [extensionName] ); + } else if (this._uriIsDeprecatedOnionHost) { + this._identityBox.className = "onionServiceDeprecated"; + tooltip = TorStrings.onionServices.v2Deprecated.tooltip; } else if (this._uriHasHost && this._isSecureConnection && this._secInfo) { // This is a secure connection. // _isSecureConnection implicitly includes onion services, which may not have an SSL certificate diff --git a/browser/base/jar.mn b/browser/base/jar.mn index df65349796b5..21b07ad9511b 100644 --- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -22,7 +22,7 @@ browser.jar: content/browser/logos/send.svg (content/logos/send.svg) content/browser/logos/tracking-protection.svg (content/logos/tracking-protection.svg) content/browser/logos/tracking-protection-dark-theme.svg (content/logos/tracking-protection-dark-theme.svg) - content/browser/aboutNetError.xhtml (content/aboutNetError.xhtml) +* content/browser/aboutNetError.xhtml (content/aboutNetError.xhtml) content/browser/aboutNetError.js (content/aboutNetError.js) content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png) content/browser/aboutFrameCrashed.html (content/aboutFrameCrashed.html) diff --git a/browser/components/onionservices/content/netError/onionNetError.js b/browser/components/onionservices/content/netError/onionNetError.js index 8fabb3f38eb7..254e50bab4a3 100644 --- a/browser/components/onionservices/content/netError/onionNetError.js +++ b/browser/components/onionservices/content/netError/onionNetError.js @@ -38,6 +38,12 @@ var OnionServicesAboutNetError = { const errPrefix = "onionServices."; const errName = err.substring(errPrefix.length);
+ // tor-browser#40416 - remove this page and updated onionNetErrors with new error once v2 no longer works at all + if (errName === "v2Deprecated") { + V2DeprecatedAboutNetError.initPage(aDoc); + return; + } + this._strings = RPMGetTorStrings();
const stringsObj = this._strings[errName]; diff --git a/browser/components/onionservices/content/netError/v2Deprecated.css b/browser/components/onionservices/content/netError/v2Deprecated.css new file mode 100644 index 000000000000..890468d09761 --- /dev/null +++ b/browser/components/onionservices/content/netError/v2Deprecated.css @@ -0,0 +1,25 @@ +%include ../../../../themes/shared/onionPattern.css + +:root { + --onion-opacity: 1; + --onion-color: var(--card-outline-color); + --onion-radius: 50px; +} + +body { + border: 1.5em solid #FED916; + justify-content: space-between; +} + +div.title { + background-image: url("chrome://browser/skin/onion-warning.svg"); +} + +div#errorPageContainer { + padding-top: 20vh; + width: 66%; +} + +div#learnMoreContainer { + display: block; +} \ No newline at end of file diff --git a/browser/components/onionservices/content/netError/v2Deprecated.js b/browser/components/onionservices/content/netError/v2Deprecated.js new file mode 100644 index 000000000000..195bc187791c --- /dev/null +++ b/browser/components/onionservices/content/netError/v2Deprecated.js @@ -0,0 +1,50 @@ +// Copyright (c) 2021, The Tor Project, Inc. + +"use strict"; + +/* eslint-env mozilla/frame-script */ + +var V2DeprecatedAboutNetError = { + + _selector: { + header: ".title-text", + longDesc: "#errorLongDesc", + learnMoreLink: "#learnMoreLink", + contentContainer: "#errorLongContent", + tryAgainButton: "div#netErrorButtonContainer button.try-again", + }, + + initPage(aDoc) { + this._insertStylesheet(aDoc); + this._populateStrings(aDoc); + }, + + _populateStrings(aDoc) { + // populate strings + const TorStrings = RPMGetTorStrings(); + + aDoc.title = TorStrings.v2Deprecated.pageTitle; + + let headerElem = aDoc.querySelector(this._selector.header); + headerElem.textContent = TorStrings.v2Deprecated.header; + + let longDescriptionElem = aDoc.querySelector(this._selector.longDesc); + longDescriptionElem.textContent = TorStrings.v2Deprecated.longDescription; + + let learnMoreElem = aDoc.querySelector(this._selector.learnMoreLink); + learnMoreElem.setAttribute("href", TorStrings.v2Deprecated.learnMoreURL); + + let tryAgainElem = aDoc.querySelector(this._selector.tryAgainButton); + tryAgainElem.textContent = TorStrings.v2Deprecated.tryAgain; + }, + + _insertStylesheet(aDoc) { + const url = + "chrome://browser/content/onionservices/netError/v2Deprecated.css"; + let linkElem = aDoc.createElement("link"); + linkElem.rel = "stylesheet"; + linkElem.href = url; + linkElem.type = "text/css"; + aDoc.head.appendChild(linkElem); + }, +}; diff --git a/browser/components/onionservices/jar.mn b/browser/components/onionservices/jar.mn index f45b16dc5d29..73258bd9c501 100644 --- a/browser/components/onionservices/jar.mn +++ b/browser/components/onionservices/jar.mn @@ -3,7 +3,13 @@ browser.jar: content/browser/onionservices/authPreferences.js (content/authPreferences.js) content/browser/onionservices/authPrompt.js (content/authPrompt.js) content/browser/onionservices/authUtil.jsm (content/authUtil.jsm) - content/browser/onionservices/netError/ (content/netError/*) + content/browser/onionservices/netError/browser.svg (content/netError/browser.svg) + content/browser/onionservices/netError/network.svg (content/netError/network.svg) + content/browser/onionservices/netError/onionNetError.css (content/netError/onionNetError.css) + content/browser/onionservices/netError/onionNetError.js (content/netError/onionNetError.js) + content/browser/onionservices/netError/onionsite.svg (content/netError/onionsite.svg) +* content/browser/onionservices/netError/v2Deprecated.css (content/netError/v2Deprecated.css) + content/browser/onionservices/netError/v2Deprecated.js (content/netError/v2Deprecated.js) content/browser/onionservices/onionservices.css (content/onionservices.css) content/browser/onionservices/savedKeysDialog.js (content/savedKeysDialog.js) content/browser/onionservices/savedKeysDialog.xhtml (content/savedKeysDialog.xhtml) diff --git a/browser/modules/TorStrings.jsm b/browser/modules/TorStrings.jsm index c086d0fe3117..6eeb2ea06a04 100644 --- a/browser/modules/TorStrings.jsm +++ b/browser/modules/TorStrings.jsm @@ -502,6 +502,14 @@ var TorStrings = { header: getString("introTimedOut.header", "Onionsite Circuit Creation Timed Out"), longDescription: getString("introTimedOut.longDescription", kLongDescFallback), }, + v2Deprecated: { // Deprecation page for v2 Onions + pageTitle: getString("v2Deprecated.pageTitle", "V2 Onion Site Deprecation Warning"), + header: getString("v2Deprecated.header", "Version 2 Onion Sites will be deprecated soon"), + longDescription: getString("v2Deprecated.longDescription", "Tor is ending its support for version 2 onion services beginning in July 2021, and this onion site will no longer be reachable at this address. If you are the site administrator, upgrade to a version 3 onion service soon."), + learnMoreURL: `https://support.torproject.org/$%7BgetLocale()%7D/onionservices/#v2-deprecat..., + tryAgain: getString("v2Deprecated.tryAgain", "Got it"), + tooltip: getString("v2Deprecated.tooltip", "This onion site will not be reachable soon"), + }, authPrompt: { description: getString("authPrompt.description2", "%S is requesting that you authenticate."), diff --git a/browser/themes/shared/identity-block/identity-block.inc.css b/browser/themes/shared/identity-block/identity-block.inc.css index 011fb9f3081c..936ab00d2080 100644 --- a/browser/themes/shared/identity-block/identity-block.inc.css +++ b/browser/themes/shared/identity-block/identity-block.inc.css @@ -181,7 +181,8 @@ toolbar[brighttext] #identity-box[pageproxystate="valid"].chromeUI > #identity-i
#identity-box[pageproxystate="valid"].onionMixedDisplayContent > #identity-icon, #identity-box[pageproxystate="valid"].onionMixedDisplayContentLoadedActiveBlocked > #identity-icon, -#identity-box[pageproxystate="valid"].onionCertUserOverridden > #identity-icon { +#identity-box[pageproxystate="valid"].onionCertUserOverridden > #identity-icon, +#identity-box[pageproxystate="valid"].onionServiceDeprecated > #identity-icon { list-style-image: url(chrome://browser/skin/onion-warning.svg); visibility: visible; } diff --git a/browser/themes/shared/onionPattern.inc.xhtml b/browser/themes/shared/onionPattern.inc.xhtml index 6bbde93684a2..0b6b8b072f9a 100644 --- a/browser/themes/shared/onionPattern.inc.xhtml +++ b/browser/themes/shared/onionPattern.inc.xhtml @@ -9,9 +9,11 @@ - most browser windows, typically the two rows of onions will fill the - bottom of the page. On really wide pages, the onions are centered at - the bottom of the page. + - The root onion-pattern-container div is hidden by default, and can be + - enabled by including onionPattern.css -->
-<div class="onion-pattern-container"> +<div class="onion-pattern-container" style="display: none"> <!-- for some reason, these two elements are focusable, seems related to - flex css somehow; disable their tabindex to fix --> diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index bf9639c82612..0f315f2f87b4 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -7,6 +7,7 @@ #include "nsDocShell.h"
#include <algorithm> +#include <regex>
#ifdef XP_WIN # include <process.h> @@ -3645,6 +3646,9 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI, case NS_ERROR_TOR_ONION_SVC_INTRO_TIMEDOUT: error = "onionServices.introTimedOut"; break; + case NS_ERROR_TOR_ONION_SVC_V2_DEPRECATED: + error = "onionServices.v2Deprecated"; + break; default: break; } @@ -9596,6 +9600,63 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState, return NS_OK; }
+ // tor-browser#40416 + // we only ever want to show the warning page once per session + const auto shouldShouldShowV2DeprecationPage = []() -> bool { + bool retval = false; + if (XRE_IsContentProcess()) { + auto* cc = ContentChild::GetSingleton(); + cc->SendShouldShowV2DeprecationPage(&retval); + } + return retval; + }; + + const auto uriIsV2Onion = [](nsIURI* uri) -> bool { + if (uri) { + nsAutoCString hostString; + uri->GetHost(hostString); + + const std::string_view host(hostString.BeginReading(), hostString.Length()); + + // matches v2 onions with any number of subdomains + const static std::regex v2OnionPattern{ + "^(.*\.)*[a-z2-7]{16}\.onion", + std::regex::icase | std::regex::optimize + }; + + // see if the uri refers to v2 onion host + return std::regex_match( + host.begin(), + host.end(), + v2OnionPattern); + } + return false; + }; + + // only dip in here if this process thinks onion warning page has not been shown + static bool v2DeprecationPageShown = false; + if (!v2DeprecationPageShown) { + // now only advance if the URI we are dealing with + // is a v2 onion address + auto uri = aLoadState->URI(); + if (uriIsV2Onion(uri)) { + // Ok, so we are dealing with a v2 onion, now make + // sure the v2 deprecation page has not been shown in + // in another content process + // + // This is a synchrynous call, so we are blocking until + // we hear back from from the parent process. Each child + // process will need to perform this wait at most once, + // since we are locally caching in v2DeprecationPageShown. + v2DeprecationPageShown = true; + if (shouldShouldShowV2DeprecationPage()) { + DisplayLoadError(NS_ERROR_TOR_ONION_SVC_V2_DEPRECATED, uri, nullptr, nullptr); + return NS_ERROR_LOAD_SHOWED_ERRORPAGE; + } + } + } + + nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service(); if (NS_WARN_IF(!uriLoader)) { return NS_ERROR_UNEXPECTED; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 4269016ec5d5..9c1c1fddf9e9 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -6515,6 +6515,17 @@ NS_IMETHODIMP ContentParent::GetActor(const nsACString& aName, return NS_OK; }
+mozilla::ipc::IPCResult ContentParent::RecvShouldShowV2DeprecationPage(bool* showPage) { + static bool v2DeprecationPageShown = false; + if (v2DeprecationPageShown) { + *showPage = false; + } else { + *showPage = true; + v2DeprecationPageShown = true; + } + return IPC_OK(); +} + } // namespace dom } // namespace mozilla
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 4f10832d8e23..83f9caa1dc6c 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -1280,6 +1280,8 @@ class ContentParent final const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset, HistoryGoResolver&& aResolveRequestedIndex);
+ mozilla::ipc::IPCResult RecvShouldShowV2DeprecationPage(bool* showPage); + // Notify the ContentChild to enable the input event prioritization when // initializing. void MaybeEnableRemoteInputEventQueue(); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index c4dd750e47a4..09d053c69738 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1678,6 +1678,9 @@ child: // WindowContext is managed using the PWindowGlobal actor's lifecycle. async CreateWindowContext(WindowContextInitializer aInit); async DiscardWindowContext(uint64_t aContextId) returns (bool unused); + +parent: + sync ShouldShowV2DeprecationPage() returns (bool showPage); };
} diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini index 88ad49d169e8..df2acb04c750 100644 --- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -1040,6 +1040,9 @@ description = Initialization of WebGL contexts is synchronous by spec. description = Synchronous RPC to allow WebGL to run graphics commands in compositor process and return results to be used in JS return values. [PSocketProcess::GetTLSClientCert] description = Synchronously get client certificate and key from parent process. Once bug 696976 has been fixed, this can be removed. +[PContent::ShouldShowV2DeprecationPage] +description = Synchronously determine whether a client process has already displayed the v2 onion deprecation warning page +
############################################################# # AVOID ADDING NEW MESSAGES TO THIS FILE # diff --git a/js/xpconnect/src/xpc.msg b/js/xpconnect/src/xpc.msg index 31e5e75ba35c..7c8cc9ef181c 100644 --- a/js/xpconnect/src/xpc.msg +++ b/js/xpconnect/src/xpc.msg @@ -262,6 +262,7 @@ XPC_MSG_DEF(NS_ERROR_TOR_ONION_SVC_MISSING_CLIENT_AUTH, "Tor onion service missi XPC_MSG_DEF(NS_ERROR_TOR_ONION_SVC_BAD_CLIENT_AUTH , "Tor onion service wrong client authorization") XPC_MSG_DEF(NS_ERROR_TOR_ONION_SVC_BAD_ADDRESS , "Tor onion service bad address") XPC_MSG_DEF(NS_ERROR_TOR_ONION_SVC_INTRO_TIMEDOUT , "Tor onion service introduction timed out") +XPC_MSG_DEF(NS_ERROR_TOR_ONION_SVC_V2_DEPRECATED , "Tor v2 onion services are deprecated")
/* Profile manager error codes */ XPC_MSG_DEF(NS_ERROR_DATABASE_CHANGED , "Flushing the profiles to disk would have overwritten changes made elsewhere.") diff --git a/xpcom/base/ErrorList.py b/xpcom/base/ErrorList.py index 5f35cf7771f9..6bcd65f9bca9 100755 --- a/xpcom/base/ErrorList.py +++ b/xpcom/base/ErrorList.py @@ -1200,6 +1200,8 @@ with modules["TOR"]: errors["NS_ERROR_TOR_ONION_SVC_BAD_ADDRESS"] = FAILURE(7) # Tor onion service introduction timed out. errors["NS_ERROR_TOR_ONION_SVC_INTRO_TIMEDOUT"] = FAILURE(8) + # Tor v2 onion services are deprecated + errors["NS_ERROR_TOR_ONION_SVC_V2_DEPRECATED"] = FAILURE(9)
# ======================================================================= # 51: NS_ERROR_MODULE_GENERAL
tor-commits@lists.torproject.org