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