/* delete and remove from cache if refCount zero */ void TPCRLCache::release( TPCrlInfo &crl) { StLock<Mutex> _(mLock); assert(crl.mRefCount > 0); crl.mRefCount--; if(crl.mRefCount == 0) { tpCrlDebug("TPCRLCache release; deleting"); removeCrl(crl); delete &crl; } else { tpCrlDebug("TPCRLCache release; in use"); } }
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; }
/* bumps ref count - caller is going to be using the CRL */ void TPCRLCache::add( TPCrlInfo &crl) { StLock<Mutex> _(mLock); tpCrlDebug("TPCRLCache add"); crl.mRefCount++; appendCrl(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; }
/* * 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; }
/* * Fetch a CRL or a cert via a GeneralNames. * Shared by cert and CRL code to avoid duplicating GeneralNames traversal * code, despite the awkward interface for this function. */ static CSSM_RETURN tpFetchViaGeneralNames( const CE_GeneralNames *names, TPCertInfo &forCert, const CSSM_DATA *issuer, // optional, and only for CRLs TPVerifyContext *verifyContext, // only for CRLs CSSM_CL_HANDLE clHand, // only for certs CSSM_CSP_HANDLE cspHand, // only for certs const char *verifyTime, // optional /* exactly one must be non-NULL, that one is returned */ TPCertInfo **certInfo, TPCrlInfo **crlInfo) { assert(certInfo || crlInfo); assert(!certInfo || !crlInfo); CSSM_RETURN crtn; for(unsigned nameDex=0; nameDex<names->numNames; nameDex++) { CE_GeneralName *name = &names->generalName[nameDex]; switch(name->nameType) { case GNT_URI: if(name->name.Length < 5) { continue; } if(strncmp((char *)name->name.Data, "ldap:", 5) && strncmp((char *)name->name.Data, "http:", 5) && strncmp((char *)name->name.Data, "https:", 6)) { /* eventually handle other schemes here */ continue; } if(certInfo) { tpDebug(" fetching cert via net"); crtn = tpIssuerCertViaNet(name->name, clHand, cspHand, verifyTime, forCert, *certInfo); } else { tpDebug(" fetching CRL via net"); assert(verifyContext != NULL); crtn = tpCrlViaNet(name->name, issuer, *verifyContext, forCert, *crlInfo); } switch(crtn) { case CSSM_OK: case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles return crtn; default: break; } /* not found/no good; try again */ break; default: tpCrlDebug(" tpFetchCrlFromNet: unknown" "nameType (%u)", (unsigned)name->nameType); break; } /* switch nameType */ } /* for each name */ if(certInfo) { return CSSMERR_TP_CERTGROUP_INCOMPLETE; } else { return CSSMERR_APPLETP_CRL_NOT_FOUND; } }
void AppleTPSession::CrlVerify(CSSM_CL_HANDLE CLHandle, CSSM_CSP_HANDLE CSPHandle, const CSSM_ENCODED_CRL &CrlToBeVerified, const CSSM_CERTGROUP &SignerCertGroup, const CSSM_TP_VERIFY_CONTEXT *VerifyContext, CSSM_TP_VERIFY_CONTEXT_RESULT *RevokerVerifyResult) { /* verify input args */ if(RevokerVerifyResult != NULL) { /* not yet, but probably someday */ CssmError::throwMe(CSSMERR_TP_INVALID_REQUEST_INPUTS); } switch(CrlToBeVerified.CrlType) { case CSSM_CRL_TYPE_X_509v1: case CSSM_CRL_TYPE_X_509v2: break; default: CssmError::throwMe(CSSMERR_TP_INVALID_CRL_TYPE); } switch(CrlToBeVerified.CrlEncoding) { case CSSM_CRL_ENCODING_BER: case CSSM_CRL_ENCODING_DER: break; default: CssmError::throwMe(CSSMERR_TP_INVALID_CRL_ENCODING); } /* optional arguments */ CSSM_TIMESTRING cssmTimeStr = NULL; const CSSM_TP_CALLERAUTH_CONTEXT *cred = NULL; uint32 NumberOfAnchorCerts = 0; CSSM_DATA_PTR AnchorCerts = NULL; CSSM_DL_DB_LIST_PTR DBList = NULL; CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0; CSSM_APPLE_TP_ACTION_DATA *actionData = NULL; if(VerifyContext != NULL) { cred = VerifyContext->Cred; actionData = (CSSM_APPLE_TP_ACTION_DATA *)VerifyContext->ActionData.Data; if(actionData != NULL) { switch(actionData->Version) { case CSSM_APPLE_TP_ACTION_VERSION: if(VerifyContext->ActionData.Length != sizeof(CSSM_APPLE_TP_ACTION_DATA)) { CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA); } break; /* handle backwards versions here if we ever go * beyond version 0 */ default: CssmError::throwMe(CSSMERR_TP_INVALID_ACTION_DATA); } actionFlags = actionData->ActionFlags; } } if(cred != NULL) { cssmTimeStr = cred->VerifyTime; NumberOfAnchorCerts = cred->NumberOfAnchorCerts; AnchorCerts = cred->AnchorCerts; DBList = cred->DBList; } /* this must be parseable, throw immediately if not */ TPCrlInfo crlToVerify(CLHandle, CSPHandle, &CrlToBeVerified.CrlBlob, TIC_NoCopy, cssmTimeStr); /* Both required at the API but in fact may be empty */ TPCertGroup inCertGroup(SignerCertGroup, CLHandle, CSPHandle, *this, cssmTimeStr, // optional 'this' time false, // firstCertMustBeValid TGO_Group); TPCertGroup gatheredCerts(*this, TGO_Group); /* common CRL/OCSP verify parameters */ TPVerifyContext vfyCtx(*this, CLHandle, CSPHandle, cssmTimeStr, NumberOfAnchorCerts, AnchorCerts, &inCertGroup, NULL, // no CRLs, we're on our own gatheredCerts, DBList, kRevokeCrlBasic, actionFlags, NULL, // crlOpts NULL, // OCSP opts &CSSMOID_APPLE_TP_REVOCATION_CRL, NULL, // UT policyString 0, CSSM_KEYUSE_VERIFY); /* * We assert the doCrlVerify flag to ensure CRL verification * if intermediate certs which verifyWithContext() gathers to * verify this CRL. */ CSSM_RETURN crtn = crlToVerify.verifyWithContext(vfyCtx, NULL, true); if(crtn) { tpCrlDebug("CrlVerify failure"); CssmError::throwMe(crtn); } }