/* * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ /* * tpCrlVerify.cpp - routines to verify CRLs and to verify certs against CRLs. * * Written 9/26/02 by Doug Mitchell. */ #include "tpCrlVerify.h" #include "TPCertInfo.h" #include "TPCrlInfo.h" #include "tpdebugging.h" #include "TPNetwork.h" #include "TPDatabase.h" #include #include /* crlrefresh does this now */ #define WRITE_FETCHED_CRLS_TO_DB 0 /* * For now, a process-wide memory resident CRL cache. * We are responsible for deleting the CRLs which get added to this * cache. Currently the only time we add a CRL to this cache is * when we fetch one from the net. We ref count CRLs in this cache * to allow multi-threaded access. */ class TPCRLCache : private TPCrlGroup { public: TPCRLCache(); ~TPCRLCache() { } TPCrlInfo *search( TPCertInfo &cert, TPCrlVerifyContext &vfyCtx); void add( TPCrlInfo &crl); void remove( TPCrlInfo &crl); void release( TPCrlInfo &crl); private: /* Protects ref count of all members of the cache */ Mutex mLock; }; TPCRLCache::TPCRLCache() : TPCrlGroup(CssmAllocator::standard(), TGO_Group) { } TPCrlInfo *TPCRLCache::search( TPCertInfo &cert, TPCrlVerifyContext &vfyCtx) { StLock _(mLock); TPCrlInfo *crl = findCrlForCert(cert); if(crl) { /* reevaluate validity */ crl->calculateCurrent(vfyCtx.verifyTime); crl->mRefCount++; } return crl; } /* bumps ref count - caller is going to be using the CRL */ void TPCRLCache::add( TPCrlInfo &crl) { StLock _(mLock); crl.mRefCount++; appendCrl(crl); } /* we delete on this one if --refCount == 0 */ void TPCRLCache::remove( TPCrlInfo &crl) { StLock _(mLock); removeCrl(crl); release(crl); assert(crl.mRefCount > 0); crl.mRefCount--; if(crl.mRefCount == 0) { delete &crl; } else { /* in use, flag for future delete */ crl.mToBeDeleted = true; } } /* only delete if refCount zero AND flagged for deletion */ void TPCRLCache::release( TPCrlInfo &crl) { StLock _(mLock); assert(crl.mRefCount > 0); crl.mRefCount--; if(crl.mToBeDeleted & (crl.mRefCount == 0)) { delete &crl; } } static ModuleNexus tpGlobalCrlCache; /* * Find CRL for specified cert. Only returns a fully verified CRL. * Cert-specific errors such as CSSMERR_APPLETP_CRL_NOT_FOUND will be added * to cert's return codes. */ static CSSM_RETURN tpFindCrlForCert( TPCertInfo &subject, TPCrlInfo *&foundCrl, // RETURNED TPCrlVerifyContext &vfyCtx) { TPCrlInfo *crl = NULL; foundCrl = NULL; CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags = 0; if(vfyCtx.crlOpts) { crlOptFlags = vfyCtx.crlOpts->CrlFlags; } /* Search inputCrls for a CRL for subject cert */ if(vfyCtx.inputCrls != NULL) { crl = vfyCtx.inputCrls->findCrlForCert(subject); if(crl && (crl->verifyWithContext(vfyCtx, &subject) == CSSM_OK)) { foundCrl = crl; crl->mFromWhere = CFW_InGroup; tpCrlDebug(" ...CRL found in CrlGroup"); return CSSM_OK; } } /* local process-wide cache */ crl = tpGlobalCrlCache().search(subject, vfyCtx); if(crl) { if(crl->verifyWithContext(vfyCtx, &subject) == CSSM_OK) { foundCrl = crl; crl->mFromWhere = CFW_LocalCache; tpCrlDebug(" ...CRL found in local cache"); return CSSM_OK; } else { tpGlobalCrlCache().remove(*crl); } } /* * Try DL/DB. * Note tpDbFindIssuerCrl() returns a verified CRL. */ crl = tpDbFindIssuerCrl(vfyCtx, *subject.issuerName(), subject); if(crl) { foundCrl = crl; crl->mFromWhere = CFW_DlDb; tpCrlDebug(" ...CRL found in DlDb"); return CSSM_OK; } /* Last resort: try net if enabled */ CSSM_RETURN crtn = CSSMERR_APPLETP_CRL_NOT_FOUND; crl = NULL; if(crlOptFlags & CSSM_TP_ACTION_FETCH_CRL_FROM_NET) { crtn = tpFetchCrlFromNet(subject, vfyCtx, crl); } if(crtn) { subject.addStatusCode(crtn); tpCrlDebug(" ...tpFindCrlForCert: CRL not found"); return crtn; } /* got one from net - add to global cache */ assert(crl != NULL); tpGlobalCrlCache().add(*crl); crl->mFromWhere = CFW_Net; tpCrlDebug(" ...CRL found from net"); #if WRITE_FETCHED_CRLS_TO_DB /* and to DLDB if enabled */ if((vfyCtx.crlOpts != NULL) && (vfyCtx.crlOpts->crlStore != NULL)) { crtn = tpDbStoreCrl(*crl, *vfyCtx.crlOpts->crlStore); if(crtn) { /* let's not let this affect the CRL verification...just log * the per-cert error. */ subject.addStatusCode(crtn); } else { tpCrlDebug(" ...CRL written to DB"); } } #endif /* WRITE_FETCHED_CRLS_TO_DB */ foundCrl = crl; return CSSM_OK; } /* * Dispose of a CRL obtained from tpFindCrlForCert(). */ static void tpDisposeCrl( TPCrlInfo &crl, TPCrlVerifyContext &vfyCtx) { switch(crl.mFromWhere) { case CFW_Nowhere: default: assert(0); CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); case CFW_InGroup: /* nothing to do, handled by TPCrlGroup */ return; case CFW_DlDb: /* cooked up specially for this call */ delete &crl; return; case CFW_LocalCache: // cache hit case CFW_Net: // fetched from net & added to cache tpGlobalCrlCache().release(crl); return; /* probably others */ } } /* * Perform CRL verification on a cert group. * The cert group has already passed basic issuer/subject and signature * verification. The status of the incoming CRLs is completely unknown. * * FIXME - No mechanism to get CRLs from net with non-NULL verifyTime. * How are we supposed to get the CRL which was valid at a specified * time in the past? */ CSSM_RETURN tpVerifyCertGroupWithCrls( TPCertGroup &certGroup, // to be verified TPCrlVerifyContext &vfyCtx) { CSSM_RETURN crtn; CSSM_RETURN ourRtn = CSSM_OK; switch(vfyCtx.policy) { case kCrlNone: return CSSM_OK; case kCrlBasic: break; default: return CSSMERR_TP_INVALID_POLICY_IDENTIFIERS; } assert(vfyCtx.clHand != 0); tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup.numCerts()); CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags = 0; if(vfyCtx.crlOpts != NULL) { optFlags = vfyCtx.crlOpts->CrlFlags; } /* found & verified CRLs we need to release */ TPCrlGroup foundCrls(vfyCtx.alloc, TGO_Caller); try { unsigned certDex; TPCrlInfo *crl = NULL; /* main loop, verify each cert */ for(certDex=0; certDexisAnchor() ? "anchor " : "", cert->index()); if(cert->isSelfSigned()) { /* CRL meaningless for a root cert */ continue; } crl = NULL; do { /* find a CRL for this cert by hook or crook */ crtn = tpFindCrlForCert(*cert, crl, vfyCtx); if(crtn) { if(!(optFlags & CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT)) { /* * This is the only place where "Best Attempt" * tolerates an error */ tpCrlDebug(" ...cert %u: no CRL; skipping", cert->index()); crtn = CSSM_OK; } break; } /* Keep track; we'll release all when done. */ assert(crl != NULL); foundCrls.appendCrl(*crl); /* revoked? */ crtn = crl->isCertRevoked(*cert); if(crtn) { break; } tpCrlDebug(" ...cert %u VERIFIED by CRL", cert->index()); } while(0); /* done processing one cert */ if(crtn) { tpCrlDebug(" ...cert at index %u FAILED crl vfy", cert->index()); if(ourRtn == CSSM_OK) { ourRtn = crtn; } /* continue on to next cert */ } /* error on one cert */ } /* for each cert */ } catch(const CssmError &cerr) { if(ourRtn == CSSM_OK) { ourRtn = cerr.cssmError(); } } /* other exceptions fatal */ /* release all found CRLs */ for(unsigned dex=0; dex