Bug 1029155 - Store peer certificate chain from failed connections on TransportSecurityInfo r=keeler

This commit is contained in:
Garrett Robinson 2014-08-15 11:27:22 -07:00
parent eab3ebcced
commit 45cc1d6a0d
7 changed files with 192 additions and 23 deletions

View File

@ -647,6 +647,7 @@ SSL_NumImplementedCiphers DATA
SSL_OptionSet
SSL_OptionSetDefault
SSL_PeerCertificate
SSL_PeerCertificateChain
SSL_PeerStapledOCSPResponses
SSL_ResetHandshake
SSL_SetCanFalseStartCallback

View File

@ -6,9 +6,18 @@
#include "nsISupports.idl"
[scriptable, uuid(8813d03b-e76c-4240-9691-d327d9b91e88)]
interface nsIX509CertList;
[scriptable, uuid(154898ce-e14f-4981-bfc5-00a41722f44b)]
interface nsITransportSecurityInfo : nsISupports {
readonly attribute unsigned long securityState;
readonly attribute wstring errorMessage;
/**
* If certificate verification failed, this will be the peer certificate
* chain provided in the handshake, so it can be used for error reporting.
* If verification succeeded, this will be null.
*/
readonly attribute nsIX509CertList failedCertChain;
};

View File

@ -621,6 +621,7 @@ public:
const void* fdForLogging,
TransportSecurityInfo* infoObject,
CERTCertificate* serverCert,
ScopedCERTCertList& peerCertChain,
SECItem* stapledOCSPResponse,
uint32_t providerFlags,
Time time,
@ -633,6 +634,7 @@ private:
const void* fdForLogging,
TransportSecurityInfo* infoObject,
CERTCertificate* cert,
CERTCertList* peerCertChain,
SECItem* stapledOCSPResponse,
uint32_t providerFlags,
Time time,
@ -641,6 +643,7 @@ private:
const void* const mFdForLogging;
const RefPtr<TransportSecurityInfo> mInfoObject;
const ScopedCERTCertificate mCert;
ScopedCERTCertList mPeerCertChain;
const uint32_t mProviderFlags;
const Time mTime;
const PRTime mPRTime;
@ -651,12 +654,13 @@ private:
SSLServerCertVerificationJob::SSLServerCertVerificationJob(
const RefPtr<SharedCertVerifier>& certVerifier, const void* fdForLogging,
TransportSecurityInfo* infoObject, CERTCertificate* cert,
SECItem* stapledOCSPResponse, uint32_t providerFlags,
Time time, PRTime prtime)
CERTCertList* peerCertChain, SECItem* stapledOCSPResponse,
uint32_t providerFlags, Time time, PRTime prtime)
: mCertVerifier(certVerifier)
, mFdForLogging(fdForLogging)
, mInfoObject(infoObject)
, mCert(CERT_DupCertificate(cert))
, mPeerCertChain(peerCertChain)
, mProviderFlags(providerFlags)
, mTime(time)
, mPRTime(prtime)
@ -733,9 +737,13 @@ BlockServerCertChangeForSpdy(nsNSSSocketInfo* infoObject,
}
SECStatus
AuthCertificate(CertVerifier& certVerifier, TransportSecurityInfo* infoObject,
CERTCertificate* cert, SECItem* stapledOCSPResponse,
uint32_t providerFlags, Time time)
AuthCertificate(CertVerifier& certVerifier,
TransportSecurityInfo* infoObject,
CERTCertificate* cert,
ScopedCERTCertList& peerCertChain,
SECItem* stapledOCSPResponse,
uint32_t providerFlags,
Time time)
{
MOZ_ASSERT(infoObject);
MOZ_ASSERT(cert);
@ -747,7 +755,6 @@ AuthCertificate(CertVerifier& certVerifier, TransportSecurityInfo* infoObject,
bool saveIntermediates =
!(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE);
ScopedCERTCertList certList;
SECOidTag evOidPolicy;
rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
time, infoObject,
@ -799,6 +806,13 @@ AuthCertificate(CertVerifier& certVerifier, TransportSecurityInfo* infoObject,
}
}
if (rv != SECSuccess) {
// Certificate validation failed; store the peer certificate chain on
// infoObject so it can be used for error reporting. Note: infoObject
// indirectly takes ownership of peerCertChain.
infoObject->SetFailedCertChain(peerCertChain);
}
return rv;
}
@ -808,6 +822,7 @@ SSLServerCertVerificationJob::Dispatch(
const void* fdForLogging,
TransportSecurityInfo* infoObject,
CERTCertificate* serverCert,
ScopedCERTCertList& peerCertChain,
SECItem* stapledOCSPResponse,
uint32_t providerFlags,
Time time,
@ -820,10 +835,18 @@ SSLServerCertVerificationJob::Dispatch(
return SECFailure;
}
// Copy the certificate list so the runnable can take ownership of it in the
// constructor.
// We can safely skip checking if NSS has already shut down here since we're
// in the middle of verifying a certificate.
nsNSSShutDownPreventionLock lock;
CERTCertList* peerCertChainCopy = nsNSSCertList::DupCertList(peerCertChain, lock);
RefPtr<SSLServerCertVerificationJob> job(
new SSLServerCertVerificationJob(certVerifier, fdForLogging, infoObject,
serverCert, stapledOCSPResponse,
providerFlags, time, prtime));
serverCert, peerCertChainCopy,
stapledOCSPResponse, providerFlags,
time, prtime));
nsresult nrv;
if (!gCertVerificationThreadPool) {
@ -873,8 +896,8 @@ SSLServerCertVerificationJob::Run()
// set the error code if/when it fails.
PR_SetError(0, 0);
SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert.get(),
mStapledOCSPResponse, mProviderFlags,
mTime);
mPeerCertChain, mStapledOCSPResponse,
mProviderFlags, mTime);
if (rv == SECSuccess) {
uint32_t interval = (uint32_t) ((TimeStamp::Now() - mJobStartTime).ToMilliseconds());
RefPtr<SSLServerCertVerificationResult> restart(
@ -975,6 +998,9 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
return SECFailure;
}
// Get the peer certificate chain for error reporting
ScopedCERTCertList peerCertChain(SSL_PeerCertificateChain(fd));
socketInfo->SetFullHandshake();
Time now(Now());
@ -1020,8 +1046,8 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
socketInfo->SetCertVerificationWaiting();
SECStatus rv = SSLServerCertVerificationJob::Dispatch(
certVerifier, static_cast<const void*>(fd), socketInfo,
serverCert, stapledOCSPResponse, providerFlags, now,
prnow);
serverCert, peerCertChain, stapledOCSPResponse,
providerFlags, now, prnow);
return rv;
}
@ -1031,7 +1057,8 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
// a non-blocking socket.
SECStatus rv = AuthCertificate(*certVerifier, socketInfo, serverCert,
stapledOCSPResponse, providerFlags, now);
peerCertChain, stapledOCSPResponse,
providerFlags, now);
if (rv == SECSuccess) {
Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
return SECSuccess;

View File

@ -289,8 +289,8 @@ TransportSecurityInfo::GetInterface(const nsIID & uuid, void * *result)
// of the previous value. This is so when older versions attempt to
// read a newer serialized TransportSecurityInfo, they will actually
// fail and return NS_ERROR_FAILURE instead of silently failing.
#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x28ea, 0x45d2, \
{ 0xa2, 0x5a, 0x35, 0x7c, 0xae, 0xfa, 0x7f, 0x82 } }
#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0xf40a, 0x4060, \
{ 0xb2, 0xe1, 0x62, 0xab, 0x2b, 0x85, 0x26, 0xa9 } }
static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC);
NS_IMETHODIMP
@ -331,6 +331,15 @@ TransportSecurityInfo::Write(nsIObjectOutputStream* stream)
if (NS_FAILED(rv)) {
return rv;
}
rv = NS_WriteOptionalCompoundObject(stream,
mFailedCertChain,
NS_GET_IID(nsIX509CertList),
true);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
@ -386,6 +395,14 @@ TransportSecurityInfo::Read(nsIObjectInputStream* stream)
if (!mSSLStatus) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsISupports> failedCertChainSupports;
rv = NS_ReadOptionalObject(stream, true, getter_AddRefs(failedCertChainSupports));
if (NS_FAILED(rv)) {
return rv;
}
mFailedCertChain = do_QueryInterface(failedCertChainSupports);
return NS_OK;
}
@ -1072,4 +1089,30 @@ TransportSecurityInfo::SetStatusErrorBits(nsIX509Cert & cert,
SECFailure);
}
NS_IMETHODIMP
TransportSecurityInfo::GetFailedCertChain(nsIX509CertList** _result)
{
NS_ASSERTION(_result, "non-NULL destination required");
*_result = mFailedCertChain;
NS_IF_ADDREF(*_result);
return NS_OK;
}
nsresult
TransportSecurityInfo::SetFailedCertChain(ScopedCERTCertList& certList)
{
nsNSSShutDownPreventionLock lock;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIX509CertList> comCertList;
// nsNSSCertList takes ownership of certList
mFailedCertChain = new nsNSSCertList(certList, lock);
return NS_OK;
}
} } // namespace mozilla::psm

View File

@ -7,16 +7,18 @@
#ifndef _MOZILLA_PSM_TRANSPORTSECURITYINFO_H
#define _MOZILLA_PSM_TRANSPORTSECURITYINFO_H
#include "ScopedNSSTypes.h"
#include "certt.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "nsIInterfaceRequestor.h"
#include "nsITransportSecurityInfo.h"
#include "nsSSLStatus.h"
#include "nsISSLStatusProvider.h"
#include "nsIAssociatedContentSecurity.h"
#include "nsNSSShutDown.h"
#include "nsDataHashtable.h"
#include "nsIAssociatedContentSecurity.h"
#include "nsIInterfaceRequestor.h"
#include "nsISSLStatusProvider.h"
#include "nsITransportSecurityInfo.h"
#include "nsNSSShutDown.h"
#include "nsSSLStatus.h"
#include "pkix/pkixtypes.h"
namespace mozilla { namespace psm {
@ -74,6 +76,8 @@ public:
nsSSLStatus* SSLStatus() { return mSSLStatus; }
void SetStatusErrorBits(nsIX509Cert & cert, uint32_t collected_errors);
nsresult SetFailedCertChain(ScopedCERTCertList& certList);
private:
mutable ::mozilla::Mutex mMutex;
@ -100,6 +104,9 @@ private:
/* SSL Status */
mozilla::RefPtr<nsSSLStatus> mSSLStatus;
/* Peer cert chain for failed connections (for error reporting) */
nsCOMPtr<nsIX509CertList> mFailedCertChain;
virtual void virtualDestroyNSSReference();
void destructorSafeDestroyNSSReference();
};

View File

@ -1545,7 +1545,9 @@ ConstructCERTCertListFromReversedDERArray(
} // namespace mozilla
NS_IMPL_ISUPPORTS(nsNSSCertList, nsIX509CertList)
NS_IMPL_ISUPPORTS(nsNSSCertList,
nsIX509CertList,
nsISerializable)
nsNSSCertList::nsNSSCertList(ScopedCERTCertList& certList,
const nsNSSShutDownPreventionLock& proofOfLock)
@ -1667,6 +1669,84 @@ nsNSSCertList::GetRawCertList()
return mCertList.get();
}
NS_IMETHODIMP
nsNSSCertList::Write(nsIObjectOutputStream* aStream)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ENSURE_STATE(mCertList);
nsresult rv = NS_OK;
// First, enumerate the certs to get the length of the list
uint32_t certListLen = 0;
CERTCertListNode* node = nullptr;
for (node = CERT_LIST_HEAD(mCertList);
!CERT_LIST_END(node, mCertList);
node = CERT_LIST_NEXT(node), ++certListLen) {
}
// Write the length of the list
rv = aStream->Write32(certListLen);
// Repeat the loop, and serialize each certificate
node = nullptr;
for (node = CERT_LIST_HEAD(mCertList);
!CERT_LIST_END(node, mCertList);
node = CERT_LIST_NEXT(node))
{
nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
if (!cert) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
nsCOMPtr<nsISerializable> serializableCert = do_QueryInterface(cert);
rv = aStream->WriteCompoundObject(serializableCert, NS_GET_IID(nsIX509Cert), true);
if (NS_FAILED(rv)) {
break;
}
}
return rv;
}
NS_IMETHODIMP
nsNSSCertList::Read(nsIObjectInputStream* aStream)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ENSURE_STATE(mCertList);
nsresult rv = NS_OK;
uint32_t certListLen;
rv = aStream->Read32(&certListLen);
if (NS_FAILED(rv)) {
return rv;
}
for(uint32_t i = 0; i < certListLen; ++i) {
nsCOMPtr<nsISupports> certSupports;
rv = aStream->ReadObject(true, getter_AddRefs(certSupports));
if (NS_FAILED(rv)) {
break;
}
nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(certSupports);
rv = AddCert(cert);
if (NS_FAILED(rv)) {
break;
}
}
return rv;
}
NS_IMETHODIMP
nsNSSCertList::GetEnumerator(nsISimpleEnumerator** _retval)
{

View File

@ -82,11 +82,13 @@ SECStatus ConstructCERTCertListFromReversedDERArray(
} // namespcae mozilla
class nsNSSCertList: public nsIX509CertList,
public nsISerializable,
public nsNSSShutDownObject
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIX509CERTLIST
NS_DECL_NSISERIALIZABLE
// certList is adopted
nsNSSCertList(mozilla::ScopedCERTCertList& certList,