[tor-commits] [tor-browser/tor-browser-60.0.1esr-8.0-1] Bug 23247: Communicating security expectations for .onion

gk at torproject.org gk at torproject.org
Tue Jun 19 08:00:25 UTC 2018


commit 06fd55e2e061c7348f86d7f0ced3d64ffac9baad
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.js                    | 66 ++++++++++++++--------
 browser/base/content/pageinfo/security.js          | 57 +++++++++++++++----
 .../shared/identity-block/identity-block.inc.css   | 20 +++++++
 .../shared/identity-block/onion-disabled.svg       |  9 +++
 .../themes/shared/identity-block/onion-lock.svg    |  9 +++
 browser/themes/shared/identity-block/onion.svg     |  8 +++
 browser/themes/shared/jar.inc.mn                   |  3 +
 dom/base/nsContentUtils.cpp                        | 21 +++++++
 dom/base/nsContentUtils.h                          |  5 ++
 dom/base/nsGlobalWindowOuter.cpp                   |  3 +-
 dom/presentation/PresentationRequest.cpp           |  3 +-
 dom/security/nsMixedContentBlocker.cpp             | 17 +++++-
 security/manager/ssl/nsSecureBrowserUIImpl.cpp     | 57 +++++++++++--------
 13 files changed, 216 insertions(+), 62 deletions(-)

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 68bbe3af99e9..fbfbf3e809ca 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7507,6 +7507,10 @@ var gIdentityHandler = {
            Services.prefs.getBoolPref("security.insecure_password.ui.enabled");
   },
 
+  get _uriIsOnionHost() {
+    return this._uriHasHost ? this._uri.host.toLowerCase().endsWith(".onion") : false;
+  },
+
   // smart getters
   get _identityPopup() {
     delete this._identityPopup;
@@ -7806,12 +7810,12 @@ var gIdentityHandler = {
   get pointerlockFsWarningClassName() {
     // Note that the fullscreen warning does not handle _isSecureInternalUI.
     if (this._uriHasHost && this._isEV) {
-      return "verifiedIdentity";
+      return this._uriIsOnionHost ? "onionVerifiedIdentity" : "verifiedIdentity";
     }
     if (this._uriHasHost && this._isSecure) {
-      return "verifiedDomain";
+      return this._uriIsOnionHost ? "onionVerifiedDomain" : "verifiedDomain";
     }
-    return "unknownIdentity";
+    return this._uriIsOnionHost ? "onionUnknownIdentity" : "unknownIdentity";
   },
 
   /**
@@ -7832,9 +7836,10 @@ var gIdentityHandler = {
       let brandBundle = document.getElementById("bundle_brand");
       icon_label = brandBundle.getString("brandShorterName");
     } else if (this._uriHasHost && this._isEV) {
-      this._identityBox.className = "verifiedIdentity";
+      let uriIsOnionHost = this._uriIsOnionHost;
+      this._identityBox.className = uriIsOnionHost ? "onionVerifiedIdentity" : "verifiedIdentity";
       if (this._isMixedActiveContentBlocked) {
-        this._identityBox.classList.add("mixedActiveBlocked");
+        this._identityBox.classList.add(uriIsOnionHost ? "onionMixedActiveBlocked" : "mixedActiveBlocked");
       }
 
       if (!this._isCertUserOverridden) {
@@ -7860,10 +7865,17 @@ var gIdentityHandler = {
       let extensionName = this._pageExtensionPolicy.name;
       icon_label = gNavigatorBundle.getFormattedString(
         "identity.extension.label", [extensionName]);
-    } else if (this._uriHasHost && this._isSecure) {
-      this._identityBox.className = "verifiedDomain";
+    // _isSecure implicitly includes onion services, which may not have an SSL certificate
+    } else if (this._uriHasHost && this._isSecure && this._sslStatus != null) {
+      let uriIsOnionHost = this._uriIsOnionHost;
+      if (uriIsOnionHost) {
+        this._identityBox.className = this._sslStatus.serverCert.isSelfSigned ? "onionSelfSigned" : "onionVerifiedDomain";
+      } else {
+        this._identityBox.className = "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.
@@ -7878,31 +7890,37 @@ var gIdentityHandler = {
         // For net errors we should not show notSecure as it's likely confusing
       this._identityBox.className = "unknownIdentity";
     } else {
+      let uriIsOnionHost = this._uriIsOnionHost;
       if (this._isBroken) {
-        this._identityBox.className = "unknownIdentity";
+        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 {
           this._identityBox.classList.add("weakCipher");
         }
       } else {
-        let warnOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_icon.enabled") ||
-                             (Services.prefs.getBoolPref("security.insecure_connection_icon.pbmode.enabled") &&
-                             PrivateBrowsingUtils.isWindowPrivate(window));
-        let className = warnOnInsecure ? "notSecure" : "unknownIdentity";
-        this._identityBox.className = className;
-
-        let warnTextOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_text.enabled") ||
-                                 (Services.prefs.getBoolPref("security.insecure_connection_text.pbmode.enabled") &&
-                                 PrivateBrowsingUtils.isWindowPrivate(window));
-        if (warnTextOnInsecure) {
-          icon_label = gNavigatorBundle.getString("identity.notSecure.label");
-          this._identityBox.classList.add("notSecureText");
+        if (!uriIsOnionHost) {
+          let warnOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_icon.enabled") ||
+                               (Services.prefs.getBoolPref("security.insecure_connection_icon.pbmode.enabled") &&
+                               PrivateBrowsingUtils.isWindowPrivate(window));
+          let className = warnOnInsecure ? "notSecure" : "unknownIdentity";
+          this._identityBox.className = className;
+
+          let warnTextOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_text.enabled") ||
+                                   (Services.prefs.getBoolPref("security.insecure_connection_text.pbmode.enabled") &&
+                                   PrivateBrowsingUtils.isWindowPrivate(window));
+          if (warnTextOnInsecure) {
+            icon_label = gNavigatorBundle.getString("identity.notSecure.label");
+            this._identityBox.classList.add("notSecureText");
+          }
+        // http onion is secure
+        } else {
+          this._identityBox.className = "onionUnknownIdentity";
         }
       }
       if (this._hasInsecureLoginForms) {
diff --git a/browser/base/content/pageinfo/security.js b/browser/base/content/pageinfo/security.js
index 2d6c5fb1d396..5a827488ccad 100644
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -12,6 +12,10 @@ ChromeUtils.defineModuleGetter(this, "LoginHelper",
 ChromeUtils.defineModuleGetter(this, "PluralForm",
                                "resource://gre/modules/PluralForm.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "gTorButtonBundle", function() {
+  return Services.strings.createBundle("chrome://torbutton/locale/torbutton.properties");
+});
+
 var security = {
   init(uri, windowInfo) {
     this.uri = uri;
@@ -48,6 +52,8 @@ var security = {
       (ui.state & Ci.nsIWebProgressListener.STATE_IS_INSECURE);
     var isEV =
       (ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
+    var isOnion = hostName.endsWith(".onion");
+
     ui.QueryInterface(nsISSLStatusProvider);
     var status = ui.SSLStatus;
 
@@ -65,6 +71,7 @@ var security = {
         isBroken,
         isMixed,
         isEV,
+        isOnion,
         cert,
         certificateTransparency: undefined
       };
@@ -121,6 +128,7 @@ var security = {
       isBroken,
       isMixed,
       isEV,
+      isOnion,
       cert: null,
       certificateTransparency: null
     };
@@ -257,20 +265,49 @@ 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], 3);
+      } 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");
     security._cert = info.cert;
   } else {
-    hdr = pkiBundle.getString("pageInfo_NoEncryption");
-    if (info.hostName != null)
-      msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]);
-    else
-      msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
-    msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
+    if (!info.isOnion) {
+      hdr = pkiBundle.getString("pageInfo_NoEncryption");
+      if (info.hostName != null) {
+        msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]);
+      } else {
+        msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
+      }
+      msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
+    } else {
+      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");
+    }
   }
   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 ac85070708c4..36bab9f13715 100644
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -200,6 +200,26 @@
   visibility: visible;
 }
 
+#urlbar[pageproxystate="valid"] > #identity-box.onionUnknownIdentity > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.onionSelfSigned > #connection-icon {
+  list-style-image: url(chrome://browser/skin/onion.svg);
+  visibility: visible;
+}
+
+#urlbar[pageproxystate="valid"] > #identity-box.onionVerifiedDomain > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.onionVerifiedIdentity > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.onionMixedActiveBlocked > #connection-icon {
+  list-style-image: url(chrome://browser/skin/onion-lock.svg);
+  visibility: visible;
+}
+
+#urlbar[pageproxystate="valid"] > #identity-box.onionMixedActiveContent > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.onionMixedDisplayContentLoadedActiveBlocked > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.onionMixedDisplayContent > #connection-icon {
+  list-style-image: url(chrome://browser/skin/onion-disabled.svg);
+  visibility: visible;
+}
+
 #identity-box.extensionPage > #extension-icon {
   list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.svg);
   visibility: visible;
diff --git a/browser/themes/shared/identity-block/onion-disabled.svg b/browser/themes/shared/identity-block/onion-disabled.svg
new file mode 100644
index 000000000000..f5b20a87a923
--- /dev/null
+++ b/browser/themes/shared/identity-block/onion-disabled.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>onion-disabled</title>
+    <defs></defs>
+    <g id="onion-disabled" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M3.55670557,13.5290809 C2.58943965,12.7067569 2,11.5773827 2,10.2383803 C2,8.40140797 3.11044937,6.84614642 4.80245393,5.95790561 C4.86922061,5.92314081 4.937062,5.88953483 5.00557508,5.85697179 C5.34048317,5.68140952 5.95495153,5.31464081 6.18735866,4.8796172 C6.261514,4.74090562 6.26057363,4.55305979 6.20885297,4.4070476 C6.11172559,4.13310092 5.57517408,3.53792744 5.57517408,3.53792744 L6.39988357,3 L7.47070238,3.38224206 C7.23186151,2.5028722 6.97233174,1.62308364 8.87366696,4.4408921e-16 L8.87366696,0.000113687544 C8.14572562,0.965320936 8.77600936,1.33867083 8.69392696,2.02329722 C9.36752565,0.933374736 10.7903253,0.789787368 11.8884333,0.334923505 C10.6431583,1.59850841 9.72300609,3.05795044 8.87524898,3.88361241 L9.6358507,4.15511863 C9.6358507,4.15511863 9.27501511,4.42489353 9.52743882,4.95911272 C9.65035936,5.21926936 9.85710767,5.38741514 10.0560643,5.49588134 C10.2394376,5.55069385 10.4187806,5.61269109 10.5936905,5.68164129 C10.7858529,5.7574053 10.972
 3418,5.84145128 11.1524919,5.93329454 L3.55670557,13.5290809 Z M5.3590214,14.5551922 L12.7674177,7.1467959 C13.5406039,7.99044704 14,9.05632625 14,10.2383803 C14,13.0968586 11.3137496,15 8,15 C7.05229776,15 6.15591919,14.8443406 5.3590214,14.5551922 Z" id="Combined-Shape" fill="#4A4A4A" fill-rule="nonzero"></path>
+        <path d="M13.7928932,1.29289322 C14.1834175,0.902368927 14.8165825,0.902368927 15.2071068,1.29289322 C15.5976311,1.68341751 15.5976311,2.31658249 15.2071068,2.70710678 L2.70710678,15.2071068 C2.31658249,15.5976311 1.68341751,15.5976311 1.29289322,15.2071068 C0.902368927,14.8165825 0.902368927,14.1834175 1.29289322,13.7928932 L13.7928932,1.29289322 Z" id="Line-2" fill="#D92D21" fill-rule="nonzero"></path>
+    </g>
+</svg>
diff --git a/browser/themes/shared/identity-block/onion-lock.svg b/browser/themes/shared/identity-block/onion-lock.svg
new file mode 100644
index 000000000000..c88247eb19d7
--- /dev/null
+++ b/browser/themes/shared/identity-block/onion-lock.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>onion+lock</title>
+    <defs></defs>
+    <g id="onion+lock" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M6.12228848,14.9544711 C3.22461281,14.6498012 1,12.8396709 1,10.2383803 C1,8.40140797 2.11044937,6.84614642 3.80245393,5.95790561 C3.86922061,5.92314081 3.937062,5.88953483 4.00557508,5.85697179 C4.34048317,5.68140952 4.95495153,5.31464081 5.18735866,4.8796172 C5.261514,4.74090562 5.26057363,4.55305979 5.20885297,4.4070476 C5.11172559,4.13310092 4.57517408,3.53792744 4.57517408,3.53792744 L5.39988357,3 L6.47070238,3.38224206 C6.23186151,2.5028722 5.97233174,1.62308364 7.87366696,0 L7.87366696,0.000113687544 C7.14572562,0.965320936 7.77600936,1.33867083 7.69392696,2.02329722 C8.36752565,0.933374736 9.79032527,0.789787368 10.8884333,0.334923505 C9.64315826,1.59850841 8.72300609,3.05795044 7.87524898,3.88361241 L8.6358507,4.15511863 C8.6358507,4.15511863 8.27501511,4.42489353 8.52743882,4.95911272 C8.56957789,5.04829846 8.6215686,5.12667085 8.67971976,5.19553247 C7.54569301,5.94640706 6.80000003,7.23368269 6.80000003,8.70000013 L6.80000003,8.80234816 C6.31732959,9.1249
 1448 6,9.67479474 6,10.3000002 L6,14.3000003 C6,14.530995 6.04331804,14.7517071 6.12228848,14.9544711 Z" id="Combined-Shape" fill="#589A0F" fill-rule="nonzero"></path>
+        <path d="M11.0000002,5.5 C9.22720009,5.5 7.80000003,6.92720006 7.80000003,8.70000013 L7.80000003,9.50000016 C7.35680001,9.50000016 7,9.85680017 7,10.3000002 L7,14.3000003 C7,14.7432004 7.35680001,15.1000004 7.80000003,15.1000004 L14.2000003,15.1000004 C14.6432003,15.1000004 15.0000003,14.7432004 15.0000003,14.3000003 L15.0000003,10.3000002 C15.0000003,9.85680017 14.6432003,9.50000016 14.2000003,9.50000016 L14.2000003,8.70000013 C14.2000003,6.92720006 12.7728002,5.5 11.0000002,5.5 Z M11.0000002,7.10000006 C11.8864002,7.10000006 12.6000002,7.81360009 12.6000002,8.70000013 L12.6000002,9.50000016 L9.4000001,9.50000016 L9.4000001,8.70000013 C9.4000001,7.81360009 10.1136001,7.10000006 11.0000002,7.10000006 Z" id="Shape" fill="#589A0F" fill-rule="nonzero"></path>
+    </g>
+</svg>
diff --git a/browser/themes/shared/identity-block/onion.svg b/browser/themes/shared/identity-block/onion.svg
new file mode 100644
index 000000000000..e102f41c5991
--- /dev/null
+++ b/browser/themes/shared/identity-block/onion.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>onion</title>
+    <defs></defs>
+    <g id="onion" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M7.47070238,3.38224206 C7.23186151,2.5028722 6.97233174,1.62308364 8.87366696,4.4408921e-16 L8.87366696,0.000113687544 C8.14572562,0.965320936 8.77600936,1.33867083 8.69392696,2.02329722 C9.36752565,0.933374736 10.7903253,0.789787368 11.8884333,0.334923505 C10.6431583,1.59850841 9.72300609,3.05795044 8.87524898,3.88361241 L9.6358507,4.15511863 C9.6358507,4.15511863 9.27501511,4.42489353 9.52743882,4.95911272 C9.65035936,5.21926936 9.85710767,5.38741514 10.0560643,5.49588134 C10.2394376,5.55069385 10.4187806,5.61269109 10.5936905,5.68164129 C12.6087813,6.47613299 14,8.18134675 14,10.2383803 C14,13.0968586 11.3137496,15 8,15 C4.68625036,15 2,13.0968586 2,10.2383803 C2,8.40140797 3.11044937,6.84614642 4.80245393,5.95790561 C4.86922061,5.92314081 4.937062,5.88953483 5.00557508,5.85697179 C5.34048317,5.68140952 5.95495153,5.31464081 6.18735866,4.8796172 C6.261514,4.74090562 6.26057363,4.55305979 6.20885297,4.4070476 C6.11172559,4.13310092 5.57517408,3.53792744 5.57517408
 ,3.53792744 L6.39988357,3 L7.47070238,3.38224206 Z" id="Combined-Shape" fill="#589A0F" fill-rule="nonzero"></path>
+    </g>
+</svg>
diff --git a/browser/themes/shared/jar.inc.mn b/browser/themes/shared/jar.inc.mn
index 17debd82ddcd..3f59d7bfac19 100644
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -45,6 +45,9 @@
   skin/classic/browser/connection-secure.svg                   (../shared/identity-block/connection-secure.svg)
   skin/classic/browser/connection-mixed-passive-loaded.svg     (../shared/identity-block/connection-mixed-passive-loaded.svg)
   skin/classic/browser/connection-mixed-active-loaded.svg      (../shared/identity-block/connection-mixed-active-loaded.svg)
+  skin/classic/browser/onion.svg                               (../shared/identity-block/onion.svg)
+  skin/classic/browser/onion-lock.svg                          (../shared/identity-block/onion-lock.svg)
+  skin/classic/browser/onion-disabled.svg                      (../shared/identity-block/onion-disabled.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-icon-notice.svg                (../shared/identity-block/identity-icon-notice.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 63e1ac06bf2d..8257283e65a2 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10039,6 +10039,27 @@ nsContentUtils::HttpsStateIsModern(nsIDocument* aDocument)
   return false;
 }
 
+/* static */ bool
+nsContentUtils::DocumentHasOnionURI(nsIDocument* 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, NS_LITERAL_CSTRING(".onion"));
+    return hasOnionURI;
+  }
+
+  return false;
+}
+
 /* static */ void
 nsContentUtils::TryToUpgradeElement(Element* aElement)
 {
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 9ef79a569ea3..246dabf340de 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3050,6 +3050,11 @@ public:
   static bool HttpsStateIsModern(nsIDocument* aDocument);
 
   /**
+   * Returns true of the document's URI is a .onion
+   */
+  static bool DocumentHasOnionURI(nsIDocument* aDocument);
+
+  /**
    * Try to upgrade an element.
    * https://html.spec.whatwg.org/multipage/custom-elements.html#concept-try-upgrade
    */
diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
index 7d947b653f70..e4b847406085 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1495,7 +1495,8 @@ nsGlobalWindowOuter::ComputeIsSecureContext(nsIDocument* aDocument, SecureContex
     return false;
   }
 
-  if (nsContentUtils::HttpsStateIsModern(aDocument)) {
+  if (nsContentUtils::HttpsStateIsModern(aDocument) ||
+      nsContentUtils::DocumentHasOnionURI(aDocument)) {
     return true;
   }
 
diff --git a/dom/presentation/PresentationRequest.cpp b/dom/presentation/PresentationRequest.cpp
index 4c00150a359d..577aa3dd739e 100644
--- a/dom/presentation/PresentationRequest.cpp
+++ b/dom/presentation/PresentationRequest.cpp
@@ -512,7 +512,8 @@ PresentationRequest::IsProhibitMixedSecurityContexts(nsIDocument* aDocument)
 
   nsCOMPtr<nsIDocument> doc = aDocument;
   while (doc && !nsContentUtils::IsChromeDoc(doc)) {
-    if (nsContentUtils::HttpsStateIsModern(doc)) {
+    if (nsContentUtils::HttpsStateIsModern(doc) ||
+        nsContentUtils::DocumentHasOnionURI(doc)) {
       return true;
     }
 
diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp
index adc0bfd80e88..7b0e5088a4de 100644
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -694,7 +694,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
     return NS_OK;
   }
 
-  // Check the parent scheme. If it is not an HTTPS page then mixed content
+  // Check the parent scheme. If it is not an HTTPS or .onion page then mixed content
   // restrictions do not apply.
   bool parentIsHttps;
   nsCOMPtr<nsIURI> innerRequestingLocation = NS_GetInnermostURI(requestingLocation);
@@ -711,8 +711,19 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
     return NS_OK;
   }
   if (!parentIsHttps) {
-    *aDecision = ACCEPT;
-    return NS_OK;
+    nsAutoCString parentHost;
+    rv = innerRequestingLocation->GetHost(parentHost);
+    if (NS_FAILED(rv)) {
+      NS_ERROR("requestingLocation->GetHost failed");
+      *aDecision = REJECT_REQUEST;
+      return NS_OK;
+    }
+
+    bool parentIsOnion = StringEndsWith(parentHost, NS_LITERAL_CSTRING(".onion"));
+    if (!parentIsOnion) {
+      *aDecision = ACCEPT;
+      return NS_OK;
+    }
   }
 
   nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.cpp b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
index a2f24df7c4af..336901f0226e 100644
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -294,36 +294,35 @@ static uint32_t GetSecurityStateFromSecurityInfoAndRequest(nsISupports* info,
   uint32_t securityState;
 
   nsCOMPtr<nsITransportSecurityInfo> psmInfo(do_QueryInterface(info));
+  MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - info is %p\n",
+                                          (nsISupports *)info));
   if (!psmInfo) {
     MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - no nsITransportSecurityInfo for %p\n",
                                          (nsISupports *)info));
-    return nsIWebProgressListener::STATE_IS_INSECURE;
+    securityState = nsIWebProgressListener::STATE_IS_INSECURE;
+  } else {
+    res = psmInfo->GetSecurityState(&securityState);
+    if (NS_FAILED(res)) {
+      MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - GetSecurityState failed: %" PRIu32 "\n",
+                                               static_cast<uint32_t>(res)));
+      securityState = nsIWebProgressListener::STATE_IS_BROKEN;
+    }
   }
-  MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - info is %p\n",
-                                       (nsISupports *)info));
 
-  res = psmInfo->GetSecurityState(&securityState);
-  if (NS_FAILED(res)) {
-    MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - GetSecurityState failed: %" PRIu32 "\n",
-                                             static_cast<uint32_t>(res)));
-    securityState = nsIWebProgressListener::STATE_IS_BROKEN;
+  nsCOMPtr<nsIURI> uri;
+  nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
+  if (channel) {
+    channel->GetURI(getter_AddRefs(uri));
+  } else {
+    nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(request));
+    if (imgRequest) {
+      imgRequest->GetURI(getter_AddRefs(uri));
+    }
   }
 
-  if (securityState != nsIWebProgressListener::STATE_IS_INSECURE) {
-    // A secure connection does not yield a secure per-uri channel if the
-    // scheme is plain http.
-
-    nsCOMPtr<nsIURI> uri;
-    nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
-    if (channel) {
-      channel->GetURI(getter_AddRefs(uri));
-    } else {
-      nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(request));
-      if (imgRequest) {
-        imgRequest->GetURI(getter_AddRefs(uri));
-      }
-    }
-    if (uri) {
+  if (uri) {
+    // http and ftp are always insecure
+    if (securityState != nsIWebProgressListener::STATE_IS_INSECURE) {
       bool isHttp, isFtp;
       if ((NS_SUCCEEDED(uri->SchemeIs("http", &isHttp)) && isHttp) ||
           (NS_SUCCEEDED(uri->SchemeIs("ftp", &isFtp)) && isFtp)) {
@@ -332,6 +331,18 @@ static uint32_t GetSecurityStateFromSecurityInfoAndRequest(nsISupports* info,
         securityState = nsIWebProgressListener::STATE_IS_INSECURE;
       }
     }
+
+    // any protocol routed over tor is secure
+    if (securityState != nsIWebProgressListener::STATE_IS_SECURE) {
+      nsAutoCString host;
+      if (NS_SUCCEEDED(uri->GetHost(host))) {
+        if (StringEndsWith(host, NS_LITERAL_CSTRING(".onion"))) {
+          MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - "
+                                                   "uri is onion.\n"));
+          securityState = nsIWebProgressListener::STATE_IS_SECURE;
+        }
+      }
+    }
   }
 
   MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - Returning %d\n",



More information about the tor-commits mailing list