X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_apple_x509_tp/lib/tpOcspCache.cpp diff --git a/Security/libsecurity_apple_x509_tp/lib/tpOcspCache.cpp b/Security/libsecurity_apple_x509_tp/lib/tpOcspCache.cpp new file mode 100644 index 00000000..5ac0ef4f --- /dev/null +++ b/Security/libsecurity_apple_x509_tp/lib/tpOcspCache.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2004,2011,2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * tpOcspCache.cpp - local OCSP response cache. + */ + +#include "tpOcspCache.h" +#include "tpdebugging.h" +#include "certGroupUtils.h" +#include +#include +#include +#include + +/* + * Set this flag nonzero to turn off this cache module. Generally used to debug + * the ocspd disk cache. + */ +#ifndef NDEBUG +#define TP_OCSP_CACHE_DISABLE 0 +#else +/* cache always enabled in production build */ +#define TP_OCSP_CACHE_DISABLE 0 +#endif + +#pragma mark ---- single cache entry ---- + +/* + * One cache entry, just a parsed OCSPResponse plus an optional URI and a + * "latest" nextUpdate time. An entry is stale when its nextUpdate time has + * come and gone. + */ +class OcspCacheEntry : public OCSPResponse +{ +public: + OcspCacheEntry( + const CSSM_DATA derEncoded, + const CSSM_DATA *localResponder); // optional + ~OcspCacheEntry(); + + /* a trusting environment, this module...all public */ + CSSM_DATA mLocalResponder; // we new[] +}; + +OcspCacheEntry::OcspCacheEntry( + const CSSM_DATA derEncoded, + const CSSM_DATA *localResponder) // optional + : 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 ---- + +/* + * The cache object; ModuleNexus provides each task with at most of of these. + * All ops which affect the contents of the cache hold the (essentially) global + * mCacheLock. + */ +class OcspCache +{ +public: + OcspCache(); + ~OcspCache(); + + /* The methods corresponding to this module's public interface */ + OCSPSingleResponse *lookup( + OCSPClientCertID &certID, + const CSSM_DATA *localResponderURI); // optional + void addResponse( + const CSSM_DATA &ocspResp, // we'll decode it + const CSSM_DATA *localResponderURI); // optional + void flush( + OCSPClientCertID &certID); + +private: + void removeEntry(unsigned dex); + void scanForStale(); + OCSPSingleResponse *lookupPriv( + OCSPClientCertID &certID, + const CSSM_DATA *localResponderURI, // optional + unsigned &rtnDex); // RETURNED on success + + Mutex mCacheLock; + + /* + * NOTE: I am aware that most folks would just use an array<> here, but + * gdb is so lame that it doesn't even let one examine the contents + * of an array<> (or just about anything else in the STL). I prefer + * debuggability over saving a few lines of trivial code. + */ + OcspCacheEntry **mEntries; // just an array of pointers + unsigned mNumEntries; // valid entries in mEntries + unsigned mSizeofEntries; // mallocd space in mEntries +}; + +OcspCache::OcspCache() + : mEntries(NULL), mNumEntries(0), mSizeofEntries(0) +{ + +} + +/* As of Tiger I believe that this code never runs */ +OcspCache::~OcspCache() +{ + for(unsigned dex=0; dexexpireTime() < now) { + tpOcspCacheDebug("OcspCache::scanForStale: deleting stale entry %p", + entry); + removeEntry(dex); + foundOne = true; + break; + } + } + } while(foundOne); +} + +/* + * Private lookup routine. Caller holds mCacheLock. We return both an + * OCSPSingleResponse and the index into mEntries at which we found it. + */ + OCSPSingleResponse *OcspCache::lookupPriv( + OCSPClientCertID &certID, + const CSSM_DATA *localResponderURI, // optional + unsigned &rtnDex) // RETURNED on success +{ + OCSPSingleResponse *resp = NULL; + for(unsigned dex=0; dexmLocalResponder.Data == NULL) { + /* came from somewhere else, skip it */ + 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) // optional +{ + StLock _(mCacheLock); + + /* take care of stale entries right away */ + scanForStale(); + + unsigned rtnDex; + return lookupPriv(certID, localResponderURI, rtnDex); +} + +void OcspCache::addResponse( + const CSSM_DATA &ocspResp, // we'll decode it + const CSSM_DATA *localResponderURI) // optional +{ + StLock _(mCacheLock); + + OcspCacheEntry *entry = new OcspCacheEntry(ocspResp, localResponderURI); + if(mNumEntries == mSizeofEntries) { + if(mSizeofEntries == 0) { + /* appending to empty array */ + 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 _(mCacheLock); + + /* take care of all stale entries */ + scanForStale(); + + unsigned rtnDex; + OCSPSingleResponse *resp; + do { + /* execute as until we find no more entries matching */ + 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 tpOcspCache; + +#pragma mark ---- Public API ---- +/* + * Lookup locally cached response. Caller must free the returned OCSPSingleResponse. + */ +OCSPSingleResponse *tpOcspCacheLookup( + OCSPClientCertID &certID, + const CSSM_DATA *localResponderURI) // optional +{ + return tpOcspCache().lookup(certID, localResponderURI); +} + +/* + * Add a fully verified OCSP response to cache. + */ +void tpOcspCacheAdd( + const CSSM_DATA &ocspResp, // we'll decode it, not keep a ref + const CSSM_DATA *localResponderURI) // optional +{ + #if TP_OCSP_CACHE_DISABLE + return; + #endif + tpOcspCache().addResponse(ocspResp, localResponderURI); +} + +/* + * Delete any entry associated with specified certID from cache. + */ +void tpOcspCacheFlush( + OCSPClientCertID &certID) +{ + tpOcspCache().flush(certID); +} +