2 * Copyright (c) 2004,2011,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * tpOcspCache.cpp - local OCSP response cache.
28 #include "tpOcspCache.h"
29 #include "tpdebugging.h"
30 #include "certGroupUtils.h"
31 #include <security_utilities/globalizer.h>
32 #include <security_utilities/threading.h>
33 #include <security_ocspd/ocspdUtils.h>
34 #include <security_utilities/simulatecrash_assert.h>
37 * Set this flag nonzero to turn off this cache module. Generally used to debug
38 * the ocspd disk cache.
41 #define TP_OCSP_CACHE_DISABLE 0
43 /* cache always enabled in production build */
44 #define TP_OCSP_CACHE_DISABLE 0
47 #pragma mark ---- single cache entry ----
50 * One cache entry, just a parsed OCSPResponse plus an optional URI and a
51 * "latest" nextUpdate time. An entry is stale when its nextUpdate time has
54 class OcspCacheEntry
: public OCSPResponse
58 const CSSM_DATA derEncoded
,
59 const CSSM_DATA
*localResponder
); // optional
62 /* a trusting environment, this module...all public */
63 CSSM_DATA mLocalResponder
; // we new[]
66 OcspCacheEntry::OcspCacheEntry(
67 const CSSM_DATA derEncoded
,
68 const CSSM_DATA
*localResponder
) // optional
69 : OCSPResponse(derEncoded
, TP_OCSP_CACHE_TTL
)
72 mLocalResponder
.Data
= new uint8
[localResponder
->Length
];
73 mLocalResponder
.Length
= localResponder
->Length
;
74 memmove(mLocalResponder
.Data
, localResponder
->Data
, localResponder
->Length
);
77 mLocalResponder
.Data
= NULL
;
78 mLocalResponder
.Length
= 0;
82 OcspCacheEntry::~OcspCacheEntry()
84 delete[] mLocalResponder
.Data
;
87 #pragma mark ---- global cache object ----
90 * The cache object; ModuleNexus provides each task with at most of of these.
91 * All ops which affect the contents of the cache hold the (essentially) global
100 /* The methods corresponding to this module's public interface */
101 OCSPSingleResponse
*lookup(
102 OCSPClientCertID
&certID
,
103 const CSSM_DATA
*localResponderURI
); // optional
105 const CSSM_DATA
&ocspResp
, // we'll decode it
106 const CSSM_DATA
*localResponderURI
); // optional
108 OCSPClientCertID
&certID
);
111 void removeEntry(unsigned dex
);
113 OCSPSingleResponse
*lookupPriv(
114 OCSPClientCertID
&certID
,
115 const CSSM_DATA
*localResponderURI
, // optional
116 unsigned &rtnDex
); // RETURNED on success
121 * NOTE: I am aware that most folks would just use an array<> here, but
122 * gdb is so lame that it doesn't even let one examine the contents
123 * of an array<> (or just about anything else in the STL). I prefer
124 * debuggability over saving a few lines of trivial code.
126 OcspCacheEntry
**mEntries
; // just an array of pointers
127 unsigned mNumEntries
; // valid entries in mEntries
128 unsigned mSizeofEntries
; // mallocd space in mEntries
131 OcspCache::OcspCache()
132 : mEntries(NULL
), mNumEntries(0), mSizeofEntries(0)
137 /* As of Tiger I believe that this code never runs */
138 OcspCache::~OcspCache()
140 for(unsigned dex
=0; dex
<mNumEntries
; dex
++) {
141 delete mEntries
[dex
];
149 * Private routine, remove entry 'n' from cache.
150 * -- caller must hold mCacheLock
151 * -- if caller is traversing mEntries, they must start over because we
154 void OcspCache::removeEntry(
157 assert(dex
<= (mNumEntries
- 1));
159 /* removed requested element and compact remaining array */
160 delete mEntries
[dex
];
161 for(unsigned i
=dex
; i
<(mNumEntries
- 1); i
++) {
162 mEntries
[i
] = mEntries
[i
+1];
168 * Private routine to scan cache, deleting stale entries.
169 * Caller must hold mCacheLock, and not be traversing mEntries.
171 void OcspCache::scanForStale()
173 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
176 /* restart every time we delete a stale entry */
178 for(unsigned dex
=0; dex
<mNumEntries
; dex
++) {
179 OcspCacheEntry
*entry
= mEntries
[dex
];
180 if(entry
->expireTime() < now
) {
181 tpOcspCacheDebug("OcspCache::scanForStale: deleting stale entry %p",
192 * Private lookup routine. Caller holds mCacheLock. We return both an
193 * OCSPSingleResponse and the index into mEntries at which we found it.
195 OCSPSingleResponse
*OcspCache::lookupPriv(
196 OCSPClientCertID
&certID
,
197 const CSSM_DATA
*localResponderURI
, // optional
198 unsigned &rtnDex
) // RETURNED on success
200 OCSPSingleResponse
*resp
= NULL
;
201 for(unsigned dex
=0; dex
<mNumEntries
; dex
++) {
202 OcspCacheEntry
*entry
= mEntries
[dex
];
203 if(localResponderURI
) {
204 /* if caller specifies, it must match */
205 if(entry
->mLocalResponder
.Data
== NULL
) {
206 /* came from somewhere else, skip it */
207 tpOcspCacheDebug("OcspCache::lookup: uri mismatch (1) on entry %p",
211 if(!tpCompareCssmData(localResponderURI
, &entry
->mLocalResponder
)) {
212 tpOcspCacheDebug("OcspCache::lookup: uri mismatch (2) on entry %p",
217 resp
= entry
->singleResponseFor(certID
);
219 tpOcspCacheDebug("OcspCache::lookupPriv: cache HIT on entry %p", entry
);
224 tpOcspCacheDebug("OcspCache::lookupPriv: cache MISS");
228 OCSPSingleResponse
*OcspCache::lookup(
229 OCSPClientCertID
&certID
,
230 const CSSM_DATA
*localResponderURI
) // optional
232 StLock
<Mutex
> _(mCacheLock
);
234 /* take care of stale entries right away */
238 return lookupPriv(certID
, localResponderURI
, rtnDex
);
241 void OcspCache::addResponse(
242 const CSSM_DATA
&ocspResp
, // we'll decode it
243 const CSSM_DATA
*localResponderURI
) // optional
245 StLock
<Mutex
> _(mCacheLock
);
247 OcspCacheEntry
*entry
= new OcspCacheEntry(ocspResp
, localResponderURI
);
248 if(mNumEntries
== mSizeofEntries
) {
249 if(mSizeofEntries
== 0) {
250 /* appending to empty array */
256 mEntries
= (OcspCacheEntry
**)realloc(mEntries
,
257 mSizeofEntries
* sizeof(OcspCacheEntry
*));
259 mEntries
[mNumEntries
++] = entry
;
260 tpOcspCacheDebug("OcspCache::addResponse: add entry %p", entry
);
263 void OcspCache::flush(
264 OCSPClientCertID
&certID
)
266 StLock
<Mutex
> _(mCacheLock
);
268 /* take care of all stale entries */
272 OCSPSingleResponse
*resp
;
274 /* execute as until we find no more entries matching */
275 resp
= lookupPriv(certID
, NULL
, rtnDex
);
277 assert((rtnDex
>= 0) && (rtnDex
< mNumEntries
));
278 tpOcspCacheDebug("OcspCache::flush: deleting entry %p", mEntries
[rtnDex
]);
281 } while(resp
!= NULL
);
285 static ModuleNexus
<OcspCache
> tpOcspCache
;
287 #pragma mark ---- Public API ----
289 * Lookup locally cached response. Caller must free the returned OCSPSingleResponse.
291 OCSPSingleResponse
*tpOcspCacheLookup(
292 OCSPClientCertID
&certID
,
293 const CSSM_DATA
*localResponderURI
) // optional
295 return tpOcspCache().lookup(certID
, localResponderURI
);
299 * Add a fully verified OCSP response to cache.
302 const CSSM_DATA
&ocspResp
, // we'll decode it, not keep a ref
303 const CSSM_DATA
*localResponderURI
) // optional
305 #if TP_OCSP_CACHE_DISABLE
308 tpOcspCache().addResponse(ocspResp
, localResponderURI
);
312 * Delete any entry associated with specified certID from cache.
314 void tpOcspCacheFlush(
315 OCSPClientCertID
&certID
)
317 tpOcspCache().flush(certID
);