]> git.saurik.com Git - apple/security.git/blob - libsecurity_apple_x509_tp/lib/tpOcspVerify.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_apple_x509_tp / lib / tpOcspVerify.cpp
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 */
48 static 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 */
85 static 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 */
159 static 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
265 errOut:
266 delete ocspReq;
267 if(deleteCertID) {
268 delete certID;
269 }
270 return ocspdReq;
271 }
272
273 static 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 */
301 static 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 */
363 static 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
388 class PendingRequest
389 {
390 public:
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
410 PendingRequest::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
425 CSSM_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
779 postOcspd:
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 }
859 errOut:
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 }