mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1560353 - Extend SSLTokensCache to store the result of VerifySSLServerCert r=dragana,keeler
Differential Revision: https://phabricator.services.mozilla.com/D46159 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
6d782a5e1e
commit
3f5bb45b8e
@ -5,6 +5,7 @@
|
||||
#include "SSLTokensCache.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsNSSIOLayer.h"
|
||||
#include "ssl.h"
|
||||
#include "sslexp.h"
|
||||
|
||||
@ -36,6 +37,28 @@ class ExpirationComparator {
|
||||
StaticRefPtr<SSLTokensCache> SSLTokensCache::gInstance;
|
||||
StaticMutex SSLTokensCache::sLock;
|
||||
|
||||
uint32_t SSLTokensCache::TokenCacheRecord::Size() const {
|
||||
uint32_t size = mToken.Length() + sizeof(mSessionCacheInfo.mEVStatus) +
|
||||
sizeof(mSessionCacheInfo.mCertificateTransparencyStatus) +
|
||||
mSessionCacheInfo.mServerCertBytes.Length();
|
||||
if (mSessionCacheInfo.mSucceededCertChainBytes) {
|
||||
for (const auto& cert : mSessionCacheInfo.mSucceededCertChainBytes.ref()) {
|
||||
size += cert.Length();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void SSLTokensCache::TokenCacheRecord::Reset() {
|
||||
mToken.Clear();
|
||||
mExpirationTime = 0;
|
||||
mSessionCacheInfo.mEVStatus = psm::EVStatus::NotEV;
|
||||
mSessionCacheInfo.mCertificateTransparencyStatus =
|
||||
nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
|
||||
mSessionCacheInfo.mServerCertBytes.Clear();
|
||||
mSessionCacheInfo.mSucceededCertChainBytes.reset();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(SSLTokensCache, nsIMemoryReporter)
|
||||
|
||||
// static
|
||||
@ -79,7 +102,8 @@ SSLTokensCache::~SSLTokensCache() { LOG(("SSLTokensCache::~SSLTokensCache")); }
|
||||
|
||||
// static
|
||||
nsresult SSLTokensCache::Put(const nsACString& aKey, const uint8_t* aToken,
|
||||
uint32_t aTokenLen) {
|
||||
uint32_t aTokenLen,
|
||||
nsITransportSecurityInfo* aSecInfo) {
|
||||
StaticMutexAutoLock lock(sLock);
|
||||
|
||||
LOG(("SSLTokensCache::Put [key=%s, tokenLen=%u]",
|
||||
@ -90,6 +114,10 @@ nsresult SSLTokensCache::Put(const nsACString& aKey, const uint8_t* aToken,
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!aSecInfo) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRUint32 expirationTime;
|
||||
SSLResumptionTokenInfo tokenInfo;
|
||||
if (SSL_GetResumptionTokenInfo(aToken, aTokenLen, &tokenInfo,
|
||||
@ -101,6 +129,53 @@ nsresult SSLTokensCache::Put(const nsACString& aKey, const uint8_t* aToken,
|
||||
expirationTime = tokenInfo.expirationTime;
|
||||
SSL_DestroyResumptionTokenInfo(&tokenInfo);
|
||||
|
||||
nsCOMPtr<nsIX509Cert> cert;
|
||||
aSecInfo->GetServerCert(getter_AddRefs(cert));
|
||||
if (!cert) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> certBytes;
|
||||
nsresult rv = cert->GetRawDER(certBytes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Maybe<nsTArray<nsTArray<uint8_t>>> succeededCertChainBytes;
|
||||
nsCOMPtr<nsIX509CertList> succeededCertChain;
|
||||
Unused << aSecInfo->GetSucceededCertChain(getter_AddRefs(succeededCertChain));
|
||||
if (succeededCertChain) {
|
||||
succeededCertChainBytes.emplace();
|
||||
rv = succeededCertChain->GetCertList()->ForEachCertificateInChain(
|
||||
[&succeededCertChainBytes](nsCOMPtr<nsIX509Cert> aCert, bool /*unused*/,
|
||||
/*out*/ bool& /*unused*/) {
|
||||
nsTArray<uint8_t> cert;
|
||||
nsresult rv = aCert->GetRawDER(cert);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
succeededCertChainBytes->AppendElement(std::move(cert));
|
||||
return NS_OK;
|
||||
});
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
bool isEV;
|
||||
rv = aSecInfo->GetIsExtendedValidation(&isEV);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint16_t certificateTransparencyStatus;
|
||||
rv = aSecInfo->GetCertificateTransparencyStatus(
|
||||
&certificateTransparencyStatus);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
TokenCacheRecord* rec = nullptr;
|
||||
|
||||
if (!gInstance->mTokenCacheRecords.Get(aKey, &rec)) {
|
||||
@ -109,14 +184,27 @@ nsresult SSLTokensCache::Put(const nsACString& aKey, const uint8_t* aToken,
|
||||
gInstance->mTokenCacheRecords.Put(aKey, rec);
|
||||
gInstance->mExpirationArray.AppendElement(rec);
|
||||
} else {
|
||||
gInstance->mCacheSize -= rec->mToken.Length();
|
||||
rec->mToken.Clear();
|
||||
gInstance->mCacheSize -= rec->Size();
|
||||
rec->Reset();
|
||||
}
|
||||
|
||||
rec->mExpirationTime = expirationTime;
|
||||
MOZ_ASSERT(rec->mToken.IsEmpty());
|
||||
rec->mToken.AppendElements(aToken, aTokenLen);
|
||||
gInstance->mCacheSize += rec->mToken.Length();
|
||||
|
||||
rec->mSessionCacheInfo.mServerCertBytes = std::move(certBytes);
|
||||
|
||||
rec->mSessionCacheInfo.mSucceededCertChainBytes =
|
||||
std::move(succeededCertChainBytes);
|
||||
|
||||
if (isEV) {
|
||||
rec->mSessionCacheInfo.mEVStatus = psm::EVStatus::EV;
|
||||
}
|
||||
|
||||
rec->mSessionCacheInfo.mCertificateTransparencyStatus =
|
||||
certificateTransparencyStatus;
|
||||
|
||||
gInstance->mCacheSize += rec->Size();
|
||||
|
||||
gInstance->LogStats();
|
||||
|
||||
@ -150,6 +238,30 @@ nsresult SSLTokensCache::Get(const nsACString& aKey,
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SSLTokensCache::GetSessionCacheInfo(const nsACString& aKey,
|
||||
SessionCacheInfo& aResult) {
|
||||
StaticMutexAutoLock lock(sLock);
|
||||
|
||||
LOG(("SSLTokensCache::GetSessionCacheInfo [key=%s]",
|
||||
PromiseFlatCString(aKey).get()));
|
||||
|
||||
if (!gInstance) {
|
||||
LOG((" service not initialized"));
|
||||
return false;
|
||||
}
|
||||
|
||||
TokenCacheRecord* rec = nullptr;
|
||||
|
||||
if (gInstance->mTokenCacheRecords.Get(aKey, &rec)) {
|
||||
aResult = rec->mSessionCacheInfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG((" token not found"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult SSLTokensCache::Remove(const nsACString& aKey) {
|
||||
StaticMutexAutoLock lock(sLock);
|
||||
@ -177,7 +289,7 @@ nsresult SSLTokensCache::RemoveLocked(const nsACString& aKey) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mCacheSize -= rec->mToken.Length();
|
||||
mCacheSize -= rec->Size();
|
||||
|
||||
if (!mExpirationArray.RemoveElement(rec)) {
|
||||
MOZ_ASSERT(false, "token not found in mExpirationArray");
|
||||
|
@ -8,13 +8,23 @@
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "TransportSecurityInfo.h" // For EVStatus
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
struct SessionCacheInfo {
|
||||
psm::EVStatus mEVStatus = psm::EVStatus::NotEV;
|
||||
uint16_t mCertificateTransparencyStatus =
|
||||
nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
|
||||
nsTArray<uint8_t> mServerCertBytes;
|
||||
Maybe<nsTArray<nsTArray<uint8_t>>> mSucceededCertChainBytes;
|
||||
};
|
||||
|
||||
class SSLTokensCache : public nsIMemoryReporter {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
@ -28,8 +38,10 @@ class SSLTokensCache : public nsIMemoryReporter {
|
||||
static bool IsEnabled() { return sEnabled; }
|
||||
|
||||
static nsresult Put(const nsACString& aKey, const uint8_t* aToken,
|
||||
uint32_t aTokenLen);
|
||||
uint32_t aTokenLen, nsITransportSecurityInfo* aSecInfo);
|
||||
static nsresult Get(const nsACString& aKey, nsTArray<uint8_t>& aToken);
|
||||
static bool GetSessionCacheInfo(const nsACString& aKey,
|
||||
SessionCacheInfo& aResult);
|
||||
static nsresult Remove(const nsACString& aKey);
|
||||
|
||||
private:
|
||||
@ -55,9 +67,13 @@ class SSLTokensCache : public nsIMemoryReporter {
|
||||
|
||||
class TokenCacheRecord {
|
||||
public:
|
||||
uint32_t Size() const;
|
||||
void Reset();
|
||||
|
||||
nsCString mKey;
|
||||
PRUint32 mExpirationTime;
|
||||
nsTArray<uint8_t> mToken;
|
||||
SessionCacheInfo mSessionCacheInfo;
|
||||
};
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, TokenCacheRecord> mTokenCacheRecords;
|
||||
|
@ -322,6 +322,7 @@ LOCAL_INCLUDES += [
|
||||
'/netwerk/protocol/http',
|
||||
'/netwerk/socket',
|
||||
'/netwerk/url-classifier',
|
||||
'/security/manager/ssl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
|
@ -1243,10 +1243,17 @@ SECStatus nsSocketTransport::StoreResumptionToken(
|
||||
if (!secCtrl) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
nsAutoCString peerId;
|
||||
secCtrl->GetPeerId(peerId);
|
||||
SSLTokensCache::Put(peerId, resumptionToken, len);
|
||||
|
||||
nsCOMPtr<nsITransportSecurityInfo> secInfo = do_QueryInterface(secCtrl);
|
||||
if (!secInfo) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (NS_FAILED(SSLTokensCache::Put(peerId, resumptionToken, len, secInfo))) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
return SECSuccess;
|
||||
}
|
||||
|
@ -87,6 +87,11 @@ class TransportSecurityInfo : public nsITransportSecurityInfo,
|
||||
// Use errorCode == 0 to indicate success;
|
||||
virtual void SetCertVerificationResult(PRErrorCode errorCode){};
|
||||
|
||||
void SetCertificateTransparencyStatus(
|
||||
uint16_t aCertificateTransparencyStatus) {
|
||||
mCertificateTransparencyStatus = aCertificateTransparencyStatus;
|
||||
}
|
||||
|
||||
uint16_t mCipherSuite;
|
||||
uint16_t mProtocolVersion;
|
||||
uint16_t mCertificateTransparencyStatus;
|
||||
|
@ -211,6 +211,7 @@ FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/dom/crypto',
|
||||
'/netwerk/base',
|
||||
'/security/certverifier',
|
||||
]
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "mozpkix/pkixtypes.h"
|
||||
#include "ssl.h"
|
||||
#include "sslproto.h"
|
||||
#include "SSLTokensCache.h"
|
||||
|
||||
#include "TrustOverrideUtils.h"
|
||||
#include "TrustOverride-SymantecData.inc"
|
||||
@ -1159,6 +1160,80 @@ nsresult IsCertificateDistrustImminent(nsIX509CertList* aCertList,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool ConstructCERTCertListFromBytesArray(
|
||||
nsTArray<nsTArray<uint8_t>>& aCertArray,
|
||||
/*out*/ UniqueCERTCertList& aCertList) {
|
||||
aCertList = UniqueCERTCertList(CERT_NewCertList());
|
||||
if (!aCertList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CERTCertDBHandle* certDB(CERT_GetDefaultCertDB()); // non-owning
|
||||
for (auto& cert : aCertArray) {
|
||||
SECItem certDER = {siBuffer, cert.Elements(),
|
||||
static_cast<unsigned int>(cert.Length())};
|
||||
UniqueCERTCertificate tmpCert(
|
||||
CERT_NewTempCertificate(certDB, &certDER, nullptr, false, true));
|
||||
if (!tmpCert) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CERT_AddCertToListTail(aCertList.get(), tmpCert.get()) != SECSuccess) {
|
||||
return false;
|
||||
}
|
||||
Unused << tmpCert.release(); // tmpCert is now owned by aCertList.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void RebuildCertificateInfoFromSSLTokenCache(
|
||||
nsNSSSocketInfo* aInfoObject) {
|
||||
MOZ_ASSERT(aInfoObject);
|
||||
|
||||
if (!aInfoObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString key;
|
||||
aInfoObject->GetPeerId(key);
|
||||
mozilla::net::SessionCacheInfo info;
|
||||
if (!mozilla::net::SSLTokensCache::GetSessionCacheInfo(key, info)) {
|
||||
MOZ_LOG(
|
||||
gPIPNSSLog, LogLevel::Debug,
|
||||
("RebuildCertificateInfoFromSSLTokenCache cannot find cached info."));
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<nsNSSCertificate> nssc = nsNSSCertificate::ConstructFromDER(
|
||||
BitwiseCast<char*, uint8_t*>(info.mServerCertBytes.Elements()),
|
||||
info.mServerCertBytes.Length());
|
||||
if (!nssc) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("RebuildCertificateInfoFromSSLTokenCache failed to construct "
|
||||
"server cert"));
|
||||
return;
|
||||
}
|
||||
|
||||
UniqueCERTCertList builtCertChain;
|
||||
if (info.mSucceededCertChainBytes) {
|
||||
if (!ConstructCERTCertListFromBytesArray(
|
||||
info.mSucceededCertChainBytes.ref(), builtCertChain)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("RebuildCertificateInfoFromSSLTokenCache failed to construct "
|
||||
"cert list"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
aInfoObject->SetServerCert(nssc, info.mEVStatus);
|
||||
aInfoObject->SetCertificateTransparencyStatus(
|
||||
info.mCertificateTransparencyStatus);
|
||||
if (builtCertChain) {
|
||||
aInfoObject->SetSucceededCertChain(std::move(builtCertChain));
|
||||
}
|
||||
}
|
||||
|
||||
void HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
SECStatus rv;
|
||||
|
||||
@ -1296,7 +1371,11 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("HandshakeCallback KEEPING existing cert\n"));
|
||||
} else {
|
||||
RebuildVerifiedCertificateInformation(fd, infoObject);
|
||||
if (mozilla::net::SSLTokensCache::IsEnabled()) {
|
||||
RebuildCertificateInfoFromSSLTokenCache(infoObject);
|
||||
} else {
|
||||
RebuildVerifiedCertificateInformation(fd, infoObject);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIX509CertList> succeededCertChain;
|
||||
|
Loading…
Reference in New Issue
Block a user