From: Mike Hommey <glandium@debian.org>
Date: Sun, 15 Jun 2008 12:54:32 +0200
Subject: Fore-port nsIBadCertListener from 1.8

This allows embedding applications to use the same dialogs as before, instead
of the new ssl alert pages from Firefox, which have several problems in
embedding applications.
---
 security/manager/ssl/public/Makefile.in            |    1 +
 security/manager/ssl/public/nsIBadCertListener.idl |  155 ++++++++++++++++++++
 security/manager/ssl/src/nsNSSIOLayer.cpp          |  103 +++++++++++++-
 security/manager/ssl/src/nsNSSIOLayer.h            |    8 +
 4 files changed, 266 insertions(+), 1 deletions(-)

diff --git a/security/manager/ssl/public/Makefile.in b/security/manager/ssl/public/Makefile.in
index fa84d3a..affd50a 100644
--- a/security/manager/ssl/public/Makefile.in
+++ b/security/manager/ssl/public/Makefile.in
@@ -60,6 +60,7 @@ SDK_XPIDLSRCS = \
 
 XPIDLSRCS = \
     nsISSLCertErrorDialog.idl \
+    nsIBadCertListener.idl \
     nsIBadCertListener2.idl \
     nsISSLErrorListener.idl \
     nsIIdentityInfo.idl \
diff --git a/security/manager/ssl/public/nsIBadCertListener.idl b/security/manager/ssl/public/nsIBadCertListener.idl
index e69de29..5e9e750 100644
--- a/security/manager/ssl/public/nsIBadCertListener.idl
+++ b/security/manager/ssl/public/nsIBadCertListener.idl
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Javier Delgadillo <javi@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsIX509Cert;
+interface nsIInterfaceRequestor;
+
+/**
+ * Functions that display warnings for problems with web site trust.
+ *
+ * @status FROZEN
+ */
+[scriptable, uuid(86960956-edb0-11d4-998b-00b0d02354a0)]
+interface nsIBadCertListener : nsISupports {
+
+  /**
+   *  No decision was made by the user, whether to trust a cert.
+   */
+  const short UNINIT_ADD_FLAG = -1;
+
+  /**
+   *  The user decided to add trust to a certificate temporarily
+   *  for the current application session only.
+   */
+  const short ADD_TRUSTED_FOR_SESSION = 1;
+
+  /**
+   *  The user decided to add trust to a certificate permanently.
+   */
+  const short ADD_TRUSTED_PERMANENTLY = 2;
+
+  /**
+   *  Inform the user there are problems with the trust of a certificate,
+   *  and request a decision from the user.
+   *  The UI should offer the user a way to look at the certificate in detail.
+   *  The following is a sample UI message to be shown to the user:
+   *
+   *    Unable to verify the identity of %S as a trusted site.
+   *    Possible reasons for this error:
+   *    - Your browser does not recognize the Certificate Authority 
+   *      that issued the site's certificate.
+   *    - The site's certificate is incomplete due to a 
+   *      server misconfiguration.
+   *    - You are connected to a site pretending to be %S, 
+   *      possibly to obtain your confidential information.
+   *    Please notify the site's webmaster about this problem.
+   *    Before accepting this certificate, you should examine this site's 
+   *      certificate carefully. Are you willing to to accept this certificate 
+   *      for the purpose of identifying the Web site %S?
+   *    o Accept this certificate permanently
+   *    x Accept this certificate temporarily for this session
+   *    o Do not accept this certificate and do not connect to this Web site
+   *
+   *  @param socketInfo A network communication context that can be used to obtain more information
+   *                    about the active connection.
+   *  @param cert The certificate that is not trusted and that is having the problem.
+   *  @param certAddType The user's trust decision. See constants defined above.
+   *
+   *  @return true if the user decided to connect anyway, false if the user decided to not connect
+   */
+  boolean confirmUnknownIssuer(in nsIInterfaceRequestor socketInfo, 
+                               in nsIX509Cert cert,
+                               out short certAddType);
+
+  /**
+   *  Inform the user there are problems with the trust of a certificate,
+   *  and request a decision from the user.
+   *  The hostname mentioned in the server's certificate is not the hostname
+   *  that was used as a destination address for the current connection.
+   *
+   *  @param socketInfo A network communication context that can be used to obtain more information
+   *                    about the active connection.
+   *  @param targetURL The URL that was used to open the current connection.
+   *  @param cert The certificate that was presented by the server.
+   *
+   *  @return true if the user decided to connect anyway, false if the user decided to not connect
+   */
+  boolean confirmMismatchDomain(in nsIInterfaceRequestor socketInfo,
+                                in AUTF8String targetURL,
+                                in nsIX509Cert cert);
+
+  /**
+   *  Inform the user there are problems with the trust of a certificate,
+   *  and request a decision from the user.
+   *  The certificate presented by the server is no longer valid because 
+   *  the validity period has expired.
+   *
+   *  @param socketInfo A network communication context that can be used to obtain more information
+   *                    about the active connection.
+   *  @param cert The certificate that was presented by the server.
+   *
+   *  @return true if the user decided to connect anyway, false if the user decided to not connect
+   */
+  boolean confirmCertExpired(in nsIInterfaceRequestor socketInfo,
+                             in nsIX509Cert cert);
+
+  /**
+   *  Inform the user there are problems with the trust of a certificate,
+   *  and request a decision from the user.
+   *  The Certificate Authority (CA) that issued the server's certificate has issued a 
+   *  Certificate Revocation List (CRL). 
+   *  However, the application does not have a current version of the CA's CRL.
+   *  Due to the application configuration, the application disallows the connection
+   *  to the remote site.
+   *
+   *  @param socketInfo A network communication context that can be used to obtain more information
+   *                    about the active connection.
+   *  @param targetURL The URL that was used to open the current connection.
+   *  @param cert The certificate that was presented by the server.
+   */
+  void notifyCrlNextupdate(in nsIInterfaceRequestor socketInfo,
+                           in AUTF8String targetURL, in nsIX509Cert cert);
+
+};
+
+%{C++
+#define NS_BADCERTLISTENER_CONTRACTID "@mozilla.org/nsBadCertListener;1"
+%}
diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp
index d6bf56c..2cb61ed 100644
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -60,6 +60,7 @@
 #include "nsIClientAuthDialogs.h"
 #include "nsClientAuthRemember.h"
 #include "nsICertOverrideService.h"
+#include "nsIBadCertListener.h"
 #include "nsIBadCertListener2.h"
 #include "nsISSLErrorListener.h"
 #include "nsIObjectInputStream.h"
@@ -807,6 +808,20 @@ void nsNSSSocketInfo::SetHandshakeInProgress(PRBool aIsIn)
   }
 }
 
+void nsNSSSocketInfo::SetBadCertUIStatus(nsNSSSocketInfo::BadCertUIStatusType aNewStatus)
+{
+  if (mBadCertUIStatus == bcuis_active &&
+      aNewStatus == bcuis_was_shown)
+  {
+    // we were blocked and going back to unblocked,
+    // so let's reset the handshake start time, in order to ensure
+    // we do not count the amount of time while the UI was shown.
+    mHandshakeStartTime = PR_IntervalNow();
+  }
+
+  mBadCertUIStatus = aNewStatus;
+}
+
 void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(PRBool aAllow)
 {
   mAllowTLSIntoleranceTimeout = aAllow;
@@ -816,7 +831,8 @@ void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(PRBool aAllow)
 
 PRBool nsNSSSocketInfo::HandshakeTimeout()
 {
-  if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout)
+  if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout ||
+      mBadCertUIStatus == bcuis_active)
     return PR_FALSE;
 
   return ((PRIntervalTime)(PR_IntervalNow() - mHandshakeStartTime)
@@ -1807,6 +1823,37 @@ isTLSIntoleranceError(PRInt32 err, PRBool withInitialCleartext)
   return PR_FALSE;
 }
 
+static PRBool
+isClosedConnectionAfterBadCertUIWasShown(PRInt32 bytesTransfered,
+                                         PRBool wasReading,
+                                         PRInt32 err,
+                                         nsNSSSocketInfo::BadCertUIStatusType aBadCertUIStatus)
+{
+  if (aBadCertUIStatus != nsNSSSocketInfo::bcuis_not_shown)
+  {
+    // Bad cert UI was shown for this socket.
+    // Server timeout possible.
+    // Retry on a simple connection close.
+
+    if (wasReading && 0 == bytesTransfered)
+      return PR_TRUE;
+
+    if (0 > bytesTransfered)
+    {
+      switch (err)
+      {
+        case PR_CONNECT_RESET_ERROR:
+        case PR_END_OF_FILE_ERROR:
+          return PR_TRUE;
+        default:
+          break;
+      }
+    }
+  }
+
+  return PR_FALSE;
+}
+
 PRInt32
 nsSSLThread::checkHandshake(PRInt32 bytesTransfered, 
                             PRBool wasReading,
@@ -1858,6 +1905,12 @@ nsSSLThread::checkHandshake(PRInt32 bytesTransfered,
         return bytesTransfered;
       }
 
+      wantRetry =
+        isClosedConnectionAfterBadCertUIWasShown(bytesTransfered,
+                                                 wasReading,
+                                                 err,
+                                                 socketInfo->GetBadCertUIStatus());
+
       if (!wantRetry // no decision yet
           && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
       {
@@ -1875,6 +1928,12 @@ nsSSLThread::checkHandshake(PRInt32 bytesTransfered,
   {
     if (handleHandshakeResultNow)
     {
+      wantRetry =
+        isClosedConnectionAfterBadCertUIWasShown(bytesTransfered,
+                                                 wasReading,
+                                                 0,
+                                                 socketInfo->GetBadCertUIStatus());
+
       if (!wantRetry // no decision yet
           && !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament
       {
@@ -3373,6 +3432,48 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
         rv = proxy_bcl->NotifyCertProblem(csi, status, hostWithPortString, 
                                           &suppressMessage);
       }
+    } else {
+      nsCOMPtr<nsIBadCertListener> handler = do_GetInterface(callbacks);
+      nsIBadCertListener *badCertHandler = nsnull;
+      if (handler) {
+        NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                             NS_GET_IID(nsIBadCertListener),
+                             handler,
+                             NS_PROXY_SYNC,
+                             (void**)&badCertHandler);
+      }
+      if (!badCertHandler) {
+        getNSSDialogs((void**)&badCertHandler,
+                      NS_GET_IID(nsIBadCertListener),
+                      NS_BADCERTLISTENER_CONTRACTID);
+      }
+      if (badCertHandler) {
+        PRBool retVal = PR_TRUE;
+        PRInt16 addType = nsIBadCertListener::UNINIT_ADD_FLAG;
+        nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
+        infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_active);
+        if (remaining_display_errors & nsICertOverrideService::ERROR_UNTRUSTED) {
+          rv = badCertHandler->ConfirmUnknownIssuer(csi, ix509, &addType, &retVal);
+          if (NS_FAILED(rv)) retVal = PR_FALSE;
+        }
+        if (retVal && (remaining_display_errors & nsICertOverrideService::ERROR_MISMATCH)) {
+          rv = badCertHandler->ConfirmMismatchDomain(csi, hostString, ix509, &retVal);
+          if (NS_FAILED(rv)) retVal = PR_FALSE;
+        }
+        if (retVal && (remaining_display_errors & nsICertOverrideService::ERROR_TIME)) {
+          rv = badCertHandler->ConfirmCertExpired(csi, ix509, &retVal);
+          if (NS_FAILED(rv)) retVal = PR_FALSE;
+        }
+        if (overrideService && retVal && addType != nsIBadCertListener::UNINIT_ADD_FLAG) {
+          overrideService->RememberValidityOverride(hostString, port, ix509,
+                                    nsICertOverrideService::ERROR_UNTRUSTED,
+                                    addType == nsIBadCertListener::ADD_TRUSTED_FOR_SESSION);
+        }
+        infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_was_shown);
+        if (retVal)
+          return SECSuccess;
+        suppressMessage = PR_TRUE;
+      }
     }
   }
 
diff --git a/security/manager/ssl/src/nsNSSIOLayer.h b/security/manager/ssl/src/nsNSSIOLayer.h
index d804895..f43c79c 100644
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -189,6 +189,13 @@ public:
 
   void SetAllowTLSIntoleranceTimeout(PRBool aAllow);
 
+  enum BadCertUIStatusType {
+    bcuis_not_shown, bcuis_active, bcuis_was_shown
+  };
+
+  void SetBadCertUIStatus(BadCertUIStatusType aNewStatus);
+  BadCertUIStatusType GetBadCertUIStatus() { return mBadCertUIStatus; }
+
   nsresult GetExternalErrorReporting(PRBool* state);
   nsresult SetExternalErrorReporting(PRBool aState);
 
@@ -225,6 +232,7 @@ protected:
   PRPackedBool mHandshakeInProgress;
   PRPackedBool mAllowTLSIntoleranceTimeout;
   PRPackedBool mRememberClientAuthCertificate;
+  BadCertUIStatusType mBadCertUIStatus;
   PRIntervalTime mHandshakeStartTime;
   PRInt32 mPort;
   nsXPIDLCString mHostName;
