2 * Copyright (c) 2002-2012 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.
289 CSSM_RETURN
tpGetCrlStatusForCert(
291 const CSSM_DATA
&issuers
)
293 CSSM_DATA
*serialNumber
=NULL
;
294 CSSM_RETURN crtn
= subject
.fetchField(&CSSMOID_X509V1SerialNumber
, &serialNumber
);
295 if(crtn
|| !serialNumber
) {
296 return CSSMERR_TP_INTERNAL_ERROR
;
298 crtn
= ocspdCRLStatus(*serialNumber
, issuers
, subject
.issuerName(), NULL
);
299 subject
.freeField(&CSSMOID_X509V1SerialNumber
, serialNumber
);
304 * Perform CRL verification on a cert group.
305 * The cert group has already passed basic issuer/subject and signature
306 * verification. The status of the incoming CRLs is completely unknown.
308 * FIXME - No mechanism to get CRLs from net with non-NULL verifyTime.
309 * How are we supposed to get the CRL which was valid at a specified
312 CSSM_RETURN
tpVerifyCertGroupWithCrls(
313 TPVerifyContext
&vfyCtx
,
314 TPCertGroup
&certGroup
) // to be verified
317 CSSM_RETURN ourRtn
= CSSM_OK
;
319 assert(vfyCtx
.clHand
!= 0);
320 assert(vfyCtx
.policy
== kRevokeCrlBasic
);
321 tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup
.numCerts());
322 CSSM_DATA issuers
= { 0, NULL
};
323 CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags
= 0;
324 if(vfyCtx
.crlOpts
!= NULL
) {
325 optFlags
= vfyCtx
.crlOpts
->CrlFlags
;
328 /* found & verified CRLs we need to release */
329 TPCrlGroup
foundCrls(vfyCtx
.alloc
, TGO_Caller
);
334 TPCrlInfo
*crl
= NULL
;
336 /* get issuers as PEM-encoded data blob; we need to release */
337 certGroup
.encodeIssuers(issuers
);
339 /* main loop, verify each cert */
340 for(certDex
=0; certDex
<certGroup
.numCerts(); certDex
++) {
341 TPCertInfo
*cert
= certGroup
.certAtIndex(certDex
);
343 tpCrlDebug("...verifying %s cert %u",
344 cert
->isAnchor() ? "anchor " : "", cert
->index());
345 if(cert
->isSelfSigned() || cert
->trustSettingsFound()) {
346 /* CRL meaningless for a root or trusted cert */
349 if(cert
->revokeCheckComplete()) {
350 /* Another revocation policy claimed that this cert is good to go */
351 tpCrlDebug(" ...cert at index %u revokeCheckComplete; skipping",
357 /* first, see if we have CRL status available for this cert */
358 crtn
= tpGetCrlStatusForCert(*cert
, issuers
);
359 tpCrlDebug("...tpGetCrlStatusForCert: %u", crtn
);
360 if(crtn
== CSSM_OK
) {
361 tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u verified by local .crl\n",
363 cert
->revokeCheckGood(true);
364 if(optFlags
& CSSM_TP_ACTION_CRL_SUFFICIENT
) {
365 /* no more revocation checking necessary for this cert */
366 cert
->revokeCheckComplete(true);
370 if(crtn
== CSSMERR_TP_CERT_REVOKED
) {
371 tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u revoked in local .crl\n",
373 cert
->addStatusCode(crtn
);
376 if(crtn
== CSSMERR_APPLETP_NETWORK_FAILURE
) {
377 /* crl is being fetched from net, but we don't have it yet */
378 if((optFlags
& CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT
) &&
379 tpCertHasCrlDistPt(*cert
)) {
380 /* crl is required; we don't have it yet, so we fail */
381 tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
385 /* "Best Attempt" case, so give the cert a pass for now */
386 tpCrlDebug(" ...cert %u: no CRL; tolerating", cert
->index());
390 /* all other CRL status results: try to fetch the CRL */
392 /* find a CRL for this cert by hook or crook */
393 crtn
= tpFindCrlForCert(*cert
, crl
, vfyCtx
);
395 /* tpFindCrlForCert may have simply caused ocspd to start
396 * downloading a CRL asynchronously; depending on the speed
397 * of the network and the CRL size, this may return 0 bytes
398 * of data with a CSSMERR_APPLETP_NETWORK_FAILURE result.
399 * We won't know the actual revocation result until the
400 * next time we call tpGetCrlStatusForCert after the full
401 * CRL has been downloaded successfully.
403 if(optFlags
& CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT
) {
404 tpCrlDebug(" ...cert %u: REQUIRE_CRL_PER_CERT abort",
408 if((optFlags
& CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT
) &&
409 tpCertHasCrlDistPt(*cert
)) {
410 tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
415 * This is the only place where "Best Attempt" tolerates an error
417 tpCrlDebug(" ...cert %u: no CRL; tolerating", cert
->index());
423 /* Keep track; we'll release all when done. */
425 foundCrls
.appendCrl(*crl
);
428 crtn
= crl
->isCertRevoked(*cert
, vfyCtx
.verifyTime
);
432 tpCrlDebug(" ...cert %u VERIFIED by CRL", cert
->index());
433 cert
->revokeCheckGood(true);
434 if(optFlags
& CSSM_TP_ACTION_CRL_SUFFICIENT
) {
435 /* no more revocation checking necessary for this cert */
436 cert
->revokeCheckComplete(true);
440 /* done processing one cert */
442 tpCrlDebug(" ...cert at index %u FAILED crl vfy",
444 if(ourRtn
== CSSM_OK
) {
447 /* continue on to next cert */
448 } /* error on one cert */
449 } /* for each cert */
451 catch(const CssmError
&cerr
) {
452 if(ourRtn
== CSSM_OK
) {
456 /* other exceptions fatal */
458 /* release all found CRLs */
459 for(unsigned dex
=0; dex
<foundCrls
.numCrls(); dex
++) {
460 TPCrlInfo
*crl
= foundCrls
.crlAtIndex(dex
);
462 tpDisposeCrl(*crl
, vfyCtx
);
464 /* release issuers */