diff --git a/security/manager/ssl/src/nsNSSCallbacks.cpp b/security/manager/ssl/src/nsNSSCallbacks.cpp index 6772e57d9cc..3822bb41c73 100644 --- a/security/manager/ssl/src/nsNSSCallbacks.cpp +++ b/security/manager/ssl/src/nsNSSCallbacks.cpp @@ -25,6 +25,7 @@ * Terry Hayes * Kai Engert * Petr Kostka + * Honza Bambas * * 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 @@ -903,6 +904,9 @@ void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) { infoObject->SetSSLStatus(status); } + nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits( + infoObject, status); + CERTCertificate *serverCert = SSL_PeerCertificate(fd); if (serverCert) { nsRefPtr nssc = new nsNSSCertificate(serverCert); @@ -1028,6 +1032,19 @@ SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd, status = new nsSSLStatus(); infoObject->SetSSLStatus(status); } + + if (rv == SECSuccess) { + // Certificate verification succeeded delete any potential record + // of certificate error bits. + nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError( + infoObject, nsnull, rv); + } + else { + // Certificate verification failed, update the status' bits. + nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits( + infoObject, status); + } + if (status && !status->mServerCert) { status->mServerCert = nsc; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index ae3d5518797..8d15c2c4bd6 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -782,6 +782,11 @@ void nsSSLIOLayerHelpers::Cleanup() PR_DestroyLock(mutex); mutex = nsnull; } + + if (mHostsWithCertErrors) { + delete mHostsWithCertErrors; + mHostsWithCertErrors = nsnull; + } } static nsresult @@ -1474,6 +1479,95 @@ nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr, return status; } +// nsPSMRememberCertErrorsTable + +nsPSMRememberCertErrorsTable::nsPSMRememberCertErrorsTable() +{ + mErrorHosts.Init(16); +} + +nsresult +nsPSMRememberCertErrorsTable::GetHostPortKey(nsNSSSocketInfo* infoObject, + nsCAutoString &result) +{ + nsresult rv; + + result.Truncate(); + + nsXPIDLCString hostName; + rv = infoObject->GetHostName(getter_Copies(hostName)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 port; + rv = infoObject->GetPort(&port); + NS_ENSURE_SUCCESS(rv, rv); + + result.Assign(hostName); + result.Append(':'); + result.AppendInt(port); + + return NS_OK; +} + +void +nsPSMRememberCertErrorsTable::RememberCertHasError(nsNSSSocketInfo* infoObject, + nsSSLStatus* status, + SECStatus certVerificationResult) +{ + nsresult rv; + + nsCAutoString hostPortKey; + rv = GetHostPortKey(infoObject, hostPortKey); + if (NS_FAILED(rv)) + return; + + if (certVerificationResult != SECSuccess) { + NS_ASSERTION(status, + "Must have nsSSLStatus object when remembering flags"); + + if (!status) + return; + + CertStateBits bits; + bits.mIsDomainMismatch = status->mIsDomainMismatch; + bits.mIsNotValidAtThisTime = status->mIsNotValidAtThisTime; + bits.mIsUntrusted = status->mIsUntrusted; + mErrorHosts.Put(hostPortKey, bits); + } + else { + mErrorHosts.Remove(hostPortKey); + } +} + +void +nsPSMRememberCertErrorsTable::LookupCertErrorBits(nsNSSSocketInfo* infoObject, + nsSSLStatus* status) +{ + // Get remembered error bits from our cache, because of SSL session caching + // the NSS library potentially hasn't notified us for this socket. + if (status->mHaveCertErrorBits) + // Rather do not modify bits if already set earlier + return; + + nsresult rv; + + nsCAutoString hostPortKey; + rv = GetHostPortKey(infoObject, hostPortKey); + if (NS_FAILED(rv)) + return; + + CertStateBits bits; + if (!mErrorHosts.Get(hostPortKey, &bits)) + // No record was found, this host had no cert errors + return; + + // This host had cert errors, update the bits correctly + status->mHaveCertErrorBits = PR_TRUE; + status->mIsDomainMismatch = bits.mIsDomainMismatch; + status->mIsNotValidAtThisTime = bits.mIsNotValidAtThisTime; + status->mIsUntrusted = bits.mIsUntrusted; +} + // Call this function to report a site that is possibly TLS intolerant. // This function will return true, if the given socket is currently using TLS. PRBool @@ -1779,6 +1873,7 @@ PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity; PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods; PRLock *nsSSLIOLayerHelpers::mutex = nsnull; nsCStringHashSet *nsSSLIOLayerHelpers::mTLSIntolerantSites = nsnull; +nsPSMRememberCertErrorsTable *nsSSLIOLayerHelpers::mHostsWithCertErrors = nsnull; PRFileDesc *nsSSLIOLayerHelpers::mSharedPollableEvent = nsnull; nsNSSSocketInfo *nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull; PRBool nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_FALSE; @@ -1988,6 +2083,10 @@ nsresult nsSSLIOLayerHelpers::Init() mTLSIntolerantSites->Init(1); + mHostsWithCertErrors = new nsPSMRememberCertErrorsTable(); + if (!mHostsWithCertErrors || !mHostsWithCertErrors->mErrorHosts.IsInitialized()) + return NS_ERROR_OUT_OF_MEMORY; + return NS_OK; } @@ -3021,6 +3120,9 @@ nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket) status->mIsDomainMismatch = collected_errors & nsICertOverrideService::ERROR_MISMATCH; status->mIsNotValidAtThisTime = collected_errors & nsICertOverrideService::ERROR_TIME; status->mIsUntrusted = collected_errors & nsICertOverrideService::ERROR_UNTRUSTED; + + nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError( + infoObject, status, SECFailure); } remaining_display_errors = collected_errors; diff --git a/security/manager/ssl/src/nsNSSIOLayer.h b/security/manager/ssl/src/nsNSSIOLayer.h index 6bb7cea866d..ac0667d1cee 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.h +++ b/security/manager/ssl/src/nsNSSIOLayer.h @@ -57,6 +57,7 @@ #include "nsNSSShutDown.h" #include "nsAutoPtr.h" #include "nsNSSCertificate.h" +#include "nsDataHashtable.h" class nsIChannel; class nsSSLThread; @@ -242,6 +243,31 @@ friend class nsSSLThread; class nsCStringHashSet; +class nsSSLStatus; +class nsNSSSocketInfo; + +class nsPSMRememberCertErrorsTable +{ +private: + struct CertStateBits + { + PRBool mIsDomainMismatch; + PRBool mIsNotValidAtThisTime; + PRBool mIsUntrusted; + }; + nsDataHashtableMT mErrorHosts; + nsresult GetHostPortKey(nsNSSSocketInfo* infoObject, nsCAutoString& result); + +public: + friend class nsSSLIOLayerHelpers; + nsPSMRememberCertErrorsTable(); + void RememberCertHasError(nsNSSSocketInfo* infoObject, + nsSSLStatus* status, + SECStatus certVerificationResult); + void LookupCertErrorBits(nsNSSSocketInfo* infoObject, + nsSSLStatus* status); +}; + class nsSSLIOLayerHelpers { public: @@ -254,6 +280,7 @@ public: static PRLock *mutex; static nsCStringHashSet *mTLSIntolerantSites; + static nsPSMRememberCertErrorsTable* mHostsWithCertErrors; static PRBool rememberPossibleTLSProblemSite(PRFileDesc* fd, nsNSSSocketInfo *socketInfo);