1/*
2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * tpOcspVerify.cpp - top-level OCSP verification
26 */
27
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>
41#include "tpTime.h"
42
43#pragma mark ---- private routines ----
44
45/*
46 * Get a smart CertID for specified cert and issuer
47 */
48static CSSM_RETURN tpOcspGetCertId(
49	TPCertInfo			&subject,
50	TPCertInfo			&issuer,
51	OCSPClientCertID	*&certID)		/* mallocd by coder and RETURNED */
52{
53	CSSM_RETURN crtn;
54	CSSM_DATA_PTR issuerSubject = NULL;
55	CSSM_DATA_PTR issuerPubKeyData = NULL;
56	CSSM_KEY_PTR issuerPubKey;
57	CSSM_DATA_PTR subjectSerial = NULL;
58
59	crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &subjectSerial);
60	if(crtn) {
61		return crtn;
62	}
63	crtn = subject.fetchField(&CSSMOID_X509V1IssuerNameStd, &issuerSubject);
64	if(crtn) {
65		return crtn;
66	}
67	crtn = issuer.fetchField(&CSSMOID_CSSMKeyStruct, &issuerPubKeyData);
68	if(crtn) {
69		return crtn;
70	}
71	assert(issuerPubKeyData->Length == sizeof(CSSM_KEY));
72	issuerPubKey = (CSSM_KEY_PTR)issuerPubKeyData->Data;
73	certID = new OCSPClientCertID(*issuerSubject, issuerPubKey->KeyData, *subjectSerial);
74
75	subject.freeField(&CSSMOID_X509V1SerialNumber, subjectSerial);
76	issuer.freeField(&CSSMOID_X509V1IssuerNameStd, issuerSubject);
77	issuer.freeField(&CSSMOID_CSSMKeyStruct, issuerPubKeyData);
78	return CSSM_OK;
79}
80
81/*
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.
84 */
85static CSSM_DATA **tpOcspUrlsFromCert(
86	TPCertInfo &subject,
87	SecNssCoder &coder)
88{
89	CSSM_DATA_PTR extField = NULL;
90	CSSM_RETURN crtn;
91
92	crtn = subject.fetchField(&CSSMOID_AuthorityInfoAccess, &extField);
93	if(crtn) {
94		tpOcspDebug("tpOcspUrlsFromCert: no AIA extension");
95		return NULL;
96	}
97	if(extField->Length != sizeof(CSSM_X509_EXTENSION)) {
98		tpErrorLog("tpOcspUrlsFromCert: malformed CSSM_FIELD");
99		return NULL;
100	}
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");
104		return NULL;
105	}
106
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)) {
114			continue;
115		}
116		CE_GeneralName *genName = &ad->accessLocation;
117		if(genName->nameType != GNT_URI) {
118			tpErrorLog("tpOcspUrlsFromCert: CSSMOID_AD_OCSP, but not type URI");
119			continue;
120		}
121
122		/* got one */
123		if(urls == NULL) {
124			urls = coder.mallocn<CSSM_DATA_PTR>(2);
125		}
126		else {
127			/* realloc */
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];
132			}
133		}
134		urls[numUrls] = coder.mallocn<CSSM_DATA>();
135		coder.allocCopyItem(genName->name, *urls[numUrls++]);
136		urls[numUrls] = NULL;
137		#ifndef	NDEBUG
138		{
139			CSSM_DATA urlStr;
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);
144		}
145		#endif
146	}
147	subject.freeField(&CSSMOID_AuthorityInfoAccess, extField);
148	return urls;
149}
150
151/*
152 * Create an SecAsn1OCSPDRequest for one cert. This consists of:
153 *
154 * -- cooking up an OCSPRequest if net fetch is enabled or a local responder
155 *    is configured;
156 * -- extracting URLs from subject cert if net fetch is enabled;
157 * -- creating an SecAsn1OCSPDRequest, mallocd in coder's space;
158 */
159static SecAsn1OCSPDRequest *tpGenOcspdReq(
160	TPVerifyContext		&vfyCtx,
161	SecNssCoder			&coder,
162	TPCertInfo			&subject,
163	TPCertInfo			&issuer,
164	OCSPClientCertID	&certId,
165	const CSSM_DATA		**urls,		// from subject's AuthorityInfoAccess
166	CSSM_DATA			&nonce)		// possibly mallocd in coder's space and RETURNED
167{
168	OCSPRequest				*ocspReq = NULL;
169	SecAsn1OCSPDRequest		*ocspdReq = NULL;	// to return
170	OCSPClientCertID		*certID = NULL;
171	CSSM_RETURN				crtn;
172	bool					deleteCertID = false;
173
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;
183	}
184	bool genNonce = optFlags & CSSM_TP_OCSP_GEN_NONCE ? true : false;
185	bool requireRespNonce = optFlags & CSSM_TP_OCSP_REQUIRE_RESP_NONCE ? true : false;
186
187	/*
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.
191	 */
192	if( ( (optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) ||
193		  !(optFlags & CSSM_TP_ACTION_OCSP_REQUIRE_PER_CERT)
194		) &&
195	   (localResponder == NULL) &&
196	   (urls == NULL)) {
197	   tpOcspDebug("tpGenOcspdReq: no route to OCSP; NULL return");
198	   return NULL;
199	}
200
201	/* do we need an OCSP request? */
202	if(!(optFlags & CSSM_TP_ACTION_OCSP_DISABLE_NET) || (localResponder != NULL)) {
203		try {
204			ocspReq = new OCSPRequest(subject, issuer, genNonce);
205			certID = ocspReq->certID();
206		}
207		catch(...) {
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) {
211				delete ocspReq;
212				return NULL;
213			}
214		}
215	}
216
217	/* certID needed one way or the other */
218	if(certID == NULL) {
219		crtn = tpOcspGetCertId(subject, issuer, certID);
220		if(crtn) {
221			goto errOut;
222		}
223		deleteCertID = true;
224	}
225
226	/*
227	 * Create the SecAsn1OCSPDRequest. All fields optional.
228	 */
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;
236	}
237	/*
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.
241	 */
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;
247	}
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);
253		if(genNonce) {
254			/* nonce not available until encode() called */
255			coder.allocCopyItem(*ocspReq->nonce(), nonce);
256		}
257	}
258	if(localResponder != NULL) {
259		ocspdReq->localRespURI = localResponder;
260	}
261	if(!(optFlags & CSSM_TP_ACTION_OCSP_DISABLE_NET)) {
262		ocspdReq->urls = const_cast<CSSM_DATA **>(urls);
263	}
264
265errOut:
266	delete ocspReq;
267	if(deleteCertID) {
268		delete certID;
269	}
270	return ocspdReq;
271}
272
273static bool revocationTimeAfterVerificationTime(CFAbsoluteTime revokedTime, CSSM_TIMESTRING verifyTimeStr)
274{
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
277
278    CFAbsoluteTime verifyTime = 0;
279
280    if (verifyTimeStr)
281    {
282        CFDateRef cfVerifyTime = NULL;  // made with CFDateCreate
283        int rtn = timeStringToCfDate((char *)verifyTimeStr, (unsigned)strlen(verifyTimeStr), &cfVerifyTime);
284        if (!rtn)
285            if (cfVerifyTime)
286            {
287                verifyTime = CFDateGetAbsoluteTime(cfVerifyTime);
288                CFRelease(cfVerifyTime);
289            }
290    }
291
292    if (verifyTime == 0)
293        verifyTime = CFAbsoluteTimeGetCurrent();
294
295    return verifyTime < revokedTime;
296}
297
298/*
299 * Apply a verified OCSPSingleResponse to a TPCertInfo.
300 */
301static CSSM_RETURN tpApplySingleResp(
302	OCSPSingleResponse				&singleResp,
303	TPCertInfo						&cert,
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
308{
309	SecAsn1OCSPCertStatusTag certStatus = singleResp.certStatus();
310	CSSM_RETURN crtn = CSSM_OK;
311    if ((certStatus == CS_Revoked) &&
312        revocationTimeAfterVerificationTime(singleResp.revokedTime(), verifyTime))
313    {
314        tpOcspDebug("tpApplySingleResp: CS_Revoked for cert %u, but revoked after verification time", dex);
315        certStatus = CS_Good;
316    }
317
318	switch(certStatus) {
319		case 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);
325			}
326			processed = true;
327			break;
328		case CS_Revoked:
329			tpOcspDebug("tpApplySingleResp: CS_Revoked for cert %u", dex);
330			switch(singleResp.crlReason()) {
331				case CE_CR_CertificateHold:
332					crtn = CSSMERR_TP_CERT_SUSPENDED;
333					break;
334				default:
335					/* FIXME - may want more detailed CRLReason-specific errors */
336					crtn = CSSMERR_TP_CERT_REVOKED;
337					break;
338			}
339			if(!cert.addStatusCode(crtn)) {
340				crtn = CSSM_OK;
341			}
342			processed = true;
343			break;
344		case CS_Unknown:
345			/* not an error, no per-cert status, nothing here */
346			tpOcspDebug("tpApplySingleResp: CS_Unknown for cert %u", dex);
347			break;
348		default:
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;
353			}
354			break;
355	}
356	return crtn;
357}
358
359/*
360 * An exceptional case: synchronously flush the OCSPD cache and send a new
361 * resquest for just this one cert.
362 */
363static OCSPResponse *tpOcspFlushAndReFetch(
364	TPVerifyContext		&vfyCtx,
365	SecNssCoder			&coder,
366	TPCertInfo			&subject,
367	TPCertInfo			&issuer,
368	OCSPClientCertID	&certID)
369{
370	const CSSM_DATA *derCertID = certID.encode();
371	CSSM_RETURN crtn;
372
373	crtn = ocspdCacheFlush(*derCertID);
374	if(crtn) {
375		#ifndef	NDEBUG
376		cssmPerror("ocspdCacheFlush", crtn);
377		#endif
378		return NULL;
379	}
380
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");
385	return NULL;
386}
387
388class PendingRequest
389{
390public:
391	PendingRequest(
392		TPCertInfo &subject,
393		TPCertInfo &issuer,
394		OCSPClientCertID &cid,
395		CSSM_DATA **u,
396		unsigned dex);
397	~PendingRequest()	{}
398
399	TPCertInfo			&subject;
400	TPCertInfo			&issuer;
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
405									//   has one
406	unsigned			dex;		// in inputCerts, for debug
407	bool				processed;
408};
409
410PendingRequest::PendingRequest(
411		TPCertInfo &subj,
412		TPCertInfo &iss,
413		OCSPClientCertID &cid,
414		CSSM_DATA **u,
415		unsigned dx)
416		: subject(subj), issuer(iss), certID(cid),
417		  urls(u), dex(dx), processed(false)
418{
419	nonce.Data = NULL;
420	nonce.Length = 0;
421}
422
423#pragma mark ---- public API ----
424
425CSSM_RETURN tpVerifyCertGroupWithOCSP(
426	TPVerifyContext	&vfyCtx,
427	TPCertGroup 	&certGroup)		// to be verified
428{
429	assert(vfyCtx.clHand != 0);
430	assert(vfyCtx.policy == kRevokeOcsp);
431
432	CSSM_RETURN ourRtn = CSSM_OK;
433	OcspRespStatus respStat;
434	SecNssCoder coder;
435	CSSM_RETURN crtn;
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;
442	unsigned numReplies;
443	unsigned numCerts = certGroup.numCerts();
444	if(numCerts <= 1) {
445		/* Can't verify without an issuer; we're done */
446		return CSSM_OK;
447	}
448	numCerts--;
449
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
459	PRErrorCode prtn;
460
461	if(ocspOpts != NULL) {
462		optFlags = vfyCtx.ocspOpts->Flags;
463		localResponder = ocspOpts->LocalResponder;
464		localResponderCert = ocspOpts->LocalResponderCert;
465	}
466	if(optFlags & CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE) {
467		cacheReadDisable = true;
468	}
469	if(optFlags & CSSM_TP_ACTION_OCSP_CACHE_WRITE_DISABLE) {
470		cacheWriteDisable = true;
471	}
472	if(optFlags & CSSM_TP_OCSP_GEN_NONCE) {
473		genNonce = true;
474	}
475	if(optFlags & CSSM_TP_OCSP_REQUIRE_RESP_NONCE) {
476		requireRespNonce = true;
477	}
478	if(requireRespNonce & !genNonce) {
479		/* no can do */
480		tpErrorLog("tpVerifyCertGroupWithOCSP: requireRespNonce, !genNonce\n");
481		return CSSMERR_TP_INVALID_REQUEST_INPUTS;
482	}
483
484	tpOcspDebug("tpVerifyCertGroupWithOCSP numCerts %u optFlags 0x%lx",
485		numCerts, (unsigned long)optFlags);
486
487	/*
488	 * create list of pendingRequests parallel to certGroup
489	 */
490	PendingRequest **pending = coder.mallocn<PendingRequest *>(numCerts);
491	memset(pending, 0, (numCerts * sizeof(PendingRequest *)));
492
493	for(unsigned dex=0; dex<numCerts; dex++) {
494		OCSPClientCertID *certID = NULL;
495		TPCertInfo *subject = certGroup.certAtIndex(dex);
496
497		if(subject->trustSettingsFound()) {
498			/* functionally equivalent to root - we're done */
499			tpOcspDebug("...tpVerifyCertGroupWithOCSP: terminate per user trust at %u",
500				(unsigned)dex);
501			goto postOcspd;
502		}
503		TPCertInfo *issuer = certGroup.certAtIndex(dex + 1);
504		crtn = tpOcspGetCertId(*subject, *issuer, certID);
505		if(crtn) {
506			tpErrorLog("tpVerifyCertGroupWithOCSP: error extracting cert fields; "
507				"aborting\n");
508			goto errOut;
509		}
510
511		/*
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.
517		 */
518		CSSM_DATA **urls = tpOcspUrlsFromCert(*subject, coder);
519		pending[dex] = new PendingRequest(*subject, *issuer, *certID, urls, dex);
520	}
521	/* subsequent errors to errOut: */
522
523	/*
524	 * Create empty SecAsn1OCSPDRequests big enough for all certs
525	 */
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;
530
531	/*
532	 * For each cert, either obtain a cached OCSPResponse, or create
533	 * a request to get one.
534	 *
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.
539	 */
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);
545		}
546		if(singleResp) {
547			tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache hit (1) dex %u",
548				(unsigned)dex);
549			crtn = tpApplySingleResp(*singleResp, pendReq->subject, dex, optFlags,
550				vfyCtx.verifyTime, pendReq->processed);
551			delete singleResp;
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 */
556					ourRtn = crtn;
557				}
558				continue;
559			}
560			if(crtn) {
561				/*
562				 * This indicates a bad cached response. Well that's kinda weird, let's
563				 * just flush this out and try a normal transaction.
564				 */
565				tpOcspCacheFlush(pendReq->certID);
566			}
567		}
568
569		/*
570		 * Prepare a request for ocspd
571		 */
572		SecAsn1OCSPDRequest *ocspdReq = tpGenOcspdReq(vfyCtx, coder,
573			pendReq->subject, pendReq->issuer, pendReq->certID,
574			const_cast<const CSSM_DATA **>(pendReq->urls),
575			pendReq->nonce);
576		if(ocspdReq == NULL) {
577			/* tpGenOcspdReq determined there was no route to OCSP responder */
578			tpOcspDebug("tpVerifyCertGroupWithOCSP: no OCSP possible for cert %u", dex);
579			continue;
580		}
581		ocspdReqs.requests[numRequests++] = ocspdReq;
582	}
583	if(numRequests == 0) {
584		/* no candidates for OCSP: almost done */
585		goto postOcspd;
586	}
587
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;
592		goto errOut;
593	}
594	crtn = ocspdFetch(vfyCtx.alloc, derOcspdRequests, derOcspdReplies);
595	if(crtn) {
596		tpErrorLog("tpVerifyCertGroupWithOCSP: error during ocspd RPC\n");
597		#ifndef	NDEBUG
598		cssmPerror("ocspdFetch", crtn);
599		#endif
600		/* But this is not necessarily fatal...update per-cert status and check
601		 * caller requirements below */
602		goto postOcspd;
603	}
604	memset(&ocspdReplies, 0, sizeof(ocspdReplies));
605	prtn = coder.decodeItem(derOcspdReplies, kSecAsn1OCSPDRepliesTemplate,
606		&ocspdReplies);
607	/* we're done with this, mallocd in ocspdFetch() */
608	vfyCtx.alloc.free(derOcspdReplies.Data);
609	if(prtn) {
610		/*
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.
615		 */
616		tpErrorLog("tpVerifyCertGroupWithOCSP: error decoding ocspd reply\n");
617		pending[0]->subject.addStatusCode(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
618		goto postOcspd;
619	}
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?
625		}
626		goto errOut;
627	}
628
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];
633
634		/* Cook up our version of an OCSPResponse from the encoded data */
635		OCSPResponse *ocspResp = NULL;
636		try {
637			ocspResp = new OCSPResponse(reply->ocspResp, TP_OCSP_CACHE_TTL);
638		}
639		catch(...) {
640			tpErrorLog("tpVerifyCertGroupWithOCSP: error decoding ocsp response\n");
641			/* what the heck, keep going */
642			continue;
643		}
644
645		/*
646		 * Find matching subject cert if possible (it's technically optional for
647		 * verification of the response in some cases, e.g., local responder).
648		 */
649		PendingRequest *pendReq = NULL;				// fully qualified
650		PendingRequest *reqWithIdMatch = NULL;		// CertID match only, not nonce
651		for(unsigned pdex=0; pdex<numCerts; pdex++) {
652
653			/* first check ID match; that is required no matter what */
654			if((pending[pdex])->certID.compareToExist(reply->certID)) {
655				reqWithIdMatch = pending[pdex];
656			}
657			if(reqWithIdMatch == NULL) {
658				continue;
659			}
660			if(!genNonce) {
661				/* that's good enough */
662				pendReq = reqWithIdMatch;
663				tpOcspDebug("OCSP processs reply: CertID match, no nonce");
664				break;
665			}
666			if(tpCompareCssmData(&reqWithIdMatch->nonce, ocspResp->nonce())) {
667				tpOcspDebug("OCSP processs reply: nonce MATCH");
668				pendReq = reqWithIdMatch;
669				break;
670			}
671
672			/*
673			 * In this case we keep going; if we never find a match, then we can
674			 * use reqWithIdMatch if !requireRespNonce.
675			 */
676			tpOcspDebug("OCSP processs reply: certID match, nonce MISMATCH");
677		}
678		if(pendReq == NULL) {
679			if(requireRespNonce) {
680				tpOcspDebug("OCSP processs reply: tossing out response due to "
681						"requireRespNonce");
682				delete ocspResp;
683				if(ourRtn == CSSM_OK) {
684					ourRtn = CSSMERR_APPLETP_OCSP_NONCE_MISMATCH;
685				}
686				continue;
687			}
688			if(reqWithIdMatch != NULL) {
689				/*
690				 * Nonce mismatch but caller thinks that's OK. Log it and proceed.
691				 */
692				assert(genNonce);
693				tpOcspDebug("OCSP processs reply: using bad nonce due to !requireRespNonce");
694				pendReq = reqWithIdMatch;
695				pendReq->subject.addStatusCode(CSSMERR_APPLETP_OCSP_NONCE_MISMATCH);
696			}
697		}
698		TPCertInfo *issuer = NULL;
699		if(pendReq != NULL) {
700			issuer = &pendReq->issuer;
701		}
702
703		/* verify response and either throw out or add to local cache */
704		respStat = tpVerifyOcspResp(vfyCtx, coder, issuer, *ocspResp, crtn);
705		switch(respStat) {
706			case ORS_Good:
707				break;
708			case ORS_Unknown:
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);
713				}
714				delete ocspResp;
715				continue;
716			case ORS_Bad:
717				delete ocspResp;
718				/*
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.
724				 */
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;
731					goto errOut;
732				}
733				respStat = tpVerifyOcspResp(vfyCtx, coder, issuer, *ocspResp, crtn);
734				if(respStat != ORS_Good) {
735					tpErrorLog("tpVerifyCertGroupWithOCSP: verify error after "
736							"flush/refetch\n");
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;
741						}
742					}
743					else {
744						ourRtn = CSSMERR_APPLETP_OCSP_BAD_RESPONSE;
745					}
746					goto errOut;
747				}
748				/* Voila! Recovery. Proceed. */
749				tpOcspDebug("tpVerifyCertGroupWithOCSP: refetch for cert %u SUCCEEDED",
750						dex);
751				break;
752		} /* switch response status */
753
754		if(!cacheWriteDisable) {
755			tpOcspCacheAdd(reply->ocspResp, localResponder);
756		}
757
758		/* attempt to apply to pendReq */
759		if(pendReq != NULL) {
760			OCSPSingleResponse *singleResp =
761				ocspResp->singleResponseFor(pendReq->certID);
762			if(singleResp) {
763				crtn = tpApplySingleResp(*singleResp, pendReq->subject, pendReq->dex,
764					optFlags, vfyCtx.verifyTime, pendReq->processed);
765				if(crtn && (ourRtn == CSSM_OK)) {
766					ourRtn = crtn;
767				}
768				delete singleResp;
769			}
770		}	/* a reply which matches a pending request */
771
772		/*
773		 * Done with this - note local OCSP response cache doesn't store this
774		 * object; it stores an encoded copy.
775		 */
776		delete ocspResp;
777	}	/* for each reply */
778
779postOcspd:
780
781	/*
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
785	 * cert each.
786	 */
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",
792					(unsigned)dex);
793			break;
794		}
795		if(pendReq->processed) {
796			continue;
797		}
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);
802		}
803		if(singleResp) {
804			tpOcspDebug("...tpVerifyCertGroupWithOCSP: localCache (2) hit dex %u",
805					(unsigned)dex);
806			crtn = tpApplySingleResp(*singleResp, pendReq->subject, dex, optFlags,
807					vfyCtx.verifyTime, pendReq->processed);
808			if(crtn) {
809				if(ourRtn == CSSM_OK) {
810					ourRtn = crtn;
811				}
812			}
813			delete singleResp;
814		}
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) {
821				/*
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)
825				 */
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;
832				}
833			}
834			if(responseStatus == CSSM_OK) {
835				/* no response available (as opposed to getting an invalid response) */
836				pendReq->subject.addStatusCode(CSSMERR_APPLETP_OCSP_UNAVAILABLE);
837			}
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);
841				required = true;
842			}
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 */
845				if(pendReq->urls) {
846					tpOcspDebug("tpVerifyCertGroupWithOCSP: OCSP URI present but no valid response for cert %u", dex);
847					required = true;
848				}
849			}
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;
855				}
856			}
857		}
858	}
859errOut:
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 */
864			break;
865		}
866		delete &pendReq->certID;
867		delete pendReq;
868	}
869	return ourRtn;
870}
871