]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/tpCrlVerify.cpp
Security-177.tar.gz
[apple/security.git] / AppleX509TP / tpCrlVerify.cpp
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 /*
20 * tpCrlVerify.cpp - routines to verify CRLs and to verify certs against CRLs.
21 *
22 * Written 9/26/02 by Doug Mitchell.
23 */
24
25 #include "tpCrlVerify.h"
26 #include "TPCertInfo.h"
27 #include "TPCrlInfo.h"
28 #include "tpdebugging.h"
29 #include "TPNetwork.h"
30 #include "TPDatabase.h"
31 #include <Security/globalizer.h>
32 #include <Security/threading.h>
33
34 /* crlrefresh does this now */
35 #define WRITE_FETCHED_CRLS_TO_DB 0
36
37 /*
38 * For now, a process-wide memory resident CRL cache.
39 * We are responsible for deleting the CRLs which get added to this
40 * cache. Currently the only time we add a CRL to this cache is
41 * when we fetch one from the net. We ref count CRLs in this cache
42 * to allow multi-threaded access.
43 */
44 class TPCRLCache : private TPCrlGroup
45 {
46 public:
47 TPCRLCache();
48 ~TPCRLCache() { }
49 TPCrlInfo *search(
50 TPCertInfo &cert,
51 TPCrlVerifyContext &vfyCtx);
52 void add(
53 TPCrlInfo &crl);
54 void remove(
55 TPCrlInfo &crl);
56 void release(
57 TPCrlInfo &crl);
58
59 private:
60 /* Protects ref count of all members of the cache */
61 Mutex mLock;
62 };
63
64 TPCRLCache::TPCRLCache()
65 : TPCrlGroup(CssmAllocator::standard(), TGO_Group)
66 {
67
68 }
69
70 TPCrlInfo *TPCRLCache::search(
71 TPCertInfo &cert,
72 TPCrlVerifyContext &vfyCtx)
73 {
74 StLock<Mutex> _(mLock);
75 TPCrlInfo *crl = findCrlForCert(cert);
76 if(crl) {
77 /* reevaluate validity */
78 crl->calculateCurrent(vfyCtx.verifyTime);
79 crl->mRefCount++;
80 }
81 return crl;
82 }
83
84 /* bumps ref count - caller is going to be using the CRL */
85 void TPCRLCache::add(
86 TPCrlInfo &crl)
87 {
88 StLock<Mutex> _(mLock);
89 crl.mRefCount++;
90 appendCrl(crl);
91 }
92
93 /* we delete on this one if --refCount == 0 */
94 void TPCRLCache::remove(
95 TPCrlInfo &crl)
96 {
97 StLock<Mutex> _(mLock);
98 removeCrl(crl);
99 release(crl);
100 assert(crl.mRefCount > 0);
101 crl.mRefCount--;
102 if(crl.mRefCount == 0) {
103 delete &crl;
104 }
105 else {
106 /* in use, flag for future delete */
107 crl.mToBeDeleted = true;
108 }
109 }
110
111 /* only delete if refCount zero AND flagged for deletion */
112 void TPCRLCache::release(
113 TPCrlInfo &crl)
114 {
115 StLock<Mutex> _(mLock);
116 assert(crl.mRefCount > 0);
117 crl.mRefCount--;
118 if(crl.mToBeDeleted & (crl.mRefCount == 0)) {
119 delete &crl;
120 }
121 }
122
123 static ModuleNexus<TPCRLCache> tpGlobalCrlCache;
124
125 /*
126 * Find CRL for specified cert. Only returns a fully verified CRL.
127 * Cert-specific errors such as CSSMERR_APPLETP_CRL_NOT_FOUND will be added
128 * to cert's return codes.
129 */
130 static CSSM_RETURN tpFindCrlForCert(
131 TPCertInfo &subject,
132 TPCrlInfo *&foundCrl, // RETURNED
133 TPCrlVerifyContext &vfyCtx)
134 {
135
136 TPCrlInfo *crl = NULL;
137 foundCrl = NULL;
138 CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags = 0;
139
140 if(vfyCtx.crlOpts) {
141 crlOptFlags = vfyCtx.crlOpts->CrlFlags;
142 }
143
144 /* Search inputCrls for a CRL for subject cert */
145 if(vfyCtx.inputCrls != NULL) {
146 crl = vfyCtx.inputCrls->findCrlForCert(subject);
147 if(crl && (crl->verifyWithContext(vfyCtx, &subject) == CSSM_OK)) {
148 foundCrl = crl;
149 crl->mFromWhere = CFW_InGroup;
150 tpCrlDebug(" ...CRL found in CrlGroup");
151 return CSSM_OK;
152 }
153 }
154
155 /* local process-wide cache */
156 crl = tpGlobalCrlCache().search(subject, vfyCtx);
157 if(crl) {
158 if(crl->verifyWithContext(vfyCtx, &subject) == CSSM_OK) {
159 foundCrl = crl;
160 crl->mFromWhere = CFW_LocalCache;
161 tpCrlDebug(" ...CRL found in local cache");
162 return CSSM_OK;
163 }
164 else {
165 tpGlobalCrlCache().remove(*crl);
166 }
167 }
168
169 /*
170 * Try DL/DB.
171 * Note tpDbFindIssuerCrl() returns a verified CRL.
172 */
173 crl = tpDbFindIssuerCrl(vfyCtx, *subject.issuerName(), subject);
174 if(crl) {
175 foundCrl = crl;
176 crl->mFromWhere = CFW_DlDb;
177 tpCrlDebug(" ...CRL found in DlDb");
178 return CSSM_OK;
179 }
180
181 /* Last resort: try net if enabled */
182 CSSM_RETURN crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
183 crl = NULL;
184 if(crlOptFlags & CSSM_TP_ACTION_FETCH_CRL_FROM_NET) {
185 crtn = tpFetchCrlFromNet(subject, vfyCtx, crl);
186 }
187
188 if(crtn) {
189 subject.addStatusCode(crtn);
190 tpCrlDebug(" ...tpFindCrlForCert: CRL not found");
191 return crtn;
192 }
193
194 /* got one from net - add to global cache */
195 assert(crl != NULL);
196 tpGlobalCrlCache().add(*crl);
197 crl->mFromWhere = CFW_Net;
198 tpCrlDebug(" ...CRL found from net");
199
200 #if WRITE_FETCHED_CRLS_TO_DB
201 /* and to DLDB if enabled */
202 if((vfyCtx.crlOpts != NULL) && (vfyCtx.crlOpts->crlStore != NULL)) {
203 crtn = tpDbStoreCrl(*crl, *vfyCtx.crlOpts->crlStore);
204 if(crtn) {
205 /* let's not let this affect the CRL verification...just log
206 * the per-cert error. */
207 subject.addStatusCode(crtn);
208 }
209 else {
210 tpCrlDebug(" ...CRL written to DB");
211 }
212 }
213 #endif /* WRITE_FETCHED_CRLS_TO_DB */
214
215 foundCrl = crl;
216 return CSSM_OK;
217 }
218
219 /*
220 * Dispose of a CRL obtained from tpFindCrlForCert().
221 */
222 static void tpDisposeCrl(
223 TPCrlInfo &crl,
224 TPCrlVerifyContext &vfyCtx)
225 {
226 switch(crl.mFromWhere) {
227 case CFW_Nowhere:
228 default:
229 assert(0);
230 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
231 case CFW_InGroup:
232 /* nothing to do, handled by TPCrlGroup */
233 return;
234 case CFW_DlDb:
235 /* cooked up specially for this call */
236 delete &crl;
237 return;
238 case CFW_LocalCache: // cache hit
239 case CFW_Net: // fetched from net & added to cache
240 tpGlobalCrlCache().release(crl);
241 return;
242 /* probably others */
243 }
244 }
245
246 /*
247 * Perform CRL verification on a cert group.
248 * The cert group has already passed basic issuer/subject and signature
249 * verification. The status of the incoming CRLs is completely unknown.
250 *
251 * FIXME - No mechanism to get CRLs from net with non-NULL verifyTime.
252 * How are we supposed to get the CRL which was valid at a specified
253 * time in the past?
254 */
255 CSSM_RETURN tpVerifyCertGroupWithCrls(
256 TPCertGroup &certGroup, // to be verified
257 TPCrlVerifyContext &vfyCtx)
258 {
259 CSSM_RETURN crtn;
260 CSSM_RETURN ourRtn = CSSM_OK;
261
262 switch(vfyCtx.policy) {
263 case kCrlNone:
264 return CSSM_OK;
265 case kCrlBasic:
266 break;
267 default:
268 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS;
269 }
270
271 assert(vfyCtx.clHand != 0);
272 tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup.numCerts());
273 CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags = 0;
274 if(vfyCtx.crlOpts != NULL) {
275 optFlags = vfyCtx.crlOpts->CrlFlags;
276 }
277
278 /* found & verified CRLs we need to release */
279 TPCrlGroup foundCrls(vfyCtx.alloc, TGO_Caller);
280
281 try {
282
283 unsigned certDex;
284 TPCrlInfo *crl = NULL;
285
286 /* main loop, verify each cert */
287 for(certDex=0; certDex<certGroup.numCerts(); certDex++) {
288 TPCertInfo *cert = certGroup.certAtIndex(certDex);
289
290 tpCrlDebug("...verifying %scert %u",
291 cert->isAnchor() ? "anchor " : "", cert->index());
292 if(cert->isSelfSigned()) {
293 /* CRL meaningless for a root cert */
294 continue;
295 }
296 crl = NULL;
297 do {
298 /* find a CRL for this cert by hook or crook */
299 crtn = tpFindCrlForCert(*cert, crl, vfyCtx);
300 if(crtn) {
301 if(!(optFlags & CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT)) {
302 /*
303 * This is the only place where "Best Attempt"
304 * tolerates an error
305 */
306 tpCrlDebug(" ...cert %u: no CRL; skipping",
307 cert->index());
308 crtn = CSSM_OK;
309 }
310 break;
311 }
312 /* Keep track; we'll release all when done. */
313 assert(crl != NULL);
314 foundCrls.appendCrl(*crl);
315
316 /* revoked? */
317 crtn = crl->isCertRevoked(*cert);
318 if(crtn) {
319 break;
320 }
321 tpCrlDebug(" ...cert %u VERIFIED by CRL", cert->index());
322 } while(0);
323
324 /* done processing one cert */
325 if(crtn) {
326 tpCrlDebug(" ...cert at index %u FAILED crl vfy",
327 cert->index());
328 if(ourRtn == CSSM_OK) {
329 ourRtn = crtn;
330 }
331 /* continue on to next cert */
332 } /* error on one cert */
333 } /* for each cert */
334 }
335 catch(const CssmError &cerr) {
336 if(ourRtn == CSSM_OK) {
337 ourRtn = cerr.cssmError();
338 }
339 }
340 /* other exceptions fatal */
341
342 /* release all found CRLs */
343 for(unsigned dex=0; dex<foundCrls.numCrls(); dex++) {
344 TPCrlInfo *crl = foundCrls.crlAtIndex(dex);
345 assert(crl != NULL);
346 tpDisposeCrl(*crl, vfyCtx);
347 }
348 return ourRtn;
349 }
350