From 415197f864439ac32cc1627660eea1d07916a2b9 Mon Sep 17 00:00:00 2001 From: "kaie@kuix.de" Date: Fri, 19 Oct 2007 12:16:34 -0700 Subject: [PATCH] relanding Bug 399043, Workaround for Add-Certificate-Exception for (mail) ports blocked by Necko r=rrelyea, a=sayrer --- .../pki/resources/content/exceptionDialog.js | 33 ++- security/manager/ssl/public/Makefile.in | 1 + .../ssl/public/nsIRecentBadCertsService.idl | 79 ++++++++ security/manager/ssl/src/Makefile.in | 1 + security/manager/ssl/src/nsNSSIOLayer.cpp | 14 +- security/manager/ssl/src/nsNSSModule.cpp | 10 + security/manager/ssl/src/nsRecentBadCerts.cpp | 190 ++++++++++++++++++ security/manager/ssl/src/nsRecentBadCerts.h | 124 ++++++++++++ 8 files changed, 449 insertions(+), 3 deletions(-) diff --git a/security/manager/pki/resources/content/exceptionDialog.js b/security/manager/pki/resources/content/exceptionDialog.js index 3340eae4c91..bd8d45168cf 100644 --- a/security/manager/pki/resources/content/exceptionDialog.js +++ b/security/manager/pki/resources/content/exceptionDialog.js @@ -83,6 +83,32 @@ function initExceptionDialog() { gDialog.getButton("extra1").disabled = true; } +// returns true if found and global status could be set +function findRecentBadCert(uri) { + try { + var recentCertsSvc = Components.classes["@mozilla.org/security/recentbadcerts;1"] + .getService(Components.interfaces.nsIRecentBadCertsService); + if (!recentCertsSvc) + return false; + + var hostWithPort = uri.host + ":" + uri.port; + gSSLStatus = recentCertsSvc.getRecentBadCert(hostWithPort); + if (!gSSLStatus) + return false; + + gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert; + if (!gCert) + return false; + + gBroken = true; + } + catch (e) { + return false; + } + updateCertStatus(); + return true; +} + /** * Attempt to download the certificate for the location specified, and populate * the Certificate Status section with the result. @@ -95,8 +121,13 @@ function checkCert() { gBroken = false; updateCertStatus(); - var req = new XMLHttpRequest(); var uri = getURI(); + + // Is the cert already known in the list of recently seen bad certs? + if (findRecentBadCert(uri) == true) + return; + + var req = new XMLHttpRequest(); try { if(uri) { req.open('GET', uri.prePath, false); diff --git a/security/manager/ssl/public/Makefile.in b/security/manager/ssl/public/Makefile.in index 109bbb3f248..e07f0bd06d9 100644 --- a/security/manager/ssl/public/Makefile.in +++ b/security/manager/ssl/public/Makefile.in @@ -62,6 +62,7 @@ SDK_XPIDLSRCS = \ XPIDLSRCS = \ nsICertOverrideService.idl \ + nsIRecentBadCertsService.idl \ nsIFormSigningDialog.idl \ nsIX509Cert2.idl \ nsIX509Cert3.idl \ diff --git a/security/manager/ssl/public/nsIRecentBadCertsService.idl b/security/manager/ssl/public/nsIRecentBadCertsService.idl index e69de29bb2d..c51afe23e7e 100644 --- a/security/manager/ssl/public/nsIRecentBadCertsService.idl +++ b/security/manager/ssl/public/nsIRecentBadCertsService.idl @@ -0,0 +1,79 @@ +/* -*- 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 + * + * 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; +interface nsISSLStatus; + +%{C++ +#define NS_RECENTBADCERTS_CONTRACTID "@mozilla.org/security/recentbadcerts;1" +%} + +/** + * This represents a global list of recently seen bad ssl status + * including the bad cert. + * The implementation will decide how many entries it will hold, + * the number is expected to be small. + */ +[scriptable, uuid(a5ae8b05-a76e-408f-b0ba-02a831265749)] +interface nsIRecentBadCertsService : nsISupports { + + /** + * Retrieve the recently seen bad ssl status for the given hostname:port. + * If no SSL cert was recently seen for the given hostname:port, return null. + * If a good cert was seen for the given hostname:port, return null. + * + * @param aHostNameWithPort The host:port whose entry should be tested + * @return null or a recently seen bad ssl status with cert + */ + nsISSLStatus getRecentBadCert(in AString aHostNameWithPort); + + /** + * A bad certificate that should be remembered by the service. + * Will be added as the most recently seen cert. + * The service may forget older entries to make room for the new one. + * + * @param aHostNameWithPort The host:port whose entry should be tested + * @param aCert The bad ssl status with certificate + */ + void addBadCert(in AString aHostNameWithPort, + in nsISSLStatus aStatus); +}; diff --git a/security/manager/ssl/src/Makefile.in b/security/manager/ssl/src/Makefile.in index 6d17425278f..93af7e008ae 100644 --- a/security/manager/ssl/src/Makefile.in +++ b/security/manager/ssl/src/Makefile.in @@ -59,6 +59,7 @@ PACKAGE_FILE = pipnss.pkg CPPSRCS = \ nsNSSCleaner.cpp \ nsCertOverrideService.cpp \ + nsRecentBadCerts.cpp \ nsPSMBackgroundThread.cpp \ nsSSLThread.cpp \ nsCertVerificationThread.cpp \ diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index 465ae5a9874..ecf4f63b51a 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -60,6 +60,7 @@ #include "nsIClientAuthDialogs.h" #include "nsICertOverrideService.h" #include "nsIBadCertListener2.h" +#include "nsRecentBadCerts.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" @@ -2342,7 +2343,8 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket) nsCString hostWithPortString = hostString; hostWithPortString.AppendLiteral(":"); hostWithPortString.AppendInt(port); - + + NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString); // Check the name field against the desired hostname. if (hostname && hostname[0] && @@ -2444,7 +2446,7 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket) { PRBool haveStoredOverride; - nsrv = overrideService->HasMatchingOverride(NS_ConvertUTF8toUTF16(hostWithPortString), + nsrv = overrideService->HasMatchingOverride(hostWithPortStringUTF16, ix509, &storedOverrideBits, &haveStoredOverride); @@ -2487,6 +2489,13 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket) } } + nsCOMPtr recentBadCertsService = + do_GetService(NS_RECENTBADCERTS_CONTRACTID); + + if (recentBadCertsService) { + recentBadCertsService->AddBadCert(hostWithPortStringUTF16, status); + } + PR_SetError(errorCodeToReport, 0); if (!suppressMessage) { nsHandleInvalidCertError(infoObject, @@ -2496,6 +2505,7 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket) errorCodeToReport, ix509); } + return cancel_and_failure(infoObject); } diff --git a/security/manager/ssl/src/nsNSSModule.cpp b/security/manager/ssl/src/nsNSSModule.cpp index f5042afd0e4..d6e71043078 100644 --- a/security/manager/ssl/src/nsNSSModule.cpp +++ b/security/manager/ssl/src/nsNSSModule.cpp @@ -24,6 +24,7 @@ * Hubbie Shaw * Doug Turner * Brian Ryner + * Kai Engert * * 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 @@ -74,6 +75,7 @@ #include "nsDataSignatureVerifier.h" #include "nsCertOverrideService.h" #include "nsRandomGenerator.h" +#include "nsRecentBadCerts.h" // We must ensure that the nsNSSComponent has been loaded before // creating any other components. @@ -196,6 +198,7 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsKeyObjectFactory) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsDataSignatureVerifier) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(PR_FALSE, nsCertOverrideService, Init) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(PR_FALSE, nsRandomGenerator) +NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(PR_FALSE, nsRecentBadCertsService, Init) static NS_METHOD RegisterPSMContentListeners( nsIComponentManager *aCompMgr, @@ -472,6 +475,13 @@ static const nsModuleComponentInfo components[] = NS_RANDOMGENERATOR_CID, NS_RANDOMGENERATOR_CONTRACTID, nsRandomGeneratorConstructor + }, + + { + "PSM Recent Bad Certs Service", + NS_RECENTBADCERTS_CID, + NS_RECENTBADCERTS_CONTRACTID, + nsRecentBadCertsServiceConstructor } }; diff --git a/security/manager/ssl/src/nsRecentBadCerts.cpp b/security/manager/ssl/src/nsRecentBadCerts.cpp index e69de29bb2d..e9a47d5adad 100644 --- a/security/manager/ssl/src/nsRecentBadCerts.cpp +++ b/security/manager/ssl/src/nsRecentBadCerts.cpp @@ -0,0 +1,190 @@ +/* -*- 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 + * + * 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 "nsRecentBadCerts.h" +#include "nsIX509Cert.h" +#include "nsSSLStatus.h" +#include "nsCOMPtr.h" +#include "nsNSSCertificate.h" +#include "nsCRT.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) + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsRecentBadCertsService, + nsIRecentBadCertsService) + +nsRecentBadCertsService::nsRecentBadCertsService() +:mNextStorePosition(0) +{ + monitor = PR_NewMonitor(); +} + +nsRecentBadCertsService::~nsRecentBadCertsService() +{ + if (monitor) + PR_DestroyMonitor(monitor); +} + +nsresult +nsRecentBadCertsService::Init() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsRecentBadCertsService::GetRecentBadCert(const nsAString & aHostNameWithPort, + nsISSLStatus **aStatus) +{ + NS_ENSURE_ARG_POINTER(aStatus); + if (!aHostNameWithPort.Length()) + return NS_ERROR_INVALID_ARG; + + *aStatus = nsnull; + nsCOMPtr status = new nsSSLStatus(); + if (!status) + return NS_ERROR_OUT_OF_MEMORY; + + SECItem foundDER; + foundDER.len = 0; + foundDER.data = nsnull; + + PRBool isDomainMismatch; + PRBool isNotValidAtThisTime; + PRBool isUntrusted; + + { + nsAutoMonitor lock(monitor); + for (size_t i=0; imServerCert = new nsNSSCertificate(nssCert); + CERT_DestroyCertificate(nssCert); + + status->mHaveCertStatus = PR_TRUE; + status->mIsDomainMismatch = isDomainMismatch; + status->mIsNotValidAtThisTime = isNotValidAtThisTime; + status->mIsUntrusted = isUntrusted; + + *aStatus = status; + NS_IF_ADDREF(*aStatus); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsRecentBadCertsService::AddBadCert(const nsAString &hostWithPort, + nsISSLStatus *aStatus) +{ + NS_ENSURE_ARG(aStatus); + + nsCOMPtr cert; + nsresult rv; + rv = aStatus->GetServerCert(getter_AddRefs(cert)); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool isDomainMismatch; + PRBool isNotValidAtThisTime; + PRBool isUntrusted; + + rv = aStatus->GetIsDomainMismatch(&isDomainMismatch); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aStatus->GetIsNotValidAtThisTime(&isNotValidAtThisTime); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aStatus->GetIsUntrusted(&isUntrusted); + NS_ENSURE_SUCCESS(rv, rv); + + SECItem tempItem; + rv = cert->GetRawDER(&tempItem.len, (PRUint8 **)&tempItem.data); + NS_ENSURE_SUCCESS(rv, rv); + + { + nsAutoMonitor lock(monitor); + RecentBadCert &updatedEntry = mCerts[mNextStorePosition]; + + ++mNextStorePosition; + if (mNextStorePosition == const_recently_seen_list_size) + mNextStorePosition = 0; + + updatedEntry.Clear(); + updatedEntry.mHostWithPort = hostWithPort; + updatedEntry.mDERCert = tempItem; // consume + updatedEntry.isDomainMismatch = isDomainMismatch; + updatedEntry.isNotValidAtThisTime = isNotValidAtThisTime; + updatedEntry.isUntrusted = isUntrusted; + } + + return NS_OK; +} diff --git a/security/manager/ssl/src/nsRecentBadCerts.h b/security/manager/ssl/src/nsRecentBadCerts.h index e69de29bb2d..13fe6685fda 100644 --- a/security/manager/ssl/src/nsRecentBadCerts.h +++ b/security/manager/ssl/src/nsRecentBadCerts.h @@ -0,0 +1,124 @@ +/* -*- 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 + * + * 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 __RECENTBADCERTS_H__ +#define __RECENTBADCERTS_H__ + +#include "nsIRecentBadCertsService.h" +#include "nsTHashtable.h" +#include "nsString.h" +#include "prmon.h" +#include "secitem.h" + +class RecentBadCert +{ +public: + + RecentBadCert() + { + mDERCert.len = 0; + mDERCert.data = nsnull; + isDomainMismatch = PR_FALSE; + isNotValidAtThisTime = PR_FALSE; + isUntrusted = PR_FALSE; + } + + ~RecentBadCert() + { + Clear(); + } + + void Clear() + { + mHostWithPort.Truncate(); + if (mDERCert.len) + nsMemory::Free(mDERCert.data); + mDERCert.len = 0; + mDERCert.data = nsnull; + } + + nsString mHostWithPort; + SECItem mDERCert; + PRBool isDomainMismatch; + PRBool isNotValidAtThisTime; + PRBool isUntrusted; + +private: + RecentBadCert(const RecentBadCert &other) + { + NS_NOTREACHED("RecentBadCert(const RecentBadCert &other) not implemented"); + this->operator=(other); + } + + RecentBadCert &operator=(const RecentBadCert &other) + { + NS_NOTREACHED("RecentBadCert &operator=(const RecentBadCert &other) not implemented"); + return *this; + } +}; + +class nsRecentBadCertsService : public nsIRecentBadCertsService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIRECENTBADCERTSSERVICE + + nsRecentBadCertsService(); + ~nsRecentBadCertsService(); + + nsresult Init(); + +protected: + PRMonitor *monitor; + + enum {const_recently_seen_list_size = 5}; + RecentBadCert mCerts[const_recently_seen_list_size]; + + // will be in the range of 0 to list_size-1 + PRUint32 mNextStorePosition; +}; + +#define NS_RECENTBADCERTS_CID { /* e7caf8c0-3570-47fe-aa1b-da47539b5d07 */ \ + 0xe7caf8c0, \ + 0x3570, \ + 0x47fe, \ + {0xaa, 0x1b, 0xda, 0x47, 0x53, 0x9b, 0x5d, 0x07} \ + } + +#endif