Bug 1409259 - Add a console warning for soon-to-be-distrusted roots r?keeler draft
authorJ.C. Jones <jjones@mozilla.com>
Wed, 18 Oct 2017 22:29:42 -0700
changeset 683910 55b14e321e3fdb3e1df6fc7589c74a6f611ae72a
parent 683664 c612d4988e4f5f094e24b26837d70ef60090ef54
child 683911 92733b0f43cf1d565f8b13fd5516c70fbca7fb12
push id85504
push userbmo:jjones@mozilla.com
push dateFri, 20 Oct 2017 14:33:49 +0000
reviewerskeeler
bugs1409259
milestone58.0a1
Bug 1409259 - Add a console warning for soon-to-be-distrusted roots r?keeler This patch adds a new diagnostic status flag to nsIWebProgressListener, STATE_CERT_DISTRUST_IMMINENT, which indicates that the certificate chain is going to change validity due to an upcoming distrust event. The first of these events is this bug, affecting various roots from Symantec. The STATE_CERT_DISTRUST_IMMINENT flag is set by nsNSSCallbacks and passed, via nsSecureBrowserUIImpl, to browser.js where it is used to alert the console. Adding this sort of diagnostic printing to be accessible to browser.js is a long-desired goal, as future functionality can start doing more decision-making there. We may, for example, also want to degrade the lock icon, which will be straightforward with this flag. This commit does not implement the IsCertificateDistrustImminent method. That is follow-on work. MozReview-Commit-ID: 75IOdc24XIV
browser/base/content/browser.js
security/manager/ssl/nsNSSCallbacks.cpp
security/manager/ssl/nsSecureBrowserUIImpl.cpp
uriloader/base/nsIWebProgressListener.idl
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7237,16 +7237,20 @@ var gIdentityHandler = {
   get _isMixedPassiveContentLoaded() {
     return this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
   },
 
   get _isCertUserOverridden() {
     return this._state & Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN;
   },
 
+  get _isCertDistrustImminent() {
+    return this._state & Ci.nsIWebProgressListener.STATE_CERT_DISTRUST_IMMINENT;
+  },
+
   get _hasInsecureLoginForms() {
     // checks if the page has been flagged for an insecure login. Also checks
     // if the pref to degrade the UI is set to true
     return LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser) &&
            Services.prefs.getBoolPref("security.insecure_password.ui.enabled");
   },
 
   // smart getters
@@ -7488,16 +7492,29 @@ var gIdentityHandler = {
     if (shouldHidePopup) {
       this._identityPopup.hidePopup();
     }
 
     // NOTE: We do NOT update the identity popup (the control center) when
     // we receive a new security state on the existing page (i.e. from a
     // subframe). If the user opened the popup and looks at the provided
     // information we don't want to suddenly change the panel contents.
+
+    // Finally, if there are warnings to issue, issue them
+    if (this._isCertDistrustImminent) {
+      let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
+      let windowId = gBrowser.selectedBrowser.innerWindowID;
+      // Use uri.prePath instead of initWithSourceURI() so that these can be
+      // de-duplicated on the scheme+host+port combination.
+      consoleMsg.initWithWindowID("The security certificate in use for this web site " +
+                      "will be invalid  in a future release of Firefox. For more " +
+                      "information, visit https://FILL_ME_IN/",
+                      uri.prePath, null, 0, 0, Ci.nsIScriptError.warningFlag, "SSL", windowId);
+      Services.console.logMessage(consoleMsg);
+    }
   },
 
   /**
    * This is called asynchronously when requested by the Logins module, after
    * the insecure login forms state for the page has been updated.
    */
   refreshForInsecureLoginForms() {
     // Check this._uri because we don't want to refresh the user interface if
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -29,16 +29,21 @@
 #include "nsNSSIOLayer.h"
 #include "nsNetUtil.h"
 #include "nsProtectedAuthThread.h"
 #include "nsProxyRelease.h"
 #include "pkix/pkixtypes.h"
 #include "ssl.h"
 #include "sslproto.h"
 
+#include "TrustOverrideUtils.h"
+#include "TrustOverride-SymantecData.inc"
+#include "TrustOverride-AppleGoogleData.inc"
+
+
 using namespace mozilla;
 using namespace mozilla::psm;
 
 extern LazyLogModule gPIPNSSLog;
 
 static void AccumulateCipherSuite(Telemetry::HistogramID probe,
                                   const SSLChannelInfo& channelInfo);
 
@@ -1253,16 +1258,37 @@ DetermineEVAndCTStatusAndSetNewCert(RefP
     sslStatus->SetServerCert(nssc, EVStatus::NotEV);
   }
 
   if (rv == Success) {
     sslStatus->SetCertificateTransparencyInfo(certificateTransparencyInfo);
   }
 }
 
+static nsresult
+IsCertificateDistrustImminent(nsIX509CertList* aCertList,
+                              /* out */ bool* aResult) {
+  MOZ_ASSERT(aCertList);
+  MOZ_ASSERT(aResult);
+  if (!aCertList || !aResult) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  // TODO: Implement algorithm below (and add tests)
+  // If the root is not one of the Symantec roots, exit false
+
+  // If the end entity's notBefore date is not before 2016-06-01, exit false
+
+  // If the intermediate is one of the whitelisted intermediates, exit false
+
+  // Otherwise, this certificate will imminently be distrusted, exit true
+  *aResult = false;
+  return NS_OK;
+}
+
 void HandshakeCallback(PRFileDesc* fd, void* client_data) {
   nsNSSShutDownPreventionLock locker;
   SECStatus rv;
 
   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
 
   // Do the bookkeeping that needs to be done after the
   // server's ServerHello...ServerHelloDone have been processed, but that doesn't
@@ -1404,16 +1430,26 @@ void HandshakeCallback(PRFileDesc* fd, v
 
   if (status->HasServerCert()) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("HandshakeCallback KEEPING existing cert\n"));
   } else {
     DetermineEVAndCTStatusAndSetNewCert(status, fd, infoObject);
   }
 
+  // TODO: Update to the real source of the certificate, once Bug 1406856 lands
+  nsCOMPtr<nsIX509CertList> succeededCertChain = new nsNSSCertList();
+  bool distrustImminent;
+  if (NS_SUCCEEDED(IsCertificateDistrustImminent(succeededCertChain,
+                                                 &distrustImminent))) {
+    if (distrustImminent) {
+      state |= nsIWebProgressListener::STATE_CERT_DISTRUST_IMMINENT;
+    }
+  }
+
   bool domainMismatch;
   bool untrusted;
   bool notValidAtThisTime;
   // These all return NS_OK, so don't even bother checking the return values.
   Unused << status->GetIsDomainMismatch(&domainMismatch);
   Unused << status->GetIsUntrusted(&untrusted);
   Unused << status->GetIsNotValidAtThisTime(&notValidAtThisTime);
   // If we're here, the TLS handshake has succeeded. Thus if any of these
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -264,16 +264,22 @@ nsSecureBrowserUIImpl::MapInternalToExte
 
   // Has Tracking Content been Blocked?
   if (docShell->GetHasTrackingContentBlocked())
     *aState |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
 
   if (docShell->GetHasTrackingContentLoaded())
     *aState |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
 
+  // Copy forward any diagnostic flags for downstream use (e.g., warnings)
+  if (mNewToplevelSecurityStateKnown &&
+      mNewToplevelSecurityState & STATE_CERT_DISTRUST_IMMINENT) {
+    *aState |= nsIWebProgressListener::STATE_CERT_DISTRUST_IMMINENT;
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSecureBrowserUIImpl::SetDocShell(nsIDocShell* aDocShell)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsresult rv;
--- a/uriloader/base/nsIWebProgressListener.idl
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -219,16 +219,28 @@ interface nsIWebProgressListener : nsISu
    * STATE_BLOCKED_UNSAFE_CONTENT
    *   Content which againts SafeBrowsing list has been blocked from loading.
    */
   const unsigned long STATE_BLOCKED_TRACKING_CONTENT         = 0x00001000;
   const unsigned long STATE_LOADED_TRACKING_CONTENT          = 0x00002000;
   const unsigned long STATE_BLOCKED_UNSAFE_CONTENT           = 0x00004000;
 
   /**
+   * Diagnostic flags
+   *
+   * May be set in addition to other security state flags to indicate that
+   * some state is countered that deserves a warning or error, but does not
+   * change the top level security state of the connection.
+   *
+   * STATE_CERT_DISTRUST_IMMINENT
+   *   The certificate in use will be distrusted in the near future.
+   */
+  const unsigned long STATE_CERT_DISTRUST_IMMINENT    = 0x00008000;
+
+  /**
    * Security Strength Flags
    *
    * These flags describe the security strength and accompany STATE_IS_SECURE
    * in a call to the onSecurityChange method.  These flags are mutually
    * exclusive.
    *
    * These flags are not meant to provide a precise description of data
    * transfer security.  These are instead intended as a rough indicator that