author | Wes Kocher <wkocher@mozilla.com> |
Wed, 02 Jul 2014 18:15:55 -0700 | |
changeset 192033 | 9acfbab6e6237bac2d9f83995fcba4c10a8fca7b |
parent 192032 | d90688dc9c516319c349099e68dffa77245581af |
child 192034 | 3ab90208b36393861ed4b77f9883214f0f90a456 |
push id | 45729 |
push user | kwierso@gmail.com |
push date | Thu, 03 Jul 2014 01:16:15 +0000 |
treeherder | mozilla-inbound@9acfbab6e623 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 940506 |
milestone | 33.0a1 |
backs out | 5206957b4f834cd82c80acedcc37a98174ac3f2c |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/security/manager/ssl/public/moz.build +++ b/security/manager/ssl/public/moz.build @@ -26,16 +26,17 @@ XPIDL_SOURCES += [ 'nsINSSVersion.idl', 'nsIPK11Token.idl', 'nsIPK11TokenDB.idl', 'nsIPKCS11.idl', 'nsIPKCS11Module.idl', 'nsIPKCS11ModuleDB.idl', 'nsIPKCS11Slot.idl', 'nsIProtectedAuthThread.idl', + 'nsIRecentBadCertsService.idl', 'nsISSLErrorListener.idl', 'nsISSLStatus.idl', 'nsIStreamCipher.idl', 'nsITokenDialogs.idl', 'nsITokenPasswordDialogs.idl', 'nsIUserCertPicker.idl', 'nsIX509Cert.idl', 'nsIX509Cert2.idl',
new file mode 100644 --- /dev/null +++ b/security/manager/ssl/public/nsIRecentBadCertsService.idl @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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(0fed7784-d152-44d6-95a7-67a59024de0f)] +interface nsIRecentBadCerts : 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); + + /** + * Clear all stored cert data. + */ + void resetStoredCerts(); +};
--- a/security/manager/ssl/public/nsIX509CertDB.idl +++ b/security/manager/ssl/public/nsIX509CertDB.idl @@ -7,16 +7,17 @@ #include "nsISupports.idl" interface nsIArray; interface nsIX509Cert; interface nsIX509Cert3; interface nsIFile; interface nsIInterfaceRequestor; interface nsIZipReader; +interface nsIRecentBadCerts; interface nsIX509CertList; %{C++ #define NS_X509CERTDB_CONTRACTID "@mozilla.org/security/x509certdb;1" %} typedef uint32_t AppTrustedRoot; @@ -27,17 +28,17 @@ interface nsIOpenSignedAppFileCallback : in nsIZipReader aZipReader, in nsIX509Cert3 aSignerCert); }; /** * This represents a service to access and manipulate * X.509 certificates stored in a database. */ -[scriptable, uuid(dd6e4af8-23bb-41d9-a1e3-9ce925429f2f)] +[scriptable, uuid(7446a5b1-84ca-491f-a2fe-0bc60a71ffa5)] interface nsIX509CertDB : nsISupports { /** * Constants that define which usages a certificate * is trusted for. */ const unsigned long UNTRUSTED = 0; const unsigned long TRUSTED_SSL = 1 << 0; @@ -275,16 +276,26 @@ interface nsIX509CertDB : nsISupports { * * @param certDER The raw representation of a certificate, * encoded as raw DER. * @param length The length of the DER string. * @return The new certificate object. */ nsIX509Cert constructX509(in string certDER, in unsigned long length); + /* + * Obtain a reference to the appropriate service for recent + * bad certificates. May only be called on the main thread. + * + * @param isPrivate True if the service for certs for private connections + * is desired, false otherwise. + * @return The requested service. + */ + nsIRecentBadCerts getRecentBadCerts(in boolean isPrivate); + /** * Verifies the signature on the given JAR file to verify that it has a * valid signature. To be considered valid, there must be exactly one * signature on the JAR file and that signature must have signed every * entry. Further, the signature must come from a certificate that * is trusted for code signing. * * On success, NS_OK, a nsIZipReader, and the trusted certificate that
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp +++ b/security/manager/ssl/src/SSLServerCertVerification.cpp @@ -100,16 +100,17 @@ #include "CertVerifier.h" #include "CryptoTask.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" #include "nsIBadCertListener2.h" #include "nsICertOverrideService.h" #include "nsISiteSecurityService.h" #include "nsNSSComponent.h" +#include "nsRecentBadCerts.h" #include "nsNSSIOLayer.h" #include "nsNSSShutDown.h" #include "mozilla/Assertions.h" #include "mozilla/Mutex.h" #include "mozilla/Telemetry.h" #include "mozilla/unused.h" #include "nsIThreadPool.h" @@ -493,16 +494,29 @@ CertErrorRunnable::CheckCertOverrides() = static_cast<nsIInterfaceRequestor*>(mInfoObject); bool suppressMessage = false; // obsolete, ignored nsrv = bcl->NotifyCertProblem(csi, mInfoObject->SSLStatus(), hostWithPortString, &suppressMessage); } } } + nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID); + nsCOMPtr<nsIRecentBadCerts> recentBadCertsService; + if (certdb) { + bool isPrivate = mProviderFlags & nsISocketProvider::NO_PERMANENT_STORAGE; + certdb->GetRecentBadCerts(isPrivate, getter_AddRefs(recentBadCertsService)); + } + + if (recentBadCertsService) { + NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString); + recentBadCertsService->AddBadCert(hostWithPortStringUTF16, + mInfoObject->SSLStatus()); + } + // pick the error code to report by priority PRErrorCode errorCodeToReport = mErrorCodeTrust ? mErrorCodeTrust : mErrorCodeMismatch ? mErrorCodeMismatch : mErrorCodeExpired ? mErrorCodeExpired : mDefaultErrorCodeToReport; SSLServerCertVerificationResult* result = new SSLServerCertVerificationResult(mInfoObject,
--- a/security/manager/ssl/src/SharedSSLState.cpp +++ b/security/manager/ssl/src/SharedSSLState.cpp @@ -8,30 +8,32 @@ #include "nsClientAuthRemember.h" #include "nsComponentManagerUtils.h" #include "nsICertOverrideService.h" #include "nsIObserverService.h" #include "mozilla/Services.h" #include "nsThreadUtils.h" #include "nsCRT.h" #include "nsServiceManagerUtils.h" +#include "nsRecentBadCerts.h" #include "PSMRunnable.h" #include "PublicSSL.h" #include "ssl.h" #include "nsNetCID.h" #include "mozilla/Atomics.h" #include "mozilla/unused.h" using mozilla::psm::SyncRunnableBase; using mozilla::Atomic; using mozilla::unused; namespace { static Atomic<bool> sCertOverrideSvcExists(false); +static Atomic<bool> sCertDBExists(false); class MainThreadClearer : public SyncRunnableBase { public: MainThreadClearer() : mShouldClearSessionCache(false) {} void RunOnTargetThread() { // In some cases it's possible to cause PSM/NSS to initialize while XPCOM shutdown @@ -44,16 +46,29 @@ public: nsCOMPtr<nsICertOverrideService> icos = do_GetService(NS_CERTOVERRIDE_CONTRACTID); if (icos) { icos->ClearValidityOverride( NS_LITERAL_CSTRING("all:temporary-certificates"), 0); } } + bool certDBExists = sCertDBExists.exchange(false); + if (certDBExists) { + sCertDBExists = true; + nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID); + if (certdb) { + nsCOMPtr<nsIRecentBadCerts> badCerts; + certdb->GetRecentBadCerts(true, getter_AddRefs(badCerts)); + if (badCerts) { + badCerts->ResetStoredCerts(); + } + } + } + // This needs to be checked on the main thread to avoid racing with NSS // initialization. mShouldClearSessionCache = mozilla::psm::PrivateSSLState() && mozilla::psm::PrivateSSLState()->SocketCreated(); } bool mShouldClearSessionCache; }; @@ -190,16 +205,22 @@ SharedSSLState::GlobalCleanup() } /*static*/ void SharedSSLState::NoteCertOverrideServiceInstantiated() { sCertOverrideSvcExists = true; } +/*static*/ void +SharedSSLState::NoteCertDBServiceInstantiated() +{ + sCertDBExists = true; +} + void SharedSSLState::Cleanup() { mIOLayerHelpers.Cleanup(); } SharedSSLState* PublicSSLState()
--- a/security/manager/ssl/src/SharedSSLState.h +++ b/security/manager/ssl/src/SharedSSLState.h @@ -39,16 +39,17 @@ public: { mOCSPStaplingEnabled = staplingEnabled; } // The following methods may be called from any thread bool SocketCreated(); void NoteSocketCreated(); static void NoteCertOverrideServiceInstantiated(); + static void NoteCertDBServiceInstantiated(); bool IsOCSPStaplingEnabled() const { return mOCSPStaplingEnabled; } private: ~SharedSSLState(); void Cleanup(); nsCOMPtr<nsIObserver> mObserver;
--- a/security/manager/ssl/src/moz.build +++ b/security/manager/ssl/src/moz.build @@ -44,16 +44,17 @@ SOURCES += [ 'nsNSSShutDown.cpp', 'nsNTLMAuthModule.cpp', 'nsPK11TokenDB.cpp', 'nsPKCS11Slot.cpp', 'nsPKCS12Blob.cpp', 'nsProtectedAuthThread.cpp', 'nsPSMBackgroundThread.cpp', 'nsRandomGenerator.cpp', + 'nsRecentBadCerts.cpp', 'nsSDR.cpp', 'NSSErrorsService.cpp', 'nsSSLSocketProvider.cpp', 'nsSSLStatus.cpp', 'nsStreamCipher.cpp', 'nsTLSSocketProvider.cpp', 'nsUsageArrayHelper.cpp', 'PSMRunnable.cpp',
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp +++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp @@ -32,16 +32,17 @@ #include "nsArrayUtils.h" #include "nsNSSShutDown.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsComponentManagerUtils.h" #include "nsIPrompt.h" #include "nsThreadUtils.h" #include "nsIObserverService.h" +#include "nsRecentBadCerts.h" #include "SharedSSLState.h" #include "nspr.h" #include "certdb.h" #include "secerr.h" #include "nssb64.h" #include "secasn1.h" #include "secder.h" @@ -78,16 +79,22 @@ attemptToLogInWithDefaultPassword() } #endif return NS_OK; } NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB, nsIX509CertDB2) +nsNSSCertificateDB::nsNSSCertificateDB() +: mBadCertsLock("nsNSSCertificateDB::mBadCertsLock") +{ + SharedSSLState::NoteCertDBServiceInstantiated(); +} + nsNSSCertificateDB::~nsNSSCertificateDB() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return; } shutdown(calledFromObject); @@ -1701,16 +1708,39 @@ nsNSSCertificateDB::GetCerts(nsIX509Cert nssCertList = new nsNSSCertList(certList, locker); *_retval = nssCertList; NS_ADDREF(*_retval); return NS_OK; } NS_IMETHODIMP +nsNSSCertificateDB::GetRecentBadCerts(bool isPrivate, nsIRecentBadCerts** result) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + MutexAutoLock lock(mBadCertsLock); + if (isPrivate) { + if (!mPrivateRecentBadCerts) { + mPrivateRecentBadCerts = new nsRecentBadCerts; + } + NS_ADDREF(*result = mPrivateRecentBadCerts); + } else { + if (!mPublicRecentBadCerts) { + mPublicRecentBadCerts = new nsRecentBadCerts; + } + NS_ADDREF(*result = mPublicRecentBadCerts); + } + return NS_OK; +} + +NS_IMETHODIMP nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags, nsIX509CertList** verifiedChain, bool* aHasEVPolicy, int32_t* /*PRErrorCode*/ _retval ) { NS_ENSURE_ARG_POINTER(aCert);
--- a/security/manager/ssl/src/nsNSSCertificateDB.h +++ b/security/manager/ssl/src/nsNSSCertificateDB.h @@ -9,27 +9,30 @@ #include "nsIX509CertDB2.h" #include "nsNSSShutDown.h" #include "mozilla/RefPtr.h" #include "mozilla/Mutex.h" #include "certt.h" class nsCString; class nsIArray; +class nsRecentBadCerts; class nsNSSCertificateDB : public nsIX509CertDB , public nsIX509CertDB2 , public nsNSSShutDownObject { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIX509CERTDB NS_DECL_NSIX509CERTDB2 + nsNSSCertificateDB(); + // Use this function to generate a default nickname for a user // certificate that is to be imported onto a token. static void get_default_nickname(CERTCertificate *cert, nsIInterfaceRequestor* ctx, nsCString &nickname, const nsNSSShutDownPreventionLock &proofOfLock); static nsresult @@ -57,16 +60,20 @@ private: CERTDERCerts *getCertsFromPackage(PLArenaPool *arena, uint8_t *data, uint32_t length, const nsNSSShutDownPreventionLock &proofOfLock); nsresult handleCACertDownload(nsIArray *x509Certs, nsIInterfaceRequestor *ctx, const nsNSSShutDownPreventionLock &proofOfLock); + mozilla::Mutex mBadCertsLock; + mozilla::RefPtr<nsRecentBadCerts> mPublicRecentBadCerts; + mozilla::RefPtr<nsRecentBadCerts> mPrivateRecentBadCerts; + // We don't own any NSS objects here, so no need to clean up virtual void virtualDestroyNSSReference() { }; }; #define NS_X509CERTDB_CID { /* fb0bbc5c-452e-4783-b32c-80124693d871 */ \ 0xfb0bbc5c, \ 0x452e, \ 0x4783, \
new file mode 100644 --- /dev/null +++ b/security/manager/ssl/src/nsRecentBadCerts.cpp @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsRecentBadCerts.h" + +#include "pkix/pkixtypes.h" +#include "nsIX509Cert.h" +#include "nsIObserverService.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Services.h" +#include "nsSSLStatus.h" +#include "nsCOMPtr.h" +#include "nsNSSCertificate.h" +#include "nsCRT.h" +#include "nsPromiseFlatString.h" +#include "nsStringBuffer.h" +#include "nspr.h" +#include "pk11pub.h" +#include "certdb.h" +#include "sechash.h" + +using namespace mozilla; + +NS_IMPL_ISUPPORTS(nsRecentBadCerts, nsIRecentBadCerts) + +nsRecentBadCerts::nsRecentBadCerts() +:monitor("nsRecentBadCerts.monitor") +,mNextStorePosition(0) +{ +} + +nsRecentBadCerts::~nsRecentBadCerts() +{ +} + +NS_IMETHODIMP +nsRecentBadCerts::GetRecentBadCert(const nsAString & aHostNameWithPort, + nsISSLStatus **aStatus) +{ + NS_ENSURE_ARG_POINTER(aStatus); + if (!aHostNameWithPort.Length()) + return NS_ERROR_INVALID_ARG; + + *aStatus = nullptr; + RefPtr<nsSSLStatus> status(new nsSSLStatus()); + + SECItem foundDER; + foundDER.len = 0; + foundDER.data = nullptr; + + bool isDomainMismatch = false; + bool isNotValidAtThisTime = false; + bool isUntrusted = false; + + { + ReentrantMonitorAutoEnter lock(monitor); + for (size_t i=0; i<const_recently_seen_list_size; ++i) { + if (mCerts[i].mHostWithPort.Equals(aHostNameWithPort)) { + SECStatus srv = SECITEM_CopyItem(nullptr, &foundDER, &mCerts[i].mDERCert); + if (srv != SECSuccess) + return NS_ERROR_OUT_OF_MEMORY; + + isDomainMismatch = mCerts[i].isDomainMismatch; + isNotValidAtThisTime = mCerts[i].isNotValidAtThisTime; + isUntrusted = mCerts[i].isUntrusted; + } + } + } + + if (foundDER.len) { + CERTCertDBHandle *certdb = CERT_GetDefaultCertDB(); + mozilla::pkix::ScopedCERTCertificate nssCert( + CERT_FindCertByDERCert(certdb, &foundDER)); + if (!nssCert) + nssCert = CERT_NewTempCertificate(certdb, &foundDER, + nullptr, // no nickname + false, // not perm + true); // copy der + + SECITEM_FreeItem(&foundDER, false); + + if (!nssCert) + return NS_ERROR_FAILURE; + + status->mServerCert = nsNSSCertificate::Create(nssCert.get()); + status->mHaveCertErrorBits = true; + status->mIsDomainMismatch = isDomainMismatch; + status->mIsNotValidAtThisTime = isNotValidAtThisTime; + status->mIsUntrusted = isUntrusted; + + *aStatus = status; + NS_IF_ADDREF(*aStatus); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsRecentBadCerts::AddBadCert(const nsAString &hostWithPort, + nsISSLStatus *aStatus) +{ + NS_ENSURE_ARG(aStatus); + + nsCOMPtr<nsIX509Cert> cert; + nsresult rv; + rv = aStatus->GetServerCert(getter_AddRefs(cert)); + NS_ENSURE_SUCCESS(rv, rv); + + bool isDomainMismatch; + bool isNotValidAtThisTime; + bool 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, (uint8_t **)&tempItem.data); + NS_ENSURE_SUCCESS(rv, rv); + + { + ReentrantMonitorAutoEnter 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; +} + +NS_IMETHODIMP +nsRecentBadCerts::ResetStoredCerts() +{ + for (size_t i = 0; i < const_recently_seen_list_size; ++i) { + RecentBadCert &entry = mCerts[i]; + entry.Clear(); + } + return NS_OK; +}
new file mode 100644 --- /dev/null +++ b/security/manager/ssl/src/nsRecentBadCerts.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __RECENTBADCERTS_H__ +#define __RECENTBADCERTS_H__ + +#include "mozilla/Attributes.h" +#include "mozilla/ReentrantMonitor.h" + +#include "nsIRecentBadCertsService.h" +#include "nsTHashtable.h" +#include "nsString.h" +#include "cert.h" +#include "secitem.h" + +class RecentBadCert +{ +public: + + RecentBadCert() + { + mDERCert.len = 0; + mDERCert.data = nullptr; + isDomainMismatch = false; + isNotValidAtThisTime = false; + isUntrusted = false; + } + + ~RecentBadCert() + { + Clear(); + } + + void Clear() + { + mHostWithPort.Truncate(); + if (mDERCert.len) + nsMemory::Free(mDERCert.data); + mDERCert.len = 0; + mDERCert.data = nullptr; + } + + nsString mHostWithPort; + SECItem mDERCert; + bool isDomainMismatch; + bool isNotValidAtThisTime; + bool isUntrusted; + +private: + RecentBadCert(const RecentBadCert &other) MOZ_DELETE; + RecentBadCert &operator=(const RecentBadCert &other) MOZ_DELETE; +}; + +class nsRecentBadCerts MOZ_FINAL : public nsIRecentBadCerts +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRECENTBADCERTS + + nsRecentBadCerts(); + +protected: + ~nsRecentBadCerts(); + + mozilla::ReentrantMonitor 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 + uint32_t mNextStorePosition; +}; + +#define NS_RECENTBADCERTS_CID { /* e7caf8c0-3570-47fe-aa1b-da47539b5d07 */ \ + 0xe7caf8c0, \ + 0x3570, \ + 0x47fe, \ + {0xaa, 0x1b, 0xda, 0x47, 0x53, 0x9b, 0x5d, 0x07} \ + } + +#endif