X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/libsecurity_apple_x509_tp/lib/tpOcspVerify.cpp diff --git a/libsecurity_apple_x509_tp/lib/tpOcspVerify.cpp b/libsecurity_apple_x509_tp/lib/tpOcspVerify.cpp deleted file mode 100644 index d85a0434..00000000 --- a/libsecurity_apple_x509_tp/lib/tpOcspVerify.cpp +++ /dev/null @@ -1,870 +0,0 @@ -/* - * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The 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. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* - * tpOcspVerify.cpp - top-level OCSP verification - */ - -#include "tpOcspVerify.h" -#include "tpdebugging.h" -#include "ocspRequest.h" -#include "tpOcspCache.h" -#include "tpOcspCertVfy.h" -#include -#include "certGroupUtils.h" -#include -#include -#include -#include -#include -#include -#include "tpTime.h" - -#pragma mark ---- private routines ---- - -/* - * Get a smart CertID for specified cert and issuer - */ -static CSSM_RETURN tpOcspGetCertId( - TPCertInfo &subject, - TPCertInfo &issuer, - OCSPClientCertID *&certID) /* mallocd by coder and RETURNED */ -{ - 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; -} - -/* - * Examine cert, looking for AuthorityInfoAccess, with id-ad-ocsp URIs. Create - * an NULL_terminated array of CSSM_DATAs containing the URIs if found. - */ -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; dexnumAccessDescriptions; 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; - } - - /* got one */ - if(urls == NULL) { - urls = coder.mallocn(2); - } - else { - /* realloc */ - CSSM_DATA **oldUrls = urls; - urls = coder.mallocn(numUrls + 2); - for(unsigned i=0; i(); - 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; -} - -/* - * Create an SecAsn1OCSPDRequest for one cert. This consists of: - * - * -- cooking up an OCSPRequest if net fetch is enabled or a local responder - * is configured; - * -- extracting URLs from subject cert if net fetch is enabled; - * -- creating an SecAsn1OCSPDRequest, mallocd in coder's space; - */ -static SecAsn1OCSPDRequest *tpGenOcspdReq( - TPVerifyContext &vfyCtx, - SecNssCoder &coder, - TPCertInfo &subject, - TPCertInfo &issuer, - OCSPClientCertID &certId, - const CSSM_DATA **urls, // from subject's AuthorityInfoAccess - CSSM_DATA &nonce) // possibly mallocd in coder's space and RETURNED -{ - OCSPRequest *ocspReq = NULL; - SecAsn1OCSPDRequest *ocspdReq = NULL; // to return - OCSPClientCertID *certID = NULL; - CSSM_RETURN crtn; - bool deleteCertID = false; - - /* gather options or their defaults */ - 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; - - /* - * One degenerate case in case we can't really do anything. - * If no URI and no local responder, only proceed if cache is not disabled - * and we're requiring full OCSP per cert. - */ - 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; - } - - /* do we need an OCSP request? */ - if(!(optFlags & CSSM_TP_ACTION_OCSP_DISABLE_NET) || (localResponder != NULL)) { - try { - ocspReq = new OCSPRequest(subject, issuer, genNonce); - certID = ocspReq->certID(); - } - catch(...) { - /* not sure how this could even happen but that was a fair amount of code */ - tpErrorLog("tpGenOcspdReq: error cooking up OCSPRequest\n"); - if(ocspReq != NULL) { - delete ocspReq; - return NULL; - } - } - } - - /* certID needed one way or the other */ - if(certID == NULL) { - crtn = tpOcspGetCertId(subject, issuer, certID); - if(crtn) { - goto errOut; - } - deleteCertID = true; - } - - /* - * Create the SecAsn1OCSPDRequest. All fields optional. - */ - ocspdReq = coder.mallocn(); - memset(ocspdReq, 0, sizeof(*ocspdReq)); - if(optFlags & CSSM_TP_ACTION_OCSP_CACHE_WRITE_DISABLE) { - ocspdReq->cacheWriteDisable = coder.mallocn(); - ocspdReq->cacheWriteDisable->Data = coder.mallocn(); - ocspdReq->cacheWriteDisable->Data[0] = 0xff; - ocspdReq->cacheWriteDisable->Length = 1; - } - /* - * Note we're enforcing a not-so-obvious policy here: if nonce match is - * required, disk cache reads by ocspd are disabled. In-core cache is - * still enabled and hits in that cache do NOT require nonce matches. - */ - if((optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) || requireRespNonce) { - ocspdReq->cacheReadDisable = coder.mallocn(); - ocspdReq->cacheReadDisable->Data = coder.mallocn(); - ocspdReq->cacheReadDisable->Data[0] = 0xff; - ocspdReq->cacheReadDisable->Length = 1; - } - /* CertID, only required field */ - coder.allocCopyItem(*certID->encode(), ocspdReq->certID); - if(ocspReq != NULL) { - ocspdReq->ocspReq = coder.mallocn(); - coder.allocCopyItem(*ocspReq->encode(), *ocspdReq->ocspReq); - if(genNonce) { - /* nonce not available until encode() called */ - coder.allocCopyItem(*ocspReq->nonce(), nonce); - } - } - if(localResponder != NULL) { - ocspdReq->localRespURI = localResponder; - } - if(!(optFlags & CSSM_TP_ACTION_OCSP_DISABLE_NET)) { - ocspdReq->urls = const_cast(urls); - } - -errOut: - delete ocspReq; - if(deleteCertID) { - delete certID; - } - return ocspdReq; -} - -static bool revocationTimeAfterVerificationTime(CFAbsoluteTime revokedTime, CSSM_TIMESTRING verifyTimeStr) -{ - // Return true if the revocation time is after the specified verification time (i.e. "good") - // If verifyTime not specified, use now for the verifyTime - - CFAbsoluteTime verifyTime = 0; - - if (verifyTimeStr) - { - CFDateRef cfVerifyTime = NULL; // made with CFDateCreate - 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; -} - -/* - * Apply a verified OCSPSingleResponse to a TPCertInfo. - */ -static CSSM_RETURN tpApplySingleResp( - OCSPSingleResponse &singleResp, - TPCertInfo &cert, - unsigned dex, // for debug - CSSM_APPLE_TP_OCSP_OPT_FLAGS flags, // for OCSP_SUFFICIENT - CSSM_TIMESTRING verifyTime, // Check revocation at specific time - bool &processed) // set true iff CS_Good or CS_Revoked found -{ - 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) { - /* no more revocation checking necessary for this cert */ - 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: - /* FIXME - may want more detailed CRLReason-specific errors */ - crtn = CSSMERR_TP_CERT_REVOKED; - break; - } - if(!cert.addStatusCode(crtn)) { - crtn = CSSM_OK; - } - processed = true; - break; - case CS_Unknown: - /* not an error, no per-cert status, nothing here */ - 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; -} - -/* - * An exceptional case: synchronously flush the OCSPD cache and send a new - * resquest for just this one cert. - */ -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; - } - - /* Cook up an OCSPDRequests, one request, just for this */ - /* send it to ocsdp */ - /* munge reply into an OCSPRsponse and return it */ - 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; // owned by caller - CSSM_DATA **urls; // owner-managed array of URLs obtained from subject's - // AuthorityInfoAccess.id-ad-ocsp. - CSSM_DATA nonce; // owner-managed copy of this requests' nonce, if it - // has one - unsigned dex; // in inputCerts, for debug - 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) // to be verified -{ - 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; // in ocspdReqs - CSSM_DATA derOcspdRequests = {0, NULL}; - CSSM_DATA derOcspdReplies = {0, NULL}; - uint8 version = OCSPD_REQUEST_VERS; - unsigned numReplies; - unsigned numCerts = certGroup.numCerts(); - if(numCerts <= 1) { - /* Can't verify without an issuer; we're done */ - return CSSM_OK; - } - numCerts--; - - /* gather options or their defaults */ - 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; // in outgoing request - bool requireRespNonce = false; // in incoming response - 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) { - /* no can do */ - tpErrorLog("tpVerifyCertGroupWithOCSP: requireRespNonce, !genNonce\n"); - return CSSMERR_TP_INVALID_REQUEST_INPUTS; - } - - tpOcspDebug("tpVerifyCertGroupWithOCSP numCerts %u optFlags 0x%lx", - numCerts, (unsigned long)optFlags); - - /* - * create list of pendingRequests parallel to certGroup - */ - PendingRequest **pending = coder.mallocn(numCerts); - memset(pending, 0, (numCerts * sizeof(PendingRequest *))); - - for(unsigned dex=0; dextrustSettingsFound()) { - /* functionally equivalent to root - we're done */ - 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; - } - - /* - * We use the URLs in the subject cert's AuthorityInfoAccess extension - * for two things - mainly to get the URL(s) for actual OCSP transactions, - * but also for CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT processing. - * So, we do the per-cert processing to get them right now even if we - * wind up using a local responder or getting verification from cache. - */ - CSSM_DATA **urls = tpOcspUrlsFromCert(*subject, coder); - pending[dex] = new PendingRequest(*subject, *issuer, *certID, urls, dex); - } - /* subsequent errors to errOut: */ - - /* - * Create empty SecAsn1OCSPDRequests big enough for all certs - */ - ocspdReqs.requests = coder.mallocn(numCerts + 1); - memset(ocspdReqs.requests, 0, (numCerts + 1) * sizeof(SecAsn1OCSPDRequest *)); - ocspdReqs.version.Data = &version; - ocspdReqs.version.Length = 1; - - /* - * For each cert, either obtain a cached OCSPResponse, or create - * a request to get one. - * - * NOTE: in-core cache reads (via tpOcspCacheLookup() do NOT involve a - * nonce check, no matter what the app says. If nonce checking is required by the - * app, responses don't get added to cache if the nonce doesn't match, but once - * a response is validated and added to cache it's fair game for that task. - */ - for(unsigned dex=0; dexcertID, 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) { - /* definitely done with this cert one way or the other */ - if(crtn && (ourRtn == CSSM_OK)) { - /* real cert error: first error encountered; we'll keep going */ - ourRtn = crtn; - } - continue; - } - if(crtn) { - /* - * This indicates a bad cached response. Well that's kinda weird, let's - * just flush this out and try a normal transaction. - */ - tpOcspCacheFlush(pendReq->certID); - } - } - - /* - * Prepare a request for ocspd - */ - SecAsn1OCSPDRequest *ocspdReq = tpGenOcspdReq(vfyCtx, coder, - pendReq->subject, pendReq->issuer, pendReq->certID, - const_cast(pendReq->urls), - pendReq->nonce); - if(ocspdReq == NULL) { - /* tpGenOcspdReq determined there was no route to OCSP responder */ - tpOcspDebug("tpVerifyCertGroupWithOCSP: no OCSP possible for cert %u", dex); - continue; - } - ocspdReqs.requests[numRequests++] = ocspdReq; - } - if(numRequests == 0) { - /* no candidates for OCSP: almost done */ - goto postOcspd; - } - - /* ship requests off to ocspd, get ocspReplies back */ - 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 - /* But this is not necessarily fatal...update per-cert status and check - * caller requirements below */ - goto postOcspd; - } - memset(&ocspdReplies, 0, sizeof(ocspdReplies)); - prtn = coder.decodeItem(derOcspdReplies, kSecAsn1OCSPDRepliesTemplate, - &ocspdReplies); - /* we're done with this, mallocd in ocspdFetch() */ - vfyCtx.alloc.free(derOcspdReplies.Data); - if(prtn) { - /* - * This can happen when an OCSP server provides bad data...we cannot - * determine which cert is associated with this bad response; - * just flag it with the first one and proceed to the loop that - * handles CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT. - */ - 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; // maybe something better? - } - goto errOut; - } - - /* process each reply */ - numReplies = ocspdArraySize((const void **)ocspdReplies.replies); - for(unsigned dex=0; dexocspResp, TP_OCSP_CACHE_TTL); - } - catch(...) { - tpErrorLog("tpVerifyCertGroupWithOCSP: error decoding ocsp response\n"); - /* what the heck, keep going */ - continue; - } - - /* - * Find matching subject cert if possible (it's technically optional for - * verification of the response in some cases, e.g., local responder). - */ - PendingRequest *pendReq = NULL; // fully qualified - PendingRequest *reqWithIdMatch = NULL; // CertID match only, not nonce - for(unsigned pdex=0; pdexcertID.compareToExist(reply->certID)) { - reqWithIdMatch = pending[pdex]; - } - if(reqWithIdMatch == NULL) { - continue; - } - if(!genNonce) { - /* that's good enough */ - 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; - } - - /* - * In this case we keep going; if we never find a match, then we can - * use reqWithIdMatch if !requireRespNonce. - */ - 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) { - /* - * Nonce mismatch but caller thinks that's OK. Log it and proceed. - */ - 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; - } - - /* verify response and either throw out or add to local cache */ - respStat = tpVerifyOcspResp(vfyCtx, coder, issuer, *ocspResp, crtn); - switch(respStat) { - case ORS_Good: - break; - case ORS_Unknown: - /* not an error but we can't use it */ - if((crtn != CSSM_OK) && (pendReq != NULL)) { - /* pass this info back to caller here... */ - pendReq->subject.addStatusCode(crtn); - } - delete ocspResp; - continue; - case ORS_Bad: - delete ocspResp; - /* - * An exceptional case: synchronously flush the OCSPD cache and send a - * new request for just this one cert. - * FIXME: does this really buy us anything? A DOS attacker who managed - * to get this bogus response into our cache is likely to be able - * to do it again and again. - */ - 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)) { - /* pass this info back to caller here... */ - if(pendReq->subject.addStatusCode(crtn)) { - ourRtn = CSSMERR_APPLETP_OCSP_BAD_RESPONSE; - } - } - else { - ourRtn = CSSMERR_APPLETP_OCSP_BAD_RESPONSE; - } - goto errOut; - } - /* Voila! Recovery. Proceed. */ - tpOcspDebug("tpVerifyCertGroupWithOCSP: refetch for cert %u SUCCEEDED", - dex); - break; - } /* switch response status */ - - if(!cacheWriteDisable) { - tpOcspCacheAdd(reply->ocspResp, localResponder); - } - - /* attempt to apply to pendReq */ - 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; - } - } /* a reply which matches a pending request */ - - /* - * Done with this - note local OCSP response cache doesn't store this - * object; it stores an encoded copy. - */ - delete ocspResp; - } /* for each reply */ - -postOcspd: - - /* - * Now process each cert which hasn't had an OCSP response applied to it. - * This can happen if we get back replies which are not strictly in 1-1 sync with - * our requests but which nevertheless contain valid info for more than one - * cert each. - */ - for(unsigned dex=0; dexprocessed) { - continue; - } - OCSPSingleResponse *singleResp = NULL; - /* Note this corner case will not work if cache is disabled. */ - 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) { - /* Couldn't perform OCSP for this cert. */ - tpOcspDebug("tpVerifyCertGroupWithOCSP: OCSP_UNAVAILABLE for cert %u", dex); - bool required = false; - CSSM_RETURN responseStatus = CSSM_OK; - if(pendReq->subject.numStatusCodes() > 0) { - /* - * Check whether we got a response for this cert, but it was rejected - * due to being improperly signed. That should result in an actual - * error, even under Best Attempt processing. (10743149) - */ - if(pendReq->subject.hasStatusCode(CSSMERR_APPLETP_OCSP_BAD_RESPONSE)) { -// responseStatus = 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) { - /* no response available (as opposed to getting an invalid response) */ - pendReq->subject.addStatusCode(CSSMERR_APPLETP_OCSP_UNAVAILABLE); - } - if(optFlags & CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT) { - /* every cert needs OCSP */ - 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) { - /* this cert needs OCSP if it had an AIA extension with an OCSP URI */ - 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)) ) { - /* fatal error, but we keep on processing */ - if(ourRtn == CSSM_OK) { - ourRtn = (responseStatus != CSSM_OK) ? responseStatus : CSSMERR_APPLETP_OCSP_UNAVAILABLE; - } - } - } - } -errOut: - for(unsigned dex=0; dexcertID; - delete pendReq; - } - return ourRtn; -}