]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_apple_x509_tp/lib/tpOcspCache.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / 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 (file)
index 0000000..5ac0ef4
--- /dev/null
@@ -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 <security_utilities/globalizer.h>
+#include <security_utilities/threading.h>
+#include <security_ocspd/ocspdUtils.h>
+#include <assert.h>
+
+/*
+ * 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; dex<mNumEntries; dex++) {
+               delete mEntries[dex];
+       }
+       if(mEntries) {
+               free(mEntries);
+       }
+}
+
+/* 
+ * Private routine, remove entry 'n' from cache.
+ * -- caller must hold mCacheLock
+ * -- if caller is traversing mEntries, they must start over because we
+ *    manipulate it.
+ */
+void OcspCache::removeEntry(
+       unsigned dex)
+{
+       assert(dex <= (mNumEntries - 1));
+       
+       /* removed requested element and compact remaining array */
+       delete mEntries[dex];
+       for(unsigned i=dex; i<(mNumEntries - 1); i++) {
+               mEntries[i] = mEntries[i+1];
+       }
+       mNumEntries--;
+}
+
+/* 
+ * Private routine to scan cache, deleting stale entries.
+ * Caller must hold mCacheLock, and not be traversing mEntries.
+ */
+void OcspCache::scanForStale()
+{
+       CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
+       bool foundOne;
+       do {
+               /* restart every time we delete a stale entry */
+               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);
+}
+
+/* 
+ * 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; dex<mNumEntries; dex++) {
+               OcspCacheEntry *entry = mEntries[dex];
+               if(localResponderURI) {
+                       /* if caller specifies, it must match */
+                       if(entry->mLocalResponder.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<Mutex> _(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<Mutex> _(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<Mutex> _(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<OcspCache> 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);
+}
+