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