Bug 327181, Improve error reporting for invalid-certificate errors (error page for https, or combined dialog) Code in mozilla/security/manager: r=rrelyea Code elsewhere: r=mconnor, sr=dveditz blocking1.9=mconnor
authorkaie@kuix.de
Wed, 03 Oct 2007 04:43:54 -0700
changeset 6597 92135ef7684c0551e4b6e04ef42c81dedb70a9e0
parent 6596 c046398ce8044422c4ea348d6aa853b97b2a0d87
child 6598 f78c615a656c9b0e2bc256e6a46991ff07f6435e
push idunknown
push userunknown
push dateunknown
reviewersrrelyea, mconnor, dveditz
bugs327181
milestone1.9a9pre
Bug 327181, Improve error reporting for invalid-certificate errors (error page for https, or combined dialog) Code in mozilla/security/manager: r=rrelyea Code elsewhere: r=mconnor, sr=dveditz blocking1.9=mconnor
config/system-headers
embedding/browser/gtk/src/EmbedPrivate.cpp
security/manager/locales/en-US/chrome/pipnss/pipnss.properties
security/manager/locales/en-US/chrome/pippki/certManager.dtd
security/manager/pki/resources/content/WebSitesOverlay.xul
security/manager/pki/resources/jar.mn
security/manager/pki/src/nsNSSDialogs.cpp
security/manager/ssl/public/nsIBadCertListener2.idl
security/manager/ssl/public/nsICertOverrideService.idl
security/manager/ssl/public/nsIX509Cert2.idl
security/manager/ssl/public/nsIX509Cert3.idl
security/manager/ssl/src/nsCertOverrideService.cpp
security/manager/ssl/src/nsCertOverrideService.h
security/manager/ssl/src/nsNSSCertificate.h
security/manager/ssl/src/nsNSSCleaner.cpp
security/manager/ssl/src/nsNSSCleaner.h
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsSSLStatus.cpp
security/manager/ssl/src/nsSSLStatus.h
security/manager/ssl/src/nsSmartCardEvent.cpp
--- a/config/system-headers
+++ b/config/system-headers
@@ -860,16 +860,17 @@ X11/Xos.h
 X11/Xutil.h
 xpt_struct.h
 xpt_xdr.h
 zmouse.h
 sslt.h
 smime.h
 cms.h
 sechash.h
+secoidt.h
 certdb.h
 secerr.h
 nssb64.h
 secasn1.h
 secder.h
 certt.h
 ocsp.h
 keyhi.h
--- a/embedding/browser/gtk/src/EmbedPrivate.cpp
+++ b/embedding/browser/gtk/src/EmbedPrivate.cpp
@@ -309,30 +309,16 @@ static const nsModuleComponentInfo defau
     EMBED_CERTIFICATES_DESCRIPTION,
     EMBED_CERTIFICATES_CID,
     NS_TOKENPASSWORDSDIALOG_CONTRACTID,
     EmbedCertificatesConstructor
   },
   {
     EMBED_CERTIFICATES_DESCRIPTION,
     EMBED_CERTIFICATES_CID,
-    NS_BADCERTLISTENER_CONTRACTID,
-    EmbedCertificatesConstructor
-  },
-#ifdef BAD_CERT_LISTENER2
-  {
-    EMBED_CERTIFICATES_DESCRIPTION,
-    EMBED_CERTIFICATES_CID,
-    NS_BADCERTLISTENER2_CONTRACTID,
-    EmbedCertificatesConstructor
-  },
-#endif
-  {
-    EMBED_CERTIFICATES_DESCRIPTION,
-    EMBED_CERTIFICATES_CID,
     NS_CERTIFICATEDIALOGS_CONTRACTID,
     EmbedCertificatesConstructor
   },
   {
     EMBED_CERTIFICATES_DESCRIPTION,
     EMBED_CERTIFICATES_CID,
     NS_CLIENTAUTHDIALOGS_CONTRACTID,
     EmbedCertificatesConstructor
--- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
@@ -331,16 +331,22 @@ UnknownCertOrg=(Unknown Organization)
 AVATemplate=%S = %S
 
 PSMERR_SSL_Disabled=Can't connect securely because the SSL protocol has been disabled.
 PSMERR_SSL2_Disabled=Can't connect securely because the site uses an older, insecure version of the SSL protocol.
 PSMERR_HostReusedIssuerSerial=You have received an invalid certificate.  Please contact the server administrator or email correspondent and give them the following information:\n\nYour certificate contains the same serial number as another certificate issued by the certificate authority.  Please get a new certificate containing a unique serial number.
 
 SSLConnectionErrorPrefix=An error occurred during a connection to %S.
 
+certErrorIntro=An error occurred during a connection to %S because it uses an invalid security certificate.
+certErrorUntrusted=The certificate is not trusted or its issuer certificate is invalid.
+certErrorMismatch=The certificate is not valid for domain name %S.
+certErrorExpired=The certificate has expired on %S.
+certErrorNotYetValid=The certificate will not be valid until %S.
+
 CertInfoIssuedFor=Issued to:
 CertInfoIssuedBy=Issued by:
 CertInfoValid=Valid
 CertInfoFrom=from
 CertInfoTo=to
 CertInfoPurposes=Purposes
 CertInfoStoredIn=Stored in:
 P12DefaultNickname=Imported Certificate
@@ -372,8 +378,10 @@ CertNoNickname=(no nickname)
 CertNoEmailAddress=(no email address)
 NicknameExpired=(expired)
 NicknameNotYetValid=(not yet valid)
 CaCertExists=This certificate is already installed as a certificate authority.
 NotACACert=This is not a certificate authority certificate, so it can't be imported into the certificate authority list.
 NotImportingUnverifiedCert=This certificate can't be verified and will not be imported. The certificate issuer might be unknown or untrusted, the certificate might have expired or been revoked, or the certificate might not have been approved.
 UserCertIgnoredNoPrivateKey=This personal certificate can't be installed because you do not own the corresponding private key which was created when the certificate was requested.
 UserCertImported=Your personal certificate has been installed. You should keep a backup copy of this certificate.
+CertOrgUnknown=(Unknown)
+CertNotStored=(Not Stored)
--- a/security/manager/locales/en-US/chrome/pippki/certManager.dtd
+++ b/security/manager/locales/en-US/chrome/pippki/certManager.dtd
@@ -88,16 +88,18 @@
 <!ENTITY certmgr.deletecert.title             "Delete Certificate">
 <!ENTITY certmgr.deletecert.beforename        "You have requested to delete this certificate:">
 <!ENTITY certmgr.deletecert.aftername         "Are you sure you want to delete this certificate?">
 <!ENTITY certmgr.deleteusercert.title         "Delete Your Certificate">
 <!ENTITY certmgr.deleteusercert.beforename    "You have requested to delete the certificate:">
 <!ENTITY certmgr.deleteusercert.aftername     "Once you have deleted this certificate, you will not be able to read mail that has been encrypted with it.">
 
 <!ENTITY certmgr.certname                     "Certificate Name">
+<!ENTITY certmgr.certsite                     "Site">
+<!ENTITY certmgr.typesofoverrides             "Exceptions">
 <!ENTITY certmgr.tokenname                    "Security Device">
 <!ENTITY certmgr.purpose                      "Purposes">
 <!ENTITY certmgr.issued                       "Issued On">
 <!ENTITY certmgr.expires                      "Expires On">
 <!ENTITY certmgr.email                        "E-Mail Address">
 <!ENTITY certmgr.serial                       "Serial Number">
 
 <!ENTITY certmgr.close.label                  "Close">
--- a/security/manager/pki/resources/content/WebSitesOverlay.xul
+++ b/security/manager/pki/resources/content/WebSitesOverlay.xul
@@ -17,16 +17,17 @@
    - The Initial Developer of the Original Code is
    - Netscape Communications Corp.
    - Portions created by the Initial Developer are Copyright (C) 2001
    - the Initial Developer. All Rights Reserved.
    -
    - Contributor(s):
    -   Bob Lord <lord@netscape.com>
    -   Ian McGreer <mcgreer@netscape.com>
+   -   Kai Engert <kengert@redhat.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
@@ -50,16 +51,24 @@
     <description>&certmgr.websites;</description>
     <separator class="thin"/>
     <tree id="server-tree" flex="1" enableColumnDrag="true"
               onselect="websites_enableButtons()">
       <treecols>
         <treecol id="certcol" label="&certmgr.certname;" primary="true" 
                      persist="hidden width ordinal" flex="1"/>
         <splitter class="tree-splitter"/>
+        <treecol id="sitecol" label="&certmgr.certsite;" 
+                 persist="hidden width ordinal" flex="1"/>
+        <splitter class="tree-splitter"/>
+<!-- this is too geeky, leave out for now
+        <treecol id="overridetypecol" label="&certmgr.typesofoverrides;" 
+                 persist="hidden width ordinal" flex="1"/>
+        <splitter class="tree-splitter"/>
+-->
 <!-- disable the purposes column until we get a solution
      to fill in this information that is compatible with OCSP
      and does not block the whole interface
         <treecol id="purposecol" label="&certmgr.purpose;"
                      persist="hidden width ordinal" flex="1"/>
 -->
         <treecol id="expiredcol" label="&certmgr.expires;"
                  persist="hidden width ordinal" flex="1"/>
--- a/security/manager/pki/resources/jar.mn
+++ b/security/manager/pki/resources/jar.mn
@@ -11,18 +11,16 @@ pippki.jar:
     content/pippki/resetpassword.js          (content/resetpassword.js)
     content/pippki/PrefOverlay.xul           (content/PrefOverlay.xul)
     content/pippki/pref-security.js          (content/pref-security.js)
     content/pippki/pref-ssl.xul              (content/pref-ssl.xul)
     content/pippki/pref-certs.xul            (content/pref-certs.xul)
 #ifndef MOZ_PHOENIX
     content/pippki/PageInfoOverlay.xul       (content/PageInfoOverlay.xul)
 #endif
-    content/pippki/newserver.js              (content/newserver.js)
-    content/pippki/newserver.xul             (content/newserver.xul)
     content/pippki/downloadcert.js           (content/downloadcert.js)
     content/pippki/downloadcert.xul          (content/downloadcert.xul)
     content/pippki/cacertexists.xul          (content/cacertexists.xul)
     content/pippki/certManager.js            (content/certManager.js)
     content/pippki/certManager.xul           (content/certManager.xul)
     content/pippki/CAOverlay.xul             (content/CAOverlay.xul)
     content/pippki/WebSitesOverlay.xul       (content/WebSitesOverlay.xul)
     content/pippki/OthersOverlay.xul         (content/OthersOverlay.xul)
@@ -34,20 +32,16 @@ pippki.jar:
     content/pippki/editsslcert.xul           (content/editsslcert.xul)
     content/pippki/editcerts.js              (content/editcerts.js)
     content/pippki/deletecert.xul            (content/deletecert.xul)
     content/pippki/deletecert.js             (content/deletecert.js)
     content/pippki/viewCertDetails.js        (content/viewCertDetails.js)
     content/pippki/getp12password.xul        (content/getp12password.xul)
     content/pippki/setp12password.xul        (content/setp12password.xul)
     content/pippki/pippki.js                 (content/pippki.js)
-    content/pippki/domainMismatch.xul        (content/domainMismatch.xul)
-    content/pippki/domainMismatch.js         (content/domainMismatch.js)
-    content/pippki/serverCertExpired.xul     (content/serverCertExpired.xul)
-    content/pippki/serverCertExpired.js      (content/serverCertExpired.js)
     content/pippki/clientauthask.xul	     (content/clientauthask.xul)
     content/pippki/clientauthask.js          (content/clientauthask.js)
     content/pippki/certpicker.xul	           (content/certpicker.xul)
     content/pippki/certpicker.js             (content/certpicker.js)
     content/pippki/certViewer.xul            (content/certViewer.xul)
     content/pippki/certDump.xul              (content/certDump.xul)
     content/pippki/device_manager.xul        (content/device_manager.xul)
     content/pippki/device_manager.js         (content/device_manager.js)
--- a/security/manager/pki/src/nsNSSDialogs.cpp
+++ b/security/manager/pki/src/nsNSSDialogs.cpp
@@ -73,18 +73,17 @@
 nsNSSDialogs::nsNSSDialogs()
 {
 }
 
 nsNSSDialogs::~nsNSSDialogs()
 {
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS8(nsNSSDialogs, nsITokenPasswordDialogs,
-                                            nsIBadCertListener,
+NS_IMPL_THREADSAFE_ISUPPORTS7(nsNSSDialogs, nsITokenPasswordDialogs,
                                             nsICertificateDialogs,
                                             nsIClientAuthDialogs,
                                             nsICertPickDialogs,
                                             nsITokenDialogs,
                                             nsIDOMCryptoDialogs,
                                             nsIGeneratingKeypairInfoDialogs)
 
 nsresult
@@ -164,226 +163,16 @@ nsNSSDialogs::GetPassword(nsIInterfaceRe
   *_canceled = (status == 0) ? PR_TRUE : PR_FALSE;
   if (!*_canceled) {
     // retrieve the password
     rv = block->GetString(2, _password);
   }
   return rv;
 }
 
-NS_IMETHODIMP
-nsNSSDialogs::ConfirmUnknownIssuer(nsIInterfaceRequestor *socketInfo,
-                                   nsIX509Cert *cert, PRInt16 *outAddType,
-                                   PRBool *_retval)
-{
-  nsresult rv;
-  PRInt32 addType;
-  
-  *_retval = PR_FALSE;
-
-  nsCOMPtr<nsIPKIParamBlock> block =
-           do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
-
-  if (!block)
-    return NS_ERROR_FAILURE;
-
-  nsXPIDLString commonName;
-  rv = block->SetISupportAtIndex(1, cert);
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = nsNSSDialogHelper::openDialog(nsnull, 
-                                     "chrome://pippki/content/newserver.xul",
-                                     block);
-
-  if (NS_FAILED(rv))
-    return rv;
-
-  PRInt32 status;
-  nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
-  rv = dialogBlock->GetInt(1, &status);
-  if (NS_FAILED(rv))
-    return rv; 
-
-  if (status == 0) {
-    *_retval = PR_FALSE;
-  } else {
-    // The user wants to continue, let's figure out
-    // what to do with this cert. 
-    rv = dialogBlock->GetInt(2, &addType);
-    switch (addType) {
-      case 0:
-        *outAddType = ADD_TRUSTED_PERMANENTLY;
-        *_retval    = PR_TRUE;
-        break;
-      case 1:
-        *outAddType = ADD_TRUSTED_FOR_SESSION;
-        *_retval    = PR_TRUE;
-        break;
-      default:
-        *outAddType = UNINIT_ADD_FLAG;
-        *_retval    = PR_FALSE;
-        break;
-    } 
-  }
-
-  return NS_OK; 
-}
-
-NS_IMETHODIMP 
-nsNSSDialogs::ConfirmMismatchDomain(nsIInterfaceRequestor *socketInfo, 
-                                    const nsACString &targetURL, 
-                                    nsIX509Cert *cert, PRBool *_retval) 
-{
-  nsresult rv;
-
-  *_retval = PR_FALSE;
-
-  nsCOMPtr<nsIPKIParamBlock> block =
-           do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
-
-  if (!block)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
-  rv = dialogBlock->SetString(1, NS_ConvertUTF8toUTF16(targetURL).get());
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = block->SetISupportAtIndex(1, cert);
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = nsNSSDialogHelper::openDialog(nsnull,
-                                 "chrome://pippki/content/domainMismatch.xul",
-                                 block);
-  if (NS_FAILED(rv))
-    return rv;
-
-  PRInt32 status;
-
-  rv = dialogBlock->GetInt(1, &status);
-  if (NS_FAILED(rv))
-    return rv;
-
-  *_retval = (status) ? PR_TRUE : PR_FALSE;
-
-  return NS_OK;  
-}
-
-NS_IMETHODIMP 
-nsNSSDialogs::ConfirmCertExpired(nsIInterfaceRequestor *socketInfo, 
-                                 nsIX509Cert *cert, PRBool *_retval)
-{
-  nsresult rv;
-  PRTime now = PR_Now();
-  PRTime notAfter, notBefore, timeToUse;
-  nsCOMPtr<nsIX509CertValidity> validity;
-  const char *key;
-  const char *titleKey;
-
-  *_retval = PR_FALSE;
-
-  nsCOMPtr<nsIPKIParamBlock> block =
-           do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
-
-  if (!block)
-    return NS_ERROR_FAILURE; 
-  rv = cert->GetValidity(getter_AddRefs(validity));
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = validity->GetNotAfter(&notAfter);
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = validity->GetNotBefore(&notBefore);
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (LL_CMP(now, >, notAfter)) {
-    key       = "serverCertExpiredMsg1"; 
-    titleKey  = "serverCertExpiredTitle";
-    timeToUse = notAfter; 
-  } else {
-    key = "serverCertNotYetValedMsg1";
-    titleKey  = "serverCertNotYetValidTitle";
-    timeToUse = notBefore;
-  }
-
-  nsXPIDLString message1;
-  nsXPIDLString title;
-  nsAutoString commonName;
-  nsAutoString formattedDate;
-
-  rv = cert->GetCommonName(commonName);
-
-  nsIDateTimeFormat *aDateTimeFormat;
-  rv = CallCreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &aDateTimeFormat);
-
-  aDateTimeFormat->FormatPRTime(nsnull, kDateFormatShort, 
-                                kTimeFormatNoSeconds, timeToUse, 
-                                formattedDate);
-  const PRUnichar *formatStrings[2] = { commonName.get(), formattedDate.get() }; 
-  NS_ConvertASCIItoUTF16 keyString(key);
-  NS_ConvertASCIItoUTF16 titleKeyString(titleKey);
-  mPIPStringBundle->FormatStringFromName(keyString.get(), formatStrings, 
-                                         2, getter_Copies(message1));
-  mPIPStringBundle->FormatStringFromName(titleKeyString.get(), formatStrings,
-                                         2, getter_Copies(title));
-  
-  nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
-  rv = dialogBlock->SetString(1,message1); 
-  rv = dialogBlock->SetString(2,title);
-
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = block->SetISupportAtIndex(1, cert);
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = nsNSSDialogHelper::openDialog(nsnull,
-                             "chrome://pippki/content/serverCertExpired.xul",
-                             block);
-
-  PRInt32 status;
-  rv = dialogBlock->GetInt(1, &status);
-  if (NS_FAILED(rv))
-    return rv; 
-
-  *_retval = (status) ? PR_TRUE : PR_FALSE;
-  
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsNSSDialogs::NotifyCrlNextupdate(nsIInterfaceRequestor *socketInfo, 
-                                  const nsACString &targetURL, nsIX509Cert *cert)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsIPKIParamBlock> block =
-           do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID);
-  nsCOMPtr<nsIDialogParamBlock> dialogBlock = do_QueryInterface(block);
-
-  rv = dialogBlock->SetString(1, NS_ConvertUTF8toUTF16(targetURL).get());
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = block->SetISupportAtIndex(1, cert);
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = nsNSSDialogHelper::openDialog(nsnull,
-                             "chrome://pippki/content/serverCrlNextupdate.xul",
-                             block);
-  return NS_OK;
-}
-
 NS_IMETHODIMP 
 nsNSSDialogs::CrlImportStatusDialog(nsIInterfaceRequestor *ctx, nsICRLInfo *crl)
 {
   nsresult rv;
 
   nsCOMPtr<nsIPKIParamBlock> block =
            do_CreateInstance(NS_PKIPARAMBLOCK_CONTRACTID,&rv);
   if (NS_FAILED(rv))
@@ -764,10 +553,8 @@ nsNSSDialogs::ConfirmKeyEscrow(nsIX509Ce
   nsCOMPtr<nsIDialogParamBlock> dlgParamBlock = do_QueryInterface(block);
   rv = dlgParamBlock->GetInt(1, &status);
  
   if (status) {
     *_retval = PR_TRUE;
   } 
   return rv;
 }
-
-
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/public/nsIBadCertListener2.idl
@@ -0,0 +1,65 @@
+/* -*- 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
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kai Engert <kengert@redhat.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 nsISSLStatus;
+interface nsIInterfaceRequestor;
+
+/**
+ * A mechanism to report a broken SSL status. The recipient should NOT block.
+ * Can be used to obtain the SSL handshake status of a connection
+ * that will be canceled because of improper cert status.
+ */
+[scriptable, uuid(2c3d268c-ad82-49f3-99aa-e9ffddd7a0dc)]
+interface nsIBadCertListener2 : nsISupports {
+
+  /**
+   *  @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 targetSite The Site name that was used to open the current connection.
+   *
+   *  @return The consumer shall return true if it wants to suppress the error message
+   *          related to the bad cert (the connection will still get canceled).
+   */
+  boolean notifyCertProblem(in nsIInterfaceRequestor socketInfo, 
+                            in nsISSLStatus status,
+                            in AUTF8String targetSite);
+};
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/public/nsICertOverrideService.idl
@@ -0,0 +1,144 @@
+/* -*- 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
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kai Engert <kengert@redhat.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 nsIArray;
+interface nsIX509Cert;
+
+%{C++
+#define NS_CERTOVERRIDE_CONTRACTID "@mozilla.org/security/certoverride;1"
+%}
+
+/**
+ * This represents the global list of triples
+ *   {host:port, cert-fingerprint, allowed-overrides} 
+ * that the user wants to accept without further warnings. 
+ */
+[scriptable, uuid(7e646227-485d-49c6-8e37-e2c62cb204e1)]
+interface nsICertOverrideService : nsISupports {
+
+  /**
+   *  Override Untrusted
+   */
+  const short ERROR_UNTRUSTED = 1;
+
+  /**
+   *  Override hostname Mismatch
+   */
+  const short ERROR_MISMATCH = 2;
+
+  /**
+   *  Override Time error
+   */
+  const short ERROR_TIME = 4;
+
+  /**
+   *  The given cert should always be accepted for the given hostname:port,
+   *  regardless of errors verifying the cert.
+   *  Host:Port is a primary key, only one entry per host:port can exist.
+   *  The implementation will store a fingerprint of the cert.
+   *  The implementation will decide which fingerprint alg is used.
+   *
+   *  @param aHostNameWithPort The host:port this mapping belongs to
+   *  @param aCert The cert that should always be accepted
+   *  @param aOverrideBits The errors we want to be overriden
+   */
+  void rememberValidityOverride(in AString aHostNameWithPort, 
+                                in nsIX509Cert aCert,
+                                in PRUint32 aOverrideBits);
+
+  /**
+   *  The given cert should always be accepted for the given hostname:port,
+   *  regardless of errors verifying the cert.
+   *  Host:Port is a primary key, only one entry per host:port can exist.
+   *  The implementation will store a fingerprint of the cert.
+   *  The implementation will decide which fingerprint alg is used.
+   *
+   *  @param aHostNameWithPort The host:port this mapping belongs to
+   *  @param aCert The cert that should always be accepted
+   *  @param aOverrideBits The errors that are currently overriden
+   *  @return whether an override entry for aHostNameWithPort is currently on file
+   *          that matches the given certificate
+   */
+  boolean hasMatchingOverride(in AString aHostNameWithPort, 
+                              in nsIX509Cert aCert,
+                              out PRUint32 aOverrideBits);
+
+  /**
+   *  Retrieve the stored override for the given hostname:port.
+   *
+   *  @param aHostNameWithPort The host:port whose entry should be tested
+   *  @param aHashAlg On return value True, the fingerprint hash algorithm
+   *                  as an OID value in dotted notation.
+   *  @param aFingerprint On return value True, the stored fingerprint 
+   *  @param aOverrideBits The errors that are currently overriden
+   *  @return whether a matching override entry for aHostNameWithPort 
+   *          and aFingerprint is currently on file
+   */
+  boolean getValidityOverride(in AString aHostNameWithPort, 
+                              out ACString aHashAlg,
+                              out ACString aFingerprint,
+                              out PRUint32 aOverrideBits);
+
+  /**
+   *  Remove a stored override for the given hostname:port.
+   *
+   *  @param aHostNameWithPort The host:port whose entry should be cleared.
+   */
+  void clearValidityOverride(in AString aHostNameWithPort);
+
+  /**
+   *  Obtain the full list of hostname:port for which overrides are stored.
+   *
+   *  @param aCount The number of host:port entries returned
+   *  @param aHostsWithPortsArray The array of host:port entries returned
+   */
+  void getAllOverrideHostsWithPorts(out PRUint32 aCount, 
+                                    [array, size_is(aCount)] out wstring aHostsWithPortsArray);
+
+  /**
+   *  Is the given cert used in rules?
+   *
+   *  @param aCert The cert we're looking for
+   *  @return how many override entries are currently on file
+   *          for the given certificate
+   */
+  PRUint32 isCertUsedForOverrides(in nsIX509Cert aCert);
+};
--- a/security/manager/ssl/public/nsIX509Cert2.idl
+++ b/security/manager/ssl/public/nsIX509Cert2.idl
@@ -32,33 +32,32 @@
  * 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"
+#include "nsIX509Cert.idl"
 
 interface nsIArray;
-interface nsIX509CertValidity;
 interface nsIASN1Object;
 
 %{ C++
  /* forward declaration */
  typedef struct CERTCertificateStr CERTCertificate;
 %}
 [ptr] native CERTCertificatePtr(CERTCertificate);
 
 /**
  * This represents additional interfaces to X.509 certificates
  */
-[scriptable, uuid(648f0d58-eedf-4b45-9174-3b92fb1fc06d)]
-interface nsIX509Cert2 : nsISupports {
+[scriptable, uuid(5b62c61c-f898-4dab-8ace-51109bb459b4)]
+interface nsIX509Cert2 : nsIX509Cert {
   /**
    *  Additional constants to classify the type of a certificate.
    */
   const unsigned long ANY_CERT  = 0xffff;
   readonly attribute unsigned long certType;
   void markForPermDeletion();
   [notxpcom, noscript] CERTCertificatePtr getCert();
 };
--- a/security/manager/ssl/public/nsIX509Cert3.idl
+++ b/security/manager/ssl/public/nsIX509Cert3.idl
@@ -30,29 +30,25 @@
  * 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"
+#include "nsIX509Cert2.idl"
 
-interface nsIX509Cert;
 interface nsICertVerificationListener;
 
 /**
  * Extending nsIX509Cert
- * 
- * TODO: nsIX509Cert3 should be derived from nsIX509Cert2
- *       (and nsIX509Cert2 derived from nsIX509Cert)
  */
-[scriptable, uuid(89d9f248-1160-4935-9f99-2bdbf67b5849)]
-interface nsIX509Cert3 : nsISupports {
+[scriptable, uuid(1362ffab-a683-4504-8038-25ce63b45370)]
+interface nsIX509Cert3 : nsIX509Cert2 {
 
   /**
    *  Constants for specifying the chain mode when exporting a certificate
    */
   const unsigned long CMS_CHAIN_MODE_CertOnly = 1;
   const unsigned long CMS_CHAIN_MODE_CertChain = 2;
   const unsigned long CMS_CHAIN_MODE_CertChainWithRoot = 3;
 
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/src/nsCertOverrideService.cpp
@@ -0,0 +1,920 @@
+/* -*- 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
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kai Engert <kengert@redhat.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 "nsCertOverrideService.h"
+#include "nsIX509Cert.h"
+#include "nsNSSCertificate.h"
+#include "nsCRT.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsStreamUtils.h"
+#include "nsNetUtil.h"
+#include "nsILineInputStream.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsPromiseFlatString.h"
+#include "nsStringBuffer.h"
+#include "nsAutoLock.h"
+#include "nsAutoPtr.h"
+#include "nspr.h"
+#include "pk11pub.h"
+#include "certdb.h"
+#include "sechash.h"
+
+#include "nsNSSCleaner.h"
+NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
+
+static const char kCertOverrideFileName[] = "cert_override.txt";
+
+void
+nsCertOverride::convertBitsToString(OverrideBits ob, nsACString &str)
+{
+  str.Truncate();
+
+  if (ob & ob_Mismatch)
+    str.Append('M');
+
+  if (ob & ob_Untrusted)
+    str.Append('U');
+
+  if (ob & ob_Time_error)
+    str.Append('T');
+}
+
+void
+nsCertOverride::convertStringToBits(const nsACString &str, OverrideBits &ob)
+{
+  const nsPromiseFlatCString &flat = PromiseFlatCString(str);
+  const char *walk = flat.get();
+
+  ob = ob_None;
+
+  for ( ; *walk; ++walk)
+  {
+    switch (*walk)
+    {
+      case 'm':
+      case 'M':
+        ob = (OverrideBits)(ob | ob_Mismatch);
+        break;
+
+      case 'u':
+      case 'U':
+        ob = (OverrideBits)(ob | ob_Untrusted);
+        break;
+
+      case 't':
+      case 'T':
+        ob = (OverrideBits)(ob | ob_Time_error);
+        break;
+
+      default:
+        break;
+    }
+  }
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsCertOverrideService, 
+                              nsICertOverrideService,
+                              nsIObserver)
+
+nsCertOverrideService::nsCertOverrideService()
+{
+  monitor = PR_NewMonitor();
+}
+
+nsCertOverrideService::~nsCertOverrideService()
+{
+  if (monitor)
+    PR_DestroyMonitor(monitor);
+}
+
+nsresult
+nsCertOverrideService::Init()
+{
+  if (!mSettingsTable.Init())
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  mOidTagForStoringNewHashes = SEC_OID_SHA256;
+
+  SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes);
+  if (!od)
+    return NS_ERROR_FAILURE;
+
+  char *dotted_oid = CERT_GetOidString(&od->oid);
+  if (!dotted_oid)
+    return NS_ERROR_FAILURE;
+
+  mDottedOidForStoringNewHashes = dotted_oid;
+  PR_smprintf_free(dotted_oid);
+
+  // cache mSettingsFile
+  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile));
+  if (mSettingsFile) {
+    mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName));
+  }
+
+  Read();
+
+  nsCOMPtr<nsIObserverService> mObserverService = 
+    do_GetService("@mozilla.org/observer-service;1");
+
+  if (mObserverService) {
+    mObserverService->AddObserver(this, "profile-before-change", PR_TRUE);
+    mObserverService->AddObserver(this, "profile-do-change", PR_TRUE);
+    mObserverService->AddObserver(this, "shutdown-cleanse", PR_TRUE);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertOverrideService::Observe(nsISupports     *aSubject,
+                               const char      *aTopic,
+                               const PRUnichar *aData)
+{
+  // check the topic
+  if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
+    // The profile is about to change,
+    // or is going away because the application is shutting down.
+
+    nsAutoMonitor lock(monitor);
+
+    if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
+      RemoveAllFromMemory();
+      // delete the storage file
+      if (mSettingsFile) {
+        mSettingsFile->Remove(PR_FALSE);
+      }
+    } else {
+      RemoveAllFromMemory();
+    }
+
+  } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
+    // The profile has already changed.
+    // Now read from the new profile location.
+    // we also need to update the cached file location
+
+    nsAutoMonitor lock(monitor);
+
+    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile));
+    if (NS_SUCCEEDED(rv)) {
+      mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName));
+    }
+    Read();
+
+  }
+
+  return NS_OK;
+}
+
+void
+nsCertOverrideService::RemoveAllFromMemory()
+{
+  nsAutoMonitor lock(monitor);
+  mSettingsTable.Clear();
+}
+
+nsresult
+nsCertOverrideService::Read()
+{
+  nsAutoMonitor lock(monitor);
+
+  nsresult rv;
+  nsCOMPtr<nsIInputStream> fileInputStream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCAutoString buffer;
+  PRBool isMore = PR_TRUE;
+  PRInt32 hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex;
+
+  /* file format is:
+   *
+   * host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey
+   *
+   *   where override-mask is a sequence of characters,
+   *     M meaning hostname-Mismatch-override
+   *     U meaning Untrusted-override
+   *     T meaning Time-error-override (expired/not yet valid) 
+   *
+   * if this format isn't respected we move onto the next line in the file.
+   */
+
+  while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
+    if (buffer.IsEmpty() || buffer.First() == '#') {
+      continue;
+    }
+
+    // this is a cheap, cheesy way of parsing a tab-delimited line into
+    // string indexes, which can be lopped off into substrings. just for
+    // purposes of obfuscation, it also checks that each token was found.
+    // todo: use iterators?
+    if ((algoIndex         = buffer.FindChar('\t', hostIndex)         + 1) == 0 ||
+        (fingerprintIndex  = buffer.FindChar('\t', algoIndex)         + 1) == 0 ||
+        (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex)  + 1) == 0 ||
+        (dbKeyIndex        = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) {
+      continue;
+    }
+
+    const nsASingleFragmentCString &host = Substring(buffer, hostIndex, algoIndex - hostIndex - 1);
+    const nsASingleFragmentCString &algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1);
+    const nsASingleFragmentCString &fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1);
+    const nsASingleFragmentCString &bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1);
+    const nsASingleFragmentCString &db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex);
+
+    nsCertOverride::OverrideBits bits;
+    nsCertOverride::convertStringToBits(bits_string, bits);
+
+    AddEntryToList(host, algo_string, fingerprint, bits, db_key);
+  }
+
+  return NS_OK;
+}
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+WriteEntryCallback(nsCertOverrideEntry *aEntry,
+                   void *aArg)
+{
+  static const char kNew[] = "\n";
+  static const char kTab[] = "\t";
+
+  nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg;
+
+  nsresult rv;
+
+  if (rawStreamPtr && aEntry)
+  {
+    const nsCertOverride &settings = aEntry->mSettings;
+
+    nsCAutoString bits_string;
+    nsCertOverride::convertBitsToString(settings.mOverrideBits, 
+                                            bits_string);
+
+    rawStreamPtr->Write(settings.mHostWithPortUTF8.get(), settings.mHostWithPortUTF8.Length(), &rv);
+    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
+    rawStreamPtr->Write(settings.mFingerprintAlgOID.get(), 
+                        settings.mFingerprintAlgOID.Length(), &rv);
+    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
+    rawStreamPtr->Write(settings.mFingerprint.get(), 
+                        settings.mFingerprint.Length(), &rv);
+    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
+    rawStreamPtr->Write(bits_string.get(), 
+                        bits_string.Length(), &rv);
+    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
+    rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &rv);
+    rawStreamPtr->Write(kNew, sizeof(kNew) - 1, &rv);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+nsresult
+nsCertOverrideService::Write()
+{
+  nsAutoMonitor lock(monitor);
+
+  if (!mSettingsFile) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIOutputStream> fileOutputStream;
+  rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream),
+                                       mSettingsFile,
+                                       -1,
+                                       0600);
+  if (NS_FAILED(rv)) {
+    NS_ERROR("failed to open cert_warn_settings.txt for writing");
+    return rv;
+  }
+
+  // get a buffered output stream 4096 bytes big, to optimize writes
+  nsCOMPtr<nsIOutputStream> bufferedOutputStream;
+  rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  static const char kHeader[] =
+      "# PSM Certificate Override Settings file\n"
+      "# This is a generated file!  Do not edit.\n";
+
+  /* see ::Read for file format */
+
+  bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv);
+
+  nsIOutputStream *rawStreamPtr = bufferedOutputStream;
+  mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr);
+
+  // All went ok. Maybe except for problems in Write(), but the stream detects
+  // that for us
+  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream);
+  NS_ASSERTION(safeStream, "expected a safe output stream!");
+  if (safeStream) {
+    rv = safeStream->Finish();
+    if (NS_FAILED(rv)) {
+      NS_WARNING("failed to save cert warn settings file! possible dataloss");
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+GetCertFingerprintByOidTag(CERTCertificate* nsscert,
+                           SECOidTag aOidTag, 
+                           nsCString &fp)
+{
+  unsigned int hash_len = HASH_ResultLenByOidTag(aOidTag);
+  nsRefPtr<nsStringBuffer> fingerprint = nsStringBuffer::Alloc(hash_len);
+  if (!fingerprint)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  PK11_HashBuf(aOidTag, (unsigned char*)fingerprint->Data(), 
+               nsscert->derCert.data, nsscert->derCert.len);
+
+  SECItem fpItem;
+  fpItem.data = (unsigned char*)fingerprint->Data();
+  fpItem.len = hash_len;
+
+  fp.Adopt(CERT_Hexify(&fpItem, 1));
+  return NS_OK;
+}
+
+static nsresult
+GetCertFingerprintByOidTag(nsIX509Cert *aCert,
+                           SECOidTag aOidTag, 
+                           nsCString &fp)
+{
+  nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
+  if (!cert2)
+    return NS_ERROR_FAILURE;
+
+  CERTCertificate* nsscert = cert2->GetCert();
+  if (!nsscert)
+    return NS_ERROR_FAILURE;
+
+  CERTCertificateCleaner nsscertCleaner(nsscert);
+  return GetCertFingerprintByOidTag(nsscert, aOidTag, fp);
+}
+
+#include <string.h>
+#include "secitem.h"
+#include "secport.h"
+#include "secerr.h"
+
+// FIXME: This is a temporary copy of NSS function SEC_StringToOID,
+//        already available on NSS trunk, but not yet delivered to
+//        the client application. Remove this function and the include
+//        statements after a new tag landed with bug 397296.
+static SECStatus
+_psm_copy_SEC_StringToOID(PLArenaPool *pool, SECItem *to, const char *from, PRUint32 len)
+{
+    PRUint32 decimal_numbers = 0;
+    PRUint32 result_bytes = 0;
+    SECStatus rv;
+    PRUint8 result[1024];
+
+    static const PRUint32 max_decimal = (0xffffffff / 10);
+    static const char OIDstring[] = {"OID."};
+
+    if (!from || !to) {
+    	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+    if (!len) {
+    	len = PL_strlen(from);
+    }
+    if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) {
+    	from += 4; /* skip leading "OID." if present */
+	len  -= 4;
+    }
+    if (!len) {
+bad_data:
+    	PORT_SetError(SEC_ERROR_BAD_DATA);
+	return SECFailure;
+    }
+    do {
+	PRUint32 decimal = 0;
+        while (len > 0 && isdigit(*from)) {
+	    PRUint32 addend = (*from++ - '0');
+	    --len;
+	    if (decimal > max_decimal)  /* overflow */
+		goto bad_data;
+	    decimal = (decimal * 10) + addend;
+	    if (decimal < addend)	/* overflow */
+		goto bad_data;
+	}
+	if (len != 0 && *from != '.') {
+	    goto bad_data;
+	}
+	if (decimal_numbers == 0) {
+	    if (decimal > 2)
+	    	goto bad_data;
+	    result[0] = decimal * 40;
+	    result_bytes = 1;
+	} else if (decimal_numbers == 1) {
+	    if (decimal > 40)
+	    	goto bad_data;
+	    result[0] += decimal;
+	} else {
+	    /* encode the decimal number,  */
+	    PRUint8 * rp;
+	    PRUint32 num_bytes = 0;
+	    PRUint32 tmp = decimal;
+	    while (tmp) {
+	        num_bytes++;
+		tmp >>= 7;
+	    }
+	    if (!num_bytes )
+	    	++num_bytes;  /* use one byte for a zero value */
+	    if (num_bytes + result_bytes > sizeof result)
+	    	goto bad_data;
+	    tmp = num_bytes;
+	    rp = result + result_bytes - 1;
+	    rp[tmp] = (PRUint8)(decimal & 0x7f);
+	    decimal >>= 7;
+	    while (--tmp > 0) {
+		rp[tmp] = (PRUint8)(decimal | 0x80);
+		decimal >>= 7;
+	    }
+	    result_bytes += num_bytes;
+	}
+	++decimal_numbers;
+	if (len > 0) { /* skip trailing '.' */
+	    ++from;
+	    --len;
+	}
+    } while (len > 0);
+    /* now result contains result_bytes of data */
+    if (to->data && to->len >= result_bytes) {
+    	PORT_Memcpy(to->data, result, to->len = result_bytes);
+	rv = SECSuccess;
+    } else {
+    	SECItem result_item = {siBuffer, NULL, 0 };
+	result_item.data = result;
+	result_item.len  = result_bytes;
+	rv = SECITEM_CopyItem(pool, to, &result_item);
+    }
+    return rv;
+}
+
+static nsresult
+GetCertFingerprintByDottedOidString(CERTCertificate* nsscert,
+                                    const nsCString &dottedOid, 
+                                    nsCString &fp)
+{
+  SECItem oid;
+  oid.data = nsnull;
+  oid.len = 0;
+  SECStatus srv = _psm_copy_SEC_StringToOID(nsnull, &oid, 
+                    dottedOid.get(), dottedOid.Length());
+  if (srv != SECSuccess)
+    return NS_ERROR_FAILURE;
+
+  SECOidTag oid_tag = SECOID_FindOIDTag(&oid);
+  SECITEM_FreeItem(&oid, PR_FALSE);
+
+  if (oid_tag == SEC_OID_UNKNOWN)
+    return NS_ERROR_FAILURE;
+
+  return GetCertFingerprintByOidTag(nsscert, oid_tag, fp);
+}
+
+static nsresult
+GetCertFingerprintByDottedOidString(nsIX509Cert *aCert,
+                                    const nsCString &dottedOid, 
+                                    nsCString &fp)
+{
+  nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
+  if (!cert2)
+    return NS_ERROR_FAILURE;
+
+  CERTCertificate* nsscert = cert2->GetCert();
+  if (!nsscert)
+    return NS_ERROR_FAILURE;
+
+  CERTCertificateCleaner nsscertCleaner(nsscert);
+  return GetCertFingerprintByDottedOidString(nsscert, dottedOid, fp);
+}
+
+NS_IMETHODIMP
+nsCertOverrideService::RememberValidityOverride(const nsAString & aHostNameWithPort, 
+                                                nsIX509Cert *aCert,
+                                                PRUint32 aOverrideBits)
+{
+  NS_ENSURE_ARG_POINTER(aCert);
+  if (aHostNameWithPort.IsEmpty())
+    return NS_ERROR_INVALID_ARG;
+
+  nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
+  if (!cert2)
+    return NS_ERROR_FAILURE;
+
+  CERTCertificate* nsscert = cert2->GetCert();
+  if (!nsscert)
+    return NS_ERROR_FAILURE;
+
+  CERTCertificateCleaner nsscertCleaner(nsscert);
+
+  nsCAutoString nickname;
+  nickname = nsNSSCertificate::defaultServerNickname(nsscert);
+  if (nickname.IsEmpty())
+    return NS_ERROR_FAILURE;
+
+  PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+  if (!slot)
+    return NS_ERROR_FAILURE;
+
+  SECStatus srv = PK11_ImportCert(slot, nsscert, CK_INVALID_HANDLE, 
+                                  const_cast<char*>(nickname.get()), PR_FALSE);
+  PK11_FreeSlot(slot);
+
+  if (srv != SECSuccess)
+    return NS_ERROR_FAILURE;
+
+  nsCString myHostPort;
+  myHostPort = NS_ConvertUTF16toUTF8(aHostNameWithPort);
+
+  PRInt32 find_colon = myHostPort.FindChar(':');
+  if (find_colon == -1) {
+    myHostPort.AppendLiteral(":443");
+  }
+
+  nsCAutoString fpStr;
+  nsresult rv = GetCertFingerprintByOidTag(nsscert, 
+                  mOidTagForStoringNewHashes, fpStr);
+  if (NS_FAILED(rv))
+    return rv;
+
+  char *dbkey = NULL;
+  rv = aCert->GetDbKey(&dbkey);
+  if (NS_FAILED(rv) || !dbkey)
+    return rv;
+
+  // change \n and \r to spaces in the possibly multi-line-base64-encoded key
+  for (char *dbkey_walk = dbkey;
+       *dbkey_walk;
+      ++dbkey_walk) {
+    char c = *dbkey_walk;
+    if (c == '\r' || c == '\n') {
+      *dbkey_walk = ' ';
+    }
+  }
+
+  {
+    nsAutoMonitor lock(monitor);
+    AddEntryToList(myHostPort, mDottedOidForStoringNewHashes, fpStr, 
+                   (nsCertOverride::OverrideBits)aOverrideBits, 
+                   nsDependentCString(dbkey));
+    Write();
+  }
+
+  PR_Free(dbkey);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertOverrideService::HasMatchingOverride(const nsAString & aHostNameWithPort, 
+                                           nsIX509Cert *aCert, 
+                                           PRUint32 *aOverrideBits,
+                                           PRBool *_retval)
+{
+  if (aHostNameWithPort.IsEmpty())
+    return NS_ERROR_INVALID_ARG;
+
+  NS_ENSURE_ARG_POINTER(aCert);
+  NS_ENSURE_ARG_POINTER(aOverrideBits);
+  NS_ENSURE_ARG_POINTER(_retval);
+  *_retval = PR_FALSE;
+  *aOverrideBits = nsCertOverride::ob_None;
+
+  NS_ConvertUTF16toUTF8 hp8(aHostNameWithPort);
+  nsCertOverride settings;
+
+  {
+    nsAutoMonitor lock(monitor);
+    nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hp8.get());
+  
+    if (!entry)
+      return NS_OK;
+  
+    settings = entry->mSettings; // copy
+  }
+
+  *aOverrideBits = settings.mOverrideBits;
+
+  nsCAutoString fpStr;
+  nsresult rv;
+
+  if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
+    rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr);
+  }
+  else {
+    rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr);
+  }
+  if (NS_FAILED(rv))
+    return rv;
+
+  *_retval = settings.mFingerprint.Equals(fpStr);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertOverrideService::GetValidityOverride(const nsAString & aHostNameWithPort, 
+                                           nsACString & aHashAlg, 
+                                           nsACString & aFingerprint, 
+                                           PRUint32 *aOverrideBits,
+                                           PRBool *_found)
+{
+  NS_ENSURE_ARG_POINTER(_found);
+  NS_ENSURE_ARG_POINTER(aOverrideBits);
+  *_found = PR_FALSE;
+  *aOverrideBits = nsCertOverride::ob_None;
+
+  NS_ConvertUTF16toUTF8 hp8(aHostNameWithPort);
+  nsCertOverride settings;
+
+  {
+    nsAutoMonitor lock(monitor);
+    nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hp8.get());
+  
+    if (entry) {
+      *_found = PR_TRUE;
+      settings = entry->mSettings; // copy
+    }
+  }
+
+  if (*_found) {
+    *aOverrideBits = settings.mOverrideBits;
+    aFingerprint = settings.mFingerprint;
+    aHashAlg = settings.mFingerprintAlgOID;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsCertOverrideService::AddEntryToList(const nsACString &hostWithPortUTF8, 
+                                      const nsACString &fingerprintAlgOID, 
+                                      const nsACString &fingerprint,
+                                      nsCertOverride::OverrideBits ob,
+                                      const nsACString &dbKey)
+{
+  const nsPromiseFlatCString &flat = PromiseFlatCString(hostWithPortUTF8);
+
+  {
+    nsAutoMonitor lock(monitor);
+    nsCertOverrideEntry *entry = mSettingsTable.PutEntry(flat.get());
+
+    if (!entry) {
+      NS_ERROR("can't insert a null entry!");
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    nsCertOverride &settings = entry->mSettings;
+    settings.mHostWithPortUTF8 = hostWithPortUTF8;
+    settings.mFingerprintAlgOID = fingerprintAlgOID;
+    settings.mFingerprint = fingerprint;
+    settings.mOverrideBits = ob;
+    settings.mDBKey = dbKey;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertOverrideService::ClearValidityOverride(const nsAString & aHostNameWithPort)
+{
+  NS_ConvertUTF16toUTF8 hp8(aHostNameWithPort);
+  {
+    nsAutoMonitor lock(monitor);
+    mSettingsTable.RemoveEntry(hp8.get());
+    Write();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertOverrideService::GetAllOverrideHostsWithPorts(PRUint32 *aCount, 
+                                                        PRUnichar ***aHostsWithPortsArray)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+static PRBool
+matchesDBKey(nsIX509Cert *cert, const char *match_dbkey)
+{
+  char *dbkey = NULL;
+  nsresult rv = cert->GetDbKey(&dbkey);
+  if (NS_FAILED(rv) || !dbkey)
+    return PR_FALSE;
+
+  PRBool found_mismatch = PR_FALSE;
+  const char *key1 = dbkey;
+  const char *key2 = match_dbkey;
+
+  // skip over any whitespace when comparing
+  while (*key1 && *key2) {
+    char c1 = *key1;
+    char c2 = *key2;
+    
+    switch (c1) {
+      case ' ':
+      case '\t':
+      case '\n':
+      case '\r':
+        ++key1;
+        continue;
+    }
+
+    switch (c2) {
+      case ' ':
+      case '\t':
+      case '\n':
+      case '\r':
+        ++key2;
+        continue;
+    }
+
+    if (c1 != c2) {
+      found_mismatch = PR_TRUE;
+      break;
+    }
+
+    ++key1;
+    ++key2;
+  }
+
+  PR_Free(dbkey);
+  return !found_mismatch;
+}
+
+struct nsCertAndInt
+{
+  nsIX509Cert *cert;
+  PRUint32 counter;
+
+  SECOidTag mOidTagForStoringNewHashes;
+  nsCString mDottedOidForStoringNewHashes;
+};
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+FindMatchingCertCallback(nsCertOverrideEntry *aEntry,
+                         void *aArg)
+{
+  nsCertAndInt *cai = (nsCertAndInt *)aArg;
+
+  if (cai && aEntry)
+  {
+    const nsCertOverride &settings = aEntry->mSettings;
+    if (matchesDBKey(cai->cert, settings.mDBKey.get())) {
+      nsCAutoString cert_fingerprint;
+      nsresult rv;
+      if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) {
+        rv = GetCertFingerprintByOidTag(cai->cert,
+               cai->mOidTagForStoringNewHashes, cert_fingerprint);
+      }
+      else {
+        rv = GetCertFingerprintByDottedOidString(cai->cert,
+               settings.mFingerprintAlgOID, cert_fingerprint);
+      }
+      if (NS_SUCCEEDED(rv) &&
+          settings.mFingerprint.Equals(cert_fingerprint)) {
+        cai->counter++;
+      }
+    }
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert, 
+                                              PRUint32 *_retval)
+{
+  NS_ENSURE_ARG(aCert);
+  NS_ENSURE_ARG(_retval);
+
+  nsCertAndInt cai;
+  cai.cert = aCert;
+  cai.counter = 0;
+  cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
+  cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
+
+  {
+    nsAutoMonitor lock(monitor);
+    mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai);
+  }
+  *_retval = cai.counter;
+  return NS_OK;
+}
+
+struct nsCertAndPointerAndCallback
+{
+  nsIX509Cert *cert;
+  void *userdata;
+  nsCertOverrideService::CertOverrideEnumerator enumerator;
+
+  SECOidTag mOidTagForStoringNewHashes;
+  nsCString mDottedOidForStoringNewHashes;
+};
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry,
+                               void *aArg)
+{
+  nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg;
+
+  if (capac && aEntry)
+  {
+    const nsCertOverride &settings = aEntry->mSettings;
+
+    if (!capac->cert) {
+      (*capac->enumerator)(settings, capac->userdata);
+    }
+    else {
+      if (matchesDBKey(capac->cert, settings.mDBKey.get())) {
+        nsCAutoString cert_fingerprint;
+        nsresult rv;
+        if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) {
+          rv = GetCertFingerprintByOidTag(capac->cert,
+                 capac->mOidTagForStoringNewHashes, cert_fingerprint);
+        }
+        else {
+          rv = GetCertFingerprintByDottedOidString(capac->cert,
+                 settings.mFingerprintAlgOID, cert_fingerprint);
+        }
+        if (NS_SUCCEEDED(rv) &&
+            settings.mFingerprint.Equals(cert_fingerprint)) {
+          (*capac->enumerator)(settings, capac->userdata);
+        }
+      }
+    }
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+nsresult 
+nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert,
+                         CertOverrideEnumerator enumerator,
+                         void *aUserData)
+{
+  nsCertAndPointerAndCallback capac;
+  capac.cert = aCert;
+  capac.userdata = aUserData;
+  capac.enumerator = enumerator;
+  capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
+  capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
+
+  {
+    nsAutoMonitor lock(monitor);
+    mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac);
+  }
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/src/nsCertOverrideService.h
@@ -0,0 +1,199 @@
+/* -*- 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
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kai Engert <kengert@redhat.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 ***** */
+
+#ifndef __NSCERTOVERRIDESERVICE_H__
+#define __NSCERTOVERRIDESERVICE_H__
+
+#include "nsICertOverrideService.h"
+#include "nsTHashtable.h"
+#include "nsIObserver.h"
+#include "nsString.h"
+#include "nsIFile.h"
+#include "prmon.h"
+#include "secoidt.h"
+
+class nsCertOverride
+{
+public:
+
+  enum OverrideBits { ob_None=0, ob_Untrusted=1, ob_Mismatch=2,
+                      ob_Time_error=4 };
+
+  nsCertOverride()
+  :mOverrideBits(ob_None)
+  {
+  }
+
+  nsCertOverride(const nsCertOverride &other)
+  {
+    this->operator=(other);
+  }
+
+  nsCertOverride &operator=(const nsCertOverride &other)
+  {
+    mHostWithPortUTF8 = other.mHostWithPortUTF8;
+    mFingerprintAlgOID = other.mFingerprintAlgOID;
+    mFingerprint = other.mFingerprint;
+    mOverrideBits = other.mOverrideBits;
+    mDBKey = other.mDBKey;
+    return *this;
+  }
+
+  nsCString mHostWithPortUTF8;
+  nsCString mFingerprint;
+  nsCString mFingerprintAlgOID;
+  OverrideBits mOverrideBits;
+  nsCString mDBKey;
+
+  static void convertBitsToString(OverrideBits ob, nsACString &str);
+  static void convertStringToBits(const nsACString &str, OverrideBits &ob);
+};
+
+
+// hash entry class
+class nsCertOverrideEntry : public PLDHashEntryHdr
+{
+  public:
+    // Hash methods
+    typedef const char* KeyType;
+    typedef const char* KeyTypePointer;
+
+    // do nothing with aHost - we require mHead to be set before we're live!
+    nsCertOverrideEntry(KeyTypePointer aHostWithPortUTF8)
+    {
+    }
+
+    nsCertOverrideEntry(const nsCertOverrideEntry& toCopy)
+    {
+      mSettings = toCopy.mSettings;
+    }
+
+    ~nsCertOverrideEntry()
+    {
+    }
+
+    KeyType GetKey() const
+    {
+      return HostWithPortPtr();
+    }
+
+    KeyTypePointer GetKeyPointer() const
+    {
+      return HostWithPortPtr();
+    }
+
+    PRBool KeyEquals(KeyTypePointer aKey) const
+    {
+      return !strcmp(HostWithPortPtr(), aKey);
+    }
+
+    static KeyTypePointer KeyToPointer(KeyType aKey)
+    {
+      return aKey;
+    }
+
+    static PLDHashNumber HashKey(KeyTypePointer aKey)
+    {
+      // PL_DHashStringKey doesn't use the table parameter, so we can safely
+      // pass nsnull
+      return PL_DHashStringKey(nsnull, aKey);
+    }
+
+    enum { ALLOW_MEMMOVE = PR_FALSE };
+
+    // get methods
+    inline const nsCString &HostWithPort() const { return mSettings.mHostWithPortUTF8; }
+
+    inline KeyTypePointer HostWithPortPtr() const
+    {
+      return mSettings.mHostWithPortUTF8.get();
+    }
+
+    nsCertOverride mSettings;
+};
+
+class nsCertOverrideService : public nsICertOverrideService
+                            , public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICERTOVERRIDESERVICE
+  NS_DECL_NSIOBSERVER
+
+  nsCertOverrideService();
+  ~nsCertOverrideService();
+
+  nsresult Init();
+
+  typedef void 
+  (*PR_CALLBACK CertOverrideEnumerator)(const nsCertOverride &aSettings,
+                                        void *aUserData);
+
+  // aCert == null: return all overrides
+  // aCert != null: return overrides that match the given cert
+  nsresult EnumerateCertOverrides(nsIX509Cert *aCert,
+                                  CertOverrideEnumerator enumerator,
+                                  void *aUserData);
+
+protected:
+    PRMonitor *monitor;
+    nsCOMPtr<nsIFile> mSettingsFile;
+    nsTHashtable<nsCertOverrideEntry> mSettingsTable;
+
+    SECOidTag mOidTagForStoringNewHashes;
+    nsCString mDottedOidForStoringNewHashes;
+
+    void RemoveAllFromMemory();
+    nsresult Read();
+    nsresult Write();
+    nsresult AddEntryToList(const nsACString &hostWithPortUTF8, 
+                            const nsACString &algo_oid, 
+                            const nsACString &fingerprint,
+                            nsCertOverride::OverrideBits ob,
+                            const nsACString &dbKey);
+};
+
+#define NS_CERTOVERRIDE_CID { /* 67ba681d-5485-4fff-952c-2ee337ffdcd6 */ \
+    0x67ba681d,                                                        \
+    0x5485,                                                            \
+    0x4fff,                                                            \
+    {0x95, 0x2c, 0x2e, 0xe3, 0x37, 0xff, 0xdc, 0xd6}                   \
+  }
+
+#endif
--- a/security/manager/ssl/src/nsNSSCertificate.h
+++ b/security/manager/ssl/src/nsNSSCertificate.h
@@ -54,19 +54,17 @@
 #include "nsIClassInfo.h"
 
 #include "nsNSSCertHeader.h"
 
 class nsINSSComponent;
 class nsIASN1Sequence;
 
 /* Certificate */
-class nsNSSCertificate : public nsIX509Cert,
-                         public nsIX509Cert2,
-                         public nsIX509Cert3,
+class nsNSSCertificate : public nsIX509Cert3,
                          public nsISMimeCert,
                          public nsISerializable,
                          public nsIClassInfo,
                          public nsNSSShutDownObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIX509CERT
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/src/nsNSSCleaner.cpp
@@ -0,0 +1,58 @@
+/* ***** 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 Communicator.
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kai Engert <kengert@redhat.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 "nsNSSCleaner.h"
+#include "cert.h"
+
+CERTVerifyLogContentsCleaner::CERTVerifyLogContentsCleaner(CERTVerifyLog *&cvl)
+:m_cvl(cvl)
+{
+}
+
+CERTVerifyLogContentsCleaner::~CERTVerifyLogContentsCleaner()
+{
+  if (!m_cvl)
+    return;
+
+  CERTVerifyLogNode *i_node;
+  for (i_node = m_cvl->head; i_node; i_node = i_node->next)
+  {
+    if (i_node->cert)
+      CERT_DestroyCertificate(i_node->cert);
+  }
+}
+
--- a/security/manager/ssl/src/nsNSSCleaner.h
+++ b/security/manager/ssl/src/nsNSSCleaner.h
@@ -119,9 +119,20 @@ public:                                 
     if (object) {                                  \
       cleanfunc(object, paramvalue);               \
       object = nsnull;                             \
     }                                              \
   }                                                \
   void detach() {object=nsnull;}                   \
 };
 
+#include "certt.h"
+
+class CERTVerifyLogContentsCleaner
+{
+public:
+  CERTVerifyLogContentsCleaner(CERTVerifyLog *&cvl);
+  ~CERTVerifyLogContentsCleaner();
+private:
+  CERTVerifyLog *&m_cvl;
+};
+
 #endif
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -46,61 +46,67 @@
 #include "prlog.h"
 #include "prnetdb.h"
 #include "nsIPrompt.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIServiceManager.h"
 #include "nsIWebProgressListener.h"
 #include "nsIChannel.h"
-#include "nsIBadCertListener.h"
 #include "nsNSSCertificate.h"
+#include "nsIX509CertValidity.h"
 #include "nsIProxyObjectManager.h"
 #include "nsProxiedService.h"
 #include "nsIDateTimeFormat.h"
 #include "nsDateTimeFormatCID.h"
 #include "nsIClientAuthDialogs.h"
+#include "nsICertOverrideService.h"
+#include "nsIBadCertListener2.h"
 
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsHashSets.h"
 #include "nsCRT.h"
 #include "nsAutoPtr.h"
 #include "nsPrintfCString.h"
 #include "nsAutoLock.h"
 #include "nsSSLThread.h"
 #include "nsNSSShutDown.h"
+#include "nsSSLStatus.h"
 #include "nsNSSCertHelper.h"
 #include "nsNSSCleaner.h"
 #include "nsThreadUtils.h"
 #include "nsIDocShell.h"
 #include "nsISecureBrowserUI.h"
 #include "nsProxyRelease.h"
 
 #include "ssl.h"
 #include "secerr.h"
 #include "sslerr.h"
 #include "secder.h"
 #include "secasn1.h"
 #include "certdb.h"
 #include "cert.h"
 #include "keyhi.h"
+#include "secport.h"
 
 
 //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal 
                             //reports when doing SSL read/write
                             
 //#define DUMP_BUFFER  //Enable this define along with
                        //DEBUG_SSL_VERBOSE to dump SSL
                        //read/write buffer to a log.
                        //Uses PR_LOG except on Mac where
                        //we always write out to our own
                        //file.
 
 NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
+NSSCleanupAutoPtrClass(char, PR_FREEIF)
+NSSCleanupAutoPtrClass_WithParam(PRArenaPool, PORT_FreeArena, FalseParam, PR_FALSE)
 
 /* SSM_UserCertChoice: enum for cert choice info */
 typedef enum {ASK, AUTO} SSM_UserCertChoice;
 
 
 static SECStatus PR_CALLBACK
 nsNSS_SSLGetClientAuthData(void *arg, PRFileDesc *socket,
 						   CERTDistNames *caNames,
@@ -190,17 +196,16 @@ nsNSSSocketInfo::nsNSSSocketInfo()
     mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE),
     mExternalErrorReporting(PR_FALSE),
     mForSTARTTLS(PR_FALSE),
     mHandshakePending(PR_TRUE),
     mCanceled(PR_FALSE),
     mHasCleartextPhase(PR_FALSE),
     mHandshakeInProgress(PR_FALSE),
     mAllowTLSIntoleranceTimeout(PR_TRUE),
-    mBadCertUIStatus(bcuis_not_shown),
     mHandshakeStartTime(0),
     mPort(0),
     mCAChain(nsnull)
 {
   mThreadData = new nsSSLSocketThreadData;
 }
 
 nsNSSSocketInfo::~nsNSSSocketInfo()
@@ -557,41 +562,26 @@ void nsNSSSocketInfo::SetHandshakeInProg
   mHandshakeInProgress = aIsIn;
 
   if (mHandshakeInProgress && !mHandshakeStartTime)
   {
     mHandshakeStartTime = PR_IntervalNow();
   }
 }
 
-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;
 }
 
 #define HANDSHAKE_TIMEOUT_SECONDS 25
 
 PRBool nsNSSSocketInfo::HandshakeTimeout()
 {
-  if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout || 
-      mBadCertUIStatus == bcuis_active)
+  if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout)
     return PR_FALSE;
 
   return ((PRIntervalTime)(PR_IntervalNow() - mHandshakeStartTime)
           > PR_SecondsToInterval(HANDSHAKE_TIMEOUT_SECONDS));
 }
 
 void nsSSLIOLayerHelpers::Cleanup()
 {
@@ -636,16 +626,138 @@ getErrorMessage(PRInt32 err, const nsStr
   rv = nsNSSErrors::getErrorMessageFromCode(err, component, explanation);
   if (NS_SUCCEEDED(rv))
     returnedMessage.Append(explanation);
 
   return NS_OK;
 }
 
 static nsresult
+getInvalidCertErrorMessage(PRUint32 multipleCollectedErrors, 
+                           PRInt32 errorCodeToReport, 
+                           const nsString &host,
+                           const nsString &hostWithPort,
+                           nsIX509Cert* ix509,
+                           nsINSSComponent *component,
+                           nsString &returnedMessage)
+{
+  NS_ENSURE_ARG_POINTER(component);
+
+  const PRUnichar *params[1];
+  nsresult rv;
+
+  if (hostWithPort.Length())
+  {
+    params[0] = hostWithPort.get();
+
+    nsString formattedString;
+    rv = component->PIPBundleFormatStringFromName("certErrorIntro", 
+                                                  params, 1, 
+                                                  formattedString);
+    if (NS_SUCCEEDED(rv))
+    {
+      returnedMessage.Append(formattedString);
+      returnedMessage.Append(NS_LITERAL_STRING("\n"));
+    }
+  }
+
+  if (multipleCollectedErrors & nsICertOverrideService::ERROR_UNTRUSTED)
+  {
+    params[0] = host.get();
+
+    nsString formattedString;
+    rv = component->GetPIPNSSBundleString("certErrorUntrusted", 
+                                          formattedString);
+    if (NS_SUCCEEDED(rv))
+    {
+      returnedMessage.Append(formattedString);
+      returnedMessage.Append(NS_LITERAL_STRING("\n"));
+    }
+  }
+
+  if (multipleCollectedErrors & nsICertOverrideService::ERROR_MISMATCH)
+  {
+    params[0] = host.get();
+
+    nsString formattedString;
+    rv = component->PIPBundleFormatStringFromName("certErrorMismatch", 
+                                                  params, 1, 
+                                                  formattedString);
+    if (NS_SUCCEEDED(rv))
+    {
+      returnedMessage.Append(formattedString);
+      returnedMessage.Append(NS_LITERAL_STRING("\n"));
+    }
+  }
+
+  if (multipleCollectedErrors & nsICertOverrideService::ERROR_TIME)
+  {
+    PRTime now = PR_Now();
+    PRTime notAfter, notBefore, timeToUse;
+    nsCOMPtr<nsIX509CertValidity> validity;
+    const char *key;
+  
+    rv = ix509->GetValidity(getter_AddRefs(validity));
+    if (NS_FAILED(rv))
+      return rv;
+  
+    rv = validity->GetNotAfter(&notAfter);
+    if (NS_FAILED(rv))
+      return rv;
+  
+    rv = validity->GetNotBefore(&notBefore);
+    if (NS_FAILED(rv))
+      return rv;
+  
+    if (LL_CMP(now, >, notAfter)) {
+      key       = "certErrorExpired"; 
+      timeToUse = notAfter; 
+    } else {
+      key       = "certErrorNotYetValid";
+      timeToUse = notBefore;
+    }
+  
+    nsAutoString formattedDate;
+    nsIDateTimeFormat* aDateTimeFormat;
+    rv = CallCreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &aDateTimeFormat);
+    if (NS_FAILED(rv))
+      return rv;
+  
+    aDateTimeFormat->FormatPRTime(nsnull, kDateFormatShort, 
+                                  kTimeFormatNoSeconds, timeToUse, 
+                                  formattedDate);
+    NS_IF_RELEASE(aDateTimeFormat);
+    params[0] = formattedDate.get(); 
+
+    nsString formattedString;
+    rv = component->PIPBundleFormatStringFromName(key, params, 
+                                                  1, formattedString);
+    if (NS_SUCCEEDED(rv))
+    {
+      returnedMessage.Append(formattedString);
+      returnedMessage.Append(NS_LITERAL_STRING("\n"));
+    }
+  }
+
+  const char *codeName = nsNSSErrors::getDefaultErrorStringName(errorCodeToReport);
+  if (codeName)
+  {
+    nsCString error_id(codeName);
+    ToLowerCase(error_id);
+    NS_ConvertASCIItoUTF16 idU(error_id);
+
+    returnedMessage.Append(NS_LITERAL_STRING(" ("));
+    returnedMessage.Append(idU);
+    returnedMessage.Append(NS_LITERAL_STRING(")"));
+  }
+
+  return NS_OK;
+}
+
+static nsresult
 displayAlert(nsAFlatString &formattedString, nsNSSSocketInfo *infoObject)
 {
   // The interface requestor object may not be safe, so proxy the call to get
   // the nsIPrompt.
 
   nsCOMPtr<nsIInterfaceRequestor> proxiedCallbacks;
   NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
                        NS_GET_IID(nsIInterfaceRequestor),
@@ -685,21 +797,63 @@ nsHandleSSLError(nsNSSSocketInfo *socket
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
   if (NS_FAILED(rv))
     return rv;
 
   nsXPIDLCString hostName;
   socketInfo->GetHostName(getter_Copies(hostName));
   NS_ConvertASCIItoUTF16 hostNameU(hostName);
 
-  nsCOMPtr<nsIStringBundleService> service = 
-           do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+  nsString formattedString;
+  rv = getErrorMessage(err, hostNameU, nssComponent, formattedString);
+
+  PRBool external = PR_FALSE;
+  socketInfo->GetExternalErrorReporting(&external);
+  
+  if (external)
+  {
+    socketInfo->SetErrorMessage(formattedString.get());
+  }
+  else
+  {
+    nsPSMUITracker tracker;
+    if (tracker.isUIForbidden()) {
+      rv = NS_ERROR_NOT_AVAILABLE;
+    }
+    else {
+      rv = displayAlert(formattedString, socketInfo);
+    }
+  }
+  return rv;
+}
+
+static nsresult
+nsHandleInvalidCertError(nsNSSSocketInfo *socketInfo, 
+                         PRUint32 multipleCollectedErrors, 
+                         const nsACString &host, 
+                         const nsACString &hostWithPort, 
+                         PRInt32 err,
+                         nsIX509Cert* ix509)
+{
+  nsresult rv;
+  NS_DEFINE_CID(nssComponentCID, NS_NSSCOMPONENT_CID);
+  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
+  if (NS_FAILED(rv))
+    return rv;
+
+  NS_ConvertASCIItoUTF16 hostU(host);
+  NS_ConvertASCIItoUTF16 hostWithPortU(hostWithPort);
 
   nsString formattedString;
-  rv = getErrorMessage(err, hostNameU, nssComponent, formattedString);
+  rv = getInvalidCertErrorMessage(multipleCollectedErrors, err, hostU, hostWithPortU, 
+                                  ix509, nssComponent, formattedString);
+
+  // What mechanism is used to inform the user?
+  // The highest priority has the "external error reporting" feature,
+  // if set, we'll provide the strings to be used by the nsINSSErrorsService
 
   PRBool external = PR_FALSE;
   socketInfo->GetExternalErrorReporting(&external);
   
   if (external)
   {
     socketInfo->SetErrorMessage(formattedString.get());
   }
@@ -938,47 +1092,16 @@ isTLSIntoleranceError(PRInt32 err, PRBoo
     case SSL_ERROR_DECODE_ERROR_ALERT:
     case SSL_ERROR_RX_UNKNOWN_ALERT:
       return PR_TRUE;
   }
   
   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,
                             PRFileDesc* ssl_layer_fd, 
                             nsNSSSocketInfo *socketInfo)
 {
   // This is where we work around all of those SSL servers that don't 
   // conform to the SSL spec and shutdown a connection when we request
@@ -1020,22 +1143,16 @@ nsSSLThread::checkHandshake(PRInt32 byte
     PRInt32 err = PR_GetError();
 
     if (handleHandshakeResultNow) {
       if (PR_WOULD_BLOCK_ERROR == err) {
         socketInfo->SetHandshakeInProgress(PR_TRUE);
         return bytesTransfered;
       }
 
-      wantRetry = 
-        isClosedConnectionAfterBadCertUIWasShown(bytesTransfered, 
-                                                 wasReading, 
-                                                 err, 
-                                                 socketInfo->GetBadCertUIStatus());
-
       if (!wantRetry // no decision yet
           && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
       {
         wantRetry = nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo);
       }
     }
     
     // This is the common place where we trigger an error message on a SSL socket.
@@ -1043,22 +1160,16 @@ nsSSLThread::checkHandshake(PRInt32 byte
     if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err))) {
       nsHandleSSLError(socketInfo, err);
     }
   }
   else if (wasReading && 0 == bytesTransfered) // zero bytes on reading, socket closed
   {
     if (handleHandshakeResultNow)
     {
-      wantRetry = 
-        isClosedConnectionAfterBadCertUIWasShown(bytesTransfered, 
-                                                 wasReading, 
-                                                 0, 
-                                                 socketInfo->GetBadCertUIStatus());
-
       if (!wantRetry // no decision yet
           && !socketInfo->GetHasCleartextPhase()) // mirror PR_CONNECT_RESET_ERROR treament
       {
         wantRetry = 
           nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo);
       }
     }
   }
@@ -1352,222 +1463,16 @@ nsSSLIOLayerNewSocket(PRInt32 family,
     PR_Close(sock);
     return rv;
   }
 
   *fd = sock;
   return NS_OK;
 }
 
-static nsresult
-addCertToDB(CERTCertificate *peerCert, PRInt16 addType)
-{
-  CERTCertTrust trust;
-  SECStatus rv;
-  nsresult retVal = NS_ERROR_FAILURE;
-  char *nickname;
-  
-  switch (addType) {
-    case nsIBadCertListener::ADD_TRUSTED_PERMANENTLY:
-      nickname = nsNSSCertificate::defaultServerNickname(peerCert);
-      if (nsnull == nickname)
-        break;
-      memset((void*)&trust, 0, sizeof(trust));
-      rv = CERT_DecodeTrustString(&trust, "P"); 
-      if (rv != SECSuccess) {
-        return NS_ERROR_FAILURE;
-      }
-      rv = CERT_AddTempCertToPerm(peerCert, nickname, &trust);
-      if (rv == SECSuccess)
-        retVal = NS_OK;
-      PR_Free(nickname);
-      break;
-    case nsIBadCertListener::ADD_TRUSTED_FOR_SESSION:
-      // XXX We need an API from NSS to do this so 
-      //     that we don't have to access the fields 
-      //     in the cert directly.
-      peerCert->keepSession = PR_TRUE;
-      CERTCertTrust *trustPtr;
-      if (!peerCert->trust) {
-        trustPtr = (CERTCertTrust*)PORT_ArenaZAlloc(peerCert->arena,
-                                                    sizeof(CERTCertTrust));
-        if (!trustPtr)
-          break;
-
-        peerCert->trust = trustPtr;
-      } else {
-        trustPtr = peerCert->trust;
-      }
-      rv = CERT_DecodeTrustString(trustPtr, "P");
-      if (rv != SECSuccess)
-        break;
-
-      retVal = NS_OK;      
-      break;
-    default:
-      PR_ASSERT(!"Invalid value for addType passed to addCertDB");
-      break;
-  }
-  return retVal;
-}
-
-static PRBool
-nsContinueDespiteCertError(nsNSSSocketInfo  *infoObject,
-                           PRFileDesc       *sslSocket,
-                           int               error,
-                           nsNSSCertificate *nssCert)
-{
-  PRBool retVal = PR_FALSE;
-  nsIBadCertListener *badCertHandler = nsnull;
-  PRInt16 addType = nsIBadCertListener::UNINIT_ADD_FLAG;
-  nsresult rv;
-
-  if (!nssCert)
-    return PR_FALSE;
-
-  // Try to get a nsIBadCertListener implementation from the socket consumer
-  // first.  If that fails, fallback to the default UI.
-  nsCOMPtr<nsIInterfaceRequestor> callbacks;
-  infoObject->GetNotificationCallbacks(getter_AddRefs(callbacks));
-  if (callbacks) {
-    nsCOMPtr<nsIBadCertListener> handler = do_GetInterface(callbacks);
-    if (handler) {
-      NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
-                           NS_GET_IID(nsIBadCertListener),
-                           handler,
-                           NS_PROXY_SYNC,
-                           (void**)&badCertHandler);
-    }
-  }
-  if (!badCertHandler) {
-    rv = getNSSDialogs((void**)&badCertHandler, 
-                       NS_GET_IID(nsIBadCertListener),
-                       NS_BADCERTLISTENER_CONTRACTID);
-    if (NS_FAILED(rv)) 
-      return PR_FALSE;
-  }
-  nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>
-                                          (infoObject);
-  nsIX509Cert *callBackCert = static_cast<nsIX509Cert*>(nssCert);
-  CERTCertificate *peerCert = nssCert->GetCert();
-  NS_ASSERTION(peerCert, "Got nsnull cert back from nsNSSCertificate");
-  switch (error) {
-  case SEC_ERROR_UNKNOWN_ISSUER:
-  case SEC_ERROR_CA_CERT_INVALID:
-  case SEC_ERROR_UNTRUSTED_ISSUER:
-  /* This is a temporay fix for bug# - We are showing a unknown ca dialog,
-     when actually the ca cert has expired/not yet valid. We need to change
-     this in future - need to define a proper ui for this situation
-  */
-  case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-    {
-      nsPSMUITracker tracker;
-      if (tracker.isUIForbidden()) {
-        rv = NS_ERROR_NOT_AVAILABLE;
-      }
-      else {
-        rv = badCertHandler->ConfirmUnknownIssuer(csi, callBackCert, &addType, &retVal);
-      }
-    }
-    break;
-  case SSL_ERROR_BAD_CERT_DOMAIN:
-    {
-      nsXPIDLCString url; url.Adopt(SSL_RevealURL(sslSocket));
-      NS_ASSERTION(url.get(), "could not find valid URL in ssl socket");
-      {
-        nsPSMUITracker tracker;
-        if (tracker.isUIForbidden()) {
-          rv = NS_ERROR_NOT_AVAILABLE;
-        }
-        else {
-        rv = badCertHandler->ConfirmMismatchDomain(csi, url,
-                                            callBackCert, &retVal);
-        }
-      }
-      if (NS_SUCCEEDED(rv) && retVal) {
-        rv = CERT_AddOKDomainName(peerCert, url);
-      }
-    }
-    break;
-  case SEC_ERROR_EXPIRED_CERTIFICATE:
-    {
-      nsPSMUITracker tracker;
-      if (tracker.isUIForbidden()) {
-        rv = NS_ERROR_NOT_AVAILABLE;
-      }
-      else {
-        rv = badCertHandler->ConfirmCertExpired(csi, callBackCert, & retVal);
-      }
-    }
-    if (rv == SECSuccess && retVal) {
-      // XXX We need an NSS API for this equivalent functionality.
-      //     Having to reach inside the cert is evil.
-      peerCert->timeOK = PR_TRUE;
-    }
-    break;
-  case SEC_ERROR_CRL_EXPIRED:
-    {
-      nsXPIDLCString url; url.Adopt(SSL_RevealURL(sslSocket));
-      NS_ASSERTION(url, "could not find valid URL in ssl socket");
-      {
-        nsPSMUITracker tracker;
-        if (tracker.isUIForbidden()) {
-          rv = NS_ERROR_NOT_AVAILABLE;
-        }
-        else {
-          rv = badCertHandler->NotifyCrlNextupdate(csi, url, callBackCert);
-        }
-      }
-      retVal = PR_FALSE;
-    }
-    break;
-  default:
-    nsHandleSSLError(infoObject,error);
-    retVal = PR_FALSE;
-    rv = NS_ERROR_FAILURE;
-  }
-  if (retVal && addType != nsIBadCertListener::UNINIT_ADD_FLAG) {
-    addCertToDB(peerCert, addType);
-  }
-  NS_RELEASE(badCertHandler);
-  CERT_DestroyCertificate(peerCert);
-  return NS_FAILED(rv) ? PR_FALSE : retVal;
-}
-
-static SECStatus
-verifyCertAgain(CERTCertificate *cert, 
-                PRFileDesc      *sslSocket,
-                nsNSSSocketInfo *infoObject)
-{
-  SECStatus rv;
-
-  // If we get here, the user has accepted the cert so
-  // far, so we don't check the signature again.
-  rv = CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), cert,
-                          PR_FALSE, certificateUsageSSLServer,
-                          (void*)infoObject, NULL);
-
-  if (rv != SECSuccess) {
-    return rv;
-  }
-  
-  // Check the name field against the desired hostname.
-  char *hostname = SSL_RevealURL(sslSocket); 
-  if (hostname && hostname[0]) {
-    rv = CERT_VerifyCertName(cert, hostname);
-  } else {
-    rv = SECFailure;
-  }
-
-  if (rv != SECSuccess) {
-    PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
-  }
-  PR_FREEIF(hostname);
-  return rv;
-}
 /*
  * Function: SECStatus nsConvertCANamesToStrings()
  * Purpose: creates CA names strings from (CERTDistNames* caNames)
  *
  * Arguments and return values
  * - arena: arena to allocate strings on
  * - caNameStrings: filled with CA names strings on return
  * - caNames: CERTDistNames to extract strings from
@@ -2384,51 +2289,219 @@ done:
 
   *pRetCert = cert;
   *pRetKey = privKey;
 
   return ret;
 }
 
 static SECStatus
+cancel_and_failure(nsNSSSocketInfo* infoObject)
+{
+  infoObject->SetCanceled(PR_TRUE);
+  return SECFailure;
+}
+
+static SECStatus
 nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
 {
   nsNSSShutDownPreventionLock locker;
-  SECStatus rv = SECFailure;
-  int error;
   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo *)arg;
-  CERTCertificate *peerCert;
-  nsNSSCertificate *nssCert;
-
-  error = PR_GetError();
-  peerCert = SSL_PeerCertificate(sslSocket);
-  nssCert = new nsNSSCertificate(peerCert);
-  if (!nssCert) {
+  if (!infoObject)
     return SECFailure;
-  } 
-  NS_ADDREF(nssCert);
-  infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_active);
-  while (rv != SECSuccess) {
-     //Func nsContinueDespiteCertError does the same set of checks as func.
-     //nsCertErrorNeedsDialog. So, removing call to nsCertErrorNeedsDialog
-     if (!nsContinueDespiteCertError(infoObject, sslSocket, 
-                                    error, nssCert)) {
-      break;
+
+  CERTCertificate *peerCert = nsnull;
+  CERTCertificateCleaner peerCertCleaner(peerCert);
+  peerCert = SSL_PeerCertificate(sslSocket);
+  if (!peerCert)
+    return cancel_and_failure(infoObject);
+
+  nsRefPtr<nsNSSCertificate> nssCert;
+  nssCert = new nsNSSCertificate(peerCert);
+  if (!nssCert)
+    return cancel_and_failure(infoObject);
+
+  nsCOMPtr<nsIX509Cert> ix509 = static_cast<nsIX509Cert*>(nssCert.get());
+
+  SECStatus srv;
+  nsresult nsrv;
+  PRUint32 collected_errors = 0;
+  PRUint32 remaining_display_errors = 0;
+
+  // There may be multiple problems with a cert, but we can only report 
+  // a single error code to the caller. We'll use the first code we see.
+  // However, in our error string we'll use a string that mentions
+  // all of expired/not-yet-valid/domain-mismatch/untrusted.
+  PRErrorCode errorCodeToReport = SECSuccess;
+  
+  char *hostname = SSL_RevealURL(sslSocket);
+  charCleaner hostnameCleaner(hostname); 
+  nsDependentCString hostString(hostname);
+
+  PRInt32 port;
+  infoObject->GetPort(&port);
+
+  nsCString hostWithPortString = hostString;
+  hostWithPortString.AppendLiteral(":");
+  hostWithPortString.AppendInt(port);
+  
+
+  // Check the name field against the desired hostname.
+  if (hostname && hostname[0] &&
+      CERT_VerifyCertName(peerCert, hostname) != SECSuccess) {
+    collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
+    errorCodeToReport = SSL_ERROR_BAD_CERT_DOMAIN;
+  }
+
+  {
+    PRArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!log_arena)    
+      return cancel_and_failure(infoObject);
+
+    PRArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
+
+    CERTVerifyLog *verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
+    if (!verify_log)
+      return cancel_and_failure(infoObject);
+
+    CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
+
+    verify_log->arena = log_arena;
+
+    srv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), peerCert,
+                                 PR_TRUE, certificateUsageSSLServer,
+                                 PR_Now(), (void*)infoObject, 
+                                 verify_log, NULL);
+
+    // We ignore the result code of the cert verification.
+    // Either it is a failure, which is expected, and we'll process the
+    //                         verify log below.
+    // Or it is a success, then a domain mismatch is the only 
+    //                     possible failure. 
+
+    CERTVerifyLogNode *i_node;
+    for (i_node = verify_log->head; i_node; i_node = i_node->next)
+    {
+      if (errorCodeToReport == SECSuccess) {
+        errorCodeToReport = i_node->error;
+      }
+
+      switch (i_node->error)
+      {
+        case SEC_ERROR_UNKNOWN_ISSUER:
+        case SEC_ERROR_CA_CERT_INVALID:
+        case SEC_ERROR_UNTRUSTED_ISSUER:
+        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+        case SEC_ERROR_UNTRUSTED_CERT:
+          // We group all these errors as "cert not trusted"
+          collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
+          break;
+        case SSL_ERROR_BAD_CERT_DOMAIN:
+          collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
+          break;
+        case SEC_ERROR_EXPIRED_CERTIFICATE:
+          collected_errors |= nsICertOverrideService::ERROR_TIME;
+          break;
+        default:
+          // we are not willing to continue on any other error
+          nsHandleSSLError(infoObject, i_node->error);
+          return cancel_and_failure(infoObject);
+      }
     }
-    rv = verifyCertAgain(peerCert, sslSocket, infoObject);
-	error = PR_GetError();
+  }
+
+  if (!collected_errors)
+  {
+    NS_NOTREACHED("why did NSS call our bad cert handler if all looks good? Let's cancel the connection");
+    return SECFailure;
+  }
+
+  nsCOMPtr<nsSSLStatus> status;
+  infoObject->GetSSLStatus(getter_AddRefs(status));
+  if (!status) {
+    status = new nsSSLStatus();
+    infoObject->SetSSLStatus(status);
+  }
+
+  if (status) {
+    if (!status->mServerCert) {
+      status->mServerCert = nssCert;
+    }
+
+    status->mHaveCertStatus = PR_TRUE;
+    status->mIsDomainMismatch = collected_errors & nsICertOverrideService::ERROR_MISMATCH;
+    status->mIsNotValidAtThisTime = collected_errors & nsICertOverrideService::ERROR_TIME;
+    status->mIsUntrusted = collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
   }
-  infoObject->SetBadCertUIStatus(nsNSSSocketInfo::bcuis_was_shown);
-  NS_RELEASE(nssCert);
-  CERT_DestroyCertificate(peerCert); 
-  if (rv != SECSuccess) {
-    // if the cert is bad, we don't want to connect
-    infoObject->SetCanceled(PR_TRUE);
+
+  remaining_display_errors = collected_errors;
+
+  nsCOMPtr<nsICertOverrideService> overrideService = 
+    do_GetService(NS_CERTOVERRIDE_CONTRACTID);
+  // it is fine to continue without the nsICertOverrideService
+
+  PRUint32 storedOverrideBits = 0; 
+
+  if (overrideService)
+  {
+    PRBool haveStoredOverride;
+  
+    nsrv = overrideService->HasMatchingOverride(NS_ConvertUTF8toUTF16(hostWithPortString), 
+                                                ix509, 
+                                                &storedOverrideBits, 
+                                                &haveStoredOverride);
+    if (NS_SUCCEEDED(nsrv) && haveStoredOverride) 
+    {
+      // remove the errors that are already overriden
+      remaining_display_errors -= storedOverrideBits;
+    }
+  }
+
+  if (!remaining_display_errors) {
+    // all errors are covered by override rules, so let's accept the cert
+    return SECSuccess;
   }
-  return rv;
+
+  // Ok, this is a full stop.
+  // First, deliver the technical details of the broken SSL status,
+  // giving the caller a chance to suppress the error messages.
+
+  PRBool suppressMessage = PR_FALSE;
+  nsresult rv;
+
+  // Try to get a nsIBadCertListener2 implementation from the socket consumer.
+  nsCOMPtr<nsIInterfaceRequestor> callbacks;
+  infoObject->GetNotificationCallbacks(getter_AddRefs(callbacks));
+  if (callbacks) {
+    nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(callbacks);
+    if (bcl) {
+      nsIBadCertListener2 *proxy_bcl = nsnull;
+      NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                           NS_GET_IID(nsIBadCertListener2),
+                           bcl,
+                           NS_PROXY_SYNC,
+                           (void**)&proxy_bcl);
+      if (proxy_bcl) {
+        nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
+        rv = proxy_bcl->NotifyCertProblem(csi, status, hostWithPortString, 
+                                          &suppressMessage);
+      }
+    }
+  }
+
+  PR_SetError(errorCodeToReport, 0);
+  if (!suppressMessage) {
+    nsHandleInvalidCertError(infoObject,
+                             remaining_display_errors,
+                             hostString,
+                             hostWithPortString, 
+                             errorCodeToReport, 
+                             ix509);
+  }
+  return cancel_and_failure(infoObject);
 }
 
 static PRFileDesc*
 nsSSLIOLayerImportFD(PRFileDesc *fd,
                      nsNSSSocketInfo *infoObject,
                      const char *host)
 {
   nsNSSShutDownPreventionLock locker;
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/src/nsSSLStatus.cpp
@@ -0,0 +1,140 @@
+/* -*- 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
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kai Engert <kengert@redhat.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 "nsSSLStatus.h"
+#include "plstr.h"
+
+NS_IMETHODIMP
+nsSSLStatus::GetServerCert(nsIX509Cert** _result)
+{
+  NS_ASSERTION(_result, "non-NULL destination required");
+
+  *_result = mServerCert;
+  NS_IF_ADDREF(*_result);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetKeyLength(PRUint32* _result)
+{
+  NS_ASSERTION(_result, "non-NULL destination required");
+  if (!mHaveKeyLengthAndCipher)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *_result = mKeyLength;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetSecretKeyLength(PRUint32* _result)
+{
+  NS_ASSERTION(_result, "non-NULL destination required");
+  if (!mHaveKeyLengthAndCipher)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *_result = mSecretKeyLength;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetCipherName(char** _result)
+{
+  NS_ASSERTION(_result, "non-NULL destination required");
+  if (!mHaveKeyLengthAndCipher)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *_result = PL_strdup(mCipherName.get());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetIsDomainMismatch(PRBool* _result)
+{
+  NS_ASSERTION(_result, "non-NULL destination required");
+  if (!mHaveCertStatus)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *_result = mIsDomainMismatch;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetIsNotValidAtThisTime(PRBool* _result)
+{
+  NS_ASSERTION(_result, "non-NULL destination required");
+  if (!mHaveCertStatus)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *_result = mIsNotValidAtThisTime;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetIsUntrusted(PRBool* _result)
+{
+  NS_ASSERTION(_result, "non-NULL destination required");
+  if (!mHaveCertStatus)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *_result = mIsUntrusted;
+
+  return NS_OK;
+}
+
+nsSSLStatus::nsSSLStatus()
+: mKeyLength(0), mSecretKeyLength(0)
+, mIsDomainMismatch(PR_FALSE)
+, mIsNotValidAtThisTime(PR_FALSE)
+, mIsUntrusted(PR_FALSE)
+, mHaveKeyLengthAndCipher(PR_FALSE)
+, mHaveCertStatus(PR_FALSE)
+{
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsSSLStatus, nsISSLStatus)
+
+nsSSLStatus::~nsSSLStatus()
+{
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/src/nsSSLStatus.h
@@ -0,0 +1,69 @@
+/* -*- 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
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kai Engert <kengert@redhat.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 "nsISSLStatus.h"
+
+#include "nsAutoPtr.h"
+#include "nsXPIDLString.h"
+#include "nsIX509Cert.h"
+
+class nsSSLStatus
+  : public nsISSLStatus
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISSLSTATUS
+
+  nsSSLStatus();
+  virtual ~nsSSLStatus();
+
+  /* public for initilization in this file */
+  nsCOMPtr<nsIX509Cert> mServerCert;
+
+  PRUint32 mKeyLength;
+  PRUint32 mSecretKeyLength;
+  nsXPIDLCString mCipherName;
+
+  PRBool mIsDomainMismatch;
+  PRBool mIsNotValidAtThisTime;
+  PRBool mIsUntrusted;
+
+  PRBool mHaveKeyLengthAndCipher;
+  PRBool mHaveCertStatus;
+};
--- a/security/manager/ssl/src/nsSmartCardEvent.cpp
+++ b/security/manager/ssl/src/nsSmartCardEvent.cpp
@@ -39,17 +39,17 @@
 #include "nsIDOMSmartCardEvent.h"
 #include "nsIDOMClassInfo.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIDOMEvent.h"
 #include "nsXPCOM.h"
 
 // DOM event class to handle progress notifications
 nsSmartCardEvent::nsSmartCardEvent(const nsAString &aTokenName) 
-    : mTokenName(aTokenName), mInner(nsnull), mPrivate(nsnull)
+    : mInner(nsnull), mPrivate(nsnull), mTokenName(aTokenName)
 {
 }
 
 nsSmartCardEvent::~nsSmartCardEvent()
 {}
 
 //NS_DECL_DOM_CLASSINFO(SmartCardEvent)