1/* 2 * Copyright (c) 2000-2011 Apple Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19/* 20 * TPCertInfo.cpp - TP's private certificate info classes 21 */ 22 23#include "TPCertInfo.h" 24#include "tpdebugging.h" 25#include "tpTime.h" 26#include "certGroupUtils.h" 27#include "TPDatabase.h" 28#include "TPNetwork.h" 29#include <Security/cssmapi.h> 30#include <Security/x509defs.h> 31#include <Security/oidscert.h> 32#include <Security/oidsalg.h> 33#include <string.h> /* for memcmp */ 34#include <security_utilities/threading.h> /* for Mutex */ 35#include <security_utilities/globalizer.h> 36#include <security_utilities/debugging.h> 37#include <security_cdsa_utilities/cssmerrors.h> 38#include <Security/cssmapple.h> 39#include <Security/SecCertificate.h> 40#include <Security/SecImportExport.h> 41#include <Security/SecTrustSettingsPriv.h> 42 43#define tpTimeDbg(args...) secdebug("tpTime", ## args) 44#define tpCertInfoDbg(args...) secdebug("tpCert", ## args) 45 46static const TPClItemCalls tpCertClCalls = 47{ 48 CSSM_CL_CertGetFirstCachedFieldValue, 49 CSSM_CL_CertAbortQuery, 50 CSSM_CL_CertCache, 51 CSSM_CL_CertAbortCache, 52 CSSM_CL_CertVerify, 53 &CSSMOID_X509V1ValidityNotBefore, 54 &CSSMOID_X509V1ValidityNotAfter, 55 CSSMERR_TP_INVALID_CERT_POINTER, 56 CSSMERR_TP_CERT_EXPIRED, 57 CSSMERR_TP_CERT_NOT_VALID_YET 58}; 59 60TPClItemInfo::TPClItemInfo( 61 CSSM_CL_HANDLE clHand, 62 CSSM_CSP_HANDLE cspHand, 63 const TPClItemCalls &clCalls, 64 const CSSM_DATA *itemData, 65 TPItemCopy copyItemData, 66 const char *verifyTime) // may be NULL 67 : 68 mClHand(clHand), 69 mCspHand(cspHand), 70 mClCalls(clCalls), 71 mWeOwnTheData(false), 72 mCacheHand(0), 73 mIssuerName(NULL), 74 mSubjectKeyID(NULL), 75 mAuthorityKeyID(NULL), 76 mItemData(NULL), 77 mSigAlg(CSSM_ALGID_NONE), 78 mNotBefore(NULL), 79 mNotAfter(NULL), 80 mIsExpired(false), 81 mIsNotValidYet(false), 82 mIndex(0) 83{ 84 try { 85 CSSM_RETURN crtn = cacheItem(itemData, copyItemData); 86 if(crtn) { 87 CssmError::throwMe(crtn); 88 } 89 90 /* 91 * Fetch standard fields... 92 * Issuer name assumes same OID for Certs and CRLs! 93 */ 94 crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName); 95 if(crtn) { 96 CssmError::throwMe(crtn); 97 } 98 99 /* 100 * Signing algorithm, infer from TBS algId 101 * Note this assumes that the OID for fetching this field is the 102 * same for CRLs and Certs. 103 */ 104 CSSM_DATA_PTR algField; 105 crtn = fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS, &algField); 106 if(crtn) { 107 releaseResources(); 108 CssmError::throwMe(crtn); 109 } 110 if(algField->Length != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)) { 111 tpErrorLog("TPClItemInfo: bad CSSM_X509_ALGORITHM_IDENTIFIER\n"); 112 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); 113 } 114 CSSM_X509_ALGORITHM_IDENTIFIER *algId = 115 (CSSM_X509_ALGORITHM_IDENTIFIER *)algField->Data; 116 bool algFound = cssmOidToAlg(&algId->algorithm, &mSigAlg); 117 if(!algFound) { 118 tpErrorLog("TPClItemInfo: unknown signature algorithm\n"); 119 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); 120 } 121 if(mSigAlg == CSSM_ALGID_ECDSA_SPECIFIED) { 122 /* Further processing needed to get digest algorithm */ 123 if(decodeECDSA_SigAlgParams(&algId->parameters, &mSigAlg)) { 124 tpErrorLog("TPClItemInfo: incomplete/unknown ECDSA signature algorithm\n"); 125 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); 126 } 127 } 128 freeField(&CSSMOID_X509V1SignatureAlgorithmTBS, algField); 129 130 /* Attempt to fetch authority key id and subject key id, 131 * ignore error if they do not exist. 132 */ 133 fetchField(&CSSMOID_SubjectKeyIdentifier, &mSubjectKeyID); 134 fetchField(&CSSMOID_AuthorityKeyIdentifier, &mAuthorityKeyID); 135 136 fetchNotBeforeAfter(); 137 calculateCurrent(verifyTime); 138 } 139 catch(...) { 140 releaseResources(); 141 throw; 142 } 143} 144 145TPClItemInfo::~TPClItemInfo() 146{ 147 tpCertInfoDbg("TPClItemInfo destruct this %p", this); 148 releaseResources(); 149} 150 151void TPClItemInfo::releaseResources() 152{ 153 if(mWeOwnTheData && (mItemData != NULL)) { 154 tpFreeCssmData(Allocator::standard(), mItemData, CSSM_TRUE); 155 mWeOwnTheData = false; 156 mItemData = NULL; 157 } 158 if(mIssuerName) { 159 freeField(&CSSMOID_X509V1IssuerName, mIssuerName); 160 mIssuerName = NULL; 161 } 162 if(mSubjectKeyID) { 163 freeField(&CSSMOID_SubjectKeyIdentifier, mSubjectKeyID); 164 mSubjectKeyID = NULL; 165 } 166 if(mAuthorityKeyID) { 167 freeField(&CSSMOID_AuthorityKeyIdentifier, mAuthorityKeyID); 168 mAuthorityKeyID = NULL; 169 } 170 if(mCacheHand != 0) { 171 mClCalls.abortCache(mClHand, mCacheHand); 172 mCacheHand = 0; 173 } 174 if(mNotBefore) { 175 CFRelease(mNotBefore); 176 mNotBefore = NULL; 177 } 178 if(mNotAfter) { 179 CFRelease(mNotAfter); 180 mNotAfter = NULL; 181 } 182} 183 184/* fetch arbitrary field from cached cert */ 185CSSM_RETURN TPClItemInfo::fetchField( 186 const CSSM_OID *fieldOid, 187 CSSM_DATA_PTR *fieldData) // mallocd by CL and RETURNED 188{ 189 CSSM_RETURN crtn; 190 191 uint32 NumberOfFields = 0; 192 CSSM_HANDLE resultHand = 0; 193 *fieldData = NULL; 194 195 assert(mClCalls.getField != NULL); 196 assert(mCacheHand != 0); 197 crtn = mClCalls.getField( 198 mClHand, 199 mCacheHand, 200 fieldOid, 201 &resultHand, 202 &NumberOfFields, 203 fieldData); 204 if(crtn) { 205 return crtn; 206 } 207 if(NumberOfFields != 1) { 208 tpCertInfoDbg("TPClItemInfo::fetchField: numFields %d, expected 1\n", 209 (int)NumberOfFields); 210 } 211 mClCalls.abortQuery(mClHand, resultHand); 212 return CSSM_OK; 213} 214 215/* free arbitrary field obtained from fetchField() */ 216CSSM_RETURN TPClItemInfo::freeField( 217 const CSSM_OID *fieldOid, 218 CSSM_DATA_PTR fieldData) 219{ 220 return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData); 221 222} 223 224/* 225 * Verify with an issuer cert - works on certs and CRLs. 226 * Issuer/subject name match already performed by caller. 227 * Optional paramCert is used to provide parameters when issuer 228 * has a partial public key. 229 */ 230CSSM_RETURN TPClItemInfo::verifyWithIssuer( 231 TPCertInfo *issuerCert, 232 TPCertInfo *paramCert /* = NULL */) const 233{ 234 CSSM_RETURN crtn; 235 236 assert(mClHand != 0); 237 assert(issuerCert->isIssuerOf(*this)); 238 assert(mCspHand != 0); 239 240 /* 241 * Special case: detect partial public key right now; don't even 242 * bother trying the cert verify in that case. 243 */ 244 if(issuerCert->hasPartialKey() && (paramCert == NULL)) { 245 /* caller deals with this later */ 246 tpVfyDebug("verifyWithIssuer PUBLIC_KEY_INCOMPLETE"); 247 return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE; 248 } 249 250 CSSM_CC_HANDLE ccHand; 251 crtn = CSSM_CSP_CreateSignatureContext(mCspHand, 252 mSigAlg, 253 NULL, // Access Creds 254 issuerCert->pubKey(), 255 &ccHand); 256 if(crtn != CSSM_OK) { 257 tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n"); 258 CssmError::throwMe(crtn); 259 } 260 if(paramCert != NULL) { 261 assert(issuerCert->hasPartialKey()); 262 263 /* add in parameter-bearing key */ 264 CSSM_CONTEXT_ATTRIBUTE newAttr; 265 266 newAttr.AttributeType = CSSM_ATTRIBUTE_PARAM_KEY; 267 newAttr.AttributeLength = sizeof(CSSM_KEY); 268 newAttr.Attribute.Key = paramCert->pubKey(); 269 crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr); 270 if(crtn) { 271 tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n"); 272 CssmError::throwMe(crtn); 273 } 274 } 275 crtn = mClCalls.itemVerify(mClHand, 276 ccHand, 277 mItemData, 278 NULL, // issuer cert 279 NULL, // VerifyScope 280 0); // ScopeSize 281 282 switch(crtn) { 283 case CSSM_OK: // success 284 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles 285 tpVfyDebug("verifyWithIssuer GOOD"); 286 break; 287 default: 288 /* all others appear here as general cert verify error */ 289 crtn = CSSMERR_TP_VERIFICATION_FAILURE; 290 tpVfyDebug("verifyWithIssuer BAD"); 291 break; 292 } 293 CSSM_DeleteContext(ccHand); 294 return crtn; 295} 296 297CSSM_RETURN TPClItemInfo::cacheItem( 298 const CSSM_DATA *itemData, 299 TPItemCopy copyItemData) 300{ 301 switch(copyItemData) { 302 case TIC_NoCopy: 303 mItemData = const_cast<CSSM_DATA *>(itemData); 304 break; 305 case TIC_CopyData: 306 mItemData = tpMallocCopyCssmData(Allocator::standard(), itemData); 307 mWeOwnTheData = true; 308 break; 309 default: 310 assert(0); 311 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); 312 } 313 314 /* cache the cert/CRL in the CL */ 315 return mClCalls.cacheItem(mClHand, mItemData, &mCacheHand); 316} 317 318/* 319 * Calculate not before/after times as struct tm. Only throws on 320 * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.). 321 * 322 * Only differences between Cert and CRL flavors of this are the 323 * OIDs used to fetch the appropriate before/after times, both of 324 * which are expressed as CSSM_X509_TIME structs for both Certs 325 * and CRLS. 326 */ 327void TPClItemInfo::fetchNotBeforeAfter() 328{ 329 CSSM_DATA_PTR notBeforeField = NULL; 330 CSSM_DATA_PTR notAfterField = NULL; 331 CSSM_RETURN crtn = CSSM_OK; 332 CSSM_X509_TIME *xTime; 333 334 assert(cacheHand() != CSSM_INVALID_HANDLE); 335 crtn = fetchField(mClCalls.notBeforeOid, ¬BeforeField); 336 if(crtn) { 337 tpErrorLog("fetchNotBeforeAfter: GetField error\n"); 338 CssmError::throwMe(mClCalls.invalidItemRtn); 339 } 340 341 /* subsequent errors to errOut */ 342 xTime = (CSSM_X509_TIME *)notBeforeField->Data; 343 if(timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, &mNotBefore)) { 344 tpErrorLog("fetchNotBeforeAfter: malformed notBefore time\n"); 345 crtn = mClCalls.invalidItemRtn; 346 goto errOut; 347 } 348 349 crtn = fetchField(mClCalls.notAfterOid, ¬AfterField); 350 if(crtn) { 351 /* 352 * Tolerate a missing NextUpdate in CRL only 353 */ 354 if(mClCalls.notAfterOid == &CSSMOID_X509V1ValidityNotAfter) { 355 tpErrorLog("fetchNotBeforeAfter: GetField error\n"); 356 crtn = mClCalls.invalidItemRtn; 357 goto errOut; 358 } 359 else { 360 /* 361 * Fake NextUpdate to be "at the end of time" 362 */ 363 timeStringToCfDate(CSSM_APPLE_CRL_END_OF_TIME, 364 strlen(CSSM_APPLE_CRL_END_OF_TIME), 365 &mNotAfter); 366 } 367 } 368 else { 369 xTime = (CSSM_X509_TIME *)notAfterField->Data; 370 if(timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, &mNotAfter)) { 371 tpErrorLog("fetchNotBeforeAfter: malformed notAfter time\n"); 372 crtn = mClCalls.invalidItemRtn; 373 goto errOut; 374 } 375 } 376 crtn = CSSM_OK; 377errOut: 378 if(notAfterField) { 379 freeField(mClCalls.notAfterOid, notAfterField); 380 } 381 if(notBeforeField) { 382 freeField(mClCalls.notBeforeOid, notBeforeField); 383 } 384 if(crtn != CSSM_OK) { 385 CssmError::throwMe(crtn); 386 } 387} 388 389/* 390 * Verify validity (not before/after) by comparing the reference 391 * time (verifyString if present, or "now" if NULL) to the 392 * not before/after fields fetched from the item at construction. 393 * 394 * Called implicitly at construction; can be called again any time 395 * to re-establish validity (e.g. after fetching an item from a cache). 396 * 397 * We use some stdlib time calls over in tpTime.c; the stdlib function 398 * gmtime() is not thread-safe, so we do the protection here. Note that 399 * this makes *our* calls to gmtime() thread-safe, but if the app has 400 * other threads which are also calling gmtime, we're out of luck. 401 */ 402ModuleNexus<Mutex> tpTimeLock; 403 404CSSM_RETURN TPClItemInfo::calculateCurrent( 405 const char *verifyString) 406{ 407 CFDateRef refTime = NULL; 408 409 if(verifyString != NULL) { 410 /* caller specifies verification time base */ 411 if(timeStringToCfDate(verifyString, (unsigned)strlen(verifyString), &refTime)) { 412 tpErrorLog("calculateCurrent: timeStringToCfDate error\n"); 413 return CSSMERR_TP_INVALID_TIMESTRING; 414 } 415 } 416 else { 417 /* time base = right now */ 418 refTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 419 } 420 if(compareTimes(refTime, mNotBefore) < 0) { 421 mIsNotValidYet = true; 422 tpTimeDbg("\nTP_CERT_NOT_VALID_YET: now %g notBefore %g", 423 CFDateGetAbsoluteTime(refTime), CFDateGetAbsoluteTime(mNotBefore)); 424 CFRelease(refTime); 425 return mClCalls.notValidYetRtn; 426 } 427 else { 428 mIsNotValidYet = false; 429 } 430 431 if(compareTimes(refTime, mNotAfter) > 0) { 432 mIsExpired = true; 433 tpTimeDbg("\nTP_CERT_EXPIRED: now %g notBefore %g", 434 CFDateGetAbsoluteTime(refTime), CFDateGetAbsoluteTime(mNotBefore)); 435 CFRelease(refTime); 436 return mClCalls.expiredRtn; 437 } 438 else { 439 mIsExpired = false; 440 CFRelease(refTime); 441 return CSSM_OK; 442 } 443} 444 445 446/* 447 * No default constructor - this is the only way. 448 * This caches the cert and fetches subjectName, issuerName, and 449 * mPublicKey to ensure the incoming certData is well-constructed. 450 */ 451TPCertInfo::TPCertInfo( 452 CSSM_CL_HANDLE clHand, 453 CSSM_CSP_HANDLE cspHand, 454 const CSSM_DATA *certData, 455 TPItemCopy copyCertData, // true: we copy, we free 456 // false - caller owns 457 const char *verifyTime) // may be NULL 458 : 459 TPClItemInfo(clHand, cspHand, tpCertClCalls, certData, 460 copyCertData, verifyTime), 461 mSubjectName(NULL), 462 mPublicKeyData(NULL), 463 mPublicKey(NULL), 464 mIsAnchor(false), 465 mIsFromInputCerts(false), 466 mIsFromNet(false), 467 mNumStatusCodes(0), 468 mStatusCodes(NULL), 469 mUniqueRecord(NULL), 470 mUsed(false), 471 mIsLeaf(false), 472 mIsRoot(TRS_Unknown), 473 mRevCheckGood(false), 474 mRevCheckComplete(false), 475 mTrustSettingsEvaluated(false), 476 mTrustSettingsDomain(kSecTrustSettingsDomainSystem), 477 mTrustSettingsResult(kSecTrustSettingsResultInvalid), 478 mTrustSettingsFoundAnyEntry(false), 479 mTrustSettingsFoundMatchingEntry(false), 480 mAllowedErrs(NULL), 481 mNumAllowedErrs(0), 482 mIgnoredError(false), 483 mTrustSettingsKeyUsage(0), 484 mCertHashStr(NULL) 485{ 486 CSSM_RETURN crtn; 487 488 tpCertInfoDbg("TPCertInfo construct this %p", this); 489 mDlDbHandle.DLHandle = 0; 490 mDlDbHandle.DBHandle = 0; 491 492 /* fetch subject name */ 493 crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName); 494 if(crtn) { 495 /* bad cert */ 496 releaseResources(); 497 CssmError::throwMe(crtn); 498 } 499 500 /* this cert's public key */ 501 crtn = fetchField(&CSSMOID_CSSMKeyStruct, &mPublicKeyData); 502 if(crtn || (mPublicKeyData->Length != sizeof(CSSM_KEY))) { 503 /* bad cert */ 504 releaseResources(); 505 CssmError::throwMe(crtn); 506 } 507 mPublicKey = (CSSM_KEY_PTR)mPublicKeyData->Data; 508 509 /* calculate other commonly used fields */ 510 if(tpCompareCssmData(mSubjectName, issuerName())) { 511 /* 512 * Per Radar 3374978, perform complete signature verification 513 * lazily - just check subject/issuer match here. 514 */ 515 tpAnchorDebug("TPCertInfo potential anchor"); 516 mIsRoot = TRS_NamesMatch; 517 } 518 else { 519 mIsRoot = TRS_NotRoot; 520 } 521} 522 523/* frees mSubjectName, mIssuerName, mCacheHand via mClHand */ 524TPCertInfo::~TPCertInfo() 525{ 526 tpCertInfoDbg("TPCertInfo destruct this %p", this); 527 releaseResources(); 528} 529 530void TPCertInfo::releaseResources() 531{ 532 if(mSubjectName) { 533 freeField(&CSSMOID_X509V1SubjectName, mSubjectName); 534 mSubjectName = NULL; 535 } 536 if(mPublicKeyData) { 537 freeField(&CSSMOID_CSSMKeyStruct, mPublicKeyData); 538 mPublicKey = NULL; 539 mPublicKeyData = NULL; 540 } 541 if(mStatusCodes) { 542 free(mStatusCodes); 543 mStatusCodes = NULL; 544 } 545 if(mAllowedErrs) { 546 free(mAllowedErrs); 547 } 548 if(mCertHashStr) { 549 CFRelease(mCertHashStr); 550 } 551 TPClItemInfo::releaseResources(); 552} 553 554const CSSM_DATA *TPCertInfo::subjectName() 555{ 556 assert(mSubjectName != NULL); 557 return mSubjectName; 558} 559 560/* 561 * Perform semi-lazy evaluation of "rootness". Subject and issuer names 562 * compared at constructor. 563 * If avoidVerify is true, we won't do the signature verify: caller 564 * just wants to know if the subject and issuer names match. 565 */ 566bool TPCertInfo::isSelfSigned(bool avoidVerify) 567{ 568 switch(mIsRoot) { 569 case TRS_NotRoot: // known not to be root 570 return false; 571 case TRS_IsRoot: 572 return true; 573 case TRS_NamesMatch: 574 if(avoidVerify) { 575 return true; 576 } 577 /* else drop through and verify */ 578 case TRS_Unknown: // actually shouldn't happen, but to be safe... 579 default: 580 /* do the signature verify */ 581 if(verifyWithIssuer(this) == CSSM_OK) { 582 tpAnchorDebug("isSelfSigned anchor verified"); 583 mIsRoot = TRS_IsRoot; 584 return true; 585 } 586 else { 587 tpAnchorDebug("isSelfSigned anchor vfy FAIL"); 588 mIsRoot = TRS_NotRoot; 589 return false; 590 } 591 } 592} 593 594/* 595 * Am I the issuer of the specified subject item? Returns true if so. 596 * Works for subject certs as well as CRLs. 597 */ 598bool TPCertInfo::isIssuerOf( 599 const TPClItemInfo &subject) 600{ 601 assert(mSubjectName != NULL); 602 assert(subject.issuerName() != NULL); 603 if(tpCompareCssmData(mSubjectName, subject.issuerName())) { 604 return true; 605 } 606 else { 607 return false; 608 } 609} 610 611/* 612 * Does my subjectKeyID match the authorityKeyID of the specified subject? 613 * Returns true if so (and if both fields are available). 614 */ 615bool TPCertInfo::isAuthorityKeyOf( 616 const TPClItemInfo &subject) 617{ 618 const CSSM_DATA *subjectKeyID = this->subjectKeyID(); 619 const CSSM_DATA *authorityKeyID = subject.authorityKeyID(); 620 if(!subjectKeyID || !authorityKeyID) { 621 tpDebug("isAuthorityKeyOf FALSE (one or both key ids missing)"); 622 return false; 623 } 624 CSSM_X509_EXTENSION *ske = (CSSM_X509_EXTENSION *)subjectKeyID->Data; 625 CSSM_X509_EXTENSION *ake = (CSSM_X509_EXTENSION *)authorityKeyID->Data; 626 if( !ske || ske->format != CSSM_X509_DATAFORMAT_PARSED || 627 !ake || ake->format != CSSM_X509_DATAFORMAT_PARSED || 628 !ske->value.parsedValue || !ake->value.parsedValue) { 629 tpDebug("isAuthorityKeyOf FALSE (no parsed value present)"); 630 return false; 631 } 632 633 const CE_SubjectKeyID *skid = (CE_SubjectKeyID *)ske->value.parsedValue; 634 const CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)ake->value.parsedValue; 635 636 if(!akid->keyIdentifierPresent) { 637 tpDebug("isAuthorityKeyOf FALSE (no key identifier present)"); 638 return false; 639 } 640 if(tpCompareCssmData(skid, &akid->keyIdentifier)) { 641 #ifndef NDEBUG 642 tpDebug("isAuthorityKeyOf TRUE (len:s=%lu/a=%lu, %08lX../%08lX..)", 643 skid->Length, 644 akid->keyIdentifier.Length, 645 (skid->Data) ? *((unsigned long *)skid->Data) : 0L, 646 (akid->keyIdentifier.Data) ? *((unsigned long *)akid->keyIdentifier.Data) : 0L); 647 #endif 648 return true; 649 } 650 else { 651 #ifndef NDEBUG 652 tpDebug("isAuthorityKeyOf FALSE (len:s=%lu/a=%lu, %08lX../%08lX..)", 653 skid->Length, 654 akid->keyIdentifier.Length, 655 (skid->Data) ? *((unsigned long *)skid->Data) : 0L, 656 (akid->keyIdentifier.Data) ? *((unsigned long *)akid->keyIdentifier.Data) : 0L); 657 #endif 658 return false; 659 } 660} 661 662bool TPCertInfo::addStatusCode(CSSM_RETURN code) 663{ 664 mNumStatusCodes++; 665 mStatusCodes = (CSSM_RETURN *)realloc(mStatusCodes, 666 mNumStatusCodes * sizeof(CSSM_RETURN)); 667 mStatusCodes[mNumStatusCodes - 1] = code; 668 return isStatusFatal(code); 669} 670 671bool TPCertInfo::hasStatusCode(CSSM_RETURN code) 672{ 673 for(unsigned dex=0; dex<mNumStatusCodes; dex++) { 674 if(mStatusCodes[dex] == code) { 675 return true; 676 } 677 } 678 return false; 679} 680 681bool TPCertInfo::isStatusFatal(CSSM_RETURN code) 682{ 683 for(unsigned dex=0; dex<mNumAllowedErrs; dex++) { 684 if(mAllowedErrs[dex] == code) { 685 tpTrustSettingsDbg("isStatusFatal(%ld): ALLOWED", (unsigned long)code); 686 mIgnoredError = true; 687 return false; 688 } 689 } 690 return true; 691} 692 693/* 694 * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL 695 * key. 696 */ 697bool TPCertInfo::hasPartialKey() 698{ 699 if(mPublicKey->KeyHeader.KeyAttr & CSSM_KEYATTR_PARTIAL) { 700 return true; 701 } 702 else { 703 return false; 704 } 705} 706 707/* 708 * <rdar://9145531> 709 */ 710bool TPCertInfo::shouldReject() 711{ 712 static unsigned char _UTN_UF_H_ISSUER_BYTES[154] = { 713 0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 714 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 715 0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 716 0x04, 0x07, 0x13, 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 717 0x45, 0x20, 0x43, 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 718 0x55, 0x04, 0x0a, 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 719 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 720 0x52, 0x4b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 721 0x18, 0x48, 0x54, 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 722 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 723 0x4d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 724 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 725 0x54, 0x2d, 0x48, 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45 726 }; 727 CSSM_DATA _UTN_UF_H_ISSUER = { sizeof(_UTN_UF_H_ISSUER_BYTES), _UTN_UF_H_ISSUER_BYTES }; 728 729 static CSSM_DATA _UTN_UF_H_SERIALS[] = { 730 { 17, (uint8*)"\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" }, // amo 731 { 17, (uint8*)"\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" }, // gt 732 { 17, (uint8*)"\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" }, // llc 733 { 17, (uint8*)"\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" }, // lsc 734 { 17, (uint8*)"\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" }, // lyc 735 { 16, (uint8*)"\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" }, // lyc1 736 { 16, (uint8*)"\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" }, // lyc2 737 { 16, (uint8*)"\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" }, // mgc 738 { 17, (uint8*)"\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" }, // wgc 739 { 0, NULL } 740 }; 741 742 const CSSM_DATA *issuer=issuerName(); 743 if(!issuer || !(tpCompareCssmData(issuer, &_UTN_UF_H_ISSUER))) 744 return false; 745 746 CSSM_DATA *serialNumber=NULL; 747 CSSM_RETURN crtn = fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber); 748 if(crtn || !serialNumber) 749 return false; 750 751 CSSM_DATA *p=_UTN_UF_H_SERIALS; 752 bool matched=false; 753 while(p->Length) { 754 if(tpCompareCssmData(serialNumber, p)) { 755 matched=true; 756 addStatusCode(CSSMERR_TP_CERT_REVOKED); 757 break; 758 } 759 ++p; 760 } 761 freeField(&CSSMOID_X509V1SerialNumber, serialNumber); 762 return matched; 763} 764 765/* 766 * Evaluate trust settings; returns true in *foundMatchingEntry if positive 767 * match found - i.e., cert chain construction is done. 768 */ 769OSStatus TPCertInfo::evaluateTrustSettings( 770 const CSSM_OID &policyOid, 771 const char *policyString, // optional 772 uint32 policyStringLen, 773 SecTrustSettingsKeyUsage keyUse, // required 774 bool *foundMatchingEntry, // RETURNED 775 bool *foundAnyEntry) // RETURNED 776{ 777 /* 778 * We might have to force a re-evaluation if the requested key usage 779 * is not a subset of what we already checked for (and cached). 780 */ 781 if(mTrustSettingsEvaluated) { 782 bool doFlush = false; 783 if(mTrustSettingsKeyUsage != kSecTrustSettingsKeyUseAny) { 784 if(keyUse == kSecTrustSettingsKeyUseAny) { 785 /* now want "any", checked something else before */ 786 doFlush = true; 787 } 788 else if((keyUse & mTrustSettingsKeyUsage) != keyUse) { 789 /* want bits that we didn't ask for before */ 790 doFlush = true; 791 } 792 } 793 if(doFlush) { 794 tpTrustSettingsDbg("evaluateTrustSettings: flushing cached trust for " 795 "%p due to keyUse 0x%x", this, (int)keyUse); 796 mTrustSettingsEvaluated = false; 797 mTrustSettingsFoundAnyEntry = false; 798 mTrustSettingsResult = kSecTrustSettingsResultInvalid; 799 mTrustSettingsFoundMatchingEntry = false; 800 if(mAllowedErrs != NULL) { 801 free(mAllowedErrs); 802 } 803 mNumAllowedErrs = 0; 804 } 805 /* else we can safely use the cached values */ 806 } 807 if(!mTrustSettingsEvaluated) { 808 809 if(mCertHashStr == NULL) { 810 const CSSM_DATA *certData = itemData(); 811 mCertHashStr = SecTrustSettingsCertHashStrFromData(certData->Data, 812 certData->Length); 813 } 814 815 OSStatus ortn = SecTrustSettingsEvaluateCert(mCertHashStr, 816 &policyOid, 817 policyString, 818 policyStringLen, 819 keyUse, 820 /* 821 * This is the purpose of the avoidVerify option, right here. 822 * If this is a root cert and it has trust settings, we avoid 823 * the signature verify. If it turns out there are no trust 824 * settings and this is a root, we'll verify the signature 825 * elsewhere (e.g. post_trust_setting: in buildCertGroup()). 826 */ 827 isSelfSigned(true), 828 &mTrustSettingsDomain, 829 &mAllowedErrs, 830 &mNumAllowedErrs, 831 &mTrustSettingsResult, 832 &mTrustSettingsFoundMatchingEntry, 833 &mTrustSettingsFoundAnyEntry); 834 if(ortn) { 835 tpTrustSettingsDbg("evaluateTrustSettings: SecTrustSettingsEvaluateCert error!"); 836 return ortn; 837 } 838 mTrustSettingsEvaluated = true; 839 mTrustSettingsKeyUsage = keyUse; 840 #ifndef NDEBUG 841 if(mTrustSettingsFoundMatchingEntry) { 842 tpTrustSettingsDbg("evaluateTrustSettings: found for %p result %d", 843 this, (int)mTrustSettingsResult); 844 } 845 #endif 846 /* one more thing... */ 847 if(shouldReject()) { 848 return CSSMERR_TP_INVALID_CERTIFICATE; 849 } 850 } 851 *foundMatchingEntry = mTrustSettingsFoundMatchingEntry; 852 *foundAnyEntry = mTrustSettingsFoundAnyEntry; 853 854 return errSecSuccess; 855} 856 857/* true means "verification terminated due to user trust setting" */ 858bool TPCertInfo::trustSettingsFound() 859{ 860 switch(mTrustSettingsResult) { 861 case kSecTrustSettingsResultUnspecified: /* entry but not definitive */ 862 case kSecTrustSettingsResultInvalid: /* no entry */ 863 return false; 864 default: 865 return true; 866 } 867} 868 869/* 870 * Determine if this has an empty SubjectName field. Returns true if so. 871 */ 872bool TPCertInfo::hasEmptySubjectName() 873{ 874 /* 875 * A "pure" empty subject is two bytes (0x30 00) - constructed sequence, 876 * short form length, length 0. We'll be robust and tolerate a missing 877 * field, as well as a possible BER-encoded subject with some extra cruft. 878 */ 879 if((mSubjectName == NULL) || (mSubjectName->Length <= 4)) { 880 return true; 881 } 882 else { 883 return false; 884 } 885} 886 887/* 888 * Free mUniqueRecord if it exists. 889 * This is *not* done in our destructor because this record sometimes 890 * has to persist in the form of a CSSM evidence chain. 891 */ 892void TPCertInfo::freeUniqueRecord() 893{ 894 if(mUniqueRecord == NULL) { 895 return; 896 } 897 tpDbDebug("freeUniqueRecord: freeing cert record %p", mUniqueRecord); 898 CSSM_DL_FreeUniqueRecord(mDlDbHandle, mUniqueRecord); 899} 900 901/*** 902 *** TPCertGroup class 903 ***/ 904 905/* build empty group */ 906TPCertGroup::TPCertGroup( 907 Allocator &alloc, 908 TPGroupOwner whoOwns) : 909 mAlloc(alloc), 910 mCertInfo(NULL), 911 mNumCerts(0), 912 mSizeofCertInfo(0), 913 mWhoOwns(whoOwns) 914{ 915 tpCertInfoDbg("TPCertGroup simple construct this %p", this); 916 /* nothing for now */ 917} 918 919/* 920 * Construct from unordered, untrusted CSSM_CERTGROUP. Resulting 921 * TPCertInfos are more or less in the same order as the incoming 922 * certs, though incoming certs are discarded if they don't parse. 923 * No verification of any sort is performed. 924 */ 925TPCertGroup::TPCertGroup( 926 const CSSM_CERTGROUP &CertGroupFrag, 927 CSSM_CL_HANDLE clHand, 928 CSSM_CSP_HANDLE cspHand, 929 Allocator &alloc, 930 const char *verifyTime, // may be NULL 931 bool firstCertMustBeValid, 932 TPGroupOwner whoOwns) : 933 mAlloc(alloc), 934 mCertInfo(NULL), 935 mNumCerts(0), 936 mSizeofCertInfo(0), 937 mWhoOwns(whoOwns) 938{ 939 tpCertInfoDbg("TPCertGroup hard construct this %p", this); 940 941 /* verify input args */ 942 if(cspHand == CSSM_INVALID_HANDLE) { 943 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE); 944 } 945 if(clHand == CSSM_INVALID_HANDLE) { 946 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE); 947 } 948 if(firstCertMustBeValid) { 949 if( (CertGroupFrag.NumCerts == 0) || 950 (CertGroupFrag.GroupList.CertList[0].Data == NULL) || 951 (CertGroupFrag.GroupList.CertList[0].Length == 0)) { 952 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE); 953 } 954 } 955 if(CertGroupFrag.CertGroupType != CSSM_CERTGROUP_DATA) { 956 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP); 957 } 958 switch(CertGroupFrag.CertType) { 959 case CSSM_CERT_X_509v1: 960 case CSSM_CERT_X_509v2: 961 case CSSM_CERT_X_509v3: 962 break; 963 default: 964 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); 965 } 966 switch(CertGroupFrag.CertEncoding) { 967 case CSSM_CERT_ENCODING_BER: 968 case CSSM_CERT_ENCODING_DER: 969 break; 970 default: 971 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT); 972 } 973 974 /* 975 * Add remaining input certs to mCertInfo. 976 */ 977 TPCertInfo *certInfo = NULL; 978 for(unsigned certDex=0; certDex<CertGroupFrag.NumCerts; certDex++) { 979 try { 980 certInfo = new TPCertInfo(clHand, 981 cspHand, 982 &CertGroupFrag.GroupList.CertList[certDex], 983 TIC_NoCopy, // caller owns 984 verifyTime); 985 } 986 catch (...) { 987 if((certDex == 0) && firstCertMustBeValid) { 988 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE); 989 } 990 /* else just ignore this cert */ 991 continue; 992 } 993 certInfo->index(certDex); 994 appendCert(certInfo); 995 } 996} 997 998/* 999 * Deletes contents of mCertInfo[] if appropriate. 1000 */ 1001TPCertGroup::~TPCertGroup() 1002{ 1003 if(mWhoOwns == TGO_Group) { 1004 unsigned i; 1005 for(i=0; i<mNumCerts; i++) { 1006 delete mCertInfo[i]; 1007 } 1008 } 1009 mAlloc.free(mCertInfo); 1010} 1011 1012/* add/remove/access TPTCertInfo's. */ 1013/* 1014 * NOTE: I am aware that most folks would just use an array<> here, but 1015 * gdb is so lame that it doesn't even let one examine the contents 1016 * of an array<> (or just about anything else in the STL). I prefer 1017 * debuggability over saving a few lines of trivial code. 1018 */ 1019void TPCertGroup::appendCert( 1020 TPCertInfo *certInfo) // appends to end of mCertInfo 1021{ 1022 if(mNumCerts == mSizeofCertInfo) { 1023 if(mSizeofCertInfo == 0) { 1024 /* appending to empty array */ 1025 mSizeofCertInfo = 1; 1026 } 1027 else { 1028 mSizeofCertInfo *= 2; 1029 } 1030 mCertInfo = (TPCertInfo **)mAlloc.realloc(mCertInfo, 1031 mSizeofCertInfo * sizeof(TPCertInfo *)); 1032 } 1033 mCertInfo[mNumCerts++] = certInfo; 1034} 1035 1036TPCertInfo *TPCertGroup::certAtIndex( 1037 unsigned index) 1038{ 1039 if(index > (mNumCerts - 1)) { 1040 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); 1041 } 1042 return mCertInfo[index]; 1043} 1044 1045TPCertInfo *TPCertGroup::removeCertAtIndex( 1046 unsigned index) // doesn't delete the cert, just 1047 // removes it from out list 1048{ 1049 if(index > (mNumCerts - 1)) { 1050 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); 1051 } 1052 TPCertInfo *rtn = mCertInfo[index]; 1053 1054 /* removed requested element and compact remaining array */ 1055 unsigned i; 1056 for(i=index; i<(mNumCerts - 1); i++) { 1057 mCertInfo[i] = mCertInfo[i+1]; 1058 } 1059 mNumCerts--; 1060 return rtn; 1061} 1062 1063TPCertInfo *TPCertGroup::firstCert() 1064{ 1065 if(mNumCerts == 0) { 1066 /* the caller really should not do this... */ 1067 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); 1068 } 1069 else { 1070 return mCertInfo[0]; 1071 } 1072} 1073 1074TPCertInfo *TPCertGroup::lastCert() 1075{ 1076 if(mNumCerts == 0) { 1077 return NULL; 1078 } 1079 else { 1080 return mCertInfo[mNumCerts - 1]; 1081 } 1082} 1083 1084/* build a CSSM_CERTGROUP corresponding with our mCertInfo */ 1085CSSM_CERTGROUP_PTR TPCertGroup::buildCssmCertGroup() 1086{ 1087 CSSM_CERTGROUP_PTR cgrp = 1088 (CSSM_CERTGROUP_PTR)mAlloc.malloc(sizeof(CSSM_CERTGROUP)); 1089 cgrp->NumCerts = mNumCerts; 1090 cgrp->CertGroupType = CSSM_CERTGROUP_DATA; 1091 cgrp->CertType = CSSM_CERT_X_509v3; 1092 cgrp->CertEncoding = CSSM_CERT_ENCODING_DER; 1093 if(mNumCerts == 0) { 1094 /* legal */ 1095 cgrp->GroupList.CertList = NULL; 1096 return cgrp; 1097 } 1098 cgrp->GroupList.CertList = (CSSM_DATA_PTR)mAlloc.calloc(mNumCerts, 1099 sizeof(CSSM_DATA)); 1100 for(unsigned i=0; i<mNumCerts; i++) { 1101 tpCopyCssmData(mAlloc, mCertInfo[i]->itemData(), 1102 &cgrp->GroupList.CertList[i]); 1103 } 1104 return cgrp; 1105} 1106 1107/* build a CSSM_TP_APPLE_EVIDENCE_INFO array */ 1108CSSM_TP_APPLE_EVIDENCE_INFO *TPCertGroup::buildCssmEvidenceInfo() 1109{ 1110 CSSM_TP_APPLE_EVIDENCE_INFO *infoArray; 1111 1112 infoArray = (CSSM_TP_APPLE_EVIDENCE_INFO *)mAlloc.calloc(mNumCerts, 1113 sizeof(CSSM_TP_APPLE_EVIDENCE_INFO)); 1114 for(unsigned i=0; i<mNumCerts; i++) { 1115 TPCertInfo *certInfo = mCertInfo[i]; 1116 CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[i]; 1117 1118 /* first the booleans */ 1119 if(certInfo->isExpired()) { 1120 evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED; 1121 } 1122 if(certInfo->isNotValidYet()) { 1123 evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET; 1124 } 1125 if(certInfo->isAnchor()) { 1126 tpAnchorDebug("buildCssmEvidenceInfo: flagging IS_IN_ANCHORS"); 1127 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS; 1128 } 1129 if(certInfo->dlDbHandle().DLHandle == 0) { 1130 if(certInfo->isFromNet()) { 1131 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_FROM_NET; 1132 } 1133 else if(certInfo->isFromInputCerts()) { 1134 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS; 1135 } 1136 } 1137 /* If trust settings apply to a root, skip verifying the signature */ 1138 bool avoidVerify = false; 1139 switch(certInfo->trustSettingsResult()) { 1140 case kSecTrustSettingsResultTrustRoot: 1141 case kSecTrustSettingsResultTrustAsRoot: 1142 /* these two can be disambiguated by IS_ROOT */ 1143 evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST; 1144 avoidVerify = true; 1145 break; 1146 case kSecTrustSettingsResultDeny: 1147 evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY; 1148 avoidVerify = true; 1149 break; 1150 case kSecTrustSettingsResultUnspecified: 1151 case kSecTrustSettingsResultInvalid: 1152 default: 1153 break; 1154 } 1155 if(certInfo->isSelfSigned(avoidVerify)) { 1156 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT; 1157 } 1158 if(certInfo->ignoredError()) { 1159 evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR; 1160 } 1161 unsigned numCodes = certInfo->numStatusCodes(); 1162 if(numCodes) { 1163 evInfo->NumStatusCodes = numCodes; 1164 evInfo->StatusCodes = (CSSM_RETURN *)mAlloc.calloc(numCodes, 1165 sizeof(CSSM_RETURN)); 1166 for(unsigned j=0; j<numCodes; j++) { 1167 evInfo->StatusCodes[j] = (certInfo->statusCodes())[j]; 1168 } 1169 } 1170 if(evInfo->StatusBits & (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST | 1171 CSSM_CERT_STATUS_TRUST_SETTINGS_DENY | 1172 CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR)) { 1173 /* Something noteworthy happened involving TrustSettings */ 1174 uint32 whichDomain = 0; 1175 switch(certInfo->trustSettingsDomain()) { 1176 case kSecTrustSettingsDomainUser: 1177 whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER; 1178 break; 1179 case kSecTrustSettingsDomainAdmin: 1180 whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN; 1181 break; 1182 case kSecTrustSettingsDomainSystem: 1183 whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM; 1184 break; 1185 } 1186 evInfo->StatusBits |= whichDomain; 1187 } 1188 evInfo->Index = certInfo->index(); 1189 evInfo->DlDbHandle = certInfo->dlDbHandle(); 1190 evInfo->UniqueRecord = certInfo->uniqueRecord(); 1191 } 1192 return infoArray; 1193} 1194 1195/* Given a status for basic construction of a cert group and a status 1196 * of (optional) policy verification, plus the implicit notBefore/notAfter 1197 * status in the certs, calculate a global return code. This just 1198 * encapsulates a policy for CertGroupConstruct and CertGroupVerify. 1199 */ 1200CSSM_RETURN TPCertGroup::getReturnCode( 1201 CSSM_RETURN constructStatus, 1202 CSSM_RETURN policyStatus, 1203 CSSM_APPLE_TP_ACTION_FLAGS actionFlags) 1204{ 1205 if(constructStatus) { 1206 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */ 1207 return constructStatus; 1208 } 1209 1210 bool expired = false; 1211 bool postdated = false; 1212 bool allowExpiredRoot = (actionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT) ? 1213 true : false; 1214 bool allowExpired = (actionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED) ? true : false; 1215 bool allowPostdated = allowExpired; // flag overrides any temporal invalidity 1216 bool requireRevPerCert = (actionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT) ? 1217 true : false; 1218 1219 /* check for expired, not valid yet */ 1220 for(unsigned i=0; i<mNumCerts; i++) { 1221 TPCertInfo *ci = mCertInfo[i]; 1222 /* 1223 * Note avoidVerify = true for isSelfSigned(); if it were appropriate to 1224 * verify the signature, that would have happened in 1225 * buildCssmEvidenceInfo() at the latest. 1226 */ 1227 if(ci->isExpired() && 1228 !(allowExpiredRoot && ci->isSelfSigned(true)) && // allowed globally 1229 ci->isStatusFatal(CSSMERR_TP_CERT_EXPIRED)) { // allowed for this cert 1230 expired = true; 1231 } 1232 if(ci->isNotValidYet() && 1233 ci->isStatusFatal(CSSMERR_TP_CERT_NOT_VALID_YET)) { 1234 postdated = true; 1235 } 1236 } 1237 if(expired && !allowExpired) { 1238 return CSSMERR_TP_CERT_EXPIRED; 1239 } 1240 if(postdated && !allowPostdated) { 1241 return CSSMERR_TP_CERT_NOT_VALID_YET; 1242 } 1243 1244 /* Check for missing revocation check */ 1245 if(requireRevPerCert) { 1246 for(unsigned i=0; i<mNumCerts; i++) { 1247 TPCertInfo *ci = mCertInfo[i]; 1248 if(ci->isSelfSigned(true)) { 1249 /* revocation check meaningless for a root cert */ 1250 tpDebug("getReturnCode: ignoring revocation for self-signed cert %d", i); 1251 continue; 1252 } 1253 if(!ci->revokeCheckGood() && 1254 ci->isStatusFatal(CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK)) { 1255 tpDebug("getReturnCode: FATAL: revocation check incomplete for cert %d", i); 1256 return CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK; 1257 } 1258 #ifndef NDEBUG 1259 else { 1260 tpDebug("getReturnCode: revocation check %s for cert %d", 1261 (ci->revokeCheckGood()) ? "GOOD" : "OK", i); 1262 } 1263 #endif 1264 } 1265 } 1266 return policyStatus; 1267} 1268 1269/* set all TPCertInfo.mUsed flags false */ 1270void TPCertGroup::setAllUnused() 1271{ 1272 for(unsigned dex=0; dex<mNumCerts; dex++) { 1273 mCertInfo[dex]->used(false); 1274 } 1275} 1276 1277/* 1278 * See if the specified error status is allowed (return true) or 1279 * fatal (return false) per each cert's mAllowedErrs[]. Returns 1280 * true if any cert returns false for its isStatusFatal() call. 1281 * The list of errors which can apply to cert-chain-wide allowedErrors 1282 * is right here; if the incoming error is not in that list, we 1283 * return false. If the incoming error code is CSSM_OK we return 1284 * true as a convenience for our callers. 1285 */ 1286bool TPCertGroup::isAllowedError( 1287 CSSM_RETURN code) 1288{ 1289 switch(code) { 1290 case CSSM_OK: 1291 return true; 1292 case CSSMERR_TP_NOT_TRUSTED: 1293 case CSSMERR_TP_INVALID_ANCHOR_CERT: 1294 case CSSMERR_TP_VERIFY_ACTION_FAILED: 1295 case CSSMERR_TP_INVALID_CERT_AUTHORITY: 1296 case CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH: 1297 case CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH: 1298 /* continue processing these candidates */ 1299 break; 1300 default: 1301 /* not a candidate for cert-chain-wide allowedErrors */ 1302 return false; 1303 } 1304 1305 for(unsigned dex=0; dex<mNumCerts; dex++) { 1306 if(!mCertInfo[dex]->isStatusFatal(code)) { 1307 tpTrustSettingsDbg("TPCertGroup::isAllowedError: allowing for cert %u", 1308 dex); 1309 return true; 1310 } 1311 } 1312 1313 /* every cert thought this was fatal; it is. */ 1314 return false; 1315} 1316 1317/* 1318 * Determine if we already have the specified cert in this group. 1319 */ 1320bool TPCertGroup::isInGroup(TPCertInfo &certInfo) 1321{ 1322 for(unsigned dex=0; dex<mNumCerts; dex++) { 1323 if(tpCompareCssmData(certInfo.itemData(), mCertInfo[dex]->itemData())) { 1324 return true; 1325 } 1326 } 1327 return false; 1328} 1329 1330/* 1331 * Encode issuing certs in this group as a PEM-encoded data blob. 1332 * Caller must free. 1333 */ 1334void TPCertGroup::encodeIssuers(CSSM_DATA &issuers) 1335{ 1336 /* FIXME: probably want to rewrite this using pemEncode() from libsecurity_cdsa_utils, 1337 * since use of Sec* APIs from this layer violates the API reentrancy contract. 1338 */ 1339 issuers.Data = NULL; 1340 issuers.Length = 0; 1341 CFMutableArrayRef certArray = CFArrayCreateMutable(kCFAllocatorDefault, 1342 0, &kCFTypeArrayCallBacks); 1343 if(!certArray) { 1344 return; 1345 } 1346 for(unsigned certDex=0; certDex<mNumCerts; certDex++) { 1347 TPCertInfo *certInfo = certAtIndex(certDex); 1348 if(!certDex && mNumCerts > 1) { 1349 continue; /* don't need the leaf */ 1350 } 1351 CSSM_DATA *cssmData = (CSSM_DATA*)((certInfo) ? certInfo->itemData() : NULL); 1352 if(!cssmData || !cssmData->Data || !cssmData->Length) { 1353 continue; 1354 } 1355 CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 1356 (const UInt8 *)cssmData->Data, cssmData->Length, 1357 kCFAllocatorNull); 1358 if(!dataRef) { 1359 continue; 1360 } 1361 SecCertificateRef certRef = SecCertificateCreateWithData(kCFAllocatorDefault, 1362 dataRef); 1363 if(!certRef) { 1364 CFRelease(dataRef); 1365 continue; 1366 } 1367 CFArrayAppendValue(certArray, certRef); 1368 CFRelease(certRef); 1369 CFRelease(dataRef); 1370 } 1371 CFDataRef exportedPEMData = NULL; 1372 OSStatus status = SecItemExport(certArray, 1373 kSecFormatPEMSequence, 1374 kSecItemPemArmour, 1375 NULL, 1376 &exportedPEMData); 1377 CFRelease(certArray); 1378 1379 if(!status) { 1380 uint8 *dataPtr = (uint8*)CFDataGetBytePtr(exportedPEMData); 1381 size_t dataLen = CFDataGetLength(exportedPEMData); 1382 issuers.Data = (uint8*)malloc(dataLen); 1383 memmove(issuers.Data, dataPtr, dataLen); 1384 issuers.Length = dataLen; 1385 CFRelease(exportedPEMData); 1386 } 1387} 1388 1389/* 1390 * Search unused incoming certs to find an issuer of specified cert or CRL. 1391 * WARNING this assumes a valid "used" state for all certs in this group. 1392 * If partialIssuerKey is true on return, caller must re-verify signature 1393 * of subject later when sufficient info is available. 1394 */ 1395TPCertInfo *TPCertGroup::findIssuerForCertOrCrl( 1396 const TPClItemInfo &subject, 1397 bool &partialIssuerKey) 1398{ 1399 partialIssuerKey = false; 1400 TPCertInfo *expiredIssuer = NULL; 1401 TPCertInfo *unmatchedKeyIDIssuer = NULL; 1402 1403 for(unsigned certDex=0; certDex<mNumCerts; certDex++) { 1404 TPCertInfo *certInfo = certAtIndex(certDex); 1405 1406 /* has this one already been used in this search? */ 1407 if(certInfo->used()) { 1408 continue; 1409 } 1410 1411 /* subject/issuer names match? */ 1412 if(certInfo->isIssuerOf(subject)) { 1413 /* yep, do a sig verify */ 1414 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig"); 1415 CSSM_RETURN crtn = subject.verifyWithIssuer(certInfo); 1416 switch(crtn) { 1417 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: 1418 /* issuer OK, check sig later */ 1419 partialIssuerKey = true; 1420 /* and fall thru */ 1421 case CSSM_OK: 1422 /* 1423 * Temporal validity check: if we're not already holding an expired 1424 * issuer, and this one's invalid, hold it and keep going. 1425 */ 1426 if((crtn == CSSM_OK) && (expiredIssuer == NULL)) { 1427 if(certInfo->isExpired() || certInfo->isNotValidYet()) { 1428 tpDebug("findIssuerForCertOrCrl: holding expired cert %p", 1429 certInfo); 1430 expiredIssuer = certInfo; 1431 break; 1432 } 1433 } 1434 /* Authority key identifier check: if we can't match subject key id, 1435 * hold onto this cert and keep going. 1436 */ 1437 if(unmatchedKeyIDIssuer == NULL) { 1438 if(!certInfo->isAuthorityKeyOf(subject)) { 1439 tpDebug("findIssuerForCertOrCrl: holding issuer without key id match %p", 1440 certInfo); 1441 unmatchedKeyIDIssuer = certInfo; 1442 break; 1443 } 1444 } 1445 /* YES */ 1446 certInfo->used(true); 1447 return certInfo; 1448 default: 1449 /* just skip this one and keep looking */ 1450 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG"); 1451 break; 1452 } 1453 } /* names match */ 1454 } 1455 if(unmatchedKeyIDIssuer != NULL) { 1456 /* OK, we'll use this one (preferred over an expired issuer) */ 1457 tpDbDebug("findIssuerForCertOrCrl: using issuer without key id match %p", unmatchedKeyIDIssuer); 1458 unmatchedKeyIDIssuer->used(true); 1459 return unmatchedKeyIDIssuer; 1460 } 1461 if(expiredIssuer != NULL) { 1462 /* OK, we'll use this one */ 1463 tpDbDebug("findIssuerForCertOrCrl: using expired cert %p", expiredIssuer); 1464 expiredIssuer->used(true); 1465 return expiredIssuer; 1466 } 1467 1468 /* not found */ 1469 return NULL; 1470} 1471 1472/* 1473 * Construct ordered, verified cert chain from a variety of inputs. 1474 * Time validity does not affect the function return or any status, 1475 * we always try to find a valid cert to replace an expired or 1476 * not-yet-valid cert if we can. Final temporal validity of each 1477 * cert must be checked by caller (it's stored in each TPCertInfo 1478 * we add to ourself during construction). 1479 * 1480 * Only possible error returns are: 1481 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial 1482 * public key, rendering full verification impossible. 1483 * CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial 1484 * public key and which failed to perform subsequent signature 1485 * verification. 1486 * 1487 * Other interesting status is returned via the verifiedToRoot and 1488 * verifiedToAnchor flags. 1489 * 1490 * NOTE: is it the caller's responsibility to call setAllUnused() for both 1491 * incoming cert groups (inCertGroup and gatheredCerts). We don't do that 1492 * here because we may call ourself recursively. 1493 */ 1494CSSM_RETURN TPCertGroup::buildCertGroup( 1495 const TPClItemInfo &subjectItem, // Cert or CRL 1496 TPCertGroup *inCertGroup, // optional 1497 const CSSM_DL_DB_LIST *dbList, // optional 1498 CSSM_CL_HANDLE clHand, 1499 CSSM_CSP_HANDLE cspHand, 1500 const char *verifyTime, // optional, for establishing 1501 // validity of new TPCertInfos 1502 /* trusted anchors, optional */ 1503 /* FIXME - maybe this should be a TPCertGroup */ 1504 uint32 numAnchorCerts, 1505 const CSSM_DATA *anchorCerts, 1506 1507 /* 1508 * Certs to be freed by caller (i.e., TPCertInfo which we allocate 1509 * as a result of using a cert from anchorCerts or dbList) are added 1510 * to this group. 1511 */ 1512 TPCertGroup &certsToBeFreed, 1513 1514 /* 1515 * Other certificates gathered during the course of this operation, 1516 * currently consisting of certs fetched from DBs and from the net. 1517 * This is not used when called by AppleTPSession::CertGroupConstructPriv; 1518 * it's an optimization for the case when we're building a cert group 1519 * for TPCrlInfo::verifyWithContext - we avoid re-fetching certs from 1520 * the net which are needed to verify both the subject cert and a CRL. 1521 * We don't modify this TPCertGroup, we only use certs from it. 1522 */ 1523 TPCertGroup *gatheredCerts, 1524 1525 /* 1526 * Indicates that subjectItem is a cert in this cert group. 1527 * If true, that cert will be tested for "root-ness", including 1528 * -- subject/issuer compare 1529 * -- signature self-verify 1530 * -- anchor compare 1531 */ 1532 CSSM_BOOL subjectIsInGroup, 1533 1534 /* 1535 * CSSM_TP_ACTION_FETCH_CERT_FROM_NET, 1536 * CSSM_TP_ACTION_TRUST_SETTING, 1537 * CSSM_TP_ACTION_IMPLICIT_ANCHORS are interesting 1538 */ 1539 CSSM_APPLE_TP_ACTION_FLAGS actionFlags, 1540 1541 /* CSSM_TP_ACTION_TRUST_SETTING parameters */ 1542 const CSSM_OID *policyOid, 1543 const char *policyStr, 1544 uint32 policyStrLen, 1545 SecTrustSettingsKeyUsage leafKeyUse, // usage of *first* cert in chain 1546 1547 /* returned */ 1548 CSSM_BOOL &verifiedToRoot, // end of chain self-verifies 1549 CSSM_BOOL &verifiedToAnchor, // end of chain in anchors 1550 CSSM_BOOL &verifiedViaTrustSettings) // chain ends per User Trust setting 1551{ 1552 const TPClItemInfo *thisSubject = &subjectItem; 1553 CSSM_RETURN crtn = CSSM_OK; 1554 TPCertInfo *issuerCert = NULL; 1555 unsigned certDex; 1556 TPCertInfo *anchorInfo = NULL; 1557 bool foundPartialIssuer = false; 1558 bool attemptNetworkFetch = false; 1559 CSSM_BOOL firstSubjectIsInGroup = subjectIsInGroup; 1560 TPCertInfo *endCert; 1561 1562 tpVfyDebug("buildCertGroup top"); 1563 1564 /* possible expired root which we'll only use if we can't find 1565 * a better one */ 1566 TPCertInfo *expiredRoot = NULL; 1567 1568 /* and the general case of an expired or not yet valid cert */ 1569 TPCertInfo *expiredIssuer = NULL; 1570 1571 /* and the case of an issuer without a matching subject key id */ 1572 TPCertInfo *unmatchedKeyIDIssuer = NULL; 1573 1574 verifiedToRoot = CSSM_FALSE; 1575 verifiedToAnchor = CSSM_FALSE; 1576 verifiedViaTrustSettings = CSSM_FALSE; 1577 1578 /*** main loop to seach inCertGroup and dbList *** 1579 * 1580 * Exit loop on: 1581 * -- find a root cert in the chain (self-signed) 1582 * -- find a non-root cert which is also in the anchors list 1583 * -- find a cert which is trusted per Trust Settings (if enabled) 1584 * -- memory error 1585 * -- or no more certs to add to chain. 1586 */ 1587 for(;;) { 1588 /* 1589 * Top of loop: thisSubject is the item we're trying to verify. 1590 */ 1591 1592 /* is thisSubject a root cert or listed in user trust list? */ 1593 if(subjectIsInGroup) { 1594 TPCertInfo *subjCert = lastCert(); 1595 assert(subjCert != NULL); 1596 1597 if(actionFlags & CSSM_TP_ACTION_TRUST_SETTINGS) { 1598 assert(policyOid != NULL); 1599 1600 /* 1601 * Figure out key usage. If this is a leaf cert, the caller - actually 1602 * the per-policy code - inferred the usage. Else it could be for 1603 * verifying a cert or a CRL. 1604 * 1605 * We want to avoid multiple calls to the effective portion of 1606 * evaluateTrustSettings(), but a CA cert could be usable for only 1607 * signing certs and not CRLs. Thus we're evaluating a CA cert, 1608 * try to evaluate for signing certs *and* CRLs in case we come 1609 * this way again later when performing CRL verification. If that 1610 * fails, then retry with just cert signing. 1611 */ 1612 SecTrustSettingsKeyUsage localKeyUse; 1613 bool doRetry = false; 1614 if(subjCert == firstCert()) { 1615 /* leaf - use caller's spec */ 1616 localKeyUse = leafKeyUse; 1617 /* FIXME - add in CRL if this is cert checking? */ 1618 } 1619 else { 1620 localKeyUse = kSecTrustSettingsKeyUseSignCert | kSecTrustSettingsKeyUseSignRevocation; 1621 /* and if necessary */ 1622 doRetry = true; 1623 } 1624 /* this lets us avoid searching for the same thing twice when there 1625 * is in fact no entry for it */ 1626 bool foundEntry = false; 1627 bool trustSettingsFound = false; 1628 OSStatus ortn = subjCert->evaluateTrustSettings(*policyOid, 1629 policyStr, policyStrLen, localKeyUse, &trustSettingsFound, &foundEntry); 1630 if(ortn) { 1631 /* this is only a dire error */ 1632 crtn = ortn; 1633 goto final_out; 1634 } 1635 if(!trustSettingsFound && foundEntry && doRetry) { 1636 tpTrustSettingsDbg("buildCertGroup: retrying evaluateTrustSettings with Cert only"); 1637 ortn = subjCert->evaluateTrustSettings(*policyOid, 1638 policyStr, policyStrLen, kSecTrustSettingsKeyUseSignCert, 1639 &trustSettingsFound, &foundEntry); 1640 if(ortn) { 1641 crtn = ortn; 1642 goto final_out; 1643 } 1644 } 1645 if(trustSettingsFound) { 1646 switch(subjCert->trustSettingsResult()) { 1647 case kSecTrustSettingsResultInvalid: 1648 /* should not happen... */ 1649 assert(0); 1650 crtn = CSSMERR_TP_INTERNAL_ERROR; 1651 break; 1652 case kSecTrustSettingsResultTrustRoot: 1653 case kSecTrustSettingsResultTrustAsRoot: 1654 tpTrustSettingsDbg("Trust[As]Root found"); 1655 crtn = CSSM_OK; 1656 break; 1657 case kSecTrustSettingsResultDeny: 1658 tpTrustSettingsDbg("TrustResultDeny found"); 1659 crtn = CSSMERR_APPLETP_TRUST_SETTING_DENY; 1660 break; 1661 case kSecTrustSettingsResultUnspecified: 1662 /* special case here: this means "keep going, we don't trust or 1663 * distrust this cert". Typically used to express allowed errors 1664 * only. 1665 */ 1666 tpTrustSettingsDbg("TrustResultUnspecified found"); 1667 goto post_trust_setting; 1668 default: 1669 tpTrustSettingsDbg("Unknown TrustResult (%d)", 1670 (int)subjCert->trustSettingsResult()); 1671 crtn = CSSMERR_TP_INTERNAL_ERROR; 1672 break; 1673 } 1674 /* cleanup partial key processing */ 1675 verifiedViaTrustSettings = CSSM_TRUE; 1676 goto final_out; 1677 } 1678 } /* CSSM_TP_ACTION_TRUST_SETTING */ 1679 1680post_trust_setting: 1681 if(subjCert->isSelfSigned()) { 1682 /* We're at the end of the chain. */ 1683 verifiedToRoot = CSSM_TRUE; 1684 1685 /* 1686 * Special case if this root is temporally invalid (and it's not 1687 * the leaf): remove it from the outgoing cert group, save it, 1688 * and proceed, looking another (good) root in anchors. 1689 * There's no way we'll find another good one in this loop. 1690 */ 1691 if((subjCert->isExpired() || subjCert->isNotValidYet()) && 1692 (!firstSubjectIsInGroup || (mNumCerts > 1))) { 1693 tpDebug("buildCertGroup: EXPIRED ROOT %p, looking for good one", subjCert); 1694 expiredRoot = subjCert; 1695 if(mNumCerts) { 1696 /* roll back to previous cert */ 1697 mNumCerts--; 1698 } 1699 if(mNumCerts == 0) { 1700 /* roll back to caller's initial condition */ 1701 thisSubject = &subjectItem; 1702 } 1703 else { 1704 thisSubject = lastCert(); 1705 } 1706 } 1707 break; /* out of main loop */ 1708 } /* root */ 1709 1710 /* 1711 * If this non-root cert is in the provided anchors list, 1712 * we can stop building the chain at this point. 1713 * 1714 * If this cert is a leaf, the chain ends in an anchor, but if it's 1715 * also temporally invalid, we can't do anything further. However, 1716 * if it's not a leaf, then we need to roll back the chain to a 1717 * point just before this cert, so Case 1 will subsequently find 1718 * the anchor (and handle the anchor correctly if it's expired.) 1719 */ 1720 if(numAnchorCerts && anchorCerts) { 1721 bool foundNonRootAnchor = false; 1722 for(certDex=0; certDex<numAnchorCerts; certDex++) { 1723 if(tp_CompareCerts(subjCert->itemData(), &anchorCerts[certDex])) { 1724 foundNonRootAnchor = true; 1725 /* if it's not the leaf, remove it from the outgoing cert group. */ 1726 if(!firstSubjectIsInGroup || (mNumCerts > 1)) { 1727 if(mNumCerts) { 1728 /* roll back to previous cert */ 1729 mNumCerts--; 1730 } 1731 if(mNumCerts == 0) { 1732 /* roll back to caller's initial condition */ 1733 thisSubject = &subjectItem; 1734 } 1735 else { 1736 thisSubject = lastCert(); 1737 } 1738 tpAnchorDebug("buildCertGroup: CA cert in input AND anchors"); 1739 } /* not leaf */ 1740 else { 1741 if(subjCert->isExpired() || subjCert->isNotValidYet()) { 1742 crtn = CSSM_CERT_STATUS_EXPIRED; 1743 } else { 1744 crtn = CSSM_OK; 1745 } 1746 subjCert->isAnchor(true); 1747 verifiedToAnchor = CSSM_TRUE; 1748 tpAnchorDebug("buildCertGroup: leaf cert in input AND anchors"); 1749 } /* leaf */ 1750 break; /* out of anchor-checking loop */ 1751 } 1752 } 1753 if(foundNonRootAnchor) { 1754 break; /* out of main loop */ 1755 } 1756 } /* non-root */ 1757 1758 } /* subjectIsInGroup */ 1759 1760 /* 1761 * Search unused incoming certs to find an issuer. 1762 * Both cert groups are optional. 1763 * We'll add issuer to outCertGroup below. 1764 * If we find a cert that's expired or not yet valid, we hold on to it 1765 * and look for a better one. If we don't find it here we drop back to the 1766 * expired one at the end of the loop. If that expired cert is a root 1767 * cert, we'll use the expiredRoot mechanism (see above) to roll back and 1768 * see if we can find a good root in the incoming anchors. 1769 */ 1770 if(inCertGroup != NULL) { 1771 bool partial = false; 1772 issuerCert = inCertGroup->findIssuerForCertOrCrl(*thisSubject, 1773 partial); 1774 if(issuerCert) { 1775 issuerCert->isFromInputCerts(true); 1776 if(partial) { 1777 /* deal with this later */ 1778 foundPartialIssuer = true; 1779 tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup"); 1780 } 1781 else { 1782 tpDebug("buildCertGroup: Cert FOUND in inCertGroup"); 1783 } 1784 } 1785 } 1786 1787 if(issuerCert != NULL) { 1788 bool stashedIssuer = false; 1789 /* Check whether candidate issuer is expired or not yet valid */ 1790 if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { 1791 if(expiredIssuer == NULL) { 1792 tpDebug("buildCertGroup: saving expired cert %p (1)", issuerCert); 1793 expiredIssuer = issuerCert; 1794 stashedIssuer = true; 1795 } 1796 /* else we already have an expired issuer candidate */ 1797 } 1798 else { 1799 /* unconditionally done with possible expiredIssuer */ 1800 #ifndef NDEBUG 1801 if(expiredIssuer != NULL) { 1802 tpDebug("buildCertGroup: DISCARDING expired cert %p (1)", expiredIssuer); 1803 } 1804 #endif 1805 expiredIssuer = NULL; 1806 } 1807 /* Check whether candidate issuer failed to match authority key id in thisSubject */ 1808 if(!issuerCert->isAuthorityKeyOf(*thisSubject)) { 1809 if(unmatchedKeyIDIssuer == NULL) { 1810 tpDebug("buildCertGroup: saving unmatched key id issuer %p (1)", issuerCert); 1811 unmatchedKeyIDIssuer = issuerCert; 1812 stashedIssuer = true; 1813 } 1814 /* else we already have an unmatched key id issuer candidate */ 1815 } 1816 else { 1817 /* unconditionally done with possible unmatchedKeyIDIssuer */ 1818 #ifndef NDEBUG 1819 if(unmatchedKeyIDIssuer != NULL) { 1820 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (1)", unmatchedKeyIDIssuer); 1821 } 1822 #endif 1823 unmatchedKeyIDIssuer = NULL; 1824 } 1825 if(stashedIssuer) { 1826 issuerCert = NULL; /* keep looking */ 1827 } 1828 } 1829 1830 if((issuerCert == NULL) && (gatheredCerts != NULL)) { 1831 bool partial = false; 1832 issuerCert = gatheredCerts->findIssuerForCertOrCrl(*thisSubject, 1833 partial); 1834 if(issuerCert) { 1835 if(partial) { 1836 /* deal with this later */ 1837 foundPartialIssuer = true; 1838 tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts"); 1839 } 1840 else { 1841 tpDebug("buildCertGroup: Cert FOUND in gatheredCerts"); 1842 } 1843 } 1844 } 1845 1846 if(issuerCert != NULL) { 1847 bool stashedIssuer = false; 1848 /* Check whether candidate issuer is expired or not yet valid */ 1849 if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { 1850 if(expiredIssuer == NULL) { 1851 tpDebug("buildCertGroup: saving expired cert %p (2)", issuerCert); 1852 expiredIssuer = issuerCert; 1853 stashedIssuer = true; 1854 } 1855 /* else we already have an expired issuer candidate */ 1856 } 1857 else { 1858 /* unconditionally done with possible expiredIssuer */ 1859 #ifndef NDEBUG 1860 if(expiredIssuer != NULL) { 1861 tpDebug("buildCertGroup: DISCARDING expired cert %p (2)", expiredIssuer); 1862 } 1863 #endif 1864 expiredIssuer = NULL; 1865 } 1866 /* Check whether candidate issuer failed to match authority key id in thisSubject */ 1867 if(!issuerCert->isAuthorityKeyOf(*thisSubject)) { 1868 if(unmatchedKeyIDIssuer == NULL) { 1869 tpDebug("buildCertGroup: saving unmatched key id issuer %p (2)", issuerCert); 1870 unmatchedKeyIDIssuer = issuerCert; 1871 stashedIssuer = true; 1872 } 1873 /* else we already have an unmatched key id issuer candidate */ 1874 } 1875 else { 1876 /* unconditionally done with possible unmatchedKeyIdIssuer */ 1877 #ifndef NDEBUG 1878 if(unmatchedKeyIDIssuer != NULL) { 1879 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (2)", unmatchedKeyIDIssuer); 1880 } 1881 #endif 1882 unmatchedKeyIDIssuer = NULL; 1883 } 1884 if(stashedIssuer) { 1885 issuerCert = NULL; /* keep looking */ 1886 } 1887 } 1888 1889 if((issuerCert == NULL) && (dbList != NULL)) { 1890 /* Issuer not in incoming cert group or gathered certs. Search DBList. */ 1891 bool partial = false; 1892 try { 1893 issuerCert = tpDbFindIssuerCert(mAlloc, 1894 clHand, 1895 cspHand, 1896 thisSubject, 1897 dbList, 1898 verifyTime, 1899 partial); 1900 } 1901 catch (...) {} 1902 1903 if(issuerCert) { 1904 /* unconditionally done with possible expiredIssuer */ 1905 #ifndef NDEBUG 1906 if(expiredIssuer != NULL) { 1907 tpDebug("buildCertGroup: DISCARDING expired cert %p (3)", expiredIssuer); 1908 } 1909 #endif 1910 expiredIssuer = NULL; 1911 /* unconditionally done with possible unmatchedKeyIDIssuer */ 1912 #ifndef NDEBUG 1913 if(unmatchedKeyIDIssuer != NULL) { 1914 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (3)", unmatchedKeyIDIssuer); 1915 } 1916 #endif 1917 unmatchedKeyIDIssuer = NULL; 1918 1919 1920 /* 1921 * Handle Radar 4566041, endless loop of cross-signed certs. 1922 * This can only happen when fetching certs from a DLDB or 1923 * from the net; we prevent that from happening when the certs 1924 * are in inCertGroup or gatheredCerts by keeping track of those 1925 * certs' mUsed state. 1926 */ 1927 if(isInGroup(*issuerCert)) { 1928 tpDebug("buildCertGroup: Multiple instances of cert"); 1929 delete issuerCert; 1930 issuerCert = NULL; 1931 } 1932 else { 1933 /* caller must free */ 1934 certsToBeFreed.appendCert(issuerCert); 1935 if(partial) { 1936 /* deal with this later */ 1937 foundPartialIssuer = true; 1938 tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList"); 1939 } 1940 else { 1941 tpDebug("buildCertGroup: Cert FOUND in dbList"); 1942 } 1943 } 1944 } 1945 } /* searching DLDB list */ 1946 1947 /* 1948 * Note: we don't handle an expired cert returned from tpDbFindIssuerCert() 1949 * in any special way like we do with findIssuerForCertOrCrl(). 1950 * tpDbFindIssuerCert() does its best to give us a temporally valid cert; if 1951 * it returns an expired cert (or, if findIssuerForCertOrCrl() gave us an 1952 * expired cert and tpDbFindIssuerCert() could not do any better), that's all 1953 * we have to work with at this point. We'll go back to the top of the loop 1954 * and apply trust settings if enabled; if an expired cert is trusted per 1955 * Trust Settings, we're done. (Note that anchors are fetched from a DLDB 1956 * when Trust Settings are enabled, so even if two roots with the same key 1957 * and subject name are in DLDBs, and one of them is expired, we'll have the 1958 * good one at this time because of tpDbFindIssuerCert()'s ability to find 1959 * the best cert.) 1960 * 1961 * If Trust Settings are not enabled, and we have an expired root at this 1962 * point, the expiredRoot mechanism is used to roll back and search for 1963 * an anchor that verifies the last good cert. 1964 */ 1965 1966 if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and 1967 * we don't have a good one */ 1968 (unmatchedKeyIDIssuer != NULL)) { /* but we have an unmatched keyID candidate */ 1969 /* 1970 * OK, we'll take the unmatched key id issuer. 1971 * Note we don't have to free unmatchedKeyIDIssuer if we found a good one since 1972 * unmatchedKeyIDIssuer can only come from inCertGroup or gatheredCerts (not from 1973 * dbList). 1974 */ 1975 tpDebug("buildCertGroup: USING unmatched key id issuer %p", unmatchedKeyIDIssuer); 1976 issuerCert = unmatchedKeyIDIssuer; 1977 unmatchedKeyIDIssuer = NULL; 1978 } 1979 if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and 1980 * we don't have a good one */ 1981 (expiredIssuer != NULL)) { /* but we have an expired candidate */ 1982 /* 1983 * OK, we'll take the expired issuer. 1984 * Note we don't have to free expiredIssuer if we found a good one since 1985 * expiredIssuer can only come from inCertGroup or gatheredCerts (not from 1986 * dbList). 1987 */ 1988 tpDebug("buildCertGroup: USING expired cert %p", expiredIssuer); 1989 issuerCert = expiredIssuer; 1990 expiredIssuer = NULL; 1991 } 1992 if(issuerCert == NULL) { 1993 /* end of search, broken chain */ 1994 break; 1995 } 1996 1997 /* 1998 * One way or the other, we've found a cert which verifies subjectCert. 1999 * Add the issuer to outCertGroup and make it the new thisSubject for 2000 * the next pass. 2001 */ 2002 appendCert(issuerCert); 2003 thisSubject = issuerCert; 2004 subjectIsInGroup = CSSM_TRUE; 2005 issuerCert = NULL; 2006 } /* main loop */ 2007 2008 /* 2009 * This can be NULL if we're evaluating a CRL (and we haven't 2010 * gotten very far). 2011 */ 2012 endCert = lastCert(); 2013 2014 /* 2015 * This, on the other hand, is always valid. It could be a CRL. 2016 */ 2017 assert(thisSubject != NULL); 2018 2019 if( (actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) && 2020 ( (endCert && endCert->isSelfSigned()) || expiredRoot) ) { 2021 /* 2022 * Caller will be satisfied with this; skip further anchor processing. 2023 */ 2024 tpAnchorDebug("buildCertGroup: found IMPLICIT anchor"); 2025 goto post_anchor; 2026 } 2027 if(numAnchorCerts == 0) { 2028 /* we're probably done */ 2029 goto post_anchor; 2030 } 2031 assert(anchorCerts != NULL); 2032 2033 /*** anchor cert handling ***/ 2034 2035 /* 2036 * Case 1: If thisSubject is not a root cert, try to validate with incoming anchor certs. 2037 */ 2038 expiredIssuer = NULL; 2039 if(!(endCert && endCert->isSelfSigned())) { 2040 for(certDex=0; certDex<numAnchorCerts; certDex++) { 2041 2042 try { 2043 anchorInfo = new TPCertInfo(clHand, 2044 cspHand, 2045 &anchorCerts[certDex], 2046 TIC_NoCopy, 2047 verifyTime); 2048 } 2049 catch(...) { 2050 /* bad anchor cert - ignore it */ 2051 anchorInfo = NULL; 2052 continue; 2053 } 2054 2055 /* 2056 * We must subsequently delete anchorInfo one way or the other. 2057 * If we add it to tpCertGroup, we also add it to certsToBeFreed. 2058 * Otherwise we delete it. 2059 */ 2060 if(!anchorInfo->isIssuerOf(*thisSubject)) { 2061 /* not this anchor */ 2062 tpAnchorDebug("buildCertGroup anchor not issuer"); 2063 delete anchorInfo; 2064 anchorInfo = NULL; 2065 continue; 2066 } 2067 2068 crtn = thisSubject->verifyWithIssuer(anchorInfo); 2069 2070 if(crtn == CSSM_OK) { 2071 if(anchorInfo->isExpired() || anchorInfo->isNotValidYet()) { 2072 if(expiredIssuer == NULL) { 2073 /* 2074 * Hang on to this one; keep looking for a better one. 2075 */ 2076 tpDebug("buildCertGroup: saving expired anchor %p", anchorInfo); 2077 expiredIssuer = anchorInfo; 2078 /* flag this condition for the switch below */ 2079 crtn = CSSM_CERT_STATUS_EXPIRED; 2080 expiredIssuer->isAnchor(true); 2081 assert(!anchorInfo->isFromInputCerts()); 2082 expiredIssuer->index(certDex); 2083 certsToBeFreed.appendCert(expiredIssuer); 2084 } 2085 /* else we already have an expired candidate anchor */ 2086 } 2087 else { 2088 /* 2089 * Done with possible expiredIssuer. We don't delete it, since we already added 2090 * it to certsToBeFreed, above. 2091 */ 2092 if(expiredIssuer != NULL) { 2093 tpDebug("buildCertGroup: DISCARDING expired anchor %p", expiredIssuer); 2094 expiredIssuer = NULL; 2095 } 2096 } 2097 } 2098 2099 switch(crtn) { 2100 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: 2101 /* 2102 * A bit of a corner case. Found an issuer in AnchorCerts, but 2103 * we can't do a signature verify since the issuer has a partial 2104 * public key. Proceed but return 2105 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE. 2106 */ 2107 if(anchorInfo->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) { 2108 foundPartialIssuer = true; 2109 crtn = CSSMERR_TP_CERTIFICATE_CANT_OPERATE; 2110 } 2111 else { 2112 /* ignore */ 2113 crtn = CSSM_OK; 2114 } 2115 /* drop thru */ 2116 case CSSM_OK: 2117 /* A fully successful return. */ 2118 verifiedToAnchor = CSSM_TRUE; 2119 if(anchorInfo->isSelfSigned()) { 2120 verifiedToRoot = CSSM_TRUE; 2121 } 2122 2123 /* 2124 * Add this anchor cert to the output group 2125 * and to certsToBeFreed. 2126 */ 2127 appendCert(anchorInfo); 2128 anchorInfo->isAnchor(true); 2129 assert(!anchorInfo->isFromInputCerts()); 2130 anchorInfo->index(certDex); 2131 certsToBeFreed.appendCert(anchorInfo); 2132 tpDebug("buildCertGroup: Cert FOUND by signer in AnchorList"); 2133 tpAnchorDebug("buildCertGroup: Cert FOUND by signer in AnchorList"); 2134 /* one more thing: partial public key processing needed? */ 2135 if(foundPartialIssuer) { 2136 return verifyWithPartialKeys(subjectItem); 2137 } 2138 else { 2139 return crtn; 2140 } 2141 2142 default: 2143 /* continue to next anchor */ 2144 if(crtn != CSSM_CERT_STATUS_EXPIRED) { 2145 /* Expired means we're saving it in expiredIssuer */ 2146 tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG"); 2147 delete anchorInfo; 2148 } 2149 anchorInfo = NULL; 2150 break; 2151 } 2152 } /* for each anchor */ 2153 } /* thisSubject not a root cert */ 2154 2155 /* 2156 * Case 2: Check whether endCert is present in anchor certs. 2157 * 2158 * Also used to validate an expiredRoot that we pulled off the chain in 2159 * hopes of finding something better (which, if we're here, we haven't done). 2160 * 2161 * Note that the main loop above did the actual root self-verify test. 2162 */ 2163 if(endCert || expiredRoot) { 2164 2165 TPCertInfo *theRoot; 2166 if(expiredRoot) { 2167 /* this is NOT in our outgoing cert group (yet) */ 2168 theRoot = expiredRoot; 2169 } 2170 else { 2171 theRoot = endCert; 2172 } 2173 /* see if that root cert is identical to one of the anchor certs */ 2174 for(certDex=0; certDex<numAnchorCerts; certDex++) { 2175 if(tp_CompareCerts(theRoot->itemData(), &anchorCerts[certDex])) { 2176 /* one fully successful return */ 2177 tpAnchorDebug("buildCertGroup: end cert in input AND anchors"); 2178 verifiedToAnchor = CSSM_TRUE; 2179 theRoot->isAnchor(true); 2180 if(!theRoot->isFromInputCerts()) { 2181 /* Don't override index into input certs */ 2182 theRoot->index(certDex); 2183 } 2184 if(expiredRoot) { 2185 /* verified to anchor but caller will see 2186 * CSSMERR_TP_CERT_EXPIRED */ 2187 appendCert(expiredRoot); 2188 } 2189 /* one more thing: partial public key processing needed? */ 2190 if(foundPartialIssuer) { 2191 return verifyWithPartialKeys(subjectItem); 2192 } 2193 else { 2194 return CSSM_OK; 2195 } 2196 } 2197 } 2198 tpAnchorDebug("buildCertGroup: end cert in input, NOT anchors"); 2199 2200 if(!expiredRoot && endCert->isSelfSigned()) { 2201 /* verified to a root cert which is not an anchor */ 2202 /* Generally maps to CSSMERR_TP_INVALID_ANCHOR_CERT by caller */ 2203 /* one more thing: partial public key processing needed? */ 2204 if(foundPartialIssuer) { 2205 return verifyWithPartialKeys(subjectItem); 2206 } 2207 else { 2208 return CSSM_OK; 2209 } 2210 } 2211 /* else try finding a good anchor */ 2212 } 2213 2214 /* regardless of anchor search status... */ 2215 crtn = CSSM_OK; 2216 if(!verifiedToAnchor && (expiredIssuer != NULL)) { 2217 /* expiredIssuer here is always an anchor */ 2218 tpDebug("buildCertGroup: accepting expired anchor %p", expiredIssuer); 2219 appendCert(expiredIssuer); 2220 verifiedToAnchor = CSSM_TRUE; 2221 if(expiredIssuer->isSelfSigned()) { 2222 verifiedToRoot = CSSM_TRUE; 2223 } 2224 /* no matter what, we don't want this one */ 2225 expiredRoot = NULL; 2226 } 2227post_anchor: 2228 if(expiredRoot) { 2229 /* 2230 * One remaining special case: expiredRoot found in input certs, but 2231 * no luck resolving the problem with the anchors. Go ahead and (re-)append 2232 * the expired root and return. 2233 */ 2234 tpDebug("buildCertGroup: accepting EXPIRED root"); 2235 appendCert(expiredRoot); 2236 if(foundPartialIssuer) { 2237 return verifyWithPartialKeys(subjectItem); 2238 } 2239 else { 2240 return CSSM_OK; 2241 } 2242 } 2243 2244 /* If we get here, determine if fetching the issuer from the network 2245 * should be attempted: <rdar://6113890&7419584&7422356> 2246 */ 2247 attemptNetworkFetch = (actionFlags & CSSM_TP_ACTION_FETCH_CERT_FROM_NET); 2248 if( (!dbList || (dbList->NumHandles == 0)) && 2249 (!anchorCerts || (numAnchorCerts == 0)) ) { 2250 /* DB list is empty *and* anchors are empty; there is no point in going 2251 * out to the network, since we cannot build a chain to a trusted root. 2252 * (This can occur when the caller wants to evaluate a single certificate 2253 * without trying to build the chain, e.g. to check its key usage.) 2254 */ 2255 attemptNetworkFetch = false; 2256 } 2257 2258 /* 2259 * If we haven't verified to a root, and net fetch of certs is enabled, 2260 * try to get the issuer of the last cert in the chain from the net. 2261 * If that succeeds, then call ourself recursively to perform the 2262 * whole search again (including comparing to or verifying against 2263 * anchor certs). 2264 */ 2265 if(!verifiedToRoot && !verifiedToAnchor && 2266 (endCert != NULL) && attemptNetworkFetch) { 2267 TPCertInfo *issuer = NULL; 2268 CSSM_RETURN cr = tpFetchIssuerFromNet(*endCert, 2269 clHand, 2270 cspHand, 2271 verifyTime, 2272 issuer); 2273 switch(cr) { 2274 case CSSMERR_TP_CERTGROUP_INCOMPLETE: 2275 /* no issuerAltName, no reason to log this */ 2276 break; 2277 default: 2278 /* gross error */ 2279 endCert->addStatusCode(cr); 2280 break; 2281 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: 2282 /* use this one but re-verify later */ 2283 foundPartialIssuer = true; 2284 /* and drop thru */ 2285 case CSSM_OK: 2286 if (!issuer) 2287 break; 2288 tpDebug("buildCertGroup: Cert FOUND from Net; recursing"); 2289 2290 if(isInGroup(*issuer)) { 2291 tpDebug("buildCertGroup: Multiple instances of cert from net"); 2292 delete issuer; 2293 issuer = NULL; 2294 crtn = CSSMERR_TP_CERTGROUP_INCOMPLETE; 2295 break; 2296 } 2297 2298 /* add this fetched cert to constructed group */ 2299 appendCert(issuer); 2300 issuer->isFromNet(true); 2301 certsToBeFreed.appendCert(issuer); 2302 2303 /* and go again */ 2304 cr = buildCertGroup(*issuer, 2305 inCertGroup, 2306 dbList, 2307 clHand, 2308 cspHand, 2309 verifyTime, 2310 numAnchorCerts, 2311 anchorCerts, 2312 certsToBeFreed, 2313 gatheredCerts, 2314 CSSM_TRUE, // subjectIsInGroup 2315 actionFlags, 2316 policyOid, 2317 policyStr, 2318 policyStrLen, 2319 leafKeyUse, // actually don't care since the leaf will not 2320 // be evaluated 2321 verifiedToRoot, 2322 verifiedToAnchor, 2323 verifiedViaTrustSettings); 2324 if(cr) { 2325 return cr; 2326 } 2327 2328 /* one more thing: partial public key processing needed? */ 2329 if(foundPartialIssuer) { 2330 return verifyWithPartialKeys(subjectItem); 2331 } 2332 else { 2333 return CSSM_OK; 2334 } 2335 } 2336 } 2337final_out: 2338 /* regardless of outcome, check for partial keys to log per-cert status */ 2339 CSSM_RETURN partRtn = CSSM_OK; 2340 if(foundPartialIssuer) { 2341 partRtn = verifyWithPartialKeys(subjectItem); 2342 } 2343 if(crtn) { 2344 return crtn; 2345 } 2346 else { 2347 return partRtn; 2348 } 2349} 2350 2351/* 2352 * Called from buildCertGroup as final processing of a constructed 2353 * group when CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE has been 2354 * detected. Perform partial public key processing. 2355 * 2356 * We don't have to verify every element, just the ones whose 2357 * issuers have partial public keys. 2358 * 2359 * Returns: 2360 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE in the case of an issuer cert 2361 * with a partial public key which can't be completed. 2362 * CSSMERR_TP_INVALID_CERT_AUTHORITY if sig verify failed with 2363 * a (supposedly) completed partial key 2364 */ 2365CSSM_RETURN TPCertGroup::verifyWithPartialKeys( 2366 const TPClItemInfo &subjectItem) // Cert or CRL 2367{ 2368 TPCertInfo *lastFullKeyCert = NULL; 2369 tpDebug("verifyWithPartialKeys top"); 2370 2371 /* start from the end - it's easier */ 2372 for(int dex=mNumCerts-1; dex >= 0; dex--) { 2373 TPCertInfo *thisCert = mCertInfo[dex]; 2374 2375 /* 2376 * If this is the start of the cert chain, and it's not being 2377 * used to verify subjectItem, then we're done. 2378 */ 2379 if(dex == 0) { 2380 if((void *)thisCert == (void *)&subjectItem) { 2381 tpDebug("verifyWithPartialKeys: success at leaf cert"); 2382 return CSSM_OK; 2383 } 2384 } 2385 if(!thisCert->hasPartialKey()) { 2386 /* 2387 * Good to know. Record this and move on. 2388 */ 2389 lastFullKeyCert = thisCert; 2390 tpDebug("full key cert found at index %d", dex); 2391 continue; 2392 } 2393 if(lastFullKeyCert == NULL) { 2394 /* 2395 * No full keys between here and the end! 2396 */ 2397 tpDebug("UNCOMPLETABLE cert at index %d", dex); 2398 if(thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) { 2399 return CSSMERR_TP_CERTIFICATE_CANT_OPERATE; 2400 } 2401 else { 2402 break; 2403 } 2404 } 2405 2406 /* do the verify - of next cert in chain or of subjectItem */ 2407 const TPClItemInfo *subject; 2408 if(dex == 0) { 2409 subject = &subjectItem; 2410 tpDebug("...verifying subject item with partial cert 0"); 2411 } 2412 else { 2413 subject = mCertInfo[dex - 1]; 2414 tpDebug("...verifying with partial cert %d", dex); 2415 } 2416 CSSM_RETURN crtn = subject->verifyWithIssuer(thisCert, 2417 lastFullKeyCert); 2418 if(crtn) { 2419 tpDebug("CERT VERIFY ERROR with partial cert at index %d", dex); 2420 if(thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) { 2421 return CSSMERR_TP_INVALID_CERT_AUTHORITY; 2422 } 2423 else { 2424 break; 2425 } 2426 } 2427 } 2428 2429 /* we just verified subjectItem - right? */ 2430 assert((void *)mCertInfo[0] != (void *)&subjectItem); 2431 tpDebug("verifyWithPartialKeys: success at subjectItem"); 2432 return CSSM_OK; 2433} 2434 2435/* 2436 * Free records obtained from DBs. Called when these records are not going to 2437 * be passed to caller of CertGroupConstruct or CertGroupVerify. 2438 */ 2439void TPCertGroup::freeDbRecords() 2440{ 2441 for(unsigned dex=0; dex<mNumCerts; dex++) { 2442 TPCertInfo *certInfo = mCertInfo[dex]; 2443 certInfo->freeUniqueRecord(); 2444 } 2445} 2446