2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
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
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.
20 * tpCrlVerify.cpp - routines to verify CRLs and to verify certs against CRLs.
22 * Written 9/26/02 by Doug Mitchell.
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>
34 /* crlrefresh does this now */
35 #define WRITE_FETCHED_CRLS_TO_DB 0
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.
44 class TPCRLCache
: private TPCrlGroup
51 TPCrlVerifyContext
&vfyCtx
);
60 /* Protects ref count of all members of the cache */
64 TPCRLCache::TPCRLCache()
65 : TPCrlGroup(CssmAllocator::standard(), TGO_Group
)
70 TPCrlInfo
*TPCRLCache::search(
72 TPCrlVerifyContext
&vfyCtx
)
74 StLock
<Mutex
> _(mLock
);
75 TPCrlInfo
*crl
= findCrlForCert(cert
);
77 /* reevaluate validity */
78 crl
->calculateCurrent(vfyCtx
.verifyTime
);
84 /* bumps ref count - caller is going to be using the CRL */
88 StLock
<Mutex
> _(mLock
);
93 /* we delete on this one if --refCount == 0 */
94 void TPCRLCache::remove(
97 StLock
<Mutex
> _(mLock
);
100 assert(crl
.mRefCount
> 0);
102 if(crl
.mRefCount
== 0) {
106 /* in use, flag for future delete */
107 crl
.mToBeDeleted
= true;
111 /* only delete if refCount zero AND flagged for deletion */
112 void TPCRLCache::release(
115 StLock
<Mutex
> _(mLock
);
116 assert(crl
.mRefCount
> 0);
118 if(crl
.mToBeDeleted
& (crl
.mRefCount
== 0)) {
123 static ModuleNexus
<TPCRLCache
> tpGlobalCrlCache
;
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.
130 static CSSM_RETURN
tpFindCrlForCert(
132 TPCrlInfo
*&foundCrl
, // RETURNED
133 TPCrlVerifyContext
&vfyCtx
)
136 TPCrlInfo
*crl
= NULL
;
138 CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags
= 0;
141 crlOptFlags
= vfyCtx
.crlOpts
->CrlFlags
;
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
)) {
149 crl
->mFromWhere
= CFW_InGroup
;
150 tpCrlDebug(" ...CRL found in CrlGroup");
155 /* local process-wide cache */
156 crl
= tpGlobalCrlCache().search(subject
, vfyCtx
);
158 if(crl
->verifyWithContext(vfyCtx
, &subject
) == CSSM_OK
) {
160 crl
->mFromWhere
= CFW_LocalCache
;
161 tpCrlDebug(" ...CRL found in local cache");
165 tpGlobalCrlCache().remove(*crl
);
171 * Note tpDbFindIssuerCrl() returns a verified CRL.
173 crl
= tpDbFindIssuerCrl(vfyCtx
, *subject
.issuerName(), subject
);
176 crl
->mFromWhere
= CFW_DlDb
;
177 tpCrlDebug(" ...CRL found in DlDb");
181 /* Last resort: try net if enabled */
182 CSSM_RETURN crtn
= CSSMERR_APPLETP_CRL_NOT_FOUND
;
184 if(crlOptFlags
& CSSM_TP_ACTION_FETCH_CRL_FROM_NET
) {
185 crtn
= tpFetchCrlFromNet(subject
, vfyCtx
, crl
);
189 subject
.addStatusCode(crtn
);
190 tpCrlDebug(" ...tpFindCrlForCert: CRL not found");
194 /* got one from net - add to global cache */
196 tpGlobalCrlCache().add(*crl
);
197 crl
->mFromWhere
= CFW_Net
;
198 tpCrlDebug(" ...CRL found from net");
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
);
205 /* let's not let this affect the CRL verification...just log
206 * the per-cert error. */
207 subject
.addStatusCode(crtn
);
210 tpCrlDebug(" ...CRL written to DB");
213 #endif /* WRITE_FETCHED_CRLS_TO_DB */
220 * Dispose of a CRL obtained from tpFindCrlForCert().
222 static void tpDisposeCrl(
224 TPCrlVerifyContext
&vfyCtx
)
226 switch(crl
.mFromWhere
) {
230 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
232 /* nothing to do, handled by TPCrlGroup */
235 /* cooked up specially for this call */
238 case CFW_LocalCache
: // cache hit
239 case CFW_Net
: // fetched from net & added to cache
240 tpGlobalCrlCache().release(crl
);
242 /* probably others */
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.
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
255 CSSM_RETURN
tpVerifyCertGroupWithCrls(
256 TPCertGroup
&certGroup
, // to be verified
257 TPCrlVerifyContext
&vfyCtx
)
260 CSSM_RETURN ourRtn
= CSSM_OK
;
262 switch(vfyCtx
.policy
) {
268 return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
;
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
;
278 /* found & verified CRLs we need to release */
279 TPCrlGroup
foundCrls(vfyCtx
.alloc
, TGO_Caller
);
284 TPCrlInfo
*crl
= NULL
;
286 /* main loop, verify each cert */
287 for(certDex
=0; certDex
<certGroup
.numCerts(); certDex
++) {
288 TPCertInfo
*cert
= certGroup
.certAtIndex(certDex
);
290 tpCrlDebug("...verifying %scert %u",
291 cert
->isAnchor() ? "anchor " : "", cert
->index());
292 if(cert
->isSelfSigned()) {
293 /* CRL meaningless for a root cert */
298 /* find a CRL for this cert by hook or crook */
299 crtn
= tpFindCrlForCert(*cert
, crl
, vfyCtx
);
301 if(!(optFlags
& CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT
)) {
303 * This is the only place where "Best Attempt"
306 tpCrlDebug(" ...cert %u: no CRL; skipping",
312 /* Keep track; we'll release all when done. */
314 foundCrls
.appendCrl(*crl
);
317 crtn
= crl
->isCertRevoked(*cert
);
321 tpCrlDebug(" ...cert %u VERIFIED by CRL", cert
->index());
324 /* done processing one cert */
326 tpCrlDebug(" ...cert at index %u FAILED crl vfy",
328 if(ourRtn
== CSSM_OK
) {
331 /* continue on to next cert */
332 } /* error on one cert */
333 } /* for each cert */
335 catch(const CssmError
&cerr
) {
336 if(ourRtn
== CSSM_OK
) {
337 ourRtn
= cerr
.cssmError();
340 /* other exceptions fatal */
342 /* release all found CRLs */
343 for(unsigned dex
=0; dex
<foundCrls
.numCrls(); dex
++) {
344 TPCrlInfo
*crl
= foundCrls
.crlAtIndex(dex
);
346 tpDisposeCrl(*crl
, vfyCtx
);