#include "tpOcspVerify.h"
#include "tpdebugging.h"
#include "ocspRequest.h"
#include "tpOcspCache.h"
#include "tpOcspCertVfy.h"
#include <security_ocspd/ocspResponse.h>
#include "certGroupUtils.h"
#include <Security/certextensions.h>
#include <Security/oidsattr.h>
#include <Security/oidscert.h>
#include <security_asn1/SecNssCoder.h>
#include <security_ocspd/ocspdClient.h>
#include <security_ocspd/ocspdUtils.h>
#include "tpTime.h"
#pragma mark ---- private routines ----
static CSSM_RETURN tpOcspGetCertId(
TPCertInfo &subject,
TPCertInfo &issuer,
OCSPClientCertID *&certID)
{
CSSM_RETURN crtn;
CSSM_DATA_PTR issuerSubject = NULL;
CSSM_DATA_PTR issuerPubKeyData = NULL;
CSSM_KEY_PTR issuerPubKey;
CSSM_DATA_PTR subjectSerial = NULL;
crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &subjectSerial);
if(crtn) {
return crtn;
}
crtn = subject.fetchField(&CSSMOID_X509V1IssuerNameStd, &issuerSubject);
if(crtn) {
return crtn;
}
crtn = issuer.fetchField(&CSSMOID_CSSMKeyStruct, &issuerPubKeyData);
if(crtn) {
return crtn;
}
assert(issuerPubKeyData->Length == sizeof(CSSM_KEY));
issuerPubKey = (CSSM_KEY_PTR)issuerPubKeyData->Data;
certID = new OCSPClientCertID(*issuerSubject, issuerPubKey->KeyData, *subjectSerial);
subject.freeField(&CSSMOID_X509V1SerialNumber, subjectSerial);
issuer.freeField(&CSSMOID_X509V1IssuerNameStd, issuerSubject);
issuer.freeField(&CSSMOID_CSSMKeyStruct, issuerPubKeyData);
return CSSM_OK;
}
static CSSM_DATA **tpOcspUrlsFromCert(
TPCertInfo &subject,
SecNssCoder &coder)
{
CSSM_DATA_PTR extField = NULL;
CSSM_RETURN crtn;
crtn = subject.fetchField(&CSSMOID_AuthorityInfoAccess, &extField);
if(crtn) {
tpOcspDebug("tpOcspUrlsFromCert: no AIA extension");
return NULL;
}
if(extField->Length != sizeof(CSSM_X509_EXTENSION)) {
tpErrorLog("tpOcspUrlsFromCert: malformed CSSM_FIELD");
return NULL;
}
CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extField->Data;
if(cssmExt->format != CSSM_X509_DATAFORMAT_PARSED) {
tpErrorLog("tpOcspUrlsFromCert: malformed CSSM_X509_EXTENSION");
return NULL;
}
CE_AuthorityInfoAccess *aia =
(CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
CSSM_DATA **urls = NULL;
unsigned numUrls = 0;
for(unsigned dex=0; dex<aia->numAccessDescriptions; dex++) {
CE_AccessDescription *ad = &aia->accessDescriptions[dex];
if(!tpCompareCssmData(&ad->accessMethod, &CSSMOID_AD_OCSP)) {
continue;
}
CE_GeneralName *genName = &ad->accessLocation;
if(genName->nameType != GNT_URI) {
tpErrorLog("tpOcspUrlsFromCert: CSSMOID_AD_OCSP, but not type URI");
continue;
}
if(urls == NULL) {
urls = coder.mallocn<CSSM_DATA_PTR>(2);
}
else {
CSSM_DATA **oldUrls = urls;
urls = coder.mallocn<CSSM_DATA_PTR>(numUrls + 2);
for(unsigned i=0; i<numUrls; i++) {
urls[i] = oldUrls[i];
}
}
urls[numUrls] = coder.mallocn<CSSM_DATA>();
coder.allocCopyItem(genName->name, *urls[numUrls++]);
urls[numUrls] = NULL;
#ifndef NDEBUG
{
CSSM_DATA urlStr;
coder.allocItem(urlStr, genName->name.Length + 1);
memmove(urlStr.Data, genName->name.Data, genName->name.Length);
urlStr.Data[urlStr.Length-1] = '\0';
tpOcspDebug("tpOcspUrlsFromCert: found URI %s", urlStr.Data);
}
#endif
}
subject.freeField(&CSSMOID_AuthorityInfoAccess, extField);
return urls;
}
static SecAsn1OCSPDRequest *tpGenOcspdReq(
TPVerifyContext &vfyCtx,
SecNssCoder &coder,
TPCertInfo &subject,
TPCertInfo &issuer,
OCSPClientCertID &certId,
const CSSM_DATA **urls, CSSM_DATA &nonce) {
OCSPRequest *ocspReq = NULL;
SecAsn1OCSPDRequest *ocspdReq = NULL; OCSPClientCertID *certID = NULL;
CSSM_RETURN crtn;
bool deleteCertID = false;
CSSM_APPLE_TP_OCSP_OPT_FLAGS optFlags = 0;
const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts = vfyCtx.ocspOpts;
CSSM_DATA_PTR localResponder = NULL;
CSSM_DATA_PTR localResponderCert = NULL;
if(ocspOpts != NULL) {
optFlags = vfyCtx.ocspOpts->Flags;
localResponder = ocspOpts->LocalResponder;
localResponderCert = ocspOpts->LocalResponderCert;
}
bool genNonce = optFlags & CSSM_TP_OCSP_GEN_NONCE ? true : false;
bool requireRespNonce = optFlags & CSSM_TP_OCSP_REQUIRE_RESP_NONCE ? true : false;
if( ( (optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) ||
!(optFlags & CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT)
) &&
(localResponder == NULL) &&
(urls == NULL)) {
tpOcspDebug("tpGenOcspdReq: no route to OCSP; NULL return");
return NULL;
}
if(!(optFlags & CSSM_TP_ACTION_OCSP_DISABLE_NET) || (localResponder != NULL)) {
try {
ocspReq = new OCSPRequest(subject, issuer, genNonce);
certID = ocspReq->certID();
}
catch(...) {
tpErrorLog("tpGenOcspdReq: error cooking up OCSPRequest\n");
if(ocspReq != NULL) {
delete ocspReq;
return NULL;
}
}
}
if(certID == NULL) {
crtn = tpOcspGetCertId(subject, issuer, certID);
if(crtn) {
goto errOut;
}
deleteCertID = true;
}
ocspdReq = coder.mallocn<SecAsn1OCSPDRequest>();
memset(ocspdReq, 0, sizeof(*ocspdReq));
if(optFlags & CSSM_TP_ACTION_OCSP_CACHE_WRITE_DISABLE) {
ocspdReq->cacheWriteDisable = coder.mallocn<CSSM_DATA>();
ocspdReq->cacheWriteDisable->Data = coder.mallocn<uint8>();
ocspdReq->cacheWriteDisable->Data[0] = 0xff;
ocspdReq->cacheWriteDisable->Length = 1;
}
if((optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) || requireRespNonce) {
ocspdReq->cacheReadDisable = coder.mallocn<CSSM_DATA>();
ocspdReq->cacheReadDisable->Data = coder.mallocn<uint8>();
ocspdReq->cacheReadDisable->Data[0] = 0xff;
ocspdReq->cacheReadDisable->Length = 1;
}
coder.allocCopyItem(*certID->encode(), ocspdReq->certID);
if(ocspReq != NULL) {
ocspdReq->ocspReq = coder.mallocn<CSSM_DATA>();
coder.allocCopyItem(*ocspReq->encode(), *ocspdReq->ocspReq);
if(genNonce) {
coder.allocCopyItem(*ocspReq->nonce(), nonce);
}
}
if(localResponder != NULL) {
ocspdReq->localRespURI = localResponder;
}
if(!(optFlags & CSSM_TP_ACTION_OCSP_DISABLE_NET)) {
ocspdReq->urls = const_cast<CSSM_DATA **>(urls);
}
errOut:
delete ocspReq;
if(deleteCertID) {
delete certID;
}
return ocspdReq;
}
static bool revocationTimeAfterVerificationTime(CFAbsoluteTime revokedTime, CSSM_TIMESTRING verifyTimeStr)
{
CFAbsoluteTime verifyTime = 0;
if (verifyTimeStr)
{
CFDateRef cfVerifyTime = NULL; int rtn = timeStringToCfDate((char *)verifyTimeStr, (unsigned)strlen(verifyTimeStr), &cfVerifyTime);
if (!rtn)
if (cfVerifyTime)
{
verifyTime = CFDateGetAbsoluteTime(cfVerifyTime);
CFRelease(cfVerifyTime);
}
}
if (verifyTime == 0)
verifyTime = CFAbsoluteTimeGetCurrent();
return verifyTime < revokedTime;
}
static CSSM_RETURN tpApplySingleResp(
OCSPSingleResponse &singleResp,
TPCertInfo &cert,
unsigned dex, CSSM_APPLE_TP_OCSP_OPT_FLAGS flags, CSSM_TIMESTRING verifyTime, bool &processed) {
SecAsn1OCSPCertStatusTag certStatus = singleResp.certStatus();
CSSM_RETURN crtn = CSSM_OK;
if ((certStatus == CS_Revoked) &&
revocationTimeAfterVerificationTime(singleResp.revokedTime(), verifyTime))
{
tpOcspDebug("tpApplySingleResp: CS_Revoked for cert %u, but revoked after verification time", dex);
certStatus = CS_Good;
}
switch(certStatus) {
case CS_Good:
tpOcspDebug("tpApplySingleResp: CS_Good for cert %u", dex);
cert.revokeCheckGood(true);
if(flags & CSSM_TP_ACTION_OCSP_SUFFICIENT) {
cert.revokeCheckComplete(true);
}
processed = true;
break;
case CS_Revoked:
tpOcspDebug("tpApplySingleResp: CS_Revoked for cert %u", dex);
switch(singleResp.crlReason()) {
case CE_CR_CertificateHold:
crtn = CSSMERR_TP_CERT_SUSPENDED;
break;
default:
crtn = CSSMERR_TP_CERT_REVOKED;
break;
}
if(!cert.addStatusCode(crtn)) {
crtn = CSSM_OK;
}
processed = true;
break;
case CS_Unknown:
tpOcspDebug("tpApplySingleResp: CS_Unknown for cert %u", dex);
break;
default:
tpOcspDebug("tpApplySingleResp: BAD certStatus (%d) for cert %u",
(int)certStatus, dex);
if(cert.addStatusCode(CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED)) {
crtn = CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED;
}
break;
}
return crtn;
}
static OCSPResponse *tpOcspFlushAndReFetch(
TPVerifyContext &vfyCtx,
SecNssCoder &coder,
TPCertInfo &subject,
TPCertInfo &issuer,
OCSPClientCertID &certID)
{
const CSSM_DATA *derCertID = certID.encode();
CSSM_RETURN crtn;
crtn = ocspdCacheFlush(*derCertID);
if(crtn) {
#ifndef NDEBUG
cssmPerror("ocspdCacheFlush", crtn);
#endif
return NULL;
}
tpOcspDebug("pOcspFlushAndReFetch: Code on demand");
return NULL;
}
class PendingRequest
{
public:
PendingRequest(
TPCertInfo &subject,
TPCertInfo &issuer,
OCSPClientCertID &cid,
CSSM_DATA **u,
unsigned dex);
~PendingRequest() {}
TPCertInfo &subject;
TPCertInfo &issuer;
OCSPClientCertID &certID; CSSM_DATA **urls; CSSM_DATA nonce; unsigned dex; bool processed;
};
PendingRequest::PendingRequest(
TPCertInfo &subj,
TPCertInfo &iss,
OCSPClientCertID &cid,
CSSM_DATA **u,
unsigned dx)
: subject(subj), issuer(iss), certID(cid),
urls(u), dex(dx), processed(false)
{
nonce.Data = NULL;
nonce.Length = 0;
}
#pragma mark ---- public API ----
CSSM_RETURN tpVerifyCertGroupWithOCSP(
TPVerifyContext &vfyCtx,
TPCertGroup &certGroup) {
assert(vfyCtx.clHand != 0);
assert(vfyCtx.policy == kRevokeOcsp);
CSSM_RETURN ourRtn = CSSM_OK;
OcspRespStatus respStat;
SecNssCoder coder;
CSSM_RETURN crtn;
SecAsn1OCSPDRequests ocspdReqs;
SecAsn1OCSPReplies ocspdReplies;
unsigned numRequests = 0; CSSM_DATA derOcspdRequests = {0, NULL};
CSSM_DATA derOcspdReplies = {0, NULL};
uint8 version = OCSPD_REQUEST_VERS;
unsigned numReplies;
unsigned numCerts = certGroup.numCerts();
if(numCerts <= 1) {
return CSSM_OK;
}
numCerts--;
CSSM_APPLE_TP_OCSP_OPT_FLAGS optFlags = 0;
const CSSM_APPLE_TP_OCSP_OPTIONS *ocspOpts = vfyCtx.ocspOpts;
CSSM_DATA_PTR localResponder = NULL;
CSSM_DATA_PTR localResponderCert = NULL;
bool cacheReadDisable = false;
bool cacheWriteDisable = false;
bool genNonce = false; bool requireRespNonce = false; PRErrorCode prtn;
if(ocspOpts != NULL) {
optFlags = vfyCtx.ocspOpts->Flags;
localResponder = ocspOpts->LocalResponder;
localResponderCert = ocspOpts->LocalResponderCert;
}
if(optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) {
cacheReadDisable = true;
}
if(optFlags & CSSM_TP_ACTION_OCSP_CACHE_WRITE_DISABLE) {
cacheWriteDisable = true;
}
if(optFlags & CSSM_TP_OCSP_GEN_NONCE) {
genNonce = true;
}
if(optFlags & CSSM_TP_OCSP_REQUIRE_RESP_NONCE) {
requireRespNonce = true;
}
if(requireRespNonce & !genNonce) {
tpErrorLog("tpVerifyCertGroupWithOCSP: requireRespNonce, !genNonce\n");
return CSSMERR_TP_INVALID_REQUEST_INPUTS;
}
tpOcspDebug("tpVerifyCertGroupWithOCSP numCerts %u optFlags 0x%lx",
numCerts, (unsigned long)optFlags);
PendingRequest **pending = coder.mallocn<PendingRequest *>(numCerts);
memset(pending, 0, (numCerts * sizeof(PendingRequest *)));
for(unsigned dex=0; dex<numCerts; dex++) {
OCSPClientCertID *certID = NULL;
TPCertInfo *subject = certGroup.certAtIndex(dex);
if(subject->trustSettingsFound()) {
tpOcspDebug("...tpVerifyCertGroupWithOCSP: terminate per user trust at %u",
(unsigned)dex);
goto postOcspd;
}
TPCertInfo *issuer = certGroup.certAtIndex(dex + 1);
crtn = tpOcspGetCertId(*subject, *issuer, certID);
if(crtn) {
tpErrorLog("tpVerifyCertGroupWithOCSP: error extracting cert fields; "
"aborting\n");
goto errOut;
}
CSSM_DATA **urls = tpOcspUrlsFromCert(*subject, coder);
pending[dex] = new PendingRequest(*subject, *issuer, *certID, urls, dex);
}
ocspdReqs.requests = coder.mallocn<SecAsn1OCSPDRequest *>(numCerts + 1);
memset(ocspdReqs.requests, 0, (numCerts + 1) * sizeof(SecAsn1OCSPDRequest *));
ocspdReqs.version.Data = &version;
ocspdReqs.version.Length = 1;
for(unsigned dex=0; dex<numCerts; dex++) {
PendingRequest *pendReq = pending[dex];
OCSPSingleResponse *singleResp = NULL;
if(!cacheReadDisable) {
singleResp = tpOcspCacheLookup(pendReq->certID, localResponder);
}
if(singleResp) {
tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache hit (1) dex %u",
(unsigned)dex);
crtn = tpApplySingleResp(*singleResp, pendReq->subject, dex, optFlags,
vfyCtx.verifyTime, pendReq->processed);
delete singleResp;
if(pendReq->processed) {
if(crtn && (ourRtn == CSSM_OK)) {
ourRtn = crtn;
}
continue;
}
if(crtn) {
tpOcspCacheFlush(pendReq->certID);
}
}
SecAsn1OCSPDRequest *ocspdReq = tpGenOcspdReq(vfyCtx, coder,
pendReq->subject, pendReq->issuer, pendReq->certID,
const_cast<const CSSM_DATA **>(pendReq->urls),
pendReq->nonce);
if(ocspdReq == NULL) {
tpOcspDebug("tpVerifyCertGroupWithOCSP: no OCSP possible for cert %u", dex);
continue;
}
ocspdReqs.requests[numRequests++] = ocspdReq;
}
if(numRequests == 0) {
goto postOcspd;
}
if(coder.encodeItem(&ocspdReqs, kSecAsn1OCSPDRequestsTemplate, derOcspdRequests)) {
tpErrorLog("tpVerifyCertGroupWithOCSP: error encoding ocspdReqs\n");
ourRtn = CSSMERR_TP_INTERNAL_ERROR;
goto errOut;
}
crtn = ocspdFetch(vfyCtx.alloc, derOcspdRequests, derOcspdReplies);
if(crtn) {
tpErrorLog("tpVerifyCertGroupWithOCSP: error during ocspd RPC\n");
#ifndef NDEBUG
cssmPerror("ocspdFetch", crtn);
#endif
goto postOcspd;
}
memset(&ocspdReplies, 0, sizeof(ocspdReplies));
prtn = coder.decodeItem(derOcspdReplies, kSecAsn1OCSPDRepliesTemplate,
&ocspdReplies);
vfyCtx.alloc.free(derOcspdReplies.Data);
if(prtn) {
tpErrorLog("tpVerifyCertGroupWithOCSP: error decoding ocspd reply\n");
pending[0]->subject.addStatusCode(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
goto postOcspd;
}
if((ocspdReplies.version.Length != 1) ||
(ocspdReplies.version.Data[0] != OCSPD_REPLY_VERS)) {
tpErrorLog("tpVerifyCertGroupWithOCSP: ocspd reply version mismatch\n");
if(ourRtn == CSSM_OK) {
ourRtn = CSSMERR_TP_INTERNAL_ERROR; }
goto errOut;
}
numReplies = ocspdArraySize((const void **)ocspdReplies.replies);
for(unsigned dex=0; dex<numReplies; dex++) {
SecAsn1OCSPDReply *reply = ocspdReplies.replies[dex];
OCSPResponse *ocspResp = NULL;
try {
ocspResp = new OCSPResponse(reply->ocspResp, TP_OCSP_CACHE_TTL);
}
catch(...) {
tpErrorLog("tpVerifyCertGroupWithOCSP: error decoding ocsp response\n");
continue;
}
PendingRequest *pendReq = NULL; PendingRequest *reqWithIdMatch = NULL; for(unsigned pdex=0; pdex<numCerts; pdex++) {
if((pending[pdex])->certID.compareToExist(reply->certID)) {
reqWithIdMatch = pending[pdex];
}
if(reqWithIdMatch == NULL) {
continue;
}
if(!genNonce) {
pendReq = reqWithIdMatch;
tpOcspDebug("OCSP processs reply: CertID match, no nonce");
break;
}
if(tpCompareCssmData(&reqWithIdMatch->nonce, ocspResp->nonce())) {
tpOcspDebug("OCSP processs reply: nonce MATCH");
pendReq = reqWithIdMatch;
break;
}
tpOcspDebug("OCSP processs reply: certID match, nonce MISMATCH");
}
if(pendReq == NULL) {
if(requireRespNonce) {
tpOcspDebug("OCSP processs reply: tossing out response due to "
"requireRespNonce");
delete ocspResp;
if(ourRtn == CSSM_OK) {
ourRtn = CSSMERR_APPLETP_OCSP_NONCE_MISMATCH;
}
continue;
}
if(reqWithIdMatch != NULL) {
assert(genNonce);
tpOcspDebug("OCSP processs reply: using bad nonce due to !requireRespNonce");
pendReq = reqWithIdMatch;
pendReq->subject.addStatusCode(CSSMERR_APPLETP_OCSP_NONCE_MISMATCH);
}
}
TPCertInfo *issuer = NULL;
if(pendReq != NULL) {
issuer = &pendReq->issuer;
}
respStat = tpVerifyOcspResp(vfyCtx, coder, issuer, *ocspResp, crtn);
switch(respStat) {
case ORS_Good:
break;
case ORS_Unknown:
if((crtn != CSSM_OK) && (pendReq != NULL)) {
pendReq->subject.addStatusCode(crtn);
}
delete ocspResp;
continue;
case ORS_Bad:
delete ocspResp;
tpOcspDebug("tpVerifyCertGroupWithOCSP: flush/refetch for cert %u", dex);
ocspResp = tpOcspFlushAndReFetch(vfyCtx, coder, pendReq->subject,
pendReq->issuer, pendReq->certID);
if(ocspResp == NULL) {
tpErrorLog("tpVerifyCertGroupWithOCSP: error on flush/refetch\n");
ourRtn = CSSMERR_APPLETP_OCSP_BAD_RESPONSE;
goto errOut;
}
respStat = tpVerifyOcspResp(vfyCtx, coder, issuer, *ocspResp, crtn);
if(respStat != ORS_Good) {
tpErrorLog("tpVerifyCertGroupWithOCSP: verify error after "
"flush/refetch\n");
if((crtn != CSSM_OK) && (pendReq != NULL)) {
if(pendReq->subject.addStatusCode(crtn)) {
ourRtn = CSSMERR_APPLETP_OCSP_BAD_RESPONSE;
}
}
else {
ourRtn = CSSMERR_APPLETP_OCSP_BAD_RESPONSE;
}
goto errOut;
}
tpOcspDebug("tpVerifyCertGroupWithOCSP: refetch for cert %u SUCCEEDED",
dex);
break;
}
if(!cacheWriteDisable) {
tpOcspCacheAdd(reply->ocspResp, localResponder);
}
if(pendReq != NULL) {
OCSPSingleResponse *singleResp =
ocspResp->singleResponseFor(pendReq->certID);
if(singleResp) {
crtn = tpApplySingleResp(*singleResp, pendReq->subject, pendReq->dex,
optFlags, vfyCtx.verifyTime, pendReq->processed);
if(crtn && (ourRtn == CSSM_OK)) {
ourRtn = crtn;
}
delete singleResp;
}
}
delete ocspResp;
}
postOcspd:
for(unsigned dex=0; dex<numCerts; dex++) {
PendingRequest *pendReq = pending[dex];
if(pendReq == NULL) {
tpOcspDebug("...tpVerifyCertGroupWithOCSP: NULL pendReq dex %u",
(unsigned)dex);
break;
}
if(pendReq->processed) {
continue;
}
OCSPSingleResponse *singleResp = NULL;
if(!cacheReadDisable) {
singleResp = tpOcspCacheLookup(pendReq->certID, localResponder);
}
if(singleResp) {
tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache (2) hit dex %u",
(unsigned)dex);
crtn = tpApplySingleResp(*singleResp, pendReq->subject, dex, optFlags,
vfyCtx.verifyTime, pendReq->processed);
if(crtn) {
if(ourRtn == CSSM_OK) {
ourRtn = crtn;
}
}
delete singleResp;
}
if(!pendReq->processed) {
tpOcspDebug("tpVerifyCertGroupWithOCSP: OCSP_UNAVAILABLE for cert %u", dex);
bool required = false;
CSSM_RETURN responseStatus = CSSM_OK;
if(pendReq->subject.numStatusCodes() > 0) {
if(pendReq->subject.hasStatusCode(CSSMERR_APPLETP_OCSP_BAD_RESPONSE)) {
} else if(pendReq->subject.hasStatusCode(CSSMERR_APPLETP_OCSP_SIG_ERROR)) {
responseStatus = CSSMERR_APPLETP_OCSP_SIG_ERROR;
} else if(pendReq->subject.hasStatusCode(CSSMERR_APPLETP_OCSP_NO_SIGNER)) {
responseStatus = CSSMERR_APPLETP_OCSP_NO_SIGNER;
}
}
if(responseStatus == CSSM_OK) {
pendReq->subject.addStatusCode(CSSMERR_APPLETP_OCSP_UNAVAILABLE);
}
if(optFlags & CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT) {
tpOcspDebug("tpVerifyCertGroupWithOCSP: response required for all certs, missing for cert %u", dex);
required = true;
}
else if(optFlags & CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT) {
if(pendReq->urls) {
tpOcspDebug("tpVerifyCertGroupWithOCSP: OCSP URI present but no valid response for cert %u", dex);
required = true;
}
}
if( (required && pendReq->subject.isStatusFatal(CSSMERR_APPLETP_OCSP_UNAVAILABLE)) ||
(responseStatus != CSSM_OK && pendReq->subject.isStatusFatal(responseStatus)) ) {
if(ourRtn == CSSM_OK) {
ourRtn = (responseStatus != CSSM_OK) ? responseStatus : CSSMERR_APPLETP_OCSP_UNAVAILABLE;
}
}
}
}
errOut:
for(unsigned dex=0; dex<numCerts; dex++) {
PendingRequest *pendReq = pending[dex];
if(pendReq == NULL) {
break;
}
delete &pendReq->certID;
delete pendReq;
}
return ourRtn;
}