2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * tpOcspVerify.cpp - top-level OCSP verification
28 #include "tpOcspVerify.h"
29 #include "tpdebugging.h"
30 #include "ocspRequest.h"
31 #include "tpOcspCache.h"
32 #include "tpOcspCertVfy.h"
33 #include <security_ocspd/ocspResponse.h>
34 #include "certGroupUtils.h"
35 #include <Security/certextensions.h>
36 #include <Security/oidsattr.h>
37 #include <Security/oidscert.h>
38 #include <security_asn1/SecNssCoder.h>
39 #include <security_ocspd/ocspdClient.h>
40 #include <security_ocspd/ocspdUtils.h>
43 #pragma mark ---- private routines ----
46 * Get a smart CertID for specified cert and issuer
48 static CSSM_RETURN
tpOcspGetCertId(
51 OCSPClientCertID
*&certID
) /* mallocd by coder and RETURNED */
54 CSSM_DATA_PTR issuerSubject
= NULL
;
55 CSSM_DATA_PTR issuerPubKeyData
= NULL
;
56 CSSM_KEY_PTR issuerPubKey
;
57 CSSM_DATA_PTR subjectSerial
= NULL
;
59 crtn
= subject
.fetchField(&CSSMOID_X509V1SerialNumber
, &subjectSerial
);
63 crtn
= subject
.fetchField(&CSSMOID_X509V1IssuerNameStd
, &issuerSubject
);
67 crtn
= issuer
.fetchField(&CSSMOID_CSSMKeyStruct
, &issuerPubKeyData
);
71 assert(issuerPubKeyData
->Length
== sizeof(CSSM_KEY
));
72 issuerPubKey
= (CSSM_KEY_PTR
)issuerPubKeyData
->Data
;
73 certID
= new OCSPClientCertID(*issuerSubject
, issuerPubKey
->KeyData
, *subjectSerial
);
75 subject
.freeField(&CSSMOID_X509V1SerialNumber
, subjectSerial
);
76 issuer
.freeField(&CSSMOID_X509V1IssuerNameStd
, issuerSubject
);
77 issuer
.freeField(&CSSMOID_CSSMKeyStruct
, issuerPubKeyData
);
82 * Examine cert, looking for AuthorityInfoAccess, with id-ad-ocsp URIs. Create
83 * an NULL_terminated array of CSSM_DATAs containing the URIs if found.
85 static CSSM_DATA
**tpOcspUrlsFromCert(
89 CSSM_DATA_PTR extField
= NULL
;
92 crtn
= subject
.fetchField(&CSSMOID_AuthorityInfoAccess
, &extField
);
94 tpOcspDebug("tpOcspUrlsFromCert: no AIA extension");
97 if(extField
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
98 tpErrorLog("tpOcspUrlsFromCert: malformed CSSM_FIELD");
101 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)extField
->Data
;
102 if(cssmExt
->format
!= CSSM_X509_DATAFORMAT_PARSED
) {
103 tpErrorLog("tpOcspUrlsFromCert: malformed CSSM_X509_EXTENSION");
107 CE_AuthorityInfoAccess
*aia
=
108 (CE_AuthorityInfoAccess
*)cssmExt
->value
.parsedValue
;
109 CSSM_DATA
**urls
= NULL
;
110 unsigned numUrls
= 0;
111 for(unsigned dex
=0; dex
<aia
->numAccessDescriptions
; dex
++) {
112 CE_AccessDescription
*ad
= &aia
->accessDescriptions
[dex
];
113 if(!tpCompareCssmData(&ad
->accessMethod
, &CSSMOID_AD_OCSP
)) {
116 CE_GeneralName
*genName
= &ad
->accessLocation
;
117 if(genName
->nameType
!= GNT_URI
) {
118 tpErrorLog("tpOcspUrlsFromCert: CSSMOID_AD_OCSP, but not type URI");
124 urls
= coder
.mallocn
<CSSM_DATA_PTR
>(2);
128 CSSM_DATA
**oldUrls
= urls
;
129 urls
= coder
.mallocn
<CSSM_DATA_PTR
>(numUrls
+ 2);
130 for(unsigned i
=0; i
<numUrls
; i
++) {
131 urls
[i
] = oldUrls
[i
];
134 urls
[numUrls
] = coder
.mallocn
<CSSM_DATA
>();
135 coder
.allocCopyItem(genName
->name
, *urls
[numUrls
++]);
136 urls
[numUrls
] = NULL
;
140 coder
.allocItem(urlStr
, genName
->name
.Length
+ 1);
141 memmove(urlStr
.Data
, genName
->name
.Data
, genName
->name
.Length
);
142 urlStr
.Data
[urlStr
.Length
-1] = '\0';
143 tpOcspDebug("tpOcspUrlsFromCert: found URI %s", urlStr
.Data
);
147 subject
.freeField(&CSSMOID_AuthorityInfoAccess
, extField
);
152 * Create an SecAsn1OCSPDRequest for one cert. This consists of:
154 * -- cooking up an OCSPRequest if net fetch is enabled or a local responder
156 * -- extracting URLs from subject cert if net fetch is enabled;
157 * -- creating an SecAsn1OCSPDRequest, mallocd in coder's space;
159 static SecAsn1OCSPDRequest
*tpGenOcspdReq(
160 TPVerifyContext
&vfyCtx
,
164 OCSPClientCertID
&certId
,
165 const CSSM_DATA
**urls
, // from subject's AuthorityInfoAccess
166 CSSM_DATA
&nonce
) // possibly mallocd in coder's space and RETURNED
168 OCSPRequest
*ocspReq
= NULL
;
169 SecAsn1OCSPDRequest
*ocspdReq
= NULL
; // to return
170 OCSPClientCertID
*certID
= NULL
;
172 bool deleteCertID
= false;
174 /* gather options or their defaults */
175 CSSM_APPLE_TP_OCSP_OPT_FLAGS optFlags
= 0;
176 const CSSM_APPLE_TP_OCSP_OPTIONS
*ocspOpts
= vfyCtx
.ocspOpts
;
177 CSSM_DATA_PTR localResponder
= NULL
;
178 CSSM_DATA_PTR localResponderCert
= NULL
;
179 if(ocspOpts
!= NULL
) {
180 optFlags
= vfyCtx
.ocspOpts
->Flags
;
181 localResponder
= ocspOpts
->LocalResponder
;
182 localResponderCert
= ocspOpts
->LocalResponderCert
;
184 bool genNonce
= optFlags
& CSSM_TP_OCSP_GEN_NONCE
? true : false;
185 bool requireRespNonce
= optFlags
& CSSM_TP_OCSP_REQUIRE_RESP_NONCE
? true : false;
188 * One degenerate case in case we can't really do anything.
189 * If no URI and no local responder, only proceed if cache is not disabled
190 * and we're requiring full OCSP per cert.
192 if( ( (optFlags
& CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE
) ||
193 !(optFlags
& CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT
)
195 (localResponder
== NULL
) &&
197 tpOcspDebug("tpGenOcspdReq: no route to OCSP; NULL return");
201 /* do we need an OCSP request? */
202 if(!(optFlags
& CSSM_TP_ACTION_OCSP_DISABLE_NET
) || (localResponder
!= NULL
)) {
204 ocspReq
= new OCSPRequest(subject
, issuer
, genNonce
);
205 certID
= ocspReq
->certID();
208 /* not sure how this could even happen but that was a fair amount of code */
209 tpErrorLog("tpGenOcspdReq: error cooking up OCSPRequest\n");
210 if(ocspReq
!= NULL
) {
217 /* certID needed one way or the other */
219 crtn
= tpOcspGetCertId(subject
, issuer
, certID
);
227 * Create the SecAsn1OCSPDRequest. All fields optional.
229 ocspdReq
= coder
.mallocn
<SecAsn1OCSPDRequest
>();
230 memset(ocspdReq
, 0, sizeof(*ocspdReq
));
231 if(optFlags
& CSSM_TP_ACTION_OCSP_CACHE_WRITE_DISABLE
) {
232 ocspdReq
->cacheWriteDisable
= coder
.mallocn
<CSSM_DATA
>();
233 ocspdReq
->cacheWriteDisable
->Data
= coder
.mallocn
<uint8
>();
234 ocspdReq
->cacheWriteDisable
->Data
[0] = 0xff;
235 ocspdReq
->cacheWriteDisable
->Length
= 1;
238 * Note we're enforcing a not-so-obvious policy here: if nonce match is
239 * required, disk cache reads by ocspd are disabled. In-core cache is
240 * still enabled and hits in that cache do NOT require nonce matches.
242 if((optFlags
& CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE
) || requireRespNonce
) {
243 ocspdReq
->cacheReadDisable
= coder
.mallocn
<CSSM_DATA
>();
244 ocspdReq
->cacheReadDisable
->Data
= coder
.mallocn
<uint8
>();
245 ocspdReq
->cacheReadDisable
->Data
[0] = 0xff;
246 ocspdReq
->cacheReadDisable
->Length
= 1;
248 /* CertID, only required field */
249 coder
.allocCopyItem(*certID
->encode(), ocspdReq
->certID
);
250 if(ocspReq
!= NULL
) {
251 ocspdReq
->ocspReq
= coder
.mallocn
<CSSM_DATA
>();
252 coder
.allocCopyItem(*ocspReq
->encode(), *ocspdReq
->ocspReq
);
254 /* nonce not available until encode() called */
255 coder
.allocCopyItem(*ocspReq
->nonce(), nonce
);
258 if(localResponder
!= NULL
) {
259 ocspdReq
->localRespURI
= localResponder
;
261 if(!(optFlags
& CSSM_TP_ACTION_OCSP_DISABLE_NET
)) {
262 ocspdReq
->urls
= const_cast<CSSM_DATA
**>(urls
);
273 static bool revocationTimeAfterVerificationTime(CFAbsoluteTime revokedTime
, CSSM_TIMESTRING verifyTimeStr
)
275 // Return true if the revocation time is after the specified verification time (i.e. "good")
276 // If verifyTime not specified, use now for the verifyTime
278 CFAbsoluteTime verifyTime
= 0;
282 CFDateRef cfVerifyTime
= NULL
; // made with CFDateCreate
283 int rtn
= timeStringToCfDate((char *)verifyTimeStr
, (unsigned)strlen(verifyTimeStr
), &cfVerifyTime
);
287 verifyTime
= CFDateGetAbsoluteTime(cfVerifyTime
);
288 CFRelease(cfVerifyTime
);
293 verifyTime
= CFAbsoluteTimeGetCurrent();
295 return verifyTime
< revokedTime
;
299 * Apply a verified OCSPSingleResponse to a TPCertInfo.
301 static CSSM_RETURN
tpApplySingleResp(
302 OCSPSingleResponse
&singleResp
,
304 unsigned dex
, // for debug
305 CSSM_APPLE_TP_OCSP_OPT_FLAGS flags
, // for OCSP_SUFFICIENT
306 CSSM_TIMESTRING verifyTime
, // Check revocation at specific time
307 bool &processed
) // set true iff CS_Good or CS_Revoked found
309 SecAsn1OCSPCertStatusTag certStatus
= singleResp
.certStatus();
310 CSSM_RETURN crtn
= CSSM_OK
;
311 if ((certStatus
== CS_Revoked
) &&
312 revocationTimeAfterVerificationTime(singleResp
.revokedTime(), verifyTime
))
314 tpOcspDebug("tpApplySingleResp: CS_Revoked for cert %u, but revoked after verification time", dex
);
315 certStatus
= CS_Good
;
320 tpOcspDebug("tpApplySingleResp: CS_Good for cert %u", dex
);
321 cert
.revokeCheckGood(true);
322 if(flags
& CSSM_TP_ACTION_OCSP_SUFFICIENT
) {
323 /* no more revocation checking necessary for this cert */
324 cert
.revokeCheckComplete(true);
329 tpOcspDebug("tpApplySingleResp: CS_Revoked for cert %u", dex
);
330 switch(singleResp
.crlReason()) {
331 case CE_CR_CertificateHold
:
332 crtn
= CSSMERR_TP_CERT_SUSPENDED
;
335 /* FIXME - may want more detailed CRLReason-specific errors */
336 crtn
= CSSMERR_TP_CERT_REVOKED
;
339 if(!cert
.addStatusCode(crtn
)) {
345 /* not an error, no per-cert status, nothing here */
346 tpOcspDebug("tpApplySingleResp: CS_Unknown for cert %u", dex
);
349 tpOcspDebug("tpApplySingleResp: BAD certStatus (%d) for cert %u",
350 (int)certStatus
, dex
);
351 if(cert
.addStatusCode(CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED
)) {
352 crtn
= CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED
;
360 * An exceptional case: synchronously flush the OCSPD cache and send a new
361 * resquest for just this one cert.
363 static OCSPResponse
*tpOcspFlushAndReFetch(
364 TPVerifyContext
&vfyCtx
,
368 OCSPClientCertID
&certID
)
370 const CSSM_DATA
*derCertID
= certID
.encode();
373 crtn
= ocspdCacheFlush(*derCertID
);
376 cssmPerror("ocspdCacheFlush", crtn
);
381 /* Cook up an OCSPDRequests, one request, just for this */
382 /* send it to ocsdp */
383 /* munge reply into an OCSPRsponse and return it */
384 tpOcspDebug("pOcspFlushAndReFetch: Code on demand");
394 OCSPClientCertID
&cid
,
401 OCSPClientCertID
&certID
; // owned by caller
402 CSSM_DATA
**urls
; // owner-managed array of URLs obtained from subject's
403 // AuthorityInfoAccess.id-ad-ocsp.
404 CSSM_DATA nonce
; // owner-managed copy of this requests' nonce, if it
406 unsigned dex
; // in inputCerts, for debug
410 PendingRequest::PendingRequest(
413 OCSPClientCertID
&cid
,
416 : subject(subj
), issuer(iss
), certID(cid
),
417 urls(u
), dex(dx
), processed(false)
423 #pragma mark ---- public API ----
425 CSSM_RETURN
tpVerifyCertGroupWithOCSP(
426 TPVerifyContext
&vfyCtx
,
427 TPCertGroup
&certGroup
) // to be verified
429 assert(vfyCtx
.clHand
!= 0);
430 assert(vfyCtx
.policy
== kRevokeOcsp
);
432 CSSM_RETURN ourRtn
= CSSM_OK
;
433 OcspRespStatus respStat
;
436 SecAsn1OCSPDRequests ocspdReqs
;
437 SecAsn1OCSPReplies ocspdReplies
;
438 unsigned numRequests
= 0; // in ocspdReqs
439 CSSM_DATA derOcspdRequests
= {0, NULL
};
440 CSSM_DATA derOcspdReplies
= {0, NULL
};
441 uint8 version
= OCSPD_REQUEST_VERS
;
443 unsigned numCerts
= certGroup
.numCerts();
445 /* Can't verify without an issuer; we're done */
450 /* gather options or their defaults */
451 CSSM_APPLE_TP_OCSP_OPT_FLAGS optFlags
= 0;
452 const CSSM_APPLE_TP_OCSP_OPTIONS
*ocspOpts
= vfyCtx
.ocspOpts
;
453 CSSM_DATA_PTR localResponder
= NULL
;
454 CSSM_DATA_PTR localResponderCert
= NULL
;
455 bool cacheReadDisable
= false;
456 bool cacheWriteDisable
= false;
457 bool genNonce
= false; // in outgoing request
458 bool requireRespNonce
= false; // in incoming response
461 if(ocspOpts
!= NULL
) {
462 optFlags
= vfyCtx
.ocspOpts
->Flags
;
463 localResponder
= ocspOpts
->LocalResponder
;
464 localResponderCert
= ocspOpts
->LocalResponderCert
;
466 if(optFlags
& CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE
) {
467 cacheReadDisable
= true;
469 if(optFlags
& CSSM_TP_ACTION_OCSP_CACHE_WRITE_DISABLE
) {
470 cacheWriteDisable
= true;
472 if(optFlags
& CSSM_TP_OCSP_GEN_NONCE
) {
475 if(optFlags
& CSSM_TP_OCSP_REQUIRE_RESP_NONCE
) {
476 requireRespNonce
= true;
478 if(requireRespNonce
& !genNonce
) {
480 tpErrorLog("tpVerifyCertGroupWithOCSP: requireRespNonce, !genNonce\n");
481 return CSSMERR_TP_INVALID_REQUEST_INPUTS
;
484 tpOcspDebug("tpVerifyCertGroupWithOCSP numCerts %u optFlags 0x%lx",
485 numCerts
, (unsigned long)optFlags
);
488 * create list of pendingRequests parallel to certGroup
490 PendingRequest
**pending
= coder
.mallocn
<PendingRequest
*>(numCerts
);
491 memset(pending
, 0, (numCerts
* sizeof(PendingRequest
*)));
493 for(unsigned dex
=0; dex
<numCerts
; dex
++) {
494 OCSPClientCertID
*certID
= NULL
;
495 TPCertInfo
*subject
= certGroup
.certAtIndex(dex
);
497 if(subject
->trustSettingsFound()) {
498 /* functionally equivalent to root - we're done */
499 tpOcspDebug("...tpVerifyCertGroupWithOCSP: terminate per user trust at %u",
503 TPCertInfo
*issuer
= certGroup
.certAtIndex(dex
+ 1);
504 crtn
= tpOcspGetCertId(*subject
, *issuer
, certID
);
506 tpErrorLog("tpVerifyCertGroupWithOCSP: error extracting cert fields; "
512 * We use the URLs in the subject cert's AuthorityInfoAccess extension
513 * for two things - mainly to get the URL(s) for actual OCSP transactions,
514 * but also for CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT processing.
515 * So, we do the per-cert processing to get them right now even if we
516 * wind up using a local responder or getting verification from cache.
518 CSSM_DATA
**urls
= tpOcspUrlsFromCert(*subject
, coder
);
519 pending
[dex
] = new PendingRequest(*subject
, *issuer
, *certID
, urls
, dex
);
521 /* subsequent errors to errOut: */
524 * Create empty SecAsn1OCSPDRequests big enough for all certs
526 ocspdReqs
.requests
= coder
.mallocn
<SecAsn1OCSPDRequest
*>(numCerts
+ 1);
527 memset(ocspdReqs
.requests
, 0, (numCerts
+ 1) * sizeof(SecAsn1OCSPDRequest
*));
528 ocspdReqs
.version
.Data
= &version
;
529 ocspdReqs
.version
.Length
= 1;
532 * For each cert, either obtain a cached OCSPResponse, or create
533 * a request to get one.
535 * NOTE: in-core cache reads (via tpOcspCacheLookup() do NOT involve a
536 * nonce check, no matter what the app says. If nonce checking is required by the
537 * app, responses don't get added to cache if the nonce doesn't match, but once
538 * a response is validated and added to cache it's fair game for that task.
540 for(unsigned dex
=0; dex
<numCerts
; dex
++) {
541 PendingRequest
*pendReq
= pending
[dex
];
542 OCSPSingleResponse
*singleResp
= NULL
;
543 if(!cacheReadDisable
) {
544 singleResp
= tpOcspCacheLookup(pendReq
->certID
, localResponder
);
547 tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache hit (1) dex %u",
549 crtn
= tpApplySingleResp(*singleResp
, pendReq
->subject
, dex
, optFlags
,
550 vfyCtx
.verifyTime
, pendReq
->processed
);
552 if(pendReq
->processed
) {
553 /* definitely done with this cert one way or the other */
554 if(crtn
&& (ourRtn
== CSSM_OK
)) {
555 /* real cert error: first error encountered; we'll keep going */
562 * This indicates a bad cached response. Well that's kinda weird, let's
563 * just flush this out and try a normal transaction.
565 tpOcspCacheFlush(pendReq
->certID
);
570 * Prepare a request for ocspd
572 SecAsn1OCSPDRequest
*ocspdReq
= tpGenOcspdReq(vfyCtx
, coder
,
573 pendReq
->subject
, pendReq
->issuer
, pendReq
->certID
,
574 const_cast<const CSSM_DATA
**>(pendReq
->urls
),
576 if(ocspdReq
== NULL
) {
577 /* tpGenOcspdReq determined there was no route to OCSP responder */
578 tpOcspDebug("tpVerifyCertGroupWithOCSP: no OCSP possible for cert %u", dex
);
581 ocspdReqs
.requests
[numRequests
++] = ocspdReq
;
583 if(numRequests
== 0) {
584 /* no candidates for OCSP: almost done */
588 /* ship requests off to ocspd, get ocspReplies back */
589 if(coder
.encodeItem(&ocspdReqs
, kSecAsn1OCSPDRequestsTemplate
, derOcspdRequests
)) {
590 tpErrorLog("tpVerifyCertGroupWithOCSP: error encoding ocspdReqs\n");
591 ourRtn
= CSSMERR_TP_INTERNAL_ERROR
;
594 crtn
= ocspdFetch(vfyCtx
.alloc
, derOcspdRequests
, derOcspdReplies
);
596 tpErrorLog("tpVerifyCertGroupWithOCSP: error during ocspd RPC\n");
598 cssmPerror("ocspdFetch", crtn
);
600 /* But this is not necessarily fatal...update per-cert status and check
601 * caller requirements below */
604 memset(&ocspdReplies
, 0, sizeof(ocspdReplies
));
605 prtn
= coder
.decodeItem(derOcspdReplies
, kSecAsn1OCSPDRepliesTemplate
,
607 /* we're done with this, mallocd in ocspdFetch() */
608 vfyCtx
.alloc
.free(derOcspdReplies
.Data
);
611 * This can happen when an OCSP server provides bad data...we cannot
612 * determine which cert is associated with this bad response;
613 * just flag it with the first one and proceed to the loop that
614 * handles CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT.
616 tpErrorLog("tpVerifyCertGroupWithOCSP: error decoding ocspd reply\n");
617 pending
[0]->subject
.addStatusCode(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
620 if((ocspdReplies
.version
.Length
!= 1) ||
621 (ocspdReplies
.version
.Data
[0] != OCSPD_REPLY_VERS
)) {
622 tpErrorLog("tpVerifyCertGroupWithOCSP: ocspd reply version mismatch\n");
623 if(ourRtn
== CSSM_OK
) {
624 ourRtn
= CSSMERR_TP_INTERNAL_ERROR
; // maybe something better?
629 /* process each reply */
630 numReplies
= ocspdArraySize((const void **)ocspdReplies
.replies
);
631 for(unsigned dex
=0; dex
<numReplies
; dex
++) {
632 SecAsn1OCSPDReply
*reply
= ocspdReplies
.replies
[dex
];
634 /* Cook up our version of an OCSPResponse from the encoded data */
635 OCSPResponse
*ocspResp
= NULL
;
637 ocspResp
= new OCSPResponse(reply
->ocspResp
, TP_OCSP_CACHE_TTL
);
640 tpErrorLog("tpVerifyCertGroupWithOCSP: error decoding ocsp response\n");
641 /* what the heck, keep going */
646 * Find matching subject cert if possible (it's technically optional for
647 * verification of the response in some cases, e.g., local responder).
649 PendingRequest
*pendReq
= NULL
; // fully qualified
650 PendingRequest
*reqWithIdMatch
= NULL
; // CertID match only, not nonce
651 for(unsigned pdex
=0; pdex
<numCerts
; pdex
++) {
653 /* first check ID match; that is required no matter what */
654 if((pending
[pdex
])->certID
.compareToExist(reply
->certID
)) {
655 reqWithIdMatch
= pending
[pdex
];
657 if(reqWithIdMatch
== NULL
) {
661 /* that's good enough */
662 pendReq
= reqWithIdMatch
;
663 tpOcspDebug("OCSP processs reply: CertID match, no nonce");
666 if(tpCompareCssmData(&reqWithIdMatch
->nonce
, ocspResp
->nonce())) {
667 tpOcspDebug("OCSP processs reply: nonce MATCH");
668 pendReq
= reqWithIdMatch
;
673 * In this case we keep going; if we never find a match, then we can
674 * use reqWithIdMatch if !requireRespNonce.
676 tpOcspDebug("OCSP processs reply: certID match, nonce MISMATCH");
678 if(pendReq
== NULL
) {
679 if(requireRespNonce
) {
680 tpOcspDebug("OCSP processs reply: tossing out response due to "
683 if(ourRtn
== CSSM_OK
) {
684 ourRtn
= CSSMERR_APPLETP_OCSP_NONCE_MISMATCH
;
688 if(reqWithIdMatch
!= NULL
) {
690 * Nonce mismatch but caller thinks that's OK. Log it and proceed.
693 tpOcspDebug("OCSP processs reply: using bad nonce due to !requireRespNonce");
694 pendReq
= reqWithIdMatch
;
695 pendReq
->subject
.addStatusCode(CSSMERR_APPLETP_OCSP_NONCE_MISMATCH
);
698 TPCertInfo
*issuer
= NULL
;
699 if(pendReq
!= NULL
) {
700 issuer
= &pendReq
->issuer
;
703 /* verify response and either throw out or add to local cache */
704 respStat
= tpVerifyOcspResp(vfyCtx
, coder
, issuer
, *ocspResp
, crtn
);
709 /* not an error but we can't use it */
710 if((crtn
!= CSSM_OK
) && (pendReq
!= NULL
)) {
711 /* pass this info back to caller here... */
712 pendReq
->subject
.addStatusCode(crtn
);
719 * An exceptional case: synchronously flush the OCSPD cache and send a
720 * new request for just this one cert.
721 * FIXME: does this really buy us anything? A DOS attacker who managed
722 * to get this bogus response into our cache is likely to be able
723 * to do it again and again.
725 tpOcspDebug("tpVerifyCertGroupWithOCSP: flush/refetch for cert %u", dex
);
726 ocspResp
= tpOcspFlushAndReFetch(vfyCtx
, coder
, pendReq
->subject
,
727 pendReq
->issuer
, pendReq
->certID
);
728 if(ocspResp
== NULL
) {
729 tpErrorLog("tpVerifyCertGroupWithOCSP: error on flush/refetch\n");
730 ourRtn
= CSSMERR_APPLETP_OCSP_BAD_RESPONSE
;
733 respStat
= tpVerifyOcspResp(vfyCtx
, coder
, issuer
, *ocspResp
, crtn
);
734 if(respStat
!= ORS_Good
) {
735 tpErrorLog("tpVerifyCertGroupWithOCSP: verify error after "
737 if((crtn
!= CSSM_OK
) && (pendReq
!= NULL
)) {
738 /* pass this info back to caller here... */
739 if(pendReq
->subject
.addStatusCode(crtn
)) {
740 ourRtn
= CSSMERR_APPLETP_OCSP_BAD_RESPONSE
;
744 ourRtn
= CSSMERR_APPLETP_OCSP_BAD_RESPONSE
;
748 /* Voila! Recovery. Proceed. */
749 tpOcspDebug("tpVerifyCertGroupWithOCSP: refetch for cert %u SUCCEEDED",
752 } /* switch response status */
754 if(!cacheWriteDisable
) {
755 tpOcspCacheAdd(reply
->ocspResp
, localResponder
);
758 /* attempt to apply to pendReq */
759 if(pendReq
!= NULL
) {
760 OCSPSingleResponse
*singleResp
=
761 ocspResp
->singleResponseFor(pendReq
->certID
);
763 crtn
= tpApplySingleResp(*singleResp
, pendReq
->subject
, pendReq
->dex
,
764 optFlags
, vfyCtx
.verifyTime
, pendReq
->processed
);
765 if(crtn
&& (ourRtn
== CSSM_OK
)) {
770 } /* a reply which matches a pending request */
773 * Done with this - note local OCSP response cache doesn't store this
774 * object; it stores an encoded copy.
777 } /* for each reply */
782 * Now process each cert which hasn't had an OCSP response applied to it.
783 * This can happen if we get back replies which are not strictly in 1-1 sync with
784 * our requests but which nevertheless contain valid info for more than one
787 for(unsigned dex
=0; dex
<numCerts
; dex
++) {
788 PendingRequest
*pendReq
= pending
[dex
];
789 if(pendReq
== NULL
) {
790 /* i.e. terminated due to user trust */
791 tpOcspDebug("...tpVerifyCertGroupWithOCSP: NULL pendReq dex %u",
795 if(pendReq
->processed
) {
798 OCSPSingleResponse
*singleResp
= NULL
;
799 /* Note this corner case will not work if cache is disabled. */
800 if(!cacheReadDisable
) {
801 singleResp
= tpOcspCacheLookup(pendReq
->certID
, localResponder
);
804 tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache (2) hit dex %u",
806 crtn
= tpApplySingleResp(*singleResp
, pendReq
->subject
, dex
, optFlags
,
807 vfyCtx
.verifyTime
, pendReq
->processed
);
809 if(ourRtn
== CSSM_OK
) {
815 if(!pendReq
->processed
) {
816 /* Couldn't perform OCSP for this cert. */
817 tpOcspDebug("tpVerifyCertGroupWithOCSP: OCSP_UNAVAILABLE for cert %u", dex
);
818 bool required
= false;
819 CSSM_RETURN responseStatus
= CSSM_OK
;
820 if(pendReq
->subject
.numStatusCodes() > 0) {
822 * Check whether we got a response for this cert, but it was rejected
823 * due to being improperly signed. That should result in an actual
824 * error, even under Best Attempt processing. (10743149)
826 if(pendReq
->subject
.hasStatusCode(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
)) {
827 // responseStatus = CSSMERR_APPLETP_OCSP_BAD_RESPONSE; <rdar://problem/10831157>
828 } else if(pendReq
->subject
.hasStatusCode(CSSMERR_APPLETP_OCSP_SIG_ERROR
)) {
829 responseStatus
= CSSMERR_APPLETP_OCSP_SIG_ERROR
;
830 } else if(pendReq
->subject
.hasStatusCode(CSSMERR_APPLETP_OCSP_NO_SIGNER
)) {
831 responseStatus
= CSSMERR_APPLETP_OCSP_NO_SIGNER
;
834 if(responseStatus
== CSSM_OK
) {
835 /* no response available (as opposed to getting an invalid response) */
836 pendReq
->subject
.addStatusCode(CSSMERR_APPLETP_OCSP_UNAVAILABLE
);
838 if(optFlags
& CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT
) {
839 /* every cert needs OCSP */
840 tpOcspDebug("tpVerifyCertGroupWithOCSP: response required for all certs, missing for cert %u", dex
);
843 else if(optFlags
& CSSM_TP_ACTION_OCSP_REQUIRE_IF_RESP_PRESENT
) {
844 /* this cert needs OCSP if it had an AIA extension with an OCSP URI */
846 tpOcspDebug("tpVerifyCertGroupWithOCSP: OCSP URI present but no valid response for cert %u", dex
);
850 if( (required
&& pendReq
->subject
.isStatusFatal(CSSMERR_APPLETP_OCSP_UNAVAILABLE
)) ||
851 (responseStatus
!= CSSM_OK
&& pendReq
->subject
.isStatusFatal(responseStatus
)) ) {
852 /* fatal error, but we keep on processing */
853 if(ourRtn
== CSSM_OK
) {
854 ourRtn
= (responseStatus
!= CSSM_OK
) ? responseStatus
: CSSMERR_APPLETP_OCSP_UNAVAILABLE
;
860 for(unsigned dex
=0; dex
<numCerts
; dex
++) {
861 PendingRequest
*pendReq
= pending
[dex
];
862 if(pendReq
== NULL
) {
863 /* i.e. terminated due to user trust */
866 delete &pendReq
->certID
;