#include "tpOcspCache.h"
#include "tpdebugging.h"
#include "certGroupUtils.h"
#include <security_utilities/globalizer.h>
#include <security_utilities/threading.h>
#include <security_ocspd/ocspdUtils.h>
#include <assert.h>
#ifndef NDEBUG
#define TP_OCSP_CACHE_DISABLE 0
#else
#define TP_OCSP_CACHE_DISABLE 0
#endif
#pragma mark ---- single cache entry ----
class OcspCacheEntry : public OCSPResponse
{
public:
OcspCacheEntry(
const CSSM_DATA derEncoded,
const CSSM_DATA *localResponder); ~OcspCacheEntry();
CSSM_DATA mLocalResponder; };
OcspCacheEntry::OcspCacheEntry(
const CSSM_DATA derEncoded,
const CSSM_DATA *localResponder) : OCSPResponse(derEncoded, TP_OCSP_CACHE_TTL)
{
if(localResponder) {
mLocalResponder.Data = new uint8[localResponder->Length];
mLocalResponder.Length = localResponder->Length;
memmove(mLocalResponder.Data, localResponder->Data, localResponder->Length);
}
else {
mLocalResponder.Data = NULL;
mLocalResponder.Length = 0;
}
}
OcspCacheEntry::~OcspCacheEntry()
{
delete[] mLocalResponder.Data;
}
#pragma mark ---- global cache object ----
class OcspCache
{
public:
OcspCache();
~OcspCache();
OCSPSingleResponse *lookup(
OCSPClientCertID &certID,
const CSSM_DATA *localResponderURI); void addResponse(
const CSSM_DATA &ocspResp, const CSSM_DATA *localResponderURI); void flush(
OCSPClientCertID &certID);
private:
void removeEntry(unsigned dex);
void scanForStale();
OCSPSingleResponse *lookupPriv(
OCSPClientCertID &certID,
const CSSM_DATA *localResponderURI, unsigned &rtnDex);
Mutex mCacheLock;
OcspCacheEntry **mEntries; unsigned mNumEntries; unsigned mSizeofEntries; };
OcspCache::OcspCache()
: mEntries(NULL), mNumEntries(0), mSizeofEntries(0)
{
}
OcspCache::~OcspCache()
{
for(unsigned dex=0; dex<mNumEntries; dex++) {
delete mEntries[dex];
}
if(mEntries) {
free(mEntries);
}
}
void OcspCache::removeEntry(
unsigned dex)
{
assert(dex <= (mNumEntries - 1));
delete mEntries[dex];
for(unsigned i=dex; i<(mNumEntries - 1); i++) {
mEntries[i] = mEntries[i+1];
}
mNumEntries--;
}
void OcspCache::scanForStale()
{
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
bool foundOne;
do {
foundOne = false;
for(unsigned dex=0; dex<mNumEntries; dex++) {
OcspCacheEntry *entry = mEntries[dex];
if(entry->expireTime() < now) {
tpOcspCacheDebug("OcspCache::scanForStale: deleting stale entry %p",
entry);
removeEntry(dex);
foundOne = true;
break;
}
}
} while(foundOne);
}
OCSPSingleResponse *OcspCache::lookupPriv(
OCSPClientCertID &certID,
const CSSM_DATA *localResponderURI, unsigned &rtnDex) {
OCSPSingleResponse *resp = NULL;
for(unsigned dex=0; dex<mNumEntries; dex++) {
OcspCacheEntry *entry = mEntries[dex];
if(localResponderURI) {
if(entry->mLocalResponder.Data == NULL) {
tpOcspCacheDebug("OcspCache::lookup: uri mismatch (1) on entry %p",
entry);
continue;
}
if(!tpCompareCssmData(localResponderURI, &entry->mLocalResponder)) {
tpOcspCacheDebug("OcspCache::lookup: uri mismatch (2) on entry %p",
entry);
continue;
}
}
resp = entry->singleResponseFor(certID);
if(resp) {
tpOcspCacheDebug("OcspCache::lookupPriv: cache HIT on entry %p", entry);
rtnDex=dex;
return resp;
}
}
tpOcspCacheDebug("OcspCache::lookupPriv: cache MISS");
return NULL;
}
OCSPSingleResponse *OcspCache::lookup(
OCSPClientCertID &certID,
const CSSM_DATA *localResponderURI) {
StLock<Mutex> _(mCacheLock);
scanForStale();
unsigned rtnDex;
return lookupPriv(certID, localResponderURI, rtnDex);
}
void OcspCache::addResponse(
const CSSM_DATA &ocspResp, const CSSM_DATA *localResponderURI) {
StLock<Mutex> _(mCacheLock);
OcspCacheEntry *entry = new OcspCacheEntry(ocspResp, localResponderURI);
if(mNumEntries == mSizeofEntries) {
if(mSizeofEntries == 0) {
mSizeofEntries = 1;
}
else {
mSizeofEntries *= 2;
}
mEntries = (OcspCacheEntry **)realloc(mEntries,
mSizeofEntries * sizeof(OcspCacheEntry *));
}
mEntries[mNumEntries++] = entry;
tpOcspCacheDebug("OcspCache::addResponse: add entry %p", entry);
}
void OcspCache::flush(
OCSPClientCertID &certID)
{
StLock<Mutex> _(mCacheLock);
scanForStale();
unsigned rtnDex;
OCSPSingleResponse *resp;
do {
resp = lookupPriv(certID, NULL, rtnDex);
if(resp) {
assert((rtnDex >= 0) && (rtnDex < mNumEntries));
tpOcspCacheDebug("OcspCache::flush: deleting entry %p", mEntries[rtnDex]);
removeEntry(rtnDex);
}
} while(resp != NULL);
}
static ModuleNexus<OcspCache> tpOcspCache;
#pragma mark ---- Public API ----
OCSPSingleResponse *tpOcspCacheLookup(
OCSPClientCertID &certID,
const CSSM_DATA *localResponderURI) {
return tpOcspCache().lookup(certID, localResponderURI);
}
void tpOcspCacheAdd(
const CSSM_DATA &ocspResp, const CSSM_DATA *localResponderURI) {
#if TP_OCSP_CACHE_DISABLE
return;
#endif
tpOcspCache().addResponse(ocspResp, localResponderURI);
}
void tpOcspCacheFlush(
OCSPClientCertID &certID)
{
tpOcspCache().flush(certID);
}