2 * Copyright (c) 2002-2011 Apple 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.
23 #include "tpCrlVerify.h"
24 #include "TPCertInfo.h"
25 #include "TPCrlInfo.h"
26 #include "tpOcspVerify.h"
27 #include "tpdebugging.h"
28 #include "TPNetwork.h"
29 #include "TPDatabase.h"
30 #include <CommonCrypto/CommonDigest.h>
31 #include <Security/oidscert.h>
32 #include <security_ocspd/ocspdClient.h>
33 #include <security_utilities/globalizer.h>
34 #include <security_utilities/threading.h>
35 #include <security_cdsa_utilities/cssmerrors.h>
38 /* general purpose, switch to policy-specific code based on TPVerifyContext.policy */
39 CSSM_RETURN
tpRevocationPolicyVerify(
40 TPVerifyContext
&tpVerifyContext
,
41 TPCertGroup
&certGroup
)
43 switch(tpVerifyContext
.policy
) {
47 return tpVerifyCertGroupWithCrls(tpVerifyContext
, certGroup
);
49 return tpVerifyCertGroupWithOCSP(tpVerifyContext
, certGroup
);
52 return CSSMERR_TP_INTERNAL_ERROR
;
57 * For now, a process-wide memory resident CRL cache.
58 * We are responsible for deleting the CRLs which get added to this
59 * cache. Currently the only time we add a CRL to this cache is
60 * when we fetch one from the net. We ref count CRLs in this cache
61 * to allow multi-threaded access.
62 * Entries do not persist past the tpVerifyCertGroupWithCrls() in
63 * which they were created unless another thread in the same
64 * process snags a refcount (also from tpVerifyCertGroupWithCrls()).
65 * I.e. when cert verification is complete the cache will be empty.
66 * This is a change from Tiger and previous. CRLs get pretty big,
67 * up to a megabyte or so, and it's just not worth it to keep those
68 * around in memory. (OCSP responses, which are much smaller than
69 * CRLs, are indeed cached in memory. See tpOcspCache.cpp.)
71 class TPCRLCache
: private TPCrlGroup
78 TPVerifyContext
&vfyCtx
);
87 /* Protects ref count of all members of the cache */
91 TPCRLCache::TPCRLCache()
92 : TPCrlGroup(Allocator::standard(), TGO_Group
)
97 TPCrlInfo
*TPCRLCache::search(
99 TPVerifyContext
&vfyCtx
)
101 StLock
<Mutex
> _(mLock
);
102 TPCrlInfo
*crl
= findCrlForCert(cert
);
104 /* reevaluate validity */
105 crl
->calculateCurrent(vfyCtx
.verifyTime
);
107 tpCrlDebug("TPCRLCache hit");
110 tpCrlDebug("TPCRLCache miss");
115 /* bumps ref count - caller is going to be using the CRL */
116 void TPCRLCache::add(
119 StLock
<Mutex
> _(mLock
);
120 tpCrlDebug("TPCRLCache add");
125 /* delete and remove from cache if refCount zero */
126 void TPCRLCache::release(
129 StLock
<Mutex
> _(mLock
);
130 assert(crl
.mRefCount
> 0);
132 if(crl
.mRefCount
== 0) {
133 tpCrlDebug("TPCRLCache release; deleting");
138 tpCrlDebug("TPCRLCache release; in use");
142 static ModuleNexus
<TPCRLCache
> tpGlobalCrlCache
;
145 * Find CRL for specified cert. Only returns a fully verified CRL.
146 * Cert-specific errors such as CSSMERR_APPLETP_CRL_NOT_FOUND will be added
147 * to cert's return codes.
149 static CSSM_RETURN
tpFindCrlForCert(
151 TPCrlInfo
*&foundCrl
, // RETURNED
152 TPVerifyContext
&vfyCtx
)
155 tpCrlDebug("tpFindCrlForCert top");
156 TPCrlInfo
*crl
= NULL
;
158 CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags
= 0;
161 crlOptFlags
= vfyCtx
.crlOpts
->CrlFlags
;
164 /* Search inputCrls for a CRL for subject cert */
165 if(vfyCtx
.inputCrls
!= NULL
) {
166 crl
= vfyCtx
.inputCrls
->findCrlForCert(subject
);
167 if(crl
&& (crl
->verifyWithContextNow(vfyCtx
, &subject
) == CSSM_OK
)) {
169 crl
->mFromWhere
= CFW_InGroup
;
170 tpCrlDebug(" ...CRL found in CrlGroup");
175 /* local process-wide cache */
176 crl
= tpGlobalCrlCache().search(subject
, vfyCtx
);
178 tpCrlDebug("...tpFindCrlForCert found CRL in cache, calling verifyWithContext");
179 if(crl
->verifyWithContextNow(vfyCtx
, &subject
) == CSSM_OK
) {
181 crl
->mFromWhere
= CFW_LocalCache
;
182 tpCrlDebug(" ...CRL found in local cache");
186 tpGlobalCrlCache().release(*crl
);
192 * Note tpDbFindIssuerCrl() returns a verified CRL.
194 crl
= tpDbFindIssuerCrl(vfyCtx
, *subject
.issuerName(), subject
);
197 crl
->mFromWhere
= CFW_DlDb
;
198 tpCrlDebug(" ...CRL found in DlDb");
202 /* Last resort: try net if enabled */
203 CSSM_RETURN crtn
= CSSMERR_APPLETP_CRL_NOT_FOUND
;
205 if(crlOptFlags
& CSSM_TP_ACTION_FETCH_CRL_FROM_NET
) {
206 crtn
= tpFetchCrlFromNet(subject
, vfyCtx
, crl
);
210 tpCrlDebug(" ...tpFindCrlForCert: CRL not found");
211 if(subject
.addStatusCode(crtn
)) {
219 /* got one from net - add to global cache */
221 tpGlobalCrlCache().add(*crl
);
222 crl
->mFromWhere
= CFW_Net
;
223 tpCrlDebug(" ...CRL found from net");
230 * Dispose of a CRL obtained from tpFindCrlForCert().
232 static void tpDisposeCrl(
234 TPVerifyContext
&vfyCtx
)
236 switch(crl
.mFromWhere
) {
240 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
242 /* nothing to do, handled by TPCrlGroup */
245 /* cooked up specially for this call */
248 case CFW_LocalCache
: // cache hit
249 case CFW_Net
: // fetched from net & added to cache
250 tpGlobalCrlCache().release(crl
);
252 /* probably others */
257 * Does this cert have a CrlDistributionPoints extension? We don't parse it, we
258 * just tell the caller whether or not it has one.
260 static bool tpCertHasCrlDistPt(
263 CSSM_DATA_PTR fieldValue
;
264 CSSM_RETURN crtn
= cert
.fetchField(&CSSMOID_CrlDistributionPoints
, &fieldValue
);
269 cert
.freeField(&CSSMOID_CrlDistributionPoints
, fieldValue
);
275 * Get current CRL status for a certificate and its issuers.
279 * CSSM_OK (we have a valid CRL; certificate is not revoked)
280 * CSSMERR_TP_CERT_REVOKED (we have a valid CRL; certificate is revoked)
281 * CSSMERR_APPLETP_NETWORK_FAILURE (CRL not available, download in progress)
282 * CSSMERR_APPLETP_CRL_NOT_FOUND (CRL not available, and not being fetched)
283 * CSSMERR_TP_INTERNAL_ERROR (unexpected error)
285 * Note that ocspdCRLStatus does NOT wait for the CRL to be downloaded before
286 * returning, nor does it initiate a CRL download.
288 CSSM_RETURN
tpGetCrlStatusForCert(
290 const CSSM_DATA
&issuers
)
292 CSSM_DATA
*serialNumber
=NULL
;
293 CSSM_RETURN crtn
= subject
.fetchField(&CSSMOID_X509V1SerialNumber
, &serialNumber
);
294 if(crtn
|| !serialNumber
) {
295 return CSSMERR_TP_INTERNAL_ERROR
;
297 crtn
= ocspdCRLStatus(*serialNumber
, issuers
, subject
.issuerName(), NULL
);
298 subject
.freeField(&CSSMOID_X509V1SerialNumber
, serialNumber
);
303 * Perform CRL verification on a cert group.
304 * The cert group has already passed basic issuer/subject and signature
305 * verification. The status of the incoming CRLs is completely unknown.
307 * FIXME - No mechanism to get CRLs from net with non-NULL verifyTime.
308 * How are we supposed to get the CRL which was valid at a specified
311 CSSM_RETURN
tpVerifyCertGroupWithCrls(
312 TPVerifyContext
&vfyCtx
,
313 TPCertGroup
&certGroup
) // to be verified
316 CSSM_RETURN ourRtn
= CSSM_OK
;
318 assert(vfyCtx
.clHand
!= 0);
319 assert(vfyCtx
.policy
== kRevokeCrlBasic
);
320 tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup
.numCerts());
321 CSSM_DATA issuers
= { 0, NULL
};
322 CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags
= 0;
323 if(vfyCtx
.crlOpts
!= NULL
) {
324 optFlags
= vfyCtx
.crlOpts
->CrlFlags
;
327 /* found & verified CRLs we need to release */
328 TPCrlGroup
foundCrls(vfyCtx
.alloc
, TGO_Caller
);
333 TPCrlInfo
*crl
= NULL
;
335 /* get issuers as PEM-encoded data blob; we need to release */
336 certGroup
.encodeIssuers(issuers
);
338 /* main loop, verify each cert */
339 for(certDex
=0; certDex
<certGroup
.numCerts(); certDex
++) {
340 TPCertInfo
*cert
= certGroup
.certAtIndex(certDex
);
342 tpCrlDebug("...verifying %s cert %u",
343 cert
->isAnchor() ? "anchor " : "", cert
->index());
344 if(cert
->isSelfSigned() || cert
->trustSettingsFound()) {
345 /* CRL meaningless for a root or trusted cert */
348 if(cert
->revokeCheckComplete()) {
349 /* Another revocation policy claimed that this cert is good to go */
350 tpCrlDebug(" ...cert at index %u revokeCheckComplete; skipping",
356 /* first, see if we have CRL status available for this cert */
357 crtn
= tpGetCrlStatusForCert(*cert
, issuers
);
358 tpCrlDebug("...tpGetCrlStatusForCert: %u", crtn
);
359 if(crtn
== CSSM_OK
) {
360 tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u verified by local .crl\n",
362 cert
->revokeCheckGood(true);
363 if(optFlags
& CSSM_TP_ACTION_CRL_SUFFICIENT
) {
364 /* no more revocation checking necessary for this cert */
365 cert
->revokeCheckComplete(true);
369 if(crtn
== CSSMERR_TP_CERT_REVOKED
) {
370 tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u revoked in local .crl\n",
372 cert
->addStatusCode(crtn
);
375 if(crtn
== CSSMERR_APPLETP_NETWORK_FAILURE
) {
376 /* crl is being fetched from net, but we don't have it yet */
377 if((optFlags
& CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT
) &&
378 tpCertHasCrlDistPt(*cert
)) {
379 /* crl is required; we don't have it yet, so we fail */
380 tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
384 /* "Best Attempt" case, so give the cert a pass for now */
385 tpCrlDebug(" ...cert %u: no CRL; tolerating", cert
->index());
389 /* all other CRL status results: try to fetch the CRL */
391 /* find a CRL for this cert by hook or crook */
392 crtn
= tpFindCrlForCert(*cert
, crl
, vfyCtx
);
394 /* tpFindCrlForCert may have simply caused ocspd to start
395 * downloading a CRL asynchronously; depending on the speed
396 * of the network and the CRL size, this may return 0 bytes
397 * of data with a CSSMERR_APPLETP_NETWORK_FAILURE result.
398 * We won't know the actual revocation result until the
399 * next time we call tpGetCrlStatusForCert after the full
400 * CRL has been downloaded successfully.
402 if(optFlags
& CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT
) {
403 tpCrlDebug(" ...cert %u: REQUIRE_CRL_PER_CERT abort",
407 if((optFlags
& CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT
) &&
408 tpCertHasCrlDistPt(*cert
)) {
409 tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
414 * This is the only place where "Best Attempt" tolerates an error
416 tpCrlDebug(" ...cert %u: no CRL; tolerating", cert
->index());
422 /* Keep track; we'll release all when done. */
424 foundCrls
.appendCrl(*crl
);
427 crtn
= crl
->isCertRevoked(*cert
, vfyCtx
.verifyTime
);
431 tpCrlDebug(" ...cert %u VERIFIED by CRL", cert
->index());
432 cert
->revokeCheckGood(true);
433 if(optFlags
& CSSM_TP_ACTION_CRL_SUFFICIENT
) {
434 /* no more revocation checking necessary for this cert */
435 cert
->revokeCheckComplete(true);
439 /* done processing one cert */
441 tpCrlDebug(" ...cert at index %u FAILED crl vfy",
443 if(ourRtn
== CSSM_OK
) {
446 /* continue on to next cert */
447 } /* error on one cert */
448 } /* for each cert */
450 catch(const CssmError
&cerr
) {
451 if(ourRtn
== CSSM_OK
) {
455 /* other exceptions fatal */
457 /* release all found CRLs */
458 for(unsigned dex
=0; dex
<foundCrls
.numCrls(); dex
++) {
459 TPCrlInfo
*crl
= foundCrls
.crlAtIndex(dex
);
461 tpDisposeCrl(*crl
, vfyCtx
);
463 /* release issuers */