[tbb-commits] [tor-browser/tor-browser-78.15.0esr-10.5-1] Bug 40416: Add v2 Onion deprecation warnings

sysrqb at torproject.org sysrqb at torproject.org
Tue Sep 28 19:07:34 UTC 2021


commit 75c668227b8899bcb81ec1b578eb5e48b96ff5b2
Author: Richard Pospesel <richard at 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/${getLocale()}/onionservices/#v2-deprecation`,
+        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





More information about the tbb-commits mailing list