2  * Copyright (c) 2000-2015 Apple Inc. All Rights Reserved. 
   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 
  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. 
  20  * TPCertInfo.cpp - TP's private certificate info classes 
  23 #include "TPCertInfo.h" 
  24 #include "tpdebugging.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> 
  43 #define tpTimeDbg(args...)              secdebug("tpTime", ## args) 
  44 #define tpCertInfoDbg(args...)  secdebug("tpCert", ## args) 
  46 static const TPClItemCalls tpCertClCalls 
= 
  48         CSSM_CL_CertGetFirstCachedFieldValue
, 
  49         CSSM_CL_CertAbortQuery
, 
  51         CSSM_CL_CertAbortCache
, 
  53         &CSSMOID_X509V1ValidityNotBefore
, 
  54         &CSSMOID_X509V1ValidityNotAfter
, 
  55         CSSMERR_TP_INVALID_CERT_POINTER
, 
  56         CSSMERR_TP_CERT_EXPIRED
, 
  57         CSSMERR_TP_CERT_NOT_VALID_YET
 
  60 TPClItemInfo::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 
  75                         mAuthorityKeyID(NULL
), 
  77                         mSigAlg(CSSM_ALGID_NONE
), 
  81                         mIsNotValidYet(false), 
  85                 CSSM_RETURN crtn 
= cacheItem(itemData
, copyItemData
); 
  87                         CssmError::throwMe(crtn
); 
  91                  * Fetch standard fields... 
  92                  * Issuer name assumes same OID for Certs and CRLs! 
  94                 crtn 
= fetchField(&CSSMOID_X509V1IssuerName
, &mIssuerName
); 
  96                         CssmError::throwMe(crtn
); 
 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. 
 104                 CSSM_DATA_PTR algField
; 
 105                 crtn 
= fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS
, &algField
); 
 108                         CssmError::throwMe(crtn
); 
 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
); 
 114                 CSSM_X509_ALGORITHM_IDENTIFIER 
*algId 
= 
 115                         (CSSM_X509_ALGORITHM_IDENTIFIER 
*)algField
->Data
; 
 116                 bool algFound 
= cssmOidToAlg(&algId
->algorithm
, &mSigAlg
); 
 118                         tpErrorLog("TPClItemInfo: unknown signature algorithm\n"); 
 119                         CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
); 
 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
); 
 128                 freeField(&CSSMOID_X509V1SignatureAlgorithmTBS
, algField
); 
 130                 /* Attempt to fetch authority key id and subject key id, 
 131                  * ignore error if they do not exist. 
 133                 fetchField(&CSSMOID_SubjectKeyIdentifier
, &mSubjectKeyID
); 
 134                 fetchField(&CSSMOID_AuthorityKeyIdentifier
, &mAuthorityKeyID
); 
 136                 fetchNotBeforeAfter(); 
 137                 calculateCurrent(verifyTime
); 
 145 TPClItemInfo::~TPClItemInfo() 
 147         tpCertInfoDbg("TPClItemInfo destruct this %p", this); 
 151 void TPClItemInfo::releaseResources() 
 153         if(mWeOwnTheData 
&& (mItemData 
!= NULL
)) { 
 154                 tpFreeCssmData(Allocator::standard(), mItemData
, CSSM_TRUE
); 
 155                 mWeOwnTheData 
= false; 
 159                 freeField(&CSSMOID_X509V1IssuerName
, mIssuerName
); 
 163                 freeField(&CSSMOID_SubjectKeyIdentifier
, mSubjectKeyID
); 
 164                 mSubjectKeyID 
= NULL
; 
 166         if(mAuthorityKeyID
) { 
 167                 freeField(&CSSMOID_AuthorityKeyIdentifier
, mAuthorityKeyID
); 
 168                 mAuthorityKeyID 
= NULL
; 
 170         if(mCacheHand 
!= 0) { 
 171                 mClCalls
.abortCache(mClHand
, mCacheHand
); 
 175                 CFRelease(mNotBefore
); 
 179                 CFRelease(mNotAfter
); 
 184 /* fetch arbitrary field from cached cert */ 
 185 CSSM_RETURN 
TPClItemInfo::fetchField( 
 186         const CSSM_OID  
*fieldOid
, 
 187         CSSM_DATA_PTR   
*fieldData
)             // mallocd by CL and RETURNED 
 191         uint32 NumberOfFields 
= 0; 
 192         CSSM_HANDLE resultHand 
= 0; 
 195         assert(mClCalls
.getField 
!= NULL
); 
 196         assert(mCacheHand 
!= 0); 
 197         crtn 
= mClCalls
.getField( 
 207         if(NumberOfFields 
!= 1) { 
 208                 tpCertInfoDbg("TPClItemInfo::fetchField: numFields %d, expected 1\n", 
 209                         (int)NumberOfFields
); 
 211         mClCalls
.abortQuery(mClHand
, resultHand
); 
 215 /* free arbitrary field obtained from fetchField() */ 
 216 CSSM_RETURN 
TPClItemInfo::freeField( 
 217         const CSSM_OID  
*fieldOid
, 
 218         CSSM_DATA_PTR   fieldData
) 
 220         return CSSM_CL_FreeFieldValue(mClHand
, fieldOid
, fieldData
); 
 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. 
 230 CSSM_RETURN 
TPClItemInfo::verifyWithIssuer( 
 231         TPCertInfo              
*issuerCert
, 
 232         TPCertInfo              
*paramCert 
/* = NULL */) const 
 236         assert(mClHand 
!= 0); 
 237         assert(issuerCert
->isIssuerOf(*this)); 
 238         assert(mCspHand 
!= 0); 
 241          * Special case: detect partial public key right now; don't even 
 242          * bother trying the cert verify in that case. 
 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
; 
 250         CSSM_CC_HANDLE ccHand
; 
 251         crtn 
= CSSM_CSP_CreateSignatureContext(mCspHand
, 
 253                 NULL
,                   // Access Creds 
 254                 issuerCert
->pubKey(), 
 256         if(crtn 
!= CSSM_OK
) { 
 257                 tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n"); 
 258                 CssmError::throwMe(crtn
); 
 260         if(paramCert 
!= NULL
) { 
 261                 assert(issuerCert
->hasPartialKey()); 
 263                 /* add in parameter-bearing key */ 
 264                 CSSM_CONTEXT_ATTRIBUTE          newAttr
; 
 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
); 
 271                         tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n"); 
 272                         CssmError::throwMe(crtn
); 
 275         crtn 
= mClCalls
.itemVerify(mClHand
, 
 283                 case CSSM_OK
:           // success 
 284                 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:   // caller handles 
 285                         tpVfyDebug("verifyWithIssuer GOOD"); 
 288                         /* all others appear here as general cert verify error */ 
 289                         crtn 
= CSSMERR_TP_VERIFICATION_FAILURE
; 
 290                         tpVfyDebug("verifyWithIssuer BAD"); 
 293         CSSM_DeleteContext(ccHand
); 
 297 CSSM_RETURN 
TPClItemInfo::cacheItem( 
 298         const CSSM_DATA         
*itemData
, 
 299         TPItemCopy                      copyItemData
) 
 301         switch(copyItemData
) { 
 303                         mItemData 
= const_cast<CSSM_DATA 
*>(itemData
); 
 306                         mItemData 
= tpMallocCopyCssmData(Allocator::standard(), itemData
); 
 307                         mWeOwnTheData 
= true; 
 311                         CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
); 
 314         /* cache the cert/CRL in the CL */ 
 315         return mClCalls
.cacheItem(mClHand
, mItemData
, &mCacheHand
); 
 319  * Calculate not before/after times as struct tm. Only throws on 
 320  * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.). 
 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 
 327 void TPClItemInfo::fetchNotBeforeAfter() 
 329         CSSM_DATA_PTR   notBeforeField 
= NULL
; 
 330         CSSM_DATA_PTR   notAfterField 
= NULL
; 
 331         CSSM_RETURN             crtn 
= CSSM_OK
; 
 332         CSSM_X509_TIME  
*xTime
; 
 334         assert(cacheHand() != CSSM_INVALID_HANDLE
); 
 335         crtn 
= fetchField(mClCalls
.notBeforeOid
, ¬BeforeField
); 
 337                 tpErrorLog("fetchNotBeforeAfter: GetField error\n"); 
 338                 CssmError::throwMe(mClCalls
.invalidItemRtn
); 
 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
; 
 349         crtn 
= fetchField(mClCalls
.notAfterOid
, ¬AfterField
); 
 352                  * Tolerate a missing NextUpdate in CRL only 
 354                 if(mClCalls
.notAfterOid 
== &CSSMOID_X509V1ValidityNotAfter
) { 
 355                         tpErrorLog("fetchNotBeforeAfter: GetField error\n"); 
 356                         crtn 
= mClCalls
.invalidItemRtn
; 
 361                          * Fake NextUpdate to be "at the end of time" 
 363                         timeStringToCfDate(CSSM_APPLE_CRL_END_OF_TIME
, 
 364                                 strlen(CSSM_APPLE_CRL_END_OF_TIME
), 
 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
; 
 379                 freeField(mClCalls
.notAfterOid
, notAfterField
); 
 382                 freeField(mClCalls
.notBeforeOid
, notBeforeField
); 
 384         if(crtn 
!= CSSM_OK
) { 
 385                 CssmError::throwMe(crtn
); 
 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. 
 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). 
 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. 
 402 ModuleNexus
<Mutex
> tpTimeLock
; 
 404 CSSM_RETURN 
TPClItemInfo::calculateCurrent( 
 405         const char                      *verifyString
) 
 407         CFDateRef refTime 
= NULL
; 
 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
; 
 417                 /* time base = right now */ 
 418                 refTime 
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent()); 
 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
)); 
 425                 return mClCalls
.notValidYetRtn
; 
 428                 mIsNotValidYet 
= false; 
 431         if(compareTimes(refTime
, mNotAfter
) > 0) { 
 433                 tpTimeDbg("\nTP_CERT_EXPIRED: now %g notBefore %g", 
 434                         CFDateGetAbsoluteTime(refTime
), CFDateGetAbsoluteTime(mNotBefore
)); 
 436                 return mClCalls
.expiredRtn
; 
 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. 
 451 TPCertInfo::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 
 459                 TPClItemInfo(clHand
, cspHand
, tpCertClCalls
, certData
, 
 460                         copyCertData
, verifyTime
), 
 462                 mPublicKeyData(NULL
), 
 465                 mIsFromInputCerts(false), 
 473                 mIsRoot(TRS_Unknown
), 
 474                 mRevCheckGood(false), 
 475                 mRevCheckComplete(false), 
 476                 mTrustSettingsEvaluated(false), 
 477                 mTrustSettingsDomain(kSecTrustSettingsDomainSystem
), 
 478                 mTrustSettingsResult(kSecTrustSettingsResultInvalid
), 
 479                 mTrustSettingsFoundAnyEntry(false), 
 480                 mTrustSettingsFoundMatchingEntry(false), 
 483                 mIgnoredError(false), 
 484                 mTrustSettingsKeyUsage(0), 
 489         tpCertInfoDbg("TPCertInfo construct this %p", this); 
 490         mDlDbHandle
.DLHandle 
= 0; 
 491         mDlDbHandle
.DBHandle 
= 0; 
 493         /* fetch subject name */ 
 494         crtn 
= fetchField(&CSSMOID_X509V1SubjectName
, &mSubjectName
); 
 498                 CssmError::throwMe(crtn
); 
 501         /* this cert's public key */ 
 502         crtn 
= fetchField(&CSSMOID_CSSMKeyStruct
, &mPublicKeyData
); 
 503         if(crtn 
|| (mPublicKeyData
->Length 
!= sizeof(CSSM_KEY
))) { 
 506                 CssmError::throwMe(crtn
); 
 508         mPublicKey 
= (CSSM_KEY_PTR
)mPublicKeyData
->Data
; 
 510         /* calculate other commonly used fields */ 
 511         if(tpCompareCssmData(mSubjectName
, issuerName())) { 
 513                  * Per Radar 3374978, perform complete signature verification 
 514                  * lazily - just check subject/issuer match here. 
 516                 tpAnchorDebug("TPCertInfo potential anchor"); 
 517                 mIsRoot 
= TRS_NamesMatch
; 
 520                 mIsRoot 
= TRS_NotRoot
; 
 524 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */ 
 525 TPCertInfo::~TPCertInfo() 
 527         tpCertInfoDbg("TPCertInfo destruct this %p", this); 
 531 void TPCertInfo::releaseResources() 
 534                 freeField(&CSSMOID_X509V1SubjectName
, mSubjectName
); 
 538                 freeField(&CSSMOID_CSSMKeyStruct
, mPublicKeyData
); 
 540                 mPublicKeyData 
= NULL
; 
 550                 CFRelease(mCertHashStr
); 
 552         TPClItemInfo::releaseResources(); 
 555 const CSSM_DATA 
*TPCertInfo::subjectName() 
 557         assert(mSubjectName 
!= NULL
); 
 562  * Perform semi-lazy evaluation of "rootness". Subject and issuer names 
 563  * compared at constructor. 
 564  * If avoidVerify is true, we won't do the signature verify: caller 
 565  * just wants to know if the subject and issuer names match. 
 567 bool TPCertInfo::isSelfSigned(bool avoidVerify
) 
 570                 case TRS_NotRoot
:                       // known not to be root 
 578                         /* else drop through and verify */ 
 579                 case TRS_Unknown
:                       // actually shouldn't happen, but to be safe... 
 581                         /* do the signature verify */ 
 582                         if(verifyWithIssuer(this) == CSSM_OK
) { 
 583                                 tpAnchorDebug("isSelfSigned anchor verified"); 
 584                                 mIsRoot 
= TRS_IsRoot
; 
 588                                 tpAnchorDebug("isSelfSigned anchor vfy FAIL"); 
 589                                 mIsRoot 
= TRS_NotRoot
; 
 596  * Am I the issuer of the specified subject item? Returns true if so. 
 597  * Works for subject certs as well as CRLs. 
 599 bool TPCertInfo::isIssuerOf( 
 600         const TPClItemInfo      
&subject
) 
 602         assert(mSubjectName 
!= NULL
); 
 603         assert(subject
.issuerName() != NULL
); 
 604         if(tpCompareCssmData(mSubjectName
, subject
.issuerName())) { 
 613  * Does my subjectKeyID match the authorityKeyID of the specified subject? 
 614  * Returns true if so (and if both fields are available). 
 616 bool TPCertInfo::isAuthorityKeyOf( 
 617         const TPClItemInfo      
&subject
) 
 619         const CSSM_DATA 
*subjectKeyID 
= this->subjectKeyID(); 
 620         const CSSM_DATA 
*authorityKeyID 
= subject
.authorityKeyID(); 
 621         if(!subjectKeyID 
|| !authorityKeyID
) { 
 622                 tpDebug("isAuthorityKeyOf FALSE (one or both key ids missing)"); 
 625         CSSM_X509_EXTENSION 
*ske 
= (CSSM_X509_EXTENSION 
*)subjectKeyID
->Data
; 
 626         CSSM_X509_EXTENSION 
*ake 
= (CSSM_X509_EXTENSION 
*)authorityKeyID
->Data
; 
 627         if( !ske 
|| ske
->format 
!= CSSM_X509_DATAFORMAT_PARSED 
|| 
 628                 !ake 
|| ake
->format 
!= CSSM_X509_DATAFORMAT_PARSED 
|| 
 629                 !ske
->value
.parsedValue 
|| !ake
->value
.parsedValue
) { 
 630                 tpDebug("isAuthorityKeyOf FALSE (no parsed value present)"); 
 634         const CE_SubjectKeyID 
*skid 
= (CE_SubjectKeyID 
*)ske
->value
.parsedValue
; 
 635         const CE_AuthorityKeyID 
*akid 
= (CE_AuthorityKeyID 
*)ake
->value
.parsedValue
; 
 637         if(!akid
->keyIdentifierPresent
) { 
 638                 tpDebug("isAuthorityKeyOf FALSE (no key identifier present)"); 
 641         if(tpCompareCssmData(skid
, &akid
->keyIdentifier
)) { 
 643                 tpDebug("isAuthorityKeyOf TRUE (len:s=%lu/a=%lu, %08lX../%08lX..)", 
 645                         akid
->keyIdentifier
.Length
, 
 646                         (skid
->Data
) ? *((unsigned long *)skid
->Data
) : 0L, 
 647                         (akid
->keyIdentifier
.Data
) ? *((unsigned long *)akid
->keyIdentifier
.Data
) : 0L); 
 653                 tpDebug("isAuthorityKeyOf FALSE (len:s=%lu/a=%lu, %08lX../%08lX..)", 
 655                         akid
->keyIdentifier
.Length
, 
 656                         (skid
->Data
) ? *((unsigned long *)skid
->Data
) : 0L, 
 657                         (akid
->keyIdentifier
.Data
) ? *((unsigned long *)akid
->keyIdentifier
.Data
) : 0L); 
 663 bool TPCertInfo::addStatusCode(CSSM_RETURN code
) 
 666         mStatusCodes 
= (CSSM_RETURN 
*)realloc(mStatusCodes
, 
 667                 mNumStatusCodes 
* sizeof(CSSM_RETURN
)); 
 668         mStatusCodes
[mNumStatusCodes 
- 1] = code
; 
 669         return isStatusFatal(code
); 
 672 bool TPCertInfo::hasStatusCode(CSSM_RETURN code
) 
 674         for(unsigned dex
=0; dex
<mNumStatusCodes
; dex
++) { 
 675                 if(mStatusCodes
[dex
] == code
) { 
 682 bool TPCertInfo::isStatusFatal(CSSM_RETURN code
) 
 684         for(unsigned dex
=0; dex
<mNumAllowedErrs
; dex
++) { 
 685                 if(mAllowedErrs
[dex
] == code
) { 
 686                         tpTrustSettingsDbg("isStatusFatal(%ld): ALLOWED", (unsigned long)code
); 
 687                         mIgnoredError 
= true; 
 695  * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL 
 698 bool TPCertInfo::hasPartialKey() 
 700         if(mPublicKey
->KeyHeader
.KeyAttr 
& CSSM_KEYATTR_PARTIAL
) { 
 711 bool TPCertInfo::shouldReject() 
 713         static unsigned char _UTN_UF_H_ISSUER_BYTES
[154] = { 
 714           0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 
 715           0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 
 716           0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 
 717           0x04, 0x07, 0x13, 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 
 718           0x45, 0x20, 0x43, 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 
 719           0x55, 0x04, 0x0a, 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 
 720           0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 
 721           0x52, 0x4b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 
 722           0x18, 0x48, 0x54, 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 
 723           0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 
 724           0x4d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 
 725           0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 
 726           0x54, 0x2d, 0x48, 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45 
 728         CSSM_DATA _UTN_UF_H_ISSUER 
= { sizeof(_UTN_UF_H_ISSUER_BYTES
), _UTN_UF_H_ISSUER_BYTES 
}; 
 730         static CSSM_DATA _UTN_UF_H_SERIALS
[] = { 
 731                 { 17, (uint8
*)"\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" }, // amo 
 732                 { 17, (uint8
*)"\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" }, // gt 
 733                 { 17, (uint8
*)"\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" }, // llc 
 734                 { 17, (uint8
*)"\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" }, // lsc 
 735                 { 17, (uint8
*)"\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" }, // lyc 
 736                 { 16, (uint8
*)"\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" },     // lyc1 
 737                 { 16, (uint8
*)"\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" },     // lyc2 
 738                 { 16, (uint8
*)"\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" },     // mgc 
 739                 { 17, (uint8
*)"\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" }, // wgc 
 743         const CSSM_DATA 
*issuer
=issuerName(); 
 744         if(!issuer 
|| !(tpCompareCssmData(issuer
, &_UTN_UF_H_ISSUER
))) 
 747         CSSM_DATA 
*serialNumber
=NULL
; 
 748         CSSM_RETURN crtn 
= fetchField(&CSSMOID_X509V1SerialNumber
, &serialNumber
); 
 749         if(crtn 
|| !serialNumber
) 
 752         CSSM_DATA 
*p
=_UTN_UF_H_SERIALS
; 
 755                 if(tpCompareCssmData(serialNumber
, p
)) { 
 757                         addStatusCode(CSSMERR_TP_CERT_REVOKED
); 
 763         freeField(&CSSMOID_X509V1SerialNumber
, serialNumber
); 
 768  * Evaluate trust settings; returns true in *foundMatchingEntry if positive 
 769  * match found - i.e., cert chain construction is done. 
 771 OSStatus 
TPCertInfo::evaluateTrustSettings( 
 772         const CSSM_OID 
&policyOid
, 
 773         const char *policyString
,                       // optional 
 774         uint32 policyStringLen
, 
 775         SecTrustSettingsKeyUsage keyUse
,        // required 
 776         bool *foundMatchingEntry
,                       // RETURNED 
 777         bool *foundAnyEntry
)                            // RETURNED 
 780          * We might have to force a re-evaluation if the requested key usage 
 781          * is not a subset of what we already checked for (and cached). 
 783         if(mTrustSettingsEvaluated
) { 
 784                 bool doFlush 
= false; 
 785                 if(mTrustSettingsKeyUsage 
!= kSecTrustSettingsKeyUseAny
) { 
 786                         if(keyUse 
== kSecTrustSettingsKeyUseAny
) { 
 787                                 /* now want "any", checked something else before */ 
 790                         else if((keyUse 
& mTrustSettingsKeyUsage
) != keyUse
) { 
 791                                 /* want bits that we didn't ask for before */ 
 796                         tpTrustSettingsDbg("evaluateTrustSettings: flushing cached trust for " 
 797                                 "%p due to keyUse 0x%x", this, (int)keyUse
); 
 798                         mTrustSettingsEvaluated 
= false; 
 799                         mTrustSettingsFoundAnyEntry 
= false; 
 800                         mTrustSettingsResult 
= kSecTrustSettingsResultInvalid
; 
 801                         mTrustSettingsFoundMatchingEntry 
= false; 
 802                         if(mAllowedErrs 
!= NULL
) { 
 807                 /* else we can safely use the cached values */ 
 809         if(!mTrustSettingsEvaluated
) { 
 811                 if(mCertHashStr 
== NULL
) { 
 812                         const CSSM_DATA 
*certData 
= itemData(); 
 813                         mCertHashStr 
= SecTrustSettingsCertHashStrFromData(certData
->Data
, 
 817                 OSStatus ortn 
= SecTrustSettingsEvaluateCert(mCertHashStr
, 
 823                          * This is the purpose of the avoidVerify option, right here. 
 824                          * If this is a root cert and it has trust settings, we avoid 
 825                          * the signature verify. If it turns out there are no trust 
 826                          * settings and this is a root, we'll verify the signature 
 827                          * elsewhere (e.g. post_trust_setting: in buildCertGroup()). 
 830                         &mTrustSettingsDomain
, 
 833                         &mTrustSettingsResult
, 
 834                         &mTrustSettingsFoundMatchingEntry
, 
 835                         &mTrustSettingsFoundAnyEntry
); 
 837                         tpTrustSettingsDbg("evaluateTrustSettings: SecTrustSettingsEvaluateCert error!"); 
 840                 mTrustSettingsEvaluated 
= true; 
 841                 mTrustSettingsKeyUsage 
= keyUse
; 
 843                 if(mTrustSettingsFoundMatchingEntry
) { 
 844                         tpTrustSettingsDbg("evaluateTrustSettings: found for %p result %d", 
 845                                 this, (int)mTrustSettingsResult
); 
 848                 /* one more thing... */ 
 850                         return CSSMERR_TP_INVALID_CERTIFICATE
; 
 853         *foundMatchingEntry 
= mTrustSettingsFoundMatchingEntry
; 
 854         *foundAnyEntry 
= mTrustSettingsFoundAnyEntry
; 
 856         return errSecSuccess
; 
 859 /* true means "verification terminated due to user trust setting" */ 
 860 bool TPCertInfo::trustSettingsFound() 
 862         switch(mTrustSettingsResult
) { 
 863                 case kSecTrustSettingsResultUnspecified
:        /* entry but not definitive */ 
 864                 case kSecTrustSettingsResultInvalid
:            /* no entry */ 
 872  * Determine if this has an empty SubjectName field. Returns true if so. 
 874 bool TPCertInfo::hasEmptySubjectName() 
 877          * A "pure" empty subject is two bytes (0x30 00) - constructed sequence, 
 878          * short form length, length 0. We'll be robust and tolerate a missing 
 879          * field, as well as a possible BER-encoded subject with some extra cruft. 
 881         if((mSubjectName 
== NULL
) || (mSubjectName
->Length 
<= 4)) { 
 890  * Free mUniqueRecord if it exists. 
 891  * This is *not* done in our destructor because this record sometimes 
 892  * has to persist in the form of a CSSM evidence chain. 
 894 void TPCertInfo::freeUniqueRecord() 
 896         if(mUniqueRecord 
== NULL
) { 
 899         tpDbDebug("freeUniqueRecord: freeing cert record %p", mUniqueRecord
); 
 900         CSSM_DL_FreeUniqueRecord(mDlDbHandle
, mUniqueRecord
); 
 904  *** TPCertGroup class 
 907 /* build empty group */ 
 908 TPCertGroup::TPCertGroup( 
 910         TPGroupOwner            whoOwns
) : 
 917         tpCertInfoDbg("TPCertGroup simple construct this %p", this); 
 918         /* nothing for now */ 
 922  * Construct from unordered, untrusted CSSM_CERTGROUP. Resulting 
 923  * TPCertInfos are more or less in the same order as the incoming 
 924  * certs, though incoming certs are discarded if they don't parse. 
 925  * No verification of any sort is performed. 
 927 TPCertGroup::TPCertGroup( 
 928         const CSSM_CERTGROUP    
&CertGroupFrag
, 
 929         CSSM_CL_HANDLE                  clHand
, 
 930         CSSM_CSP_HANDLE                 cspHand
, 
 932         const char                              *verifyTime
,                    // may be NULL 
 933         bool                                    firstCertMustBeValid
, 
 934         TPGroupOwner                    whoOwns
) : 
 941         tpCertInfoDbg("TPCertGroup hard construct this %p", this); 
 943         /* verify input args */ 
 944         if(cspHand 
== CSSM_INVALID_HANDLE
) { 
 945                 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE
); 
 947         if(clHand 
== CSSM_INVALID_HANDLE
)       { 
 948                 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE
); 
 950         if(firstCertMustBeValid
) { 
 951                 if( (CertGroupFrag
.NumCerts 
== 0) || 
 952                 (CertGroupFrag
.GroupList
.CertList
[0].Data 
== NULL
) || 
 953                 (CertGroupFrag
.GroupList
.CertList
[0].Length 
== 0)) { 
 954                                 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE
); 
 957         if(CertGroupFrag
.CertGroupType 
!= CSSM_CERTGROUP_DATA
) { 
 958                 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP
); 
 960         switch(CertGroupFrag
.CertType
) { 
 961                 case CSSM_CERT_X_509v1
: 
 962                 case CSSM_CERT_X_509v2
: 
 963                 case CSSM_CERT_X_509v3
: 
 966                         CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
); 
 968         switch(CertGroupFrag
.CertEncoding
) { 
 969                 case CSSM_CERT_ENCODING_BER
: 
 970                 case CSSM_CERT_ENCODING_DER
: 
 973                         CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
); 
 977          * Add remaining input certs to mCertInfo. 
 979         TPCertInfo 
*certInfo 
= NULL
; 
 980         for(unsigned certDex
=0; certDex
<CertGroupFrag
.NumCerts
; certDex
++) { 
 982                         certInfo 
= new TPCertInfo(clHand
, 
 984                                 &CertGroupFrag
.GroupList
.CertList
[certDex
], 
 985                                 TIC_NoCopy
,                     // caller owns 
 989                         if((certDex 
== 0) && firstCertMustBeValid
) { 
 990                                 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE
); 
 992                         /* else just ignore this cert */ 
 995                 certInfo
->index(certDex
); 
 996                 appendCert(certInfo
); 
1001  * Deletes contents of mCertInfo[] if appropriate. 
1003 TPCertGroup::~TPCertGroup() 
1005         if(mWhoOwns 
== TGO_Group
) { 
1007                 for(i
=0; i
<mNumCerts
; i
++) { 
1008                         delete mCertInfo
[i
]; 
1011         mAlloc
.free(mCertInfo
); 
1014 /* add/remove/access TPTCertInfo's. */ 
1016  * NOTE: I am aware that most folks would just use an array<> here, but 
1017  * gdb is so lame that it doesn't even let one examine the contents 
1018  * of an array<> (or just about anything else in the STL). I prefer 
1019  * debuggability over saving a few lines of trivial code. 
1021 void TPCertGroup::appendCert( 
1022         TPCertInfo                      
*certInfo
)                      // appends to end of mCertInfo 
1024         if(mNumCerts 
== mSizeofCertInfo
) { 
1025                 if(mSizeofCertInfo 
== 0) { 
1026                         /* appending to empty array */ 
1027                         mSizeofCertInfo 
= 1; 
1030                         mSizeofCertInfo 
*= 2; 
1032                 mCertInfo 
= (TPCertInfo 
**)mAlloc
.realloc(mCertInfo
, 
1033                         mSizeofCertInfo 
* sizeof(TPCertInfo 
*)); 
1035         mCertInfo
[mNumCerts
++] = certInfo
; 
1038 TPCertInfo 
*TPCertGroup::certAtIndex( 
1041         if(index 
> (mNumCerts 
- 1)) { 
1042                 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
); 
1044         return mCertInfo
[index
]; 
1047 TPCertInfo 
*TPCertGroup::removeCertAtIndex( 
1048         unsigned                        index
)                          // doesn't delete the cert, just 
1049                                                                                         // removes it from out list 
1051         if(index 
> (mNumCerts 
- 1)) { 
1052                 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
); 
1054         TPCertInfo 
*rtn 
= mCertInfo
[index
]; 
1056         /* removed requested element and compact remaining array */ 
1058         for(i
=index
; i
<(mNumCerts 
- 1); i
++) { 
1059                 mCertInfo
[i
] = mCertInfo
[i
+1]; 
1065 TPCertInfo 
*TPCertGroup::firstCert() 
1067         if(mNumCerts 
== 0) { 
1068                 /* the caller really should not do this... */ 
1069                 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
); 
1072                 return mCertInfo
[0]; 
1076 TPCertInfo 
*TPCertGroup::lastCert() 
1078         if(mNumCerts 
== 0) { 
1082                 return mCertInfo
[mNumCerts 
- 1]; 
1086 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */ 
1087 CSSM_CERTGROUP_PTR 
TPCertGroup::buildCssmCertGroup() 
1089         CSSM_CERTGROUP_PTR cgrp 
= 
1090                 (CSSM_CERTGROUP_PTR
)mAlloc
.malloc(sizeof(CSSM_CERTGROUP
)); 
1091         cgrp
->NumCerts 
= mNumCerts
; 
1092         cgrp
->CertGroupType 
= CSSM_CERTGROUP_DATA
; 
1093         cgrp
->CertType 
= CSSM_CERT_X_509v3
; 
1094         cgrp
->CertEncoding 
= CSSM_CERT_ENCODING_DER
; 
1095         if(mNumCerts 
== 0) { 
1097                 cgrp
->GroupList
.CertList 
= NULL
; 
1100         cgrp
->GroupList
.CertList 
= (CSSM_DATA_PTR
)mAlloc
.calloc(mNumCerts
, 
1102         for(unsigned i
=0; i
<mNumCerts
; i
++) { 
1103                 tpCopyCssmData(mAlloc
, mCertInfo
[i
]->itemData(), 
1104                         &cgrp
->GroupList
.CertList
[i
]); 
1109 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */ 
1110 CSSM_TP_APPLE_EVIDENCE_INFO 
*TPCertGroup::buildCssmEvidenceInfo() 
1112         CSSM_TP_APPLE_EVIDENCE_INFO 
*infoArray
; 
1114         infoArray 
= (CSSM_TP_APPLE_EVIDENCE_INFO 
*)mAlloc
.calloc(mNumCerts
, 
1115                 sizeof(CSSM_TP_APPLE_EVIDENCE_INFO
)); 
1116         for(unsigned i
=0; i
<mNumCerts
; i
++) { 
1117                 TPCertInfo 
*certInfo 
= mCertInfo
[i
]; 
1118                 CSSM_TP_APPLE_EVIDENCE_INFO 
*evInfo 
= &infoArray
[i
]; 
1120                 /* first the booleans */ 
1121                 if(certInfo
->isExpired()) { 
1122                         evInfo
->StatusBits 
|= CSSM_CERT_STATUS_EXPIRED
; 
1124                 if(certInfo
->isNotValidYet()) { 
1125                         evInfo
->StatusBits 
|= CSSM_CERT_STATUS_NOT_VALID_YET
; 
1127                 if(certInfo
->isAnchor()) { 
1128                         tpAnchorDebug("buildCssmEvidenceInfo: flagging IS_IN_ANCHORS"); 
1129                         evInfo
->StatusBits 
|= CSSM_CERT_STATUS_IS_IN_ANCHORS
; 
1131                 if(certInfo
->dlDbHandle().DLHandle 
== 0) { 
1132                         if(certInfo
->isFromNet()) { 
1133                                 evInfo
->StatusBits 
|= CSSM_CERT_STATUS_IS_FROM_NET
; 
1135                         else if(certInfo
->isFromInputCerts()) { 
1136                                 evInfo
->StatusBits 
|= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
; 
1139                 /* If trust settings apply to a root, skip verifying the signature */ 
1140                 bool avoidVerify 
= false; 
1141                 switch(certInfo
->trustSettingsResult()) { 
1142                         case kSecTrustSettingsResultTrustRoot
: 
1143                         case kSecTrustSettingsResultTrustAsRoot
: 
1144                                 /* these two can be disambiguated by IS_ROOT */ 
1145                                 evInfo
->StatusBits 
|= CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST
; 
1148                         case kSecTrustSettingsResultDeny
: 
1149                                 evInfo
->StatusBits 
|= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY
; 
1152                         case kSecTrustSettingsResultUnspecified
: 
1153                         case kSecTrustSettingsResultInvalid
: 
1157                 if(certInfo
->isSelfSigned(avoidVerify
)) { 
1158                         evInfo
->StatusBits 
|= CSSM_CERT_STATUS_IS_ROOT
; 
1160                 if(certInfo
->ignoredError()) { 
1161                         evInfo
->StatusBits 
|= CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR
; 
1163                 unsigned numCodes 
= certInfo
->numStatusCodes(); 
1164                 evInfo
->NumStatusCodes 
= numCodes
; 
1166                  * Always alloc StatusCodes array with one more element than the actual 
1167                  * number of codes, so we can stash CrlReason at the end of the array. 
1169                 evInfo
->StatusCodes 
= (CSSM_RETURN 
*)mAlloc
.calloc(numCodes
+1, 
1170                         sizeof(CSSM_RETURN
)); 
1171                 for(unsigned j
=0; j
<numCodes
; j
++) { 
1172                         evInfo
->StatusCodes
[j
] = (certInfo
->statusCodes())[j
]; 
1174                 evInfo
->StatusCodes
[numCodes
] = (CSSM_RETURN
)certInfo
->crlReason(); 
1175                 if(evInfo
->StatusBits 
& (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST 
| 
1176                                                                  CSSM_CERT_STATUS_TRUST_SETTINGS_DENY 
| 
1177                                                                  CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR
)) { 
1178                         /* Something noteworthy happened involving TrustSettings */ 
1179                         uint32 whichDomain 
= 0; 
1180                         switch(certInfo
->trustSettingsDomain()) { 
1181                                 case kSecTrustSettingsDomainUser
: 
1182                                         whichDomain 
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER
; 
1184                                 case kSecTrustSettingsDomainAdmin
: 
1185                                         whichDomain 
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN
; 
1187                                 case kSecTrustSettingsDomainSystem
: 
1188                                         whichDomain 
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM
; 
1191                         evInfo
->StatusBits 
|= whichDomain
; 
1193                 evInfo
->Index 
= certInfo
->index(); 
1194                 evInfo
->DlDbHandle 
= certInfo
->dlDbHandle(); 
1195                 evInfo
->UniqueRecord 
= certInfo
->uniqueRecord(); 
1200 /* Given a status for basic construction of a cert group and a status 
1201  * of (optional) policy verification, plus the implicit notBefore/notAfter 
1202  * status in the certs, calculate a global return code. This just 
1203  * encapsulates a policy for CertGroupConstruct and CertGroupVerify. 
1205 CSSM_RETURN 
TPCertGroup::getReturnCode( 
1206         CSSM_RETURN                                     constructStatus
, 
1207         CSSM_RETURN                                     policyStatus
, 
1208         CSSM_APPLE_TP_ACTION_FLAGS      actionFlags
) 
1210         if(constructStatus
) { 
1211                 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */ 
1212                 return constructStatus
; 
1215         bool expired 
= false; 
1216         bool postdated 
= false; 
1217         bool allowExpiredRoot 
= (actionFlags 
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
) ? 
1219         bool allowExpired 
= (actionFlags 
& CSSM_TP_ACTION_ALLOW_EXPIRED
) ? true : false; 
1220         bool allowPostdated 
= allowExpired
; // flag overrides any temporal invalidity 
1221         bool requireRevPerCert 
= (actionFlags 
& CSSM_TP_ACTION_REQUIRE_REV_PER_CERT
) ? 
1224         /* check for expired, not valid yet */ 
1225         for(unsigned i
=0; i
<mNumCerts
; i
++) { 
1226                 TPCertInfo 
*ci 
= mCertInfo
[i
]; 
1228                  * Note avoidVerify = true for isSelfSigned(); if it were appropriate to 
1229                  * verify the signature, that would have happened in 
1230                  * buildCssmEvidenceInfo() at the latest. 
1232                 if(ci
->isExpired() && 
1233                    !(allowExpiredRoot 
&& ci
->isSelfSigned(true)) &&             // allowed globally 
1234                     ci
->isStatusFatal(CSSMERR_TP_CERT_EXPIRED
)) {       // allowed for this cert 
1237                 if(ci
->isNotValidYet() && 
1238                    ci
->isStatusFatal(CSSMERR_TP_CERT_NOT_VALID_YET
)) { 
1242         if(expired 
&& !allowExpired
) { 
1243                 return CSSMERR_TP_CERT_EXPIRED
; 
1245         if(postdated 
&& !allowPostdated
) { 
1246                 return CSSMERR_TP_CERT_NOT_VALID_YET
; 
1249         /* Check for missing revocation check */ 
1250         if(requireRevPerCert
) { 
1251                 for(unsigned i
=0; i
<mNumCerts
; i
++) { 
1252                         TPCertInfo 
*ci 
= mCertInfo
[i
]; 
1253                         if(ci
->isSelfSigned(true)) { 
1254                                 /* revocation check meaningless for a root cert */ 
1255                                 tpDebug("getReturnCode: ignoring revocation for self-signed cert %d", i
); 
1258                         if(!ci
->revokeCheckGood() && 
1259                            ci
->isStatusFatal(CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK
)) { 
1260                                 tpDebug("getReturnCode: FATAL: revocation check incomplete for cert %d", i
); 
1261                                 return CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK
; 
1265                                 tpDebug("getReturnCode: revocation check %s for cert %d", 
1266                                         (ci
->revokeCheckGood()) ? "GOOD" : "OK", i
); 
1271         return policyStatus
; 
1274 /* set all TPCertInfo.mUsed flags false */ 
1275 void TPCertGroup::setAllUnused() 
1277         for(unsigned dex
=0; dex
<mNumCerts
; dex
++) { 
1278                 mCertInfo
[dex
]->used(false); 
1283  * See if the specified error status is allowed (return true) or 
1284  * fatal (return false) per each cert's mAllowedErrs[]. Returns 
1285  * true if any cert returns false for its isStatusFatal() call. 
1286  * The list of errors which can apply to cert-chain-wide allowedErrors 
1287  * is right here; if the incoming error is not in that list, we 
1288  * return false. If the incoming error code is CSSM_OK we return 
1289  * true as a convenience for our callers. 
1291 bool TPCertGroup::isAllowedError( 
1297                 case CSSMERR_TP_NOT_TRUSTED
: 
1298                 case CSSMERR_TP_INVALID_ANCHOR_CERT
: 
1299                 case CSSMERR_TP_VERIFY_ACTION_FAILED
: 
1300                 case CSSMERR_TP_INVALID_CERT_AUTHORITY
: 
1301                 case CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
: 
1302                 case CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
: 
1303                         /* continue processing these candidates */ 
1306                         /* not a candidate for cert-chain-wide allowedErrors */ 
1310         for(unsigned dex
=0; dex
<mNumCerts
; dex
++) { 
1311                 if(!mCertInfo
[dex
]->isStatusFatal(code
)) { 
1312                         tpTrustSettingsDbg("TPCertGroup::isAllowedError: allowing for cert %u", 
1318         /* every cert thought this was fatal; it is. */ 
1323  * Determine if we already have the specified cert in this group. 
1325 bool TPCertGroup::isInGroup(TPCertInfo 
&certInfo
) 
1327         for(unsigned dex
=0; dex
<mNumCerts
; dex
++) { 
1328                 if(tpCompareCssmData(certInfo
.itemData(), mCertInfo
[dex
]->itemData())) { 
1336  * Encode issuing certs in this group as a PEM-encoded data blob. 
1339 void TPCertGroup::encodeIssuers(CSSM_DATA 
&issuers
) 
1341         /* FIXME: probably want to rewrite this using pemEncode() from libsecurity_cdsa_utils, 
1342          * since use of Sec* APIs from this layer violates the API reentrancy contract. 
1344         issuers
.Data 
= NULL
; 
1346         CFMutableArrayRef certArray 
= CFArrayCreateMutable(kCFAllocatorDefault
, 
1347                 0, &kCFTypeArrayCallBacks
); 
1351         for(unsigned certDex
=0; certDex
<mNumCerts
; certDex
++) { 
1352                 TPCertInfo 
*certInfo 
= certAtIndex(certDex
); 
1353                 if(!certDex 
&& mNumCerts 
> 1) { 
1354                         continue; /* don't need the leaf */ 
1356                 CSSM_DATA 
*cssmData 
= (CSSM_DATA
*)((certInfo
) ? certInfo
->itemData() : NULL
); 
1357                 if(!cssmData 
|| !cssmData
->Data 
|| !cssmData
->Length
) { 
1360                 CFDataRef dataRef 
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, 
1361                         (const UInt8 
*)cssmData
->Data
, cssmData
->Length
, 
1366                 SecCertificateRef certRef 
= SecCertificateCreateWithData(kCFAllocatorDefault
, 
1372                 CFArrayAppendValue(certArray
, certRef
); 
1376         CFDataRef exportedPEMData 
= NULL
; 
1377         OSStatus status 
= SecItemExport(certArray
, 
1378                 kSecFormatPEMSequence
, 
1382         CFRelease(certArray
); 
1385                 uint8 
*dataPtr 
= (uint8
*)CFDataGetBytePtr(exportedPEMData
); 
1386                 size_t dataLen 
= CFDataGetLength(exportedPEMData
); 
1387                 issuers
.Data 
= (uint8
*)malloc(dataLen
); 
1388                 memmove(issuers
.Data
, dataPtr
, dataLen
); 
1389                 issuers
.Length 
= dataLen
; 
1390                 CFRelease(exportedPEMData
); 
1395  * Search unused incoming certs to find an issuer of specified cert or CRL. 
1396  * WARNING this assumes a valid "used" state for all certs in this group. 
1397  * If partialIssuerKey is true on return, caller must re-verify signature 
1398  * of subject later when sufficient info is available. 
1400 TPCertInfo 
*TPCertGroup::findIssuerForCertOrCrl( 
1401         const TPClItemInfo 
&subject
, 
1402         bool &partialIssuerKey
) 
1404         partialIssuerKey 
= false; 
1405         TPCertInfo 
*expiredIssuer 
= NULL
; 
1406         TPCertInfo 
*unmatchedKeyIDIssuer 
= NULL
; 
1408         for(unsigned certDex
=0; certDex
<mNumCerts
; certDex
++) { 
1409                 TPCertInfo 
*certInfo 
= certAtIndex(certDex
); 
1411                 /* has this one already been used in this search? */ 
1412                 if(certInfo
->used()) { 
1416                 /* subject/issuer names match? */ 
1417                 if(certInfo
->isIssuerOf(subject
)) { 
1418                         /* yep, do a sig verify */ 
1419                         tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig"); 
1420                         CSSM_RETURN crtn 
= subject
.verifyWithIssuer(certInfo
); 
1422                                 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: 
1423                                         /* issuer OK, check sig later */ 
1424                                         partialIssuerKey 
= true; 
1428                                          * Temporal validity check: if we're not already holding an expired 
1429                                          * issuer, and this one's invalid, hold it and keep going. 
1431                                         if((crtn 
== CSSM_OK
) && (expiredIssuer 
== NULL
)) { 
1432                                                 if(certInfo
->isExpired() || certInfo
->isNotValidYet()) { 
1433                                                         tpDebug("findIssuerForCertOrCrl: holding expired cert %p", 
1435                                                         expiredIssuer 
= certInfo
; 
1439                                         /* Authority key identifier check: if we can't match subject key id, 
1440                                          * hold onto this cert and keep going. 
1442                                         if(unmatchedKeyIDIssuer 
== NULL
) { 
1443                                                 if(!certInfo
->isAuthorityKeyOf(subject
)) { 
1444                                                         tpDebug("findIssuerForCertOrCrl: holding issuer without key id match %p", 
1446                                                         unmatchedKeyIDIssuer 
= certInfo
; 
1451                                         certInfo
->used(true); 
1454                                         /* just skip this one and keep looking */ 
1455                                         tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG"); 
1460         if(unmatchedKeyIDIssuer 
!= NULL
) { 
1461                 /* OK, we'll use this one (preferred over an expired issuer) */ 
1462                 tpDbDebug("findIssuerForCertOrCrl: using issuer without key id match %p", unmatchedKeyIDIssuer
); 
1463                 unmatchedKeyIDIssuer
->used(true); 
1464                 return unmatchedKeyIDIssuer
; 
1466         if(expiredIssuer 
!= NULL
) { 
1467                 /* OK, we'll use this one */ 
1468                 tpDbDebug("findIssuerForCertOrCrl: using expired cert %p", expiredIssuer
); 
1469                 expiredIssuer
->used(true); 
1470                 return expiredIssuer
; 
1478  * Construct ordered, verified cert chain from a variety of inputs. 
1479  * Time validity does not affect the function return or any status, 
1480  * we always try to find a valid cert to replace an expired or 
1481  * not-yet-valid cert if we can. Final temporal validity of each 
1482  * cert must be checked by caller (it's stored in each TPCertInfo 
1483  * we add to ourself during construction). 
1485  * Only possible error returns are: 
1486  *       CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial 
1487  *                      public key, rendering full verification impossible. 
1488  *   CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial 
1489  *                      public key and which failed to perform subsequent signature 
1492  * Other interesting status is returned via the verifiedToRoot and 
1493  * verifiedToAnchor flags. 
1495  * NOTE: is it the caller's responsibility to call setAllUnused() for both 
1496  * incoming cert groups (inCertGroup and gatheredCerts). We don't do that 
1497  * here because we may call ourself recursively. 
1499 CSSM_RETURN 
TPCertGroup::buildCertGroup( 
1500         const TPClItemInfo              
&subjectItem
,   // Cert or CRL 
1501         TPCertGroup                             
*inCertGroup
,   // optional 
1502         const CSSM_DL_DB_LIST   
*dbList
,                // optional 
1503         CSSM_CL_HANDLE                  clHand
, 
1504         CSSM_CSP_HANDLE                 cspHand
, 
1505         const char                              *verifyTime
,    // optional, for establishing 
1506                                                                                         //   validity of new TPCertInfos 
1507         /* trusted anchors, optional */ 
1508         /* FIXME - maybe this should be a TPCertGroup */ 
1509         uint32                                  numAnchorCerts
, 
1510         const CSSM_DATA                 
*anchorCerts
, 
1513          * Certs to be freed by caller (i.e., TPCertInfo which we allocate 
1514          * as a result of using a cert from anchorCerts or dbList) are added 
1517         TPCertGroup                             
&certsToBeFreed
, 
1520          * Other certificates gathered during the course of this operation, 
1521          * currently consisting of certs fetched from DBs and from the net. 
1522          * This is not used when called by AppleTPSession::CertGroupConstructPriv; 
1523          * it's an optimization for the case when we're building a cert group 
1524          * for TPCrlInfo::verifyWithContext - we avoid re-fetching certs from 
1525          * the net which are needed to verify both the subject cert and a CRL. 
1526          * We don't modify this TPCertGroup, we only use certs from it. 
1528         TPCertGroup                             
*gatheredCerts
, 
1531          * Indicates that subjectItem is a cert in this cert group. 
1532          * If true, that cert will be tested for "root-ness", including 
1533          *   -- subject/issuer compare 
1534          *   -- signature self-verify 
1537         CSSM_BOOL                               subjectIsInGroup
, 
1540          * CSSM_TP_ACTION_FETCH_CERT_FROM_NET, 
1541          * CSSM_TP_ACTION_TRUST_SETTING, 
1542          * CSSM_TP_ACTION_IMPLICIT_ANCHORS are interesting 
1544         CSSM_APPLE_TP_ACTION_FLAGS      actionFlags
, 
1546         /* CSSM_TP_ACTION_TRUST_SETTING parameters */ 
1547         const CSSM_OID                  
*policyOid
, 
1548         const char                              *policyStr
, 
1549         uint32                                  policyStrLen
, 
1550         SecTrustSettingsKeyUsage leafKeyUse
,                            // usage of *first* cert in chain 
1553         CSSM_BOOL                               
&verifiedToRoot
,                        // end of chain self-verifies 
1554         CSSM_BOOL                               
&verifiedToAnchor
,                      // end of chain in anchors 
1555         CSSM_BOOL                               
&verifiedViaTrustSettings
)      // chain ends per User Trust setting 
1557         const TPClItemInfo 
*thisSubject 
= &subjectItem
; 
1558         CSSM_RETURN crtn 
= CSSM_OK
; 
1559         TPCertInfo 
*issuerCert 
= NULL
; 
1561         TPCertInfo 
*anchorInfo 
= NULL
; 
1562         bool foundPartialIssuer 
= false; 
1563         bool attemptNetworkFetch 
= false; 
1564         CSSM_BOOL firstSubjectIsInGroup 
= subjectIsInGroup
; 
1565         TPCertInfo 
*endCert
; 
1567         tpVfyDebug("buildCertGroup top"); 
1569         /* possible expired root which we'll only use if we can't find 
1571         TPCertInfo 
*expiredRoot 
= NULL
; 
1573         /* and the general case of an expired or not yet valid cert */ 
1574         TPCertInfo 
*expiredIssuer 
= NULL
; 
1576         /* and the case of an issuer without a matching subject key id */ 
1577         TPCertInfo 
*unmatchedKeyIDIssuer 
= NULL
; 
1579     /* and the case of a root that isn't trusted or an anchor */ 
1580     TPCertInfo 
*untrustedRoot 
= NULL
; 
1582         verifiedToRoot 
= CSSM_FALSE
; 
1583         verifiedToAnchor 
= CSSM_FALSE
; 
1584         verifiedViaTrustSettings 
= CSSM_FALSE
; 
1586         /*** main loop to seach inCertGroup and dbList *** 
1589          *   -- find a root cert in the chain (self-signed) 
1590          *   -- find a non-root cert which is also in the anchors list 
1591          *   -- find a cert which is trusted per Trust Settings (if enabled) 
1593          *   -- or no more certs to add to chain. 
1597                  * Top of loop: thisSubject is the item we're trying to verify. 
1600                 /* is thisSubject a root cert or listed in user trust list?  */ 
1601                 if(subjectIsInGroup
) { 
1602                         TPCertInfo 
*subjCert 
= lastCert(); 
1603                         assert(subjCert 
!= NULL
); 
1605                         if(actionFlags 
& CSSM_TP_ACTION_TRUST_SETTINGS
) { 
1606                                 assert(policyOid 
!= NULL
); 
1609                                  * Figure out key usage. If this is a leaf cert, the caller - actually 
1610                                  * the per-policy code - inferred the usage. Else it could be for 
1611                                  * verifying a cert or a CRL. 
1613                                  * We want to avoid multiple calls to the effective portion of 
1614                                  * evaluateTrustSettings(), but a CA cert could be usable for only 
1615                                  * signing certs and not CRLs. Thus we're evaluating a CA cert, 
1616                                  * try to evaluate for signing certs *and* CRLs in case we come 
1617                                  * this way again later when performing CRL verification. If that 
1618                                  * fails, then retry with just cert signing. 
1620                                 SecTrustSettingsKeyUsage localKeyUse
; 
1621                                 bool doRetry 
= false; 
1622                                 if(subjCert 
== firstCert()) { 
1623                                         /* leaf - use caller's spec */ 
1624                                         localKeyUse 
= leafKeyUse
; 
1625                                         /* FIXME - add in CRL if this is cert checking? */ 
1628                                         localKeyUse 
= kSecTrustSettingsKeyUseSignCert 
| kSecTrustSettingsKeyUseSignRevocation
; 
1629                                         /* and if necessary */ 
1632                                 /* this lets us avoid searching for the same thing twice when there 
1633                                  * is in fact no entry for it */ 
1634                                 bool foundEntry 
= false; 
1635                                 bool trustSettingsFound 
= false; 
1636                                 OSStatus ortn 
= subjCert
->evaluateTrustSettings(*policyOid
, 
1637                                         policyStr
, policyStrLen
, localKeyUse
, &trustSettingsFound
, &foundEntry
); 
1639                                         /* this is only a dire error */ 
1643                                 if(!trustSettingsFound 
&& foundEntry 
&& doRetry
) { 
1644                                         tpTrustSettingsDbg("buildCertGroup: retrying evaluateTrustSettings with Cert only"); 
1645                                         ortn 
= subjCert
->evaluateTrustSettings(*policyOid
, 
1646                                                 policyStr
, policyStrLen
, kSecTrustSettingsKeyUseSignCert
, 
1647                                                 &trustSettingsFound
, &foundEntry
); 
1653                                 if(trustSettingsFound
) { 
1654                                         switch(subjCert
->trustSettingsResult()) { 
1655                                                 case kSecTrustSettingsResultInvalid
: 
1656                                                         /* should not happen... */ 
1658                                                         crtn 
= CSSMERR_TP_INTERNAL_ERROR
; 
1660                                                 case kSecTrustSettingsResultTrustRoot
: 
1661                                                 case kSecTrustSettingsResultTrustAsRoot
: 
1662                                                         tpTrustSettingsDbg("Trust[As]Root found"); 
1665                                                 case kSecTrustSettingsResultDeny
: 
1666                                                         tpTrustSettingsDbg("TrustResultDeny found"); 
1667                                                         crtn 
= CSSMERR_APPLETP_TRUST_SETTING_DENY
; 
1669                                                 case kSecTrustSettingsResultUnspecified
: 
1670                                                         /* special case here: this means "keep going, we don't trust or 
1671                                                          * distrust this cert". Typically used to express allowed errors 
1674                                                         tpTrustSettingsDbg("TrustResultUnspecified found"); 
1675                                                         goto post_trust_setting
; 
1677                                                         tpTrustSettingsDbg("Unknown TrustResult (%d)", 
1678                                                                 (int)subjCert
->trustSettingsResult()); 
1679                                                         crtn 
= CSSMERR_TP_INTERNAL_ERROR
; 
1682                                         /* cleanup partial key processing */ 
1683                                         verifiedViaTrustSettings 
= CSSM_TRUE
; 
1686                         }       /* CSSM_TP_ACTION_TRUST_SETTING */ 
1690              * If this cert is in the provided anchors list, 
1691              * we can stop building the chain at this point. 
1693              * If this cert is a leaf, the chain ends in an anchor, but if it's 
1694              * also temporally invalid, we can't do anything further. However, 
1695              * if it's not a leaf, then we need to roll back the chain to a 
1696              * point just before this cert, so Case 1 will subsequently find 
1697              * the anchor (and handle the anchor correctly if it's expired.) 
1699             if(numAnchorCerts 
&& anchorCerts
) { 
1700                 bool foundAnchor 
= false; 
1701                 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) { 
1702                     if(tp_CompareCerts(subjCert
->itemData(), &anchorCerts
[certDex
])) { 
1704                         /* if it's not the leaf, remove it from the outgoing cert group. */ 
1705                         if(!firstSubjectIsInGroup 
|| (mNumCerts 
> 1)) { 
1707                                 /* roll back to previous cert */ 
1710                             if(mNumCerts 
== 0) { 
1711                                 /* roll back to caller's initial condition */ 
1712                                 thisSubject 
= &subjectItem
; 
1715                                 thisSubject 
= lastCert(); 
1717                             tpAnchorDebug("buildCertGroup: CA cert in input AND anchors"); 
1720                             if(subjCert
->isExpired() || subjCert
->isNotValidYet()) { 
1721                                 crtn 
= CSSM_CERT_STATUS_EXPIRED
; 
1725                             subjCert
->isAnchor(true); 
1726                             verifiedToAnchor 
= CSSM_TRUE
; 
1727                             tpAnchorDebug("buildCertGroup: leaf cert in input AND anchors"); 
1729                         if(subjCert
->isSelfSigned()) { 
1730                             verifiedToRoot 
= CSSM_TRUE
; 
1732                         break;  /* out of anchor-checking loop */ 
1736                     break; /* out of main loop */ 
1740                         if(subjCert
->isSelfSigned()) { 
1742                                  * Special case if this root is temporally invalid (and it's not 
1743                                  * the leaf): remove it from the outgoing cert group, save it, 
1744                                  * and proceed, looking another (good) root in anchors. 
1745                                  * There's no way we'll find another good one in this loop. 
1747                                 if((subjCert
->isExpired() || subjCert
->isNotValidYet()) && 
1748                                    (!firstSubjectIsInGroup 
|| (mNumCerts 
> 1))) { 
1749                     verifiedToRoot 
= CSSM_TRUE
; 
1750                                         tpDebug("buildCertGroup: EXPIRED ROOT %p, looking for good one", subjCert
); 
1751                                         expiredRoot 
= subjCert
; 
1753                                                 /* roll back to previous cert */ 
1756                                         if(mNumCerts 
== 0) { 
1757                                                 /* roll back to caller's initial condition */ 
1758                                                 thisSubject 
= &subjectItem
; 
1761                                                 thisSubject 
= lastCert(); 
1763                     break;              /* out of main loop */ 
1766                  * If any root is considered an anchor, we don't need to look  
1767                  * for a better chain that ends in an anchor or trusted root. 
1769                 if(actionFlags 
& CSSM_TP_ACTION_IMPLICIT_ANCHORS
) { 
1770                     verifiedToRoot 
= CSSM_TRUE
; 
1771                     break;      /*out of main loop */ 
1774                  * The root we have is neither trusted nor an anchor. Continue in 
1775                  * the loop to look for a better chain if this is not a leaf. 
1777                 if(!firstSubjectIsInGroup 
|| (mNumCerts 
> 1)) { 
1778                     tpDebug("buildCertGroup: UNTRUSTED ROOT %p, looking for better chain", subjCert
); 
1779                     untrustedRoot 
= subjCert
; 
1781                         /* roll back to previous cert */ 
1784                     if(mNumCerts 
== 0) { 
1785                         /* roll back to caller's initial condition */ 
1786                         thisSubject 
= &subjectItem
; 
1789                         thisSubject 
= lastCert(); 
1793                     /* the leaf is a root */ 
1794                     break;      /* out of main loop */ 
1797                 }       /* subjectIsInGroup */ 
1800                  * Search unused incoming certs to find an issuer. 
1801                  * Both cert groups are optional. 
1802                  * We'll add issuer to outCertGroup below. 
1803                  * If we find a cert that's expired or not yet valid, we hold on to it 
1804                  * and look for a better one. If we don't find it here we drop back to the 
1805                  * expired one at the end of the loop. If that expired cert is a root 
1806                  * cert, we'll use the expiredRoot mechanism (see above) to roll back and 
1807                  * see if we can find a good root in the incoming anchors. 
1809                 if(inCertGroup 
!= NULL
) { 
1810                         bool partial 
= false; 
1811                         issuerCert 
= inCertGroup
->findIssuerForCertOrCrl(*thisSubject
, 
1814                                 issuerCert
->isFromInputCerts(true); 
1816                                         /* deal with this later */ 
1817                                         foundPartialIssuer 
= true; 
1818                                         tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup"); 
1821                                         tpDebug("buildCertGroup: Cert FOUND in inCertGroup"); 
1826                 if(issuerCert 
!= NULL
) { 
1827                         bool stashedIssuer 
= false; 
1828             /* Check whether candidate issuer is the same as unstrustedRoot */ 
1829             if((untrustedRoot 
!= NULL
) && tp_CompareCerts(issuerCert
->itemData(), untrustedRoot
->itemData())) { 
1830                 /* already stashed */ 
1831                 stashedIssuer 
= true; 
1833                         /* Check whether candidate issuer is expired or not yet valid */ 
1834                         if(issuerCert
->isExpired() || issuerCert
->isNotValidYet()) { 
1835                                 if(expiredIssuer 
== NULL
) { 
1836                                         tpDebug("buildCertGroup: saving expired cert %p (1)", issuerCert
); 
1837                                         expiredIssuer 
= issuerCert
; 
1838                                         stashedIssuer 
= true; 
1840                                 /* else we already have an expired issuer candidate */ 
1843                                 /* unconditionally done with possible expiredIssuer */ 
1845                                 if(expiredIssuer 
!= NULL
) { 
1846                                         tpDebug("buildCertGroup: DISCARDING expired cert %p (1)", expiredIssuer
); 
1849                                 expiredIssuer 
= NULL
; 
1851                         /* Check whether candidate issuer failed to match authority key id in thisSubject */ 
1852                         if(!issuerCert
->isAuthorityKeyOf(*thisSubject
)) { 
1853                                 if(unmatchedKeyIDIssuer 
== NULL
) { 
1854                                         tpDebug("buildCertGroup: saving unmatched key id issuer %p (1)", issuerCert
); 
1855                                         unmatchedKeyIDIssuer 
= issuerCert
; 
1856                                         stashedIssuer 
= true; 
1858                                 /* else we already have an unmatched key id issuer candidate */ 
1861                                 /* unconditionally done with possible unmatchedKeyIDIssuer */ 
1863                                 if(unmatchedKeyIDIssuer 
!= NULL
) { 
1864                                         tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (1)", unmatchedKeyIDIssuer
); 
1867                                 unmatchedKeyIDIssuer 
= NULL
; 
1870                                 issuerCert 
= NULL
; /* keep looking */ 
1874                 if((issuerCert 
== NULL
) && (gatheredCerts 
!= NULL
)) { 
1875                         bool partial 
= false; 
1876                         issuerCert 
= gatheredCerts
->findIssuerForCertOrCrl(*thisSubject
, 
1880                                         /* deal with this later */ 
1881                                         foundPartialIssuer 
= true; 
1882                                         tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts"); 
1885                                         tpDebug("buildCertGroup: Cert FOUND in gatheredCerts"); 
1890                 if(issuerCert 
!= NULL
) { 
1891                         bool stashedIssuer 
= false; 
1892             /* Check whether candidate issuer is the same as untrustedRoot */ 
1893             if((untrustedRoot 
!= NULL
) && tp_CompareCerts(issuerCert
->itemData(), untrustedRoot
->itemData())) { 
1894                 /* already stashed */ 
1895                 stashedIssuer 
= true; 
1897                         /* Check whether candidate issuer is expired or not yet valid */ 
1898                         if(issuerCert
->isExpired() || issuerCert
->isNotValidYet()) { 
1899                                 if(expiredIssuer 
== NULL
) { 
1900                                         tpDebug("buildCertGroup: saving expired cert %p (2)", issuerCert
); 
1901                                         expiredIssuer 
= issuerCert
; 
1902                                         stashedIssuer 
= true; 
1904                                 /* else we already have an expired issuer candidate */ 
1907                                 /* unconditionally done with possible expiredIssuer */ 
1909                                 if(expiredIssuer 
!= NULL
) { 
1910                                         tpDebug("buildCertGroup: DISCARDING expired cert %p (2)", expiredIssuer
); 
1913                                 expiredIssuer 
= NULL
; 
1915                         /* Check whether candidate issuer failed to match authority key id in thisSubject */ 
1916                         if(!issuerCert
->isAuthorityKeyOf(*thisSubject
)) { 
1917                                 if(unmatchedKeyIDIssuer 
== NULL
) { 
1918                                         tpDebug("buildCertGroup: saving unmatched key id issuer %p (2)", issuerCert
); 
1919                                         unmatchedKeyIDIssuer 
= issuerCert
; 
1920                                         stashedIssuer 
= true; 
1922                                 /* else we already have an unmatched key id issuer candidate */ 
1925                                 /* unconditionally done with possible unmatchedKeyIdIssuer */ 
1927                                 if(unmatchedKeyIDIssuer 
!= NULL
) { 
1928                                         tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (2)", unmatchedKeyIDIssuer
); 
1931                                 unmatchedKeyIDIssuer 
= NULL
; 
1934                                 issuerCert 
= NULL
; /* keep looking */ 
1939                  * If we found a candidate issuer in input or gathered certs, check whether it 
1940                  * might be a cross-signed intermediate that can be replaced with an anchor. 
1942                 if(issuerCert 
!= NULL 
&& !issuerCert
->isSelfSigned() && (untrustedRoot 
== NULL
)) { 
1943                         bool partial 
= false; 
1944                         TPCertInfo 
*possibleAnchorCert 
= NULL
; 
1946                                 possibleAnchorCert 
= tpDbFindIssuerCert(mAlloc
, 
1957                         if(possibleAnchorCert 
!= NULL
) { 
1958                                 if(possibleAnchorCert
->isSelfSigned()) { 
1960                                          * We found a better replacement issuer, so use it. 
1961                                          * note that we don't need to free the old issuerCert first as it 
1962                                          * comes from inCertGroup or gatheredCerts (not from dbList). 
1963                                          * However, code from this point on cannot assume the same thing. 
1965                                         tpDebug("buildCertGroup: replacement anchor for issuer FOUND in dbList"); 
1966                     /* mark non-root candidate as unused since we won't use it */ 
1967                     issuerCert
->used(false); 
1968                                         issuerCert 
= possibleAnchorCert
; 
1970                                         /* Caller must free, since this cert came from a DLDB */ 
1971                                         certsToBeFreed
.appendCert(issuerCert
); 
1973                                                 /* deal with this later */ 
1974                                                 foundPartialIssuer 
= true; 
1977                                         /* unconditionally done with possible expiredIssuer */ 
1979                                         if(expiredIssuer 
!= NULL
) { 
1980                                                 tpDebug("buildCertGroup: DISCARDING expired cert %p (3)", expiredIssuer
); 
1983                                         expiredIssuer 
= NULL
; 
1984                                         /* unconditionally done with possible unmatchedKeyIDIssuer */ 
1986                                         if(unmatchedKeyIDIssuer 
!= NULL
) { 
1987                                                 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (3)", unmatchedKeyIDIssuer
); 
1990                                         unmatchedKeyIDIssuer 
= NULL
; 
1993                                         possibleAnchorCert
->freeUniqueRecord(); 
1994                                         delete possibleAnchorCert
; 
1995                                         possibleAnchorCert 
= NULL
; 
2000                 if((issuerCert 
== NULL
) && (dbList 
!= NULL
)) { 
2001                         /* Issuer not in incoming cert group or gathered certs. Search DBList. */ 
2002                         bool partial 
= false; 
2004                                 issuerCert 
= tpDbFindIssuerCert(mAlloc
, 
2016                                 /* unconditionally done with possible expiredIssuer */ 
2018                                 if(expiredIssuer 
!= NULL
) { 
2019                                         tpDebug("buildCertGroup: DISCARDING expired cert %p (4)", expiredIssuer
); 
2022                                 expiredIssuer 
= NULL
; 
2023                                 /* unconditionally done with possible unmatchedKeyIDIssuer */ 
2025                                 if(unmatchedKeyIDIssuer 
!= NULL
) { 
2026                                         tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (4)", unmatchedKeyIDIssuer
); 
2029                                 unmatchedKeyIDIssuer 
= NULL
; 
2032                                  * Handle Radar 4566041, endless loop of cross-signed certs. 
2033                                  * This can only happen when fetching certs from a DLDB or 
2034                                  * from the net; we prevent that from happening when the certs 
2035                                  * are in inCertGroup or gatheredCerts by keeping track of those 
2036                                  * certs' mUsed state. 
2038                                 if(isInGroup(*issuerCert
)) { 
2039                                         tpDebug("buildCertGroup: Multiple instances of cert"); 
2044                                         /* caller must free */ 
2045                                         certsToBeFreed
.appendCert(issuerCert
); 
2047                                                 /* deal with this later */ 
2048                                                 foundPartialIssuer 
= true; 
2049                                                 tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList"); 
2052                                                 tpDebug("buildCertGroup: Cert FOUND in dbList"); 
2056                 }       /*  searching DLDB list */ 
2059                  * Note: we don't handle an expired cert returned from tpDbFindIssuerCert() 
2060                  * in any special way like we do with findIssuerForCertOrCrl(). 
2061                  * tpDbFindIssuerCert() does its best to give us a temporally valid cert; if 
2062                  * it returns an expired cert (or, if findIssuerForCertOrCrl() gave us an 
2063                  * expired cert and tpDbFindIssuerCert() could not do any better), that's all 
2064                  * we have to work with at this point. We'll go back to the top of the loop 
2065                  * and apply trust settings if enabled; if an expired cert is trusted per 
2066                  * Trust Settings, we're done. (Note that anchors are fetched from a DLDB 
2067                  * when Trust Settings are enabled, so even if two roots with the same key 
2068                  * and subject name are in DLDBs, and one of them is expired, we'll have the 
2069                  * good one at this time because of tpDbFindIssuerCert()'s ability to find 
2072                  * If Trust Settings are not enabled, and we have an expired root at this 
2073                  * point, the expiredRoot mechanism is used to roll back and search for 
2074                  * an anchor that verifies the last good cert. 
2076         if((issuerCert 
== NULL
) &&                      /* tpDbFindIssuerCert() hasn't found one and 
2077                                              * we don't have a good one */ 
2078            (untrustedRoot 
!= NULL
)) {           /* but we have an untrusted root available*/ 
2080              * We couldn't find a better issuer, so end loop. In Case 1, we'll look for 
2081              * an alternate anchor issuer. If we can't find an anchor, we'll end up using 
2082              * the untrusted root. 
2084             break;          /* from main loop */ 
2087                 if((issuerCert 
== NULL
) &&                      /* tpDbFindIssuerCert() hasn't found one and 
2088                                                                                          * we don't have a good one */ 
2089                    (unmatchedKeyIDIssuer 
!= NULL
)) {            /* but we have an unmatched keyID candidate */ 
2091                          * OK, we'll take the unmatched key id issuer. 
2092                          * Note we don't have to free unmatchedKeyIDIssuer if we found a good one since 
2093                          * unmatchedKeyIDIssuer can only come from inCertGroup or gatheredCerts (not from 
2096                         tpDebug("buildCertGroup: USING unmatched key id issuer %p", unmatchedKeyIDIssuer
); 
2097                         issuerCert 
= unmatchedKeyIDIssuer
; 
2098                         unmatchedKeyIDIssuer 
= NULL
; 
2100                 if((issuerCert 
== NULL
) &&                      /* tpDbFindIssuerCert() hasn't found one and 
2101                                                                                          * we don't have a good one */ 
2102                    (expiredIssuer 
!= NULL
)) {           /* but we have an expired candidate */ 
2104                          * OK, we'll take the expired issuer. 
2105                          * Note we don't have to free expiredIssuer if we found a good one since 
2106                          * expiredIssuer can only come from inCertGroup or gatheredCerts (not from 
2109                         tpDebug("buildCertGroup: USING expired cert %p", expiredIssuer
); 
2110                         issuerCert 
= expiredIssuer
; 
2111                         expiredIssuer 
= NULL
; 
2113                 if(issuerCert 
== NULL
) { 
2114                         /* end of search, broken chain */ 
2119                  * One way or the other, we've found a cert which verifies subjectCert. 
2120                  * Add the issuer to outCertGroup and make it the new thisSubject for 
2123                 appendCert(issuerCert
); 
2124                 thisSubject 
= issuerCert
; 
2125                 subjectIsInGroup 
= CSSM_TRUE
; 
2128          * We've found a (potentially) better chain, so discard the untrusted root. 
2129          * Note we don't have to free untrustedRoot because it either: 
2130          *    -came from inCertGroup or gatherCerts; or 
2131          *    -came from the dbList and was already added to certToBeFreed. 
2133         untrustedRoot 
= NULL
; 
2137          * This can be NULL if we're evaluating a CRL (and we haven't 
2140         endCert 
= lastCert(); 
2143          * This, on the other hand, is always valid. It could be a CRL. 
2145         assert(thisSubject 
!= NULL
); 
2147         if( (actionFlags 
& CSSM_TP_ACTION_IMPLICIT_ANCHORS
) && 
2148                 ( (endCert 
&& endCert
->isSelfSigned()) || expiredRoot
) ) { 
2150                  * Caller will be satisfied with this; skip further anchor processing. 
2152                 tpAnchorDebug("buildCertGroup: found IMPLICIT anchor"); 
2155         if(numAnchorCerts 
== 0) { 
2156                 /* we're probably done */ 
2159         assert(anchorCerts 
!= NULL
); 
2161         /*** anchor cert handling ***/ 
2164          * Case 1: If thisSubject is not a root cert, try to validate with incoming anchor certs. 
2166         expiredIssuer 
= NULL
; 
2167         if(!(endCert 
&& endCert
->isSelfSigned())) { 
2168                 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) { 
2171                                 anchorInfo 
= new TPCertInfo(clHand
, 
2173                                         &anchorCerts
[certDex
], 
2178                                 /* bad anchor cert - ignore it */ 
2184                          * We must subsequently delete anchorInfo one way or the other. 
2185                          * If we add it to tpCertGroup, we also add it to certsToBeFreed. 
2186                          * Otherwise we delete it. 
2188                         if(!anchorInfo
->isIssuerOf(*thisSubject
)) { 
2189                                 /* not this anchor */ 
2190                                 tpAnchorDebug("buildCertGroup anchor not issuer"); 
2196                         crtn 
= thisSubject
->verifyWithIssuer(anchorInfo
); 
2198                         if(crtn 
== CSSM_OK
) { 
2199                                 if(anchorInfo
->isExpired() || anchorInfo
->isNotValidYet()) { 
2200                                         if(expiredIssuer 
== NULL
) { 
2202                                                  * Hang on to this one; keep looking for a better one. 
2204                                                 tpDebug("buildCertGroup: saving expired anchor %p", anchorInfo
); 
2205                                                 expiredIssuer 
= anchorInfo
; 
2206                                                 /* flag this condition for the switch below */ 
2207                                                 crtn 
= CSSM_CERT_STATUS_EXPIRED
; 
2208                                                 expiredIssuer
->isAnchor(true); 
2209                                                 assert(!anchorInfo
->isFromInputCerts()); 
2210                                                 expiredIssuer
->index(certDex
); 
2211                                                 certsToBeFreed
.appendCert(expiredIssuer
); 
2213                                         /* else we already have an expired candidate anchor */ 
2217                                          * Done with possible expiredIssuer. We don't delete it, since we already added 
2218                                          * it to certsToBeFreed, above. 
2220                                         if(expiredIssuer 
!= NULL
) { 
2221                                                 tpDebug("buildCertGroup: DISCARDING expired anchor %p", expiredIssuer
); 
2222                                                 expiredIssuer 
= NULL
; 
2228                                 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: 
2230                                          * A bit of a corner case. Found an issuer in AnchorCerts, but 
2231                                          * we can't do a signature verify since the issuer has a partial 
2232                                          * public key. Proceed but return 
2233                                          * CSSMERR_TP_CERTIFICATE_CANT_OPERATE. 
2235                                         if(anchorInfo
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) { 
2236                                                 foundPartialIssuer 
= true; 
2237                                                 crtn 
= CSSMERR_TP_CERTIFICATE_CANT_OPERATE
; 
2245                                         /*  A fully successful return. */ 
2246                                         verifiedToAnchor 
= CSSM_TRUE
; 
2247                                         if(anchorInfo
->isSelfSigned()) { 
2248                                                 verifiedToRoot 
= CSSM_TRUE
; 
2252                                          * Add this anchor cert to the output group 
2253                                          * and to certsToBeFreed. 
2255                                         appendCert(anchorInfo
); 
2256                                         anchorInfo
->isAnchor(true); 
2257                                         assert(!anchorInfo
->isFromInputCerts()); 
2258                                         anchorInfo
->index(certDex
); 
2259                                         certsToBeFreed
.appendCert(anchorInfo
); 
2260                                         tpDebug("buildCertGroup: Cert FOUND by signer in AnchorList"); 
2261                                         tpAnchorDebug("buildCertGroup: Cert FOUND by signer in AnchorList"); 
2262                                         /* one more thing: partial public key processing needed? */ 
2263                                         if(foundPartialIssuer
) { 
2264                                                 return verifyWithPartialKeys(subjectItem
); 
2271                                         /* continue to next anchor */ 
2272                                         if(crtn 
!= CSSM_CERT_STATUS_EXPIRED
) { 
2273                                                 /* Expired means we're saving it in expiredIssuer */ 
2274                                                 tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG"); 
2280                 }       /* for each anchor */ 
2281         }       /* thisSubject not a root cert */ 
2284          * Case 2: Check whether endCert is present in anchor certs. 
2286          * Also used to validate an expiredRoot that we pulled off the chain in 
2287          * hopes of finding something better (which, if we're here, we haven't done). 
2289          * Note that the main loop above did the actual root self-verify test. 
2291         if(endCert 
|| expiredRoot
) { 
2293                 TPCertInfo 
*theRoot
; 
2295                         /* this is NOT in our outgoing cert group (yet) */ 
2296                         theRoot 
= expiredRoot
; 
2301                 /* see if that root cert is identical to one of the anchor certs */ 
2302                 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) { 
2303                         if(tp_CompareCerts(theRoot
->itemData(), &anchorCerts
[certDex
])) { 
2304                                 /* one fully successful return */ 
2305                                 tpAnchorDebug("buildCertGroup: end cert in input AND anchors"); 
2306                                 verifiedToAnchor 
= CSSM_TRUE
; 
2307                                 theRoot
->isAnchor(true); 
2308                                 if(!theRoot
->isFromInputCerts()) { 
2309                                         /* Don't override index into input certs */ 
2310                                         theRoot
->index(certDex
); 
2313                                         /* verified to anchor but caller will see 
2314                                          * CSSMERR_TP_CERT_EXPIRED */ 
2315                                         appendCert(expiredRoot
); 
2317                                 /* one more thing: partial public key processing needed? */ 
2318                                 if(foundPartialIssuer
) { 
2319                                         return verifyWithPartialKeys(subjectItem
); 
2326                 tpAnchorDebug("buildCertGroup: end cert in input, NOT anchors"); 
2328                 if(!expiredRoot 
&& endCert
->isSelfSigned()) { 
2329                         /* verified to a root cert which is not an anchor */ 
2330                         /* Generally maps to CSSMERR_TP_INVALID_ANCHOR_CERT by caller */ 
2331                         /* one more thing: partial public key processing needed? */ 
2332                         if(foundPartialIssuer
) { 
2333                                 return verifyWithPartialKeys(subjectItem
); 
2339                 /* else try finding a good anchor */ 
2342         /* regardless of anchor search status... */ 
2344         if(!verifiedToAnchor 
&& (expiredIssuer 
!= NULL
)) { 
2345                 /* expiredIssuer here is always an anchor */ 
2346                 tpDebug("buildCertGroup: accepting expired anchor %p", expiredIssuer
); 
2347                 appendCert(expiredIssuer
); 
2348                 verifiedToAnchor 
= CSSM_TRUE
; 
2349                 if(expiredIssuer
->isSelfSigned()) { 
2350                         verifiedToRoot 
= CSSM_TRUE
; 
2352                 /* no matter what, we don't want these */ 
2354         untrustedRoot 
= NULL
; 
2359          * Special case: untrustedRoot found, but no luck resolving the problem with 
2360          * anchors.  Go ahead and (re-)append the untrusted root and return 
2362         tpDebug("buildCertGroup: accepted UNTRUSTED root"); 
2363         appendCert(untrustedRoot
); 
2364         if(foundPartialIssuer
) { 
2365             return verifyWithPartialKeys(subjectItem
); 
2373                  * One remaining special case: expiredRoot found in input certs, but 
2374                  * no luck resolving the problem with the anchors. Go ahead and (re-)append 
2375                  * the expired root and return. 
2377                 tpDebug("buildCertGroup: accepting EXPIRED root"); 
2378                 appendCert(expiredRoot
); 
2379                 if(foundPartialIssuer
) { 
2380                         return verifyWithPartialKeys(subjectItem
); 
2387         /* If we get here, determine if fetching the issuer from the network 
2388          * should be attempted: <rdar://6113890&7419584&7422356> 
2390         attemptNetworkFetch 
= (actionFlags 
& CSSM_TP_ACTION_FETCH_CERT_FROM_NET
); 
2391         if( (!dbList 
|| (dbList
->NumHandles 
== 0)) && 
2392                  (!anchorCerts 
|| (numAnchorCerts 
== 0)) ) { 
2393                 /* DB list is empty *and* anchors are empty; there is no point in going 
2394                  * out to the network, since we cannot build a chain to a trusted root. 
2395                  * (This can occur when the caller wants to evaluate a single certificate 
2396                  * without trying to build the chain, e.g. to check its key usage.) 
2398                 attemptNetworkFetch 
= false; 
2402          * If we haven't verified to a root, and net fetch of certs is enabled, 
2403          * try to get the issuer of the last cert in the chain from the net. 
2404          * If that succeeds, then call ourself recursively to perform the 
2405          * whole search again (including comparing to or verifying against 
2408         if(!verifiedToRoot 
&& !verifiedToAnchor 
&& 
2409                 (endCert 
!= NULL
) && attemptNetworkFetch
) { 
2410                 TPCertInfo 
*issuer 
= NULL
; 
2411                 CSSM_RETURN cr 
= tpFetchIssuerFromNet(*endCert
, 
2417                         case CSSMERR_TP_CERTGROUP_INCOMPLETE
: 
2418                                 /* no issuerAltName, no reason to log this */ 
2422                                 endCert
->addStatusCode(cr
); 
2424                         case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: 
2425                                 /* use this one but re-verify later */ 
2426                                 foundPartialIssuer 
= true; 
2431                                 tpDebug("buildCertGroup: Cert FOUND from Net; recursing"); 
2433                                 if(isInGroup(*issuer
)) { 
2434                                         tpDebug("buildCertGroup: Multiple instances of cert from net"); 
2437                                         crtn 
= CSSMERR_TP_CERTGROUP_INCOMPLETE
; 
2441                                 /* add this fetched cert to constructed group */ 
2443                                 issuer
->isFromNet(true); 
2444                                 certsToBeFreed
.appendCert(issuer
); 
2447                                 cr 
= buildCertGroup(*issuer
, 
2457                                         CSSM_TRUE
,              // subjectIsInGroup 
2462                                         leafKeyUse
,             // actually don't care since the leaf will not 
2466                                         verifiedViaTrustSettings
); 
2471                                 /* one more thing: partial public key processing needed? */ 
2472                                 if(foundPartialIssuer
) { 
2473                                         return verifyWithPartialKeys(subjectItem
); 
2481         /* regardless of outcome, check for partial keys to log per-cert status */ 
2482         CSSM_RETURN partRtn 
= CSSM_OK
; 
2483         if(foundPartialIssuer
) { 
2484                 partRtn 
= verifyWithPartialKeys(subjectItem
); 
2495  * Called from buildCertGroup as final processing of a constructed 
2496  * group when CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE has been 
2497  * detected. Perform partial public key processing. 
2499  * We don't have to verify every element, just the ones whose 
2500  * issuers have partial public keys. 
2503  *       CSSMERR_TP_CERTIFICATE_CANT_OPERATE in the case of an issuer cert 
2504  *              with a partial public key which can't be completed. 
2505  *       CSSMERR_TP_INVALID_CERT_AUTHORITY if sig verify failed with 
2506  *              a (supposedly) completed partial key 
2508 CSSM_RETURN 
TPCertGroup::verifyWithPartialKeys( 
2509         const TPClItemInfo      
&subjectItem
)           // Cert or CRL 
2511         TPCertInfo 
*lastFullKeyCert 
= NULL
; 
2512         tpDebug("verifyWithPartialKeys top"); 
2514         /* start from the end - it's easier */ 
2515         for(int dex
=mNumCerts
-1; dex 
>= 0; dex
--) { 
2516                 TPCertInfo 
*thisCert 
= mCertInfo
[dex
]; 
2519                  * If this is the start of the cert chain, and it's not being 
2520                  * used to verify subjectItem, then we're done. 
2523                         if((void *)thisCert 
== (void *)&subjectItem
) { 
2524                                 tpDebug("verifyWithPartialKeys: success at leaf cert"); 
2528                 if(!thisCert
->hasPartialKey()) { 
2530                          * Good to know. Record this and move on. 
2532                         lastFullKeyCert 
= thisCert
; 
2533                         tpDebug("full key cert found at index %d", dex
); 
2536                 if(lastFullKeyCert 
== NULL
) { 
2538                          * No full keys between here and the end! 
2540                         tpDebug("UNCOMPLETABLE cert at index %d", dex
); 
2541                         if(thisCert
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) { 
2542                                 return CSSMERR_TP_CERTIFICATE_CANT_OPERATE
; 
2549                 /* do the verify - of next cert in chain or of subjectItem */ 
2550                 const TPClItemInfo 
*subject
; 
2552                         subject 
= &subjectItem
; 
2553                         tpDebug("...verifying subject item with partial cert 0"); 
2556                         subject 
= mCertInfo
[dex 
- 1]; 
2557                         tpDebug("...verifying with partial cert %d", dex
); 
2559                 CSSM_RETURN crtn 
= subject
->verifyWithIssuer(thisCert
, 
2562                         tpDebug("CERT VERIFY ERROR with partial cert at index %d", dex
); 
2563                         if(thisCert
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) { 
2564                                 return CSSMERR_TP_INVALID_CERT_AUTHORITY
; 
2572         /* we just verified subjectItem - right?  */ 
2573         assert((void *)mCertInfo
[0] != (void *)&subjectItem
); 
2574         tpDebug("verifyWithPartialKeys: success at subjectItem"); 
2579  * Free records obtained from DBs. Called when these records are not going to 
2580  * be passed to caller of CertGroupConstruct or CertGroupVerify. 
2582 void TPCertGroup::freeDbRecords() 
2584         for(unsigned dex
=0; dex
<mNumCerts
; dex
++) { 
2585                 TPCertInfo 
*certInfo 
= mCertInfo
[dex
]; 
2586                 certInfo
->freeUniqueRecord();