]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/tpOcspCache.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_tp / lib / tpOcspCache.cpp
1 /*
2 * Copyright (c) 2004,2011,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * tpOcspCache.cpp - local OCSP response cache.
26 */
27
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>
35
36 /*
37 * Set this flag nonzero to turn off this cache module. Generally used to debug
38 * the ocspd disk cache.
39 */
40 #ifndef NDEBUG
41 #define TP_OCSP_CACHE_DISABLE 0
42 #else
43 /* cache always enabled in production build */
44 #define TP_OCSP_CACHE_DISABLE 0
45 #endif
46
47 #pragma mark ---- single cache entry ----
48
49 /*
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
52 * come and gone.
53 */
54 class OcspCacheEntry : public OCSPResponse
55 {
56 public:
57 OcspCacheEntry(
58 const CSSM_DATA derEncoded,
59 const CSSM_DATA *localResponder); // optional
60 ~OcspCacheEntry();
61
62 /* a trusting environment, this module...all public */
63 CSSM_DATA mLocalResponder; // we new[]
64 };
65
66 OcspCacheEntry::OcspCacheEntry(
67 const CSSM_DATA derEncoded,
68 const CSSM_DATA *localResponder) // optional
69 : OCSPResponse(derEncoded, TP_OCSP_CACHE_TTL)
70 {
71 if(localResponder) {
72 mLocalResponder.Data = new uint8[localResponder->Length];
73 mLocalResponder.Length = localResponder->Length;
74 memmove(mLocalResponder.Data, localResponder->Data, localResponder->Length);
75 }
76 else {
77 mLocalResponder.Data = NULL;
78 mLocalResponder.Length = 0;
79 }
80 }
81
82 OcspCacheEntry::~OcspCacheEntry()
83 {
84 delete[] mLocalResponder.Data;
85 }
86
87 #pragma mark ---- global cache object ----
88
89 /*
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
92 * mCacheLock.
93 */
94 class OcspCache
95 {
96 public:
97 OcspCache();
98 ~OcspCache();
99
100 /* The methods corresponding to this module's public interface */
101 OCSPSingleResponse *lookup(
102 OCSPClientCertID &certID,
103 const CSSM_DATA *localResponderURI); // optional
104 void addResponse(
105 const CSSM_DATA &ocspResp, // we'll decode it
106 const CSSM_DATA *localResponderURI); // optional
107 void flush(
108 OCSPClientCertID &certID);
109
110 private:
111 void removeEntry(unsigned dex);
112 void scanForStale();
113 OCSPSingleResponse *lookupPriv(
114 OCSPClientCertID &certID,
115 const CSSM_DATA *localResponderURI, // optional
116 unsigned &rtnDex); // RETURNED on success
117
118 Mutex mCacheLock;
119
120 /*
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.
125 */
126 OcspCacheEntry **mEntries; // just an array of pointers
127 unsigned mNumEntries; // valid entries in mEntries
128 unsigned mSizeofEntries; // mallocd space in mEntries
129 };
130
131 OcspCache::OcspCache()
132 : mEntries(NULL), mNumEntries(0), mSizeofEntries(0)
133 {
134
135 }
136
137 /* As of Tiger I believe that this code never runs */
138 OcspCache::~OcspCache()
139 {
140 for(unsigned dex=0; dex<mNumEntries; dex++) {
141 delete mEntries[dex];
142 }
143 if(mEntries) {
144 free(mEntries);
145 }
146 }
147
148 /*
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
152 * manipulate it.
153 */
154 void OcspCache::removeEntry(
155 unsigned dex)
156 {
157 assert(dex <= (mNumEntries - 1));
158
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];
163 }
164 mNumEntries--;
165 }
166
167 /*
168 * Private routine to scan cache, deleting stale entries.
169 * Caller must hold mCacheLock, and not be traversing mEntries.
170 */
171 void OcspCache::scanForStale()
172 {
173 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
174 bool foundOne;
175 do {
176 /* restart every time we delete a stale entry */
177 foundOne = false;
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",
182 entry);
183 removeEntry(dex);
184 foundOne = true;
185 break;
186 }
187 }
188 } while(foundOne);
189 }
190
191 /*
192 * Private lookup routine. Caller holds mCacheLock. We return both an
193 * OCSPSingleResponse and the index into mEntries at which we found it.
194 */
195 OCSPSingleResponse *OcspCache::lookupPriv(
196 OCSPClientCertID &certID,
197 const CSSM_DATA *localResponderURI, // optional
198 unsigned &rtnDex) // RETURNED on success
199 {
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",
208 entry);
209 continue;
210 }
211 if(!tpCompareCssmData(localResponderURI, &entry->mLocalResponder)) {
212 tpOcspCacheDebug("OcspCache::lookup: uri mismatch (2) on entry %p",
213 entry);
214 continue;
215 }
216 }
217 resp = entry->singleResponseFor(certID);
218 if(resp) {
219 tpOcspCacheDebug("OcspCache::lookupPriv: cache HIT on entry %p", entry);
220 rtnDex=dex;
221 return resp;
222 }
223 }
224 tpOcspCacheDebug("OcspCache::lookupPriv: cache MISS");
225 return NULL;
226 }
227
228 OCSPSingleResponse *OcspCache::lookup(
229 OCSPClientCertID &certID,
230 const CSSM_DATA *localResponderURI) // optional
231 {
232 StLock<Mutex> _(mCacheLock);
233
234 /* take care of stale entries right away */
235 scanForStale();
236
237 unsigned rtnDex;
238 return lookupPriv(certID, localResponderURI, rtnDex);
239 }
240
241 void OcspCache::addResponse(
242 const CSSM_DATA &ocspResp, // we'll decode it
243 const CSSM_DATA *localResponderURI) // optional
244 {
245 StLock<Mutex> _(mCacheLock);
246
247 OcspCacheEntry *entry = new OcspCacheEntry(ocspResp, localResponderURI);
248 if(mNumEntries == mSizeofEntries) {
249 if(mSizeofEntries == 0) {
250 /* appending to empty array */
251 mSizeofEntries = 1;
252 }
253 else {
254 mSizeofEntries *= 2;
255 }
256 mEntries = (OcspCacheEntry **)realloc(mEntries,
257 mSizeofEntries * sizeof(OcspCacheEntry *));
258 }
259 mEntries[mNumEntries++] = entry;
260 tpOcspCacheDebug("OcspCache::addResponse: add entry %p", entry);
261 }
262
263 void OcspCache::flush(
264 OCSPClientCertID &certID)
265 {
266 StLock<Mutex> _(mCacheLock);
267
268 /* take care of all stale entries */
269 scanForStale();
270
271 unsigned rtnDex;
272 OCSPSingleResponse *resp;
273 do {
274 /* execute as until we find no more entries matching */
275 resp = lookupPriv(certID, NULL, rtnDex);
276 if(resp) {
277 assert((rtnDex >= 0) && (rtnDex < mNumEntries));
278 tpOcspCacheDebug("OcspCache::flush: deleting entry %p", mEntries[rtnDex]);
279 removeEntry(rtnDex);
280 }
281 } while(resp != NULL);
282 }
283
284
285 static ModuleNexus<OcspCache> tpOcspCache;
286
287 #pragma mark ---- Public API ----
288 /*
289 * Lookup locally cached response. Caller must free the returned OCSPSingleResponse.
290 */
291 OCSPSingleResponse *tpOcspCacheLookup(
292 OCSPClientCertID &certID,
293 const CSSM_DATA *localResponderURI) // optional
294 {
295 return tpOcspCache().lookup(certID, localResponderURI);
296 }
297
298 /*
299 * Add a fully verified OCSP response to cache.
300 */
301 void tpOcspCacheAdd(
302 const CSSM_DATA &ocspResp, // we'll decode it, not keep a ref
303 const CSSM_DATA *localResponderURI) // optional
304 {
305 #if TP_OCSP_CACHE_DISABLE
306 return;
307 #endif
308 tpOcspCache().addResponse(ocspResp, localResponderURI);
309 }
310
311 /*
312 * Delete any entry associated with specified certID from cache.
313 */
314 void tpOcspCacheFlush(
315 OCSPClientCertID &certID)
316 {
317 tpOcspCache().flush(certID);
318 }
319