TPCrlInfo *TPCRLCache::search( TPCertInfo &cert, TPVerifyContext &vfyCtx) { StLock<Mutex> _(mLock); TPCrlInfo *crl = findCrlForCert(cert); if(crl) { /* reevaluate validity */ crl->calculateCurrent(vfyCtx.verifyTime); crl->mRefCount++; tpCrlDebug("TPCRLCache hit"); } else { tpCrlDebug("TPCRLCache miss"); } return crl; }
/* * 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( TPVerifyContext &vfyCtx, TPCertGroup &certGroup) // to be verified { CSSM_RETURN crtn; CSSM_RETURN ourRtn = CSSM_OK; assert(vfyCtx.clHand != 0); assert(vfyCtx.policy == kRevokeCrlBasic); tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup.numCerts()); CSSM_DATA issuers = { 0, NULL }; 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; /* get issuers as PEM-encoded data blob; we need to release */ certGroup.encodeIssuers(issuers); /* main loop, verify each cert */ for(certDex=0; certDex<certGroup.numCerts(); certDex++) { TPCertInfo *cert = certGroup.certAtIndex(certDex); tpCrlDebug("...verifying %s cert %u", cert->isAnchor() ? "anchor " : "", cert->index()); if(cert->isSelfSigned() || cert->trustSettingsFound()) { /* CRL meaningless for a root or trusted cert */ continue; } if(cert->revokeCheckComplete()) { /* Another revocation policy claimed that this cert is good to go */ tpCrlDebug(" ...cert at index %u revokeCheckComplete; skipping", cert->index()); continue; } crl = NULL; do { /* first, see if we have CRL status available for this cert */ crtn = tpGetCrlStatusForCert(*cert, issuers); tpCrlDebug("...tpGetCrlStatusForCert: %u", crtn); if(crtn == CSSM_OK) { tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u verified by local .crl\n", cert->index()); cert->revokeCheckGood(true); if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) { /* no more revocation checking necessary for this cert */ cert->revokeCheckComplete(true); } break; } if(crtn == CSSMERR_TP_CERT_REVOKED) { tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u revoked in local .crl\n", cert->index()); cert->addStatusCode(crtn); break; } if(crtn == CSSMERR_APPLETP_NETWORK_FAILURE) { /* crl is being fetched from net, but we don't have it yet */ if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) && tpCertHasCrlDistPt(*cert)) { /* crl is required; we don't have it yet, so we fail */ tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort", cert->index()); break; } /* "Best Attempt" case, so give the cert a pass for now */ tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index()); crtn = CSSM_OK; break; } /* all other CRL status results: try to fetch the CRL */ /* find a CRL for this cert by hook or crook */ crtn = tpFindCrlForCert(*cert, crl, vfyCtx); if(crtn) { /* tpFindCrlForCert may have simply caused ocspd to start * downloading a CRL asynchronously; depending on the speed * of the network and the CRL size, this may return 0 bytes * of data with a CSSMERR_APPLETP_NETWORK_FAILURE result. * We won't know the actual revocation result until the * next time we call tpGetCrlStatusForCert after the full * CRL has been downloaded successfully. */ if(optFlags & CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT) { tpCrlDebug(" ...cert %u: REQUIRE_CRL_PER_CERT abort", cert->index()); break; } if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) && tpCertHasCrlDistPt(*cert)) { tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort", cert->index()); break; } /* * This is the only place where "Best Attempt" tolerates an error */ tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index()); crtn = CSSM_OK; assert(crl == NULL); break; } /* Keep track; we'll release all when done. */ assert(crl != NULL); foundCrls.appendCrl(*crl); /* revoked? */ crtn = crl->isCertRevoked(*cert, vfyCtx.verifyTime); if(crtn) { break; } tpCrlDebug(" ...cert %u VERIFIED by CRL", cert->index()); cert->revokeCheckGood(true); if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) { /* no more revocation checking necessary for this cert */ cert->revokeCheckComplete(true); } } 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.error; } } /* other exceptions fatal */ /* release all found CRLs */ for(unsigned dex=0; dex<foundCrls.numCrls(); dex++) { TPCrlInfo *crl = foundCrls.crlAtIndex(dex); assert(crl != NULL); tpDisposeCrl(*crl, vfyCtx); } /* release issuers */ if(issuers.Data) { free(issuers.Data); } return ourRtn; }
static CSSM_RETURN tpCrlViaNet( const CSSM_DATA &url, const CSSM_DATA *issuer, // optional, only if cert and CRL have same issuer TPVerifyContext &vfyCtx, TPCertInfo &forCert, // for verifyWithContext TPCrlInfo *&rtnCrl) { TPCrlInfo *crl = NULL; CSSM_DATA crlData; CSSM_RETURN crtn; Allocator &alloc = Allocator::standard(); char cssmTime[CSSM_TIME_STRLEN+1]; rtnCrl = NULL; /* verifyTime: we want a CRL that's valid right now. */ { StLock<Mutex> _(tpTimeLock()); timeAtNowPlus(0, TIME_CSSM, cssmTime); } crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData); if(crtn) { return crtn; } try { crl = new TPCrlInfo(vfyCtx.clHand, vfyCtx.cspHand, &crlData, TIC_CopyData, NULL); // verifyTime = Now } catch(...) { alloc.free(crlData.Data); /* * There is a slight possibility of recovering from this error. In case * the CRL came from disk cache, flush the cache and try to get the CRL * from the net. */ tpDebug(" bad CRL; flushing from cache and retrying"); ocspdCRLFlush(url); crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData); if(crtn == CSSM_OK) { try { crl = new TPCrlInfo(vfyCtx.clHand, vfyCtx.cspHand, &crlData, TIC_CopyData, NULL); tpDebug(" RECOVERY: good CRL obtained from net"); } catch(...) { alloc.free(crlData.Data); tpDebug(" bad CRL; recovery FAILED (1)"); return CSSMERR_APPLETP_CRL_NOT_FOUND; } } else { /* it was in cache but we can't find it on the net */ tpDebug(" bad CRL; recovery FAILED (2)"); return CSSMERR_APPLETP_CRL_NOT_FOUND; } } alloc.free(crlData.Data); /* * Full CRL verify. * The verify time in the TPVerifyContext is the time at which various * entities (CRL and its own cert chain) are to be verified; that's * NULL for "right now". The current vfyCtx.verifyTime is the time at * which the cert's revocation status to be determined; this call to * verifyWithContextNow() doesn't do that. */ crtn = crl->verifyWithContextNow(vfyCtx, &forCert); if(crtn == CSSM_OK) { crl->uri(url); } else { delete crl; crl = NULL; } rtnCrl = crl; return crtn; }
/* * 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 TPVerifyContext &vfyCtx) { tpCrlDebug("tpFindCrlForCert top"); 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->verifyWithContextNow(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) { tpCrlDebug("...tpFindCrlForCert found CRL in cache, calling verifyWithContext"); if(crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK) { foundCrl = crl; crl->mFromWhere = CFW_LocalCache; tpCrlDebug(" ...CRL found in local cache"); return CSSM_OK; } else { tpGlobalCrlCache().release(*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) { tpCrlDebug(" ...tpFindCrlForCert: CRL not found"); if(subject.addStatusCode(crtn)) { return crtn; } else { return CSSM_OK; } } /* got one from net - add to global cache */ assert(crl != NULL); tpGlobalCrlCache().add(*crl); crl->mFromWhere = CFW_Net; tpCrlDebug(" ...CRL found from net"); foundCrl = crl; return CSSM_OK; }
/* * Search a list of DBs for a CRL from the specified issuer and (optional) * TPVerifyContext.verifyTime. * Just a boolean return - we found it, or not. If we did, we return a * TPCrlInfo which has been verified with the specified TPVerifyContext. */ TPCrlInfo *tpDbFindIssuerCrl( TPVerifyContext &vfyCtx, const CSSM_DATA &issuer, TPCertInfo &forCert) { StLock<Mutex> _(SecTrustKeychainsGetMutex()); uint32 dbDex; CSSM_HANDLE resultHand; CSSM_DATA crl; CSSM_DL_DB_HANDLE dlDb; CSSM_DB_UNIQUE_RECORD_PTR record; TPCrlInfo *issuerCrl = NULL; CSSM_DL_DB_LIST_PTR dbList = vfyCtx.dbList; CSSM_RETURN crtn; if(dbList == NULL) { return NULL; } for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) { dlDb = dbList->DLDBHandle[dbDex]; crl.Data = NULL; crl.Length = 0; record = tpCrlLookup(dlDb, &issuer, vfyCtx.verifyTime, &resultHand, &crl); /* remember we have to: * -- abort this query regardless, and * -- free the CSSM_DATA crl regardless, and * -- free the unique record if we don't use it * (by placing it in issuerCert)... */ if(record != NULL) { /* Found one */ assert(crl.Data != NULL); issuerCrl = new TPCrlInfo(vfyCtx.clHand, vfyCtx.cspHand, &crl, TIC_CopyData, vfyCtx.verifyTime); /* we're done with raw CRL data */ /* FIXME this assumes that vfyCtx.alloc is the same as the * allocator associated with DlDB...OK? */ tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE); crl.Data = NULL; crl.Length = 0; /* and we're done with the record */ CSSM_DL_FreeUniqueRecord(dlDb, record); /* Does it verify with specified context? */ crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert); if(crtn) { delete issuerCrl; issuerCrl = NULL; /* * Verify fail. Continue searching this DB. Break on * finding the holy grail or no more records found. */ for(;;) { crl.Data = NULL; crl.Length = 0; crtn = CSSM_DL_DataGetNext(dlDb, resultHand, NULL, // no attrs &crl, &record); if(crtn) { /* no more, done with this DB */ assert(crl.Data == NULL); break; } assert(crl.Data != NULL); /* found one - is it any good? */ issuerCrl = new TPCrlInfo(vfyCtx.clHand, vfyCtx.cspHand, &crl, TIC_CopyData, vfyCtx.verifyTime); /* we're done with raw CRL data */ /* FIXME this assumes that vfyCtx.alloc is the same as the * allocator associated with DlDB...OK? */ tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE); crl.Data = NULL; crl.Length = 0; CSSM_DL_FreeUniqueRecord(dlDb, record); crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert); if(crtn == CSSM_OK) { /* yes! */ break; } delete issuerCrl; issuerCrl = NULL; } /* searching subsequent records */ } /* verify fail */ /* else success! */ if(issuerCrl != NULL) { /* successful return */ CSSM_DL_DataAbortQuery(dlDb, resultHand); tpDebug("tpDbFindIssuerCrl: found CRL record %p", record); return issuerCrl; } } /* tpCrlLookup, i.e., CSSM_DL_DataGetFirst, succeeded */ else { assert(crl.Data == NULL); } /* in any case, abort the query for this db */ CSSM_DL_DataAbortQuery(dlDb, resultHand); } /* main loop searching dbList */ /* issuer not found */ return NULL; }