[tbb-commits] [tor-browser/tor-browser-91.4.0esr-11.0-1] Bug 23247: Communicating security expectations for .onion

sysrqb at torproject.org sysrqb at torproject.org
Thu Dec 2 23:12:04 UTC 2021


commit e4b1a35514931573417c7d3d7c76239609f5afdf
Author: Richard Pospesel <richard at torproject.org>
Date:   Fri Jun 8 13:38:40 2018 -0700

    Bug 23247: Communicating security expectations for .onion
    
    Encrypting pages hosted on Onion Services with SSL/TLS is redundant
    (in terms of hiding content) as all traffic within the Tor network is
    already fully encrypted.  Therefore, serving HTTP pages from an Onion
    Service is more or less fine.
    
    Prior to this patch, Tor Browser would mostly treat pages delivered
    via Onion Services as well as pages delivered in the ordinary fashion
    over the internet in the same way.  This created some inconsistencies
    in behaviour and misinformation presented to the user relating to the
    security of pages delivered via Onion Services:
    
     - HTTP Onion Service pages did not have any 'lock' icon indicating
       the site was secure
     - HTTP Onion Service pages would be marked as unencrypted in the Page
       Info screen
     - Mixed-mode content restrictions did not apply to HTTP Onion Service
       pages embedding Non-Onion HTTP content
    
    This patch fixes the above issues, and also adds several new 'Onion'
    icons to the mix to indicate all of the various permutations of Onion
    Services hosted HTTP or HTTPS pages with HTTP or HTTPS content.
    
    Strings for Onion Service Page Info page are pulled from Torbutton's
    localization strings.
---
 browser/base/content/browser-siteIdentity.js       | 39 ++++++++-----
 browser/base/content/pageinfo/security.js          | 64 ++++++++++++++++++----
 .../shared/identity-block/identity-block.inc.css   | 19 +++++++
 .../themes/shared/identity-block/onion-slash.svg   | 13 +++++
 .../themes/shared/identity-block/onion-warning.svg |  9 +++
 browser/themes/shared/identity-block/onion.svg     |  8 +++
 browser/themes/shared/jar.inc.mn                   |  3 +
 dom/base/nsContentUtils.cpp                        | 19 +++++++
 dom/base/nsContentUtils.h                          |  5 ++
 dom/base/nsGlobalWindowOuter.cpp                   |  3 +-
 dom/ipc/WindowGlobalActor.cpp                      |  4 +-
 dom/ipc/WindowGlobalChild.cpp                      |  6 +-
 dom/security/nsMixedContentBlocker.cpp             | 16 +++++-
 .../modules/geckoview/GeckoViewProgress.jsm        |  4 ++
 security/manager/ssl/nsSecureBrowserUI.cpp         | 12 ++++
 15 files changed, 193 insertions(+), 31 deletions(-)

diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js
index 91940db44ca4..b616e3d3a635 100644
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -140,6 +140,10 @@ var gIdentityHandler = {
     );
   },
 
+  get _uriIsOnionHost() {
+    return this._uriHasHost ? this._uri.host.toLowerCase().endsWith(".onion") : false;
+  },
+
   get _isAboutNetErrorPage() {
     return (
       gBrowser.selectedBrowser.documentURI &&
@@ -743,9 +747,9 @@ var gIdentityHandler = {
   get pointerlockFsWarningClassName() {
     // Note that the fullscreen warning does not handle _isSecureInternalUI.
     if (this._uriHasHost && this._isSecureConnection) {
-      return "verifiedDomain";
+      return this._uriIsOnionHost ? "onionVerifiedDomain" : "verifiedDomain";
     }
-    return "unknownIdentity";
+    return this._uriIsOnionHost ? "onionUnknownIdentity" : "unknownIdentity";
   },
 
   /**
@@ -753,6 +757,10 @@ var gIdentityHandler = {
    * built-in (returns false) or imported (returns true).
    */
   _hasCustomRoot() {
+    if (!this._secInfo) {
+      return false;
+    }
+
     let issuerCert = null;
     issuerCert = this._secInfo.succeededCertChain[
       this._secInfo.succeededCertChain.length - 1
@@ -795,11 +803,13 @@ var gIdentityHandler = {
         "identity.extension.label",
         [extensionName]
       );
-    } else if (this._uriHasHost && this._isSecureConnection) {
+    } else if (this._uriHasHost && this._isSecureConnection && this._secInfo) {
       // This is a secure connection.
-      this._identityBox.className = "verifiedDomain";
+      // _isSecureConnection implicitly includes onion services, which may not have an SSL certificate
+      const uriIsOnionHost = this._uriIsOnionHost;
+      this._identityBox.className = uriIsOnionHost ? "onionVerifiedDomain" : "verifiedDomain";
       if (this._isMixedActiveContentBlocked) {
-        this._identityBox.classList.add("mixedActiveBlocked");
+        this._identityBox.classList.add(uriIsOnionHost ? "onionMixedActiveBlocked" : "mixedActiveBlocked");
       }
       if (!this._isCertUserOverridden) {
         // It's a normal cert, verifier is the CA Org.
@@ -810,17 +820,17 @@ var gIdentityHandler = {
       }
     } else if (this._isBrokenConnection) {
       // This is a secure connection, but something is wrong.
-      this._identityBox.className = "unknownIdentity";
+      const uriIsOnionHost = this._uriIsOnionHost;
+      this._identityBox.className = uriIsOnionHost ? "onionUnknownIdentity" : "unknownIdentity";
 
       if (this._isMixedActiveContentLoaded) {
-        this._identityBox.classList.add("mixedActiveContent");
+        this._identityBox.classList.add(uriIsOnionHost ? "onionMixedActiveContent" : "mixedActiveContent");
       } else if (this._isMixedActiveContentBlocked) {
-        this._identityBox.classList.add(
-          "mixedDisplayContentLoadedActiveBlocked"
-        );
+        this._identityBox.classList.add(uriIsOnionHost ? "onionMixedDisplayContentLoadedActiveBlocked" : "mixedDisplayContentLoadedActiveBlocked");
       } else if (this._isMixedPassiveContentLoaded) {
-        this._identityBox.classList.add("mixedDisplayContent");
+        this._identityBox.classList.add(uriIsOnionHost ? "onionMixedDisplayContent" : "mixedDisplayContent");
       } else {
+        // TODO: ignore weak https cipher for onionsites?
         this._identityBox.classList.add("weakCipher");
       }
     } else if (this._isAboutCertErrorPage) {
@@ -833,8 +843,8 @@ var gIdentityHandler = {
       // Network errors and blocked pages get a more neutral icon
       this._identityBox.className = "unknownIdentity";
     } else if (this._isPotentiallyTrustworthy) {
-      // This is a local resource (and shouldn't be marked insecure).
-      this._identityBox.className = "localResource";
+      // This is a local resource or an onion site (and shouldn't be marked insecure).
+      this._identityBox.className = this._uriIsOnionHost ? "onionUnknownIdentity" : "localResource";
     } else {
       // This is an insecure connection.
       let warnOnInsecure =
@@ -858,7 +868,8 @@ var gIdentityHandler = {
     }
 
     if (this._isCertUserOverridden) {
-      this._identityBox.classList.add("certUserOverridden");
+      const uriIsOnionHost = this._uriIsOnionHost;
+      this._identityBox.classList.add(uriIsOnionHost ? "onionCertUserOverridden" : "certUserOverridden");
       // Cert is trusted because of a security exception, verifier is a special string.
       tooltip = gNavigatorBundle.getString(
         "identity.identified.verified_by_you"
diff --git a/browser/base/content/pageinfo/security.js b/browser/base/content/pageinfo/security.js
index 1222c8b0ec35..8d10c8df814c 100644
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -22,6 +22,13 @@ ChromeUtils.defineModuleGetter(
   "PluralForm",
   "resource://gre/modules/PluralForm.jsm"
 );
+XPCOMUtils.defineLazyGetter(
+  this,
+  "gTorButtonBundle",
+  function() {
+    return Services.strings.createBundle("chrome://torbutton/locale/torbutton.properties");
+  }
+);
 
 var security = {
   async init(uri, windowInfo) {
@@ -60,6 +67,11 @@ var security = {
       (Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT |
         Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
     var isEV = ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
+    var isOnion = false;
+    const hostName = this.windowInfo.hostName;
+    if (hostName && hostName.endsWith(".onion")) {
+      isOnion = true;
+    }
 
     let retval = {
       cAName: "",
@@ -69,6 +81,7 @@ var security = {
       isBroken,
       isMixed,
       isEV,
+      isOnion,
       cert: null,
       certificateTransparency: null,
     };
@@ -107,6 +120,7 @@ var security = {
       isBroken,
       isMixed,
       isEV,
+      isOnion,
       cert,
       certChain: certChainArray,
       certificateTransparency: undefined,
@@ -348,22 +362,50 @@ async function securityOnLoad(uri, windowInfo) {
     }
     msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
   } else if (info.encryptionStrength > 0) {
-    hdr = pkiBundle.getFormattedString(
-      "pageInfo_EncryptionWithBitsAndProtocol",
-      [info.encryptionAlgorithm, info.encryptionStrength + "", info.version]
-    );
+    if (!info.isOnion) {
+      hdr = pkiBundle.getFormattedString(
+        "pageInfo_EncryptionWithBitsAndProtocol",
+        [info.encryptionAlgorithm, info.encryptionStrength + "", info.version]
+      );
+    } else {
+      try {
+        hdr = gTorButtonBundle.formatStringFromName(
+          "pageInfo_OnionEncryptionWithBitsAndProtocol",
+          [info.encryptionAlgorithm, info.encryptionStrength + "", info.version]
+        );
+      } catch(err) {
+        hdr = "Connection Encrypted (Onion Service, "
+               + info.encryptionAlgorithm
+               + ", "
+               + info.encryptionStrength
+               + " bit keys, "
+               + info.version
+               + ")";
+      }
+    }
     msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
     msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
   } else {
-    hdr = pkiBundle.getString("pageInfo_NoEncryption");
-    if (windowInfo.hostName != null) {
-      msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [
-        windowInfo.hostName,
-      ]);
+    if (!info.isOnion) {
+      hdr = pkiBundle.getString("pageInfo_NoEncryption");
+      if (windowInfo.hostName != null) {
+        msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [
+          windowInfo.hostName,
+        ]);
+      } else {
+        msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
+      }
+      msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
     } else {
-      msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
+      try {
+        hdr = gTorButtonBundle.GetStringFromName("pageInfo_OnionEncryption");
+      } catch (err) {
+        hdr = "Connection Encrypted (Onion Service)";
+      }
+
+      msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
+      msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
     }
-    msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
   }
   setText("security-technical-shortform", hdr);
   setText("security-technical-longform1", msg1);
diff --git a/browser/themes/shared/identity-block/identity-block.inc.css b/browser/themes/shared/identity-block/identity-block.inc.css
index cd117f6d0cf3..a863d1d7d20e 100644
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -206,6 +206,25 @@ toolbar[brighttext] #identity-box[pageproxystate="valid"].chromeUI #identity-ico
   list-style-image: url(chrome://global/skin/icons/security-broken.svg);
 }
 
+#identity-box[pageproxystate="valid"].onionUnknownIdentity #identity-icon,
+#identity-box[pageproxystate="valid"].onionVerifiedDomain #identity-icon,
+#identity-box[pageproxystate="valid"].onionMixedActiveBlocked #identity-icon {
+  list-style-image: url(chrome://browser/skin/onion.svg);
+  visibility: visible;
+}
+
+#identity-box[pageproxystate="valid"].onionMixedDisplayContent #identity-icon,
+#identity-box[pageproxystate="valid"].onionMixedDisplayContentLoadedActiveBlocked #identity-icon,
+#identity-box[pageproxystate="valid"].onionCertUserOverridden #identity-icon {
+  list-style-image: url(chrome://browser/skin/onion-warning.svg);
+  visibility: visible;
+}
+
+#identity-box[pageproxystate="valid"].onionMixedActiveContent #identity-icon {
+  list-style-image: url(chrome://browser/skin/onion-slash.svg);
+  visibility: visible;
+}
+
 #permissions-granted-icon {
   list-style-image: url(chrome://browser/skin/permissions.svg);
 }
diff --git a/browser/themes/shared/identity-block/onion-slash.svg b/browser/themes/shared/identity-block/onion-slash.svg
new file mode 100644
index 000000000000..d049bcd39cae
--- /dev/null
+++ b/browser/themes/shared/identity-block/onion-slash.svg
@@ -0,0 +1,13 @@
+<svg viewBox="0 0 16 16" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
+  <g fill-opacity="context-fill-opacity" fill="context-fill">
+    <path d="m13.3034 13.3032c-1.3572 1.3573-3.2323 2.1968-5.3034 2.1968-4.14214 0-7.5-3.3579-7.5-7.5 0-2.07093.83935-3.94582 2.19643-5.30303l.82867.82861c-1.14502 1.14515-1.85322 2.72708-1.85322 4.47442 0 3.4949 2.83319 6.3281 6.32812 6.3281 1.74752 0 3.3296-.7083 4.4748-1.8536z"/>
+    <path d="m14.1137 12.3453c.8729-1.226 1.3863-2.72567 1.3863-4.3453 0-4.14214-3.3579-7.5-7.5-7.5-1.61963 0-3.11935.51339-4.34531 1.38631l.84258.84258c1.00297-.66783 2.2074-1.05701 3.50273-1.05701 3.4949 0 6.3281 2.83319 6.3281 6.32812 0 1.29533-.3892 2.4998-1.057 3.5027z"/>
+    <path d="m12.4902 10.7218c.4822-.79365.7598-1.7253.7598-2.72181 0-2.89949-2.3505-5.25-5.25001-5.25-.9965 0-1.92816.27764-2.72184.75978l.86063.86062c.558-.28671 1.19071-.44852 1.86121-.44852 2.25231 0 4.07811 1.82584 4.07811 4.07812 0 .67051-.1618 1.30322-.4485 1.86122z"/>
+    <path d="m11.7124 11.7122-.8287-.8286c-.738.738-1.75754 1.1945-2.88371 1.1945-2.25228 0-4.07812-1.8258-4.07812-4.07811 0-1.12605.45639-2.14551 1.19428-2.88349l-.82868-.82861c-.94994.95005-1.53748 2.26246-1.53748 3.7121 0 2.89951 2.35051 5.25001 5.25 5.25001 1.44979 0 2.76231-.5877 3.71241-1.5378z"/>
+    <path d="m5.87853 5.87883c-.5428.54288-.87853 1.29282-.87853 2.12117 0 1.65686 1.34315 3 3 3 .82844 0 1.57845-.3358 2.1213-.8787l-.82863-.8286c-.33083.33081-.78785.53543-1.29267.53543-1.00964 0-1.82812-.81848-1.82812-1.82813 0-.50476.20457-.96175.53533-1.29256z"/>
+    <path d="m9.8272 8.05881c.00062-.01952.00093-.03913.00093-.05881 0-1.00964-.81848-1.82812-1.82813-1.82812-.01968 0-.03928.00031-.05881.00093l-.98589-.9859c.32532-.12086.6773-.18691 1.0447-.18691 1.65686 0 3 1.34315 3 3 0 .3674-.066.71938-.1869 1.04471z"/>
+    <path d="m8 15.5c-4.14214 0-7.5-3.3579-7.5-7.5 0-2.07093.83935-3.94582 2.19643-5.30303l5.30357 5.30316z"/>
+    <path d="m8 6.23161v-5.73161c-1.61963 0-3.11935.51339-4.34531 1.38631z"/>
+  </g>
+  <path d="m14.1161 15.6245c-.0821.0001-.1634-.016-.2393-.0474-.0758-.0314-.1447-.0775-.2027-.1356l-12.749984-12.749c-.109266-.11882-.168406-.27526-.165071-.43666.003335-.16139.068886-.31525.182967-.42946.114078-.11421.267868-.17994.429258-.18345.16139-.00352.3179.05544.43685.16457l12.74998 12.75c.1168.1176.1824.2767.1824.4425s-.0656.3249-.1824.4425c-.058.058-.1269.1039-.2028.1352-.0759.0312-.1571.0471-.2392.0468z" fill="#ff0039"/>
+</svg>
diff --git a/browser/themes/shared/identity-block/onion-warning.svg b/browser/themes/shared/identity-block/onion-warning.svg
new file mode 100644
index 000000000000..e078f5ea6e33
--- /dev/null
+++ b/browser/themes/shared/identity-block/onion-warning.svg
@@ -0,0 +1,9 @@
+<svg viewBox="0 0 16 16" width="16" height="16"  xmlns="http://www.w3.org/2000/svg">
+  <g fill-opacity="context-fill-opacity" fill="context-fill">
+    <path d="m6.23025 15.393c-3.53742-.6033-6.23025-3.6837-6.23025-7.393 0-4.14214 3.35786-7.5 7.5-7.5 4.1421 0 7.5 3.35786 7.5 7.5 0 .66904-.0876 1.31762-.252 1.9349l-.9277-1.61757c.0052-.10512.0078-.21092.0078-.31733 0-3.49493-2.8332-6.32812-6.3281-6.32812-3.49493 0-6.32812 2.83319-6.32812 6.32812 0 2.9851 2.06684 5.4874 4.84752 6.154-.05998.4351.02161.8644.21085 1.239z"/>
+    <path d="m6.42277 13.1394c-2.38278-.4969-4.17278-2.6091-4.17278-5.13941 0-2.89949 2.35051-5.25 5.25-5.25 2.48426 0 4.56551 1.72549 5.11071 4.04336-.4001-.23081-.8624-.32542-1.3135-.28382-.5952-1.51508-2.07094-2.58766-3.79721-2.58766-2.25228 0-4.07812 1.82584-4.07812 4.07812 0 2.09871 1.58539 3.82721 3.6239 4.05311z"/>
+    <path d="m10.2788 6.8674c-.44688-1.09541-1.52269-1.8674-2.7788-1.8674-1.65685 0-3 1.34315-3 3 0 1.65686 1.34315 3 3 3 .05095 0 .10161-.0013.15193-.0038l.83612-1.45782c-.28491.18337-.62405.28975-.98805.28975-1.00964 0-1.82812-.81848-1.82812-1.82813 0-1.00964.81848-1.82812 1.82812-1.82812 1.00965 0 1.82813.81848 1.82813 1.82812 0 .02558-.00053.05104-.00157.07637l.27582-.48091c.17642-.30737.40992-.55005.67642-.72806z"/>
+    <path d="m6.23025 15.393c-3.53742-.6033-6.23025-3.6837-6.23025-7.393 0-4.14214 3.35786-7.5 7.5-7.5v10.7611l-1.20826 2.1067c-.39168.6811-.36535 1.4237-.06149 2.0252z"/>
+    <path d="m15.8456 13.8662-3.311-5.77295c-.454-.791-1.615-.791-2.069 0l-3.31095 5.77295c-.446.775.126 1.7341 1.034 1.7341h6.62295c.907 0 1.479-.9591 1.034-1.7341zm-3.721-1.892c0 .1658-.0658.3248-.183.442s-.2762.183-.442.183c-.1657 0-.3247-.0658-.4419-.183s-.1831-.2762-.1831-.442v-1.747c0-.1657.0659-.32468.1831-.44189s.2762-.18306.4419-.18306c.1658 0 .3248.06585.442.18306s.183.27619.183.44189zm-.625 2.626c-.1657 0-.3247-.0658-.4419-.183s-.1831-.2762-.1831-.442c0-.1657.0659-.3247.1831-.4419s.2762-.1831.4419-.1831c.1658 0 .3248.0659.442.1831s.183.2762.183.4419c0 .1658-.0658.3248-.183.442s-.2762.183-.442.183z"/>
+  </g>
+</svg>
\ No newline at end of file
diff --git a/browser/themes/shared/identity-block/onion.svg b/browser/themes/shared/identity-block/onion.svg
new file mode 100644
index 000000000000..382a061774aa
--- /dev/null
+++ b/browser/themes/shared/identity-block/onion.svg
@@ -0,0 +1,8 @@
+<svg fill="context-fill" fill-opacity="context-fill-opacity" viewBox="0 0 16 16" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
+  <g clip-rule="evenodd" fill-rule="evenodd">
+    <path d="m11 8c0 1.65686-1.34314 3-3 3-1.65685 0-3-1.34314-3-3 0-1.65685 1.34315-3 3-3 1.65686 0 3 1.34315 3 3zm-1.17187 0c0 1.00965-.81848 1.82813-1.82813 1.82813-1.00964 0-1.82812-.81848-1.82812-1.82813 0-1.00964.81848-1.82812 1.82812-1.82812 1.00965 0 1.82813.81848 1.82813 1.82812z"/>
+    <path d="m7.99999 13.25c2.89951 0 5.25001-2.3505 5.25001-5.25001 0-2.89949-2.3505-5.25-5.25001-5.25-2.89949 0-5.25 2.35051-5.25 5.25 0 2.89951 2.35051 5.25001 5.25 5.25001zm0-1.1719c2.25231 0 4.07811-1.8258 4.07811-4.07811 0-2.25228-1.8258-4.07812-4.07811-4.07812-2.25228 0-4.07812 1.82584-4.07812 4.07812 0 2.25231 1.82584 4.07811 4.07812 4.07811z"/>
+    <path d="m8 15.5c4.1421 0 7.5-3.3579 7.5-7.5 0-4.14214-3.3579-7.5-7.5-7.5-4.14214 0-7.5 3.35786-7.5 7.5 0 4.1421 3.35786 7.5 7.5 7.5zm0-1.1719c3.4949 0 6.3281-2.8332 6.3281-6.3281 0-3.49493-2.8332-6.32812-6.3281-6.32812-3.49493 0-6.32812 2.83319-6.32812 6.32812 0 3.4949 2.83319 6.3281 6.32812 6.3281z"/>
+  </g>
+  <path d="m.5 8c0 4.1421 3.35786 7.5 7.5 7.5v-15c-4.14214 0-7.5 3.35786-7.5 7.5z"/>
+</svg>
\ No newline at end of file
diff --git a/browser/themes/shared/jar.inc.mn b/browser/themes/shared/jar.inc.mn
index 4f74932df96f..c1039e790245 100644
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -49,6 +49,9 @@
   skin/classic/browser/downloads/notification-start-animation.svg        (../shared/downloads/notification-start-animation.svg)
   skin/classic/browser/drm-icon.svg                            (../shared/drm-icon.svg)
   skin/classic/browser/permissions.svg                         (../shared/identity-block/permissions.svg)
+  skin/classic/browser/onion.svg                               (../shared/identity-block/onion.svg)
+  skin/classic/browser/onion-slash.svg                         (../shared/identity-block/onion-slash.svg)
+  skin/classic/browser/onion-warning.svg                       (../shared/identity-block/onion-warning.svg)
   skin/classic/browser/illustrations/error-malformed-url.svg                (../shared/illustrations/error-malformed-url.svg)
   skin/classic/browser/notification-icons/autoplay-media.svg                (../shared/notification-icons/autoplay-media.svg)
   skin/classic/browser/notification-icons/autoplay-media-blocked.svg        (../shared/notification-icons/autoplay-media-blocked.svg)
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 4cc0cd114ce5..b816f8b05067 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9325,6 +9325,25 @@ bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
   return principal->GetIsOriginPotentiallyTrustworthy();
 }
 
+/* static */ bool nsContentUtils::DocumentHasOnionURI(Document* aDocument) {
+  if (!aDocument) {
+    return false;
+  }
+
+  nsIURI* uri = aDocument->GetDocumentURI();
+  if (!uri) {
+    return false;
+  }
+
+  nsAutoCString host;
+  if (NS_SUCCEEDED(uri->GetHost(host))) {
+    bool hasOnionURI = StringEndsWith(host, ".onion"_ns);
+    return hasOnionURI;
+  }
+
+  return false;
+}
+
 /* static */
 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
   NodeInfo* nodeInfo = aElement->NodeInfo();
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 94805199a8fd..4b6256fb9c2c 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2999,6 +2999,11 @@ class nsContentUtils {
    */
   static bool HttpsStateIsModern(Document* aDocument);
 
+  /**
+   * Returns true of the document's URI is a .onion
+   */
+  static bool DocumentHasOnionURI(Document* aDocument);
+
   /**
    * Returns true if the channel is for top-level window and is over secure
    * context.
diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
index 41c93c51cf3b..4da5365f214d 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1880,7 +1880,8 @@ bool nsGlobalWindowOuter::ComputeIsSecureContext(Document* aDocument,
     return false;
   }
 
-  if (nsContentUtils::HttpsStateIsModern(aDocument)) {
+  if (nsContentUtils::HttpsStateIsModern(aDocument) ||
+      nsContentUtils::DocumentHasOnionURI(aDocument)) {
     return true;
   }
 
diff --git a/dom/ipc/WindowGlobalActor.cpp b/dom/ipc/WindowGlobalActor.cpp
index 8a3b49edd4d7..9975136e8e18 100644
--- a/dom/ipc/WindowGlobalActor.cpp
+++ b/dom/ipc/WindowGlobalActor.cpp
@@ -21,6 +21,7 @@
 #include "mozilla/net/CookieJarSettings.h"
 #include "mozilla/dom/WindowGlobalChild.h"
 #include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/nsMixedContentBlocker.h"
 
 #include "nsGlobalWindowInner.h"
 #include "nsNetUtil.h"
@@ -131,7 +132,8 @@ WindowGlobalInit WindowGlobalActor::WindowInitializer(
 
   // Init Mixed Content Fields
   nsCOMPtr<nsIURI> innerDocURI = NS_GetInnermostURI(doc->GetDocumentURI());
-  fields.mIsSecure = innerDocURI && innerDocURI->SchemeIs("https");
+  fields.mIsSecure = innerDocURI && (innerDocURI->SchemeIs("https") ||
+      nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI));
 
   nsCOMPtr<nsITransportSecurityInfo> securityInfo;
   if (nsCOMPtr<nsIChannel> channel = doc->GetChannel()) {
diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp
index 84c060c41534..73ac6a0cf96d 100644
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -48,6 +48,8 @@
 #  include "GeckoProfiler.h"
 #endif
 
+#include "mozilla/dom/nsMixedContentBlocker.h"
+
 using namespace mozilla::ipc;
 using namespace mozilla::dom::ipc;
 
@@ -234,7 +236,9 @@ void WindowGlobalChild::OnNewDocument(Document* aDocument) {
   nsCOMPtr<nsIURI> innerDocURI =
       NS_GetInnermostURI(aDocument->GetDocumentURI());
   if (innerDocURI) {
-    txn.SetIsSecure(innerDocURI->SchemeIs("https"));
+    txn.SetIsSecure(
+        innerDocURI->SchemeIs("https") ||
+        nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI));
   }
 
   MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp
index 01c7877e020d..dab3f19bad40 100644
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -634,8 +634,8 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
     return NS_OK;
   }
 
-  // Check the parent scheme. If it is not an HTTPS page then mixed content
-  // restrictions do not apply.
+  // Check the parent scheme. If it is not an HTTPS or .onion page then mixed
+  // content restrictions do not apply.
   nsCOMPtr<nsIURI> innerRequestingLocation =
       NS_GetInnermostURI(requestingLocation);
   if (!innerRequestingLocation) {
@@ -650,6 +650,17 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
 
   bool parentIsHttps = innerRequestingLocation->SchemeIs("https");
   if (!parentIsHttps) {
+    bool parentIsOnion = IsPotentiallyTrustworthyOnion(innerRequestingLocation);
+    if (!parentIsOnion) {
+      *aDecision = ACCEPT;
+      return NS_OK;
+    }
+  }
+
+  bool isHttpScheme = innerContentLocation->SchemeIs("http");
+  // .onion URLs are encrypted and authenticated. Don't treat them as mixed
+  // content if potentially trustworthy (i.e. whitelisted).
+  if (isHttpScheme && IsPotentiallyTrustworthyOnion(innerContentLocation)) {
     *aDecision = ACCEPT;
     MOZ_LOG(sMCBLog, LogLevel::Verbose,
             ("  -> decision: Request will be allowed because the requesting "
@@ -676,7 +687,6 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
     return NS_OK;
   }
 
-  bool isHttpScheme = innerContentLocation->SchemeIs("http");
   if (isHttpScheme && IsPotentiallyTrustworthyOrigin(innerContentLocation)) {
     *aDecision = ACCEPT;
     return NS_OK;
diff --git a/mobile/android/modules/geckoview/GeckoViewProgress.jsm b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
index 17069dbe657f..c1346b1858cf 100644
--- a/mobile/android/modules/geckoview/GeckoViewProgress.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
@@ -145,6 +145,10 @@ var IdentityHandler = {
       result.host = uri.host;
     }
 
+    if (!aBrowser.securityUI.secInfo) {
+      return result;
+    }
+
     const cert = aBrowser.securityUI.secInfo.serverCert;
 
     result.certificate = aBrowser.securityUI.secInfo.serverCert.getBase64DERString();
diff --git a/security/manager/ssl/nsSecureBrowserUI.cpp b/security/manager/ssl/nsSecureBrowserUI.cpp
index b4de1a331ffc..f1ce39582854 100644
--- a/security/manager/ssl/nsSecureBrowserUI.cpp
+++ b/security/manager/ssl/nsSecureBrowserUI.cpp
@@ -9,6 +9,7 @@
 #include "mozilla/Logging.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/Document.h"
+#include "mozilla/dom/nsMixedContentBlocker.h"
 #include "nsContentUtils.h"
 #include "nsIChannel.h"
 #include "nsDocShell.h"
@@ -85,6 +86,17 @@ void nsSecureBrowserUI::RecomputeSecurityFlags() {
         }
       }
     }
+
+    // any protocol routed over tor is secure
+    if (!(mState & nsIWebProgressListener::STATE_IS_SECURE)) {
+      nsCOMPtr<nsIURI> innerDocURI = NS_GetInnermostURI(win->GetDocumentURI());
+      if (innerDocURI &&
+          nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI)) {
+        MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  is onion"));
+        mState = (mState & ~nsIWebProgressListener::STATE_IS_INSECURE) |
+                 nsIWebProgressListener::STATE_IS_SECURE;
+      }
+    }
   }
 
   // Add upgraded-state flags when request has been





More information about the tbb-commits mailing list