]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2004,2011,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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 <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 |