1/*
2 * Copyright (c) 2002 Apple Computer, 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 * TPCrlInfo.h - TP's private CRL and CRL group
21 *
22 * Written 9/30/2002 by Doug Mitchell.
23 */
24
25#include "TPCrlInfo.h"
26#include "tpdebugging.h"
27#include "certGroupUtils.h"
28#include "tpCrlVerify.h"
29#include "tpPolicies.h"
30#include "tpTime.h"
31#include <Security/cssmapi.h>
32#include <Security/x509defs.h>
33#include <Security/oidscert.h>
34#include <Security/oidscrl.h>
35#include <security_cdsa_utilities/cssmerrors.h>
36#include <string.h>						/* for memcmp */
37#include <Security/cssmapple.h>
38
39/*
40 * Replacement for CSSM_CL_CrlGetFirstCachedFieldValue for use with
41 * TPCrlItemInfo's generic getFirstCachedField mechanism.
42 */
43static CSSM_RETURN tpGetFirstCachedFieldValue (CSSM_CL_HANDLE CLHandle,
44                                     CSSM_HANDLE CrlHandle,
45                                     const CSSM_OID *CrlField,
46                                     CSSM_HANDLE_PTR ResultsHandle,
47                                     uint32 *NumberOfMatchedFields,
48                                     CSSM_DATA_PTR *Value)
49{
50	return CSSM_CL_CrlGetFirstCachedFieldValue(CLHandle,
51		CrlHandle,
52        NULL,		// const CSSM_DATA *CrlRecordIndex,
53		CrlField,
54		ResultsHandle,
55		NumberOfMatchedFields,
56		Value);
57}
58
59static const TPClItemCalls tpCrlClCalls =
60{
61	tpGetFirstCachedFieldValue,
62	CSSM_CL_CrlAbortQuery,
63	CSSM_CL_CrlCache,
64	CSSM_CL_CrlAbortCache,
65	CSSM_CL_CrlVerify,
66	&CSSMOID_X509V1CRLThisUpdate,
67	&CSSMOID_X509V1CRLNextUpdate,
68	CSSMERR_TP_INVALID_CRL_POINTER,
69	CSSMERR_APPLETP_CRL_EXPIRED,
70	CSSMERR_APPLETP_CRL_NOT_VALID_YET
71};
72
73
74/*
75 * No default constructor - this is the only way.
76 * This caches the cert and fetches subjectName and issuerName
77 * to ensure the incoming certData is well-constructed.
78 */
79TPCrlInfo::TPCrlInfo(
80	CSSM_CL_HANDLE		clHand,
81	CSSM_CSP_HANDLE		cspHand,
82	const CSSM_DATA		*crlData,
83	TPItemCopy			copyCrlData,		// true: we copy, we free
84											// false - caller owns
85	const char 			*verifyTime)		// = NULL
86
87	: TPClItemInfo(clHand, cspHand, tpCrlClCalls, crlData,
88			copyCrlData, verifyTime),
89		mRefCount(0),
90		mFromWhere(CFW_Nowhere),
91		mX509Crl(NULL),
92		mCrlFieldToFree(NULL),
93		mVerifyState(CVS_Unknown),
94		mVerifyError(CSSMERR_TP_INTERNAL_ERROR)
95{
96	CSSM_RETURN	crtn;
97
98	mUri.Data = NULL;
99	mUri.Length = 0;
100
101	/* fetch parsed CRL */
102	crtn = fetchField(&CSSMOID_X509V2CRLSignedCrlCStruct, &mCrlFieldToFree);
103	if(crtn) {
104		/* bad CRL */
105		releaseResources();
106		CssmError::throwMe(crtn);
107	}
108	if(mCrlFieldToFree->Length != sizeof(CSSM_X509_SIGNED_CRL)) {
109		tpErrorLog("fetchField(SignedCrlCStruct) length error\n");
110		releaseResources();
111		CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
112	}
113	mX509Crl = (CSSM_X509_SIGNED_CRL *)mCrlFieldToFree->Data;
114	/* any other other commonly used fields? */
115}
116
117TPCrlInfo::~TPCrlInfo()
118{
119	releaseResources();
120}
121
122void TPCrlInfo::releaseResources()
123{
124	if(mCrlFieldToFree) {
125		freeField(&CSSMOID_X509V2CRLSignedCrlCStruct, mCrlFieldToFree);
126		mCrlFieldToFree = NULL;
127	}
128	if(mUri.Data) {
129		Allocator::standard().free(mUri.Data);
130		mUri.Data = NULL;
131		mUri.Length = 0;
132	}
133	TPClItemInfo::releaseResources();
134}
135
136void TPCrlInfo::uri(const CSSM_DATA &uri)
137{
138	tpCopyCssmData(Allocator::standard(), &uri, &mUri);
139}
140
141/*
142 * List of extensions we understand and can accept as critical.
143 */
144static const CSSM_OID *const TPGoodCrlExtens[] =
145{
146	&CSSMOID_CrlNumber,
147	/* Note NOT CSSMOID_DeltaCrlIndicator! That's fatal */
148	&CSSMOID_CrlReason,
149	&CSSMOID_CertIssuer,
150	&CSSMOID_IssuingDistributionPoint,
151	&CSSMOID_HoldInstructionCode,
152	&CSSMOID_InvalidityDate,
153	&CSSMOID_AuthorityKeyIdentifier,
154	&CSSMOID_SubjectAltName,
155	&CSSMOID_IssuerAltName
156};
157
158#define NUM_KNOWN_EXTENS (sizeof(TPGoodCrlExtens) / sizeof(CSSM_OID_PTR))
159
160/*
161 * Do our best to understand all the entries in a CSSM_X509_EXTENSIONS,
162 * which may be per-CRL or per-entry.
163 *
164 * For now, we just ensure that for every critical extension,
165 * we actually understand it and can deal it.
166 */
167CSSM_RETURN TPCrlInfo::parseExtensions(
168	TPVerifyContext				&vfyCtx,
169	bool						isPerEntry,
170	uint32						entryIndex,		// if isPerEntry
171	const CSSM_X509_EXTENSIONS	&extens,
172	TPCertInfo					*forCert,		// optional
173	bool						&isIndirectCrl)	// RETURNED
174{
175	isIndirectCrl = false;
176	for(uint32 dex=0; dex<extens.numberOfExtensions; dex++) {
177		CSSM_X509_EXTENSION_PTR exten = &extens.extensions[dex];
178		if(exten->critical) {
179			/* critical: is it in our list of understood extensions? */
180			unsigned i;
181			for(i=0; i<NUM_KNOWN_EXTENS; i++) {
182				if(tpCompareOids(&exten->extnId, TPGoodCrlExtens[i])) {
183					/* we're cool with this one */
184					break;
185				}
186			}
187			if(i == NUM_KNOWN_EXTENS) {
188				tpCrlDebug("parseExtensions: Unknown Critical Extension\n");
189				return CSSMERR_APPLETP_UNKNOWN_CRL_EXTEN;
190			}
191		}
192
193		/* Specific extension handling. */
194		if(tpCompareOids(&exten->extnId,
195				&CSSMOID_IssuingDistributionPoint)) {
196			/*
197			 * If this assertion fails, we're out of sync with the CL
198			 */
199			assert(exten->format == CSSM_X509_DATAFORMAT_PARSED);
200			CE_IssuingDistributionPoint *idp =
201				(CE_IssuingDistributionPoint *)
202					exten->value.parsedValue;
203
204			/*
205			 * Snag indirectCrl flag for caller in any case
206			 */
207			if(idp->indirectCrlPresent && idp->indirectCrl) {
208				isIndirectCrl = true;
209			}
210			if(forCert != NULL) {
211				/* If no target cert, i.e., we're just verifying a CRL,
212				 * skip the remaining IDP checks. */
213
214				/* verify onlyCACerts/onlyUserCerts */
215				bool isUserCert;
216				if(forCert->isLeaf() &&
217					!(vfyCtx.actionFlags & CSSM_TP_ACTION_LEAF_IS_CA)) {
218						isUserCert = true;
219				}
220				else {
221					isUserCert = false;
222				}
223				if((idp->onlyUserCertsPresent) && (idp->onlyUserCerts)) {
224					if(!isUserCert) {
225						tpCrlDebug("parseExtensions: onlyUserCerts, "
226							"!leaf\n");
227						return CSSMERR_APPLETP_IDP_FAIL;
228					}
229				}
230				if((idp->onlyCACertsPresent) && (idp->onlyCACerts)) {
231					if(isUserCert) {
232						tpCrlDebug("parseExtensions: onlyCACerts, leaf\n");
233						return CSSMERR_APPLETP_IDP_FAIL;
234					}
235				}
236			}	/* IDP */
237		} 		/* have target cert */
238	}
239
240	return CSSM_OK;
241}
242
243/*
244 * The heavyweight "perform full verification of this CRL" op.
245 * Must verify to an anchor cert in tpVerifyContext or via
246 * Trust Settings if so enabled.
247 * Intermediate certs can come from signerCerts or dBList.
248 */
249CSSM_RETURN TPCrlInfo::verifyWithContext(
250	TPVerifyContext			&tpVerifyContext,
251	TPCertInfo				*forCert,		// optional
252	bool					doCrlVerify)
253{
254	/*
255	 * Step 1: this CRL must be current. Caller might have re-evaluated
256	 * expired/notValidYet since our construction via calculateCurrent().
257	 */
258	if(isExpired()) {
259		return CSSMERR_APPLETP_CRL_EXPIRED;
260	}
261	if(isNotValidYet()) {
262		return CSSMERR_APPLETP_CRL_NOT_VALID_YET;
263	}
264
265	/* subsequent verify state is cached */
266	switch(mVerifyState) {
267		case CVS_Good:
268			return CSSM_OK;
269		case CVS_Bad:
270			return mVerifyError;
271		case CVS_Unknown:
272			break;
273		default:
274			tpErrorLog("verifyWithContext: bad verifyState\n");
275			return CSSMERR_TP_INTERNAL_ERROR;
276	}
277
278	/*
279	 * Step 2: parse & understand all critical CRL extensions.
280	 */
281	CSSM_RETURN crtn;
282	bool isIndirectCrl;
283	crtn = parseExtensions(tpVerifyContext,
284		false,
285		0,
286		mX509Crl->tbsCertList.extensions,
287		forCert,
288		isIndirectCrl);
289	if(crtn) {
290		mVerifyState = CVS_Bad;
291		if(!forCert || forCert->addStatusCode(crtn)) {
292			return crtn;
293		}
294		/* else continue */
295	}
296	CSSM_X509_REVOKED_CERT_LIST_PTR revoked =
297			mX509Crl->tbsCertList.revokedCertificates;
298	if(revoked != NULL) {
299		for(uint32 dex=0; dex<revoked->numberOfRevokedCertEntries; dex++) {
300			bool dummyIsIndirect;	// can't be set here
301			crtn = parseExtensions(tpVerifyContext,
302				true,
303				dex,
304				revoked->revokedCertEntry[dex].extensions,
305				forCert,
306				dummyIsIndirect);
307			if(crtn) {
308				if(!forCert || forCert->addStatusCode(crtn)) {
309					mVerifyState = CVS_Bad;
310					return crtn;
311				}
312			}
313		}
314	}
315
316	/*
317	 * Step 3: obtain a fully verified cert chain which verifies this CRL.
318	 */
319	CSSM_BOOL	verifiedToRoot;
320	CSSM_BOOL	verifiedToAnchor;
321	CSSM_BOOL	verifiedViaTrustSetting;
322
323	TPCertGroup outCertGroup(tpVerifyContext.alloc,
324		TGO_Caller);			// CRLs owned by inCertGroup
325
326	/* set up for disposal of TPCertInfos created by
327	 * CertGroupConstructPriv */
328	TPCertGroup	certsToBeFreed(tpVerifyContext.alloc, TGO_Group);
329
330	if(tpVerifyContext.signerCerts) {
331		/* start from scratch with this group */
332		tpVerifyContext.signerCerts->setAllUnused();
333	}
334	crtn = outCertGroup.buildCertGroup(
335			*this,							// subject item
336			tpVerifyContext.signerCerts,	// inCertGroup, optional
337			tpVerifyContext.dbList,			// optional
338			tpVerifyContext.clHand,
339			tpVerifyContext.cspHand,
340			tpVerifyContext.verifyTime,
341			tpVerifyContext.numAnchorCerts,
342			tpVerifyContext.anchorCerts,
343			certsToBeFreed,
344			&tpVerifyContext.gatheredCerts,
345			CSSM_FALSE,						// subjectIsInGroup
346			tpVerifyContext.actionFlags,
347			tpVerifyContext.policyOid,
348			tpVerifyContext.policyStr,
349			tpVerifyContext.policyStrLen,
350			kSecTrustSettingsKeyUseSignRevocation,
351			verifiedToRoot,
352			verifiedToAnchor,
353			verifiedViaTrustSetting);
354	/* subsequent errors to errOut: */
355
356	if(crtn) {
357		tpCrlDebug("TPCrlInfo::verifyWithContext buildCertGroup failure "
358			"index %u",	index());
359		if(!forCert || forCert->addStatusCode(crtn)) {
360			goto errOut;
361		}
362	}
363	if (verifiedToRoot && (tpVerifyContext.actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS))
364		verifiedToAnchor = CSSM_TRUE;
365	if(!verifiedToAnchor && !verifiedViaTrustSetting) {
366		/* required */
367		if(verifiedToRoot) {
368			/* verified to root which is not an anchor */
369			tpCrlDebug("TPCrlInfo::verifyWithContext root, no anchor, "
370				"index %u",	index());
371			crtn = CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT;
372		}
373		else {
374			/* partial chain, no root, not verifiable by anchor */
375			tpCrlDebug("TPCrlInfo::verifyWithContext no root, no anchor, "
376				"index %u",	index());
377			crtn = CSSMERR_APPLETP_CRL_NOT_TRUSTED;
378		}
379		if(!forCert || forCert->addStatusCode(crtn)) {
380			mVerifyState = CVS_Bad;
381			goto errOut;
382		}
383	}
384
385	/*
386	 * Step 4: policy verification on the returned cert group
387	 * We need to (temporarily) assert the "leaf cert is a CA" flag
388	 * here.
389	 */
390	outCertGroup.certAtIndex(0)->isLeaf(true);
391	crtn = tp_policyVerify(kCrlPolicy,
392		tpVerifyContext.alloc,
393		tpVerifyContext.clHand,
394		tpVerifyContext.cspHand,
395		&outCertGroup,
396		verifiedToRoot,
397		verifiedViaTrustSetting,
398		tpVerifyContext.actionFlags | CSSM_TP_ACTION_LEAF_IS_CA,
399		NULL,							// sslOpts
400		NULL);							// policyOpts, not currently used
401	if(crtn) {
402		tpCrlDebug("   ...verifyWithContext policy FAILURE CRL %u",
403			index());
404		if(!forCert || forCert->addStatusCode(CSSMERR_APPLETP_CRL_POLICY_FAIL)) {
405			mVerifyState = CVS_Bad;
406			goto errOut;
407		}
408	}
409
410	/*
411	 * Step 5: recursively perform CRL verification on the certs
412	 * gathered to verify this CRL.
413	 * Only performed if this CRL is an indirect CRL or the caller
414	 * explicitly told us to do this (i.e., caller is verifying a
415	 * CRL, not a cert chain).
416	 */
417	if(isIndirectCrl || doCrlVerify) {
418		tpCrlDebug("verifyWithContext recursing to "
419			"tpVerifyCertGroupWithCrls");
420		crtn = tpVerifyCertGroupWithCrls(tpVerifyContext,
421			outCertGroup);
422		if(crtn) {
423			tpCrlDebug("   ...verifyWithContext CRL reverify FAILURE CRL %u",
424				index());
425			if(!forCert || forCert->addStatusCode(crtn)) {
426				mVerifyState = CVS_Bad;
427				goto errOut;
428			}
429		}
430	}
431
432	tpCrlDebug("   ...verifyWithContext CRL %u SUCCESS", index());
433	mVerifyState = CVS_Good;
434errOut:
435	/* we own these, we free the DB records */
436	certsToBeFreed.freeDbRecords();
437	return crtn;
438}
439
440/*
441 * Wrapper for verifyWithContext for use when evaluating a CRL
442 * "now" instead of at the time in TPVerifyContext.verifyTime.
443 * In this case, on entry, TPVerifyContext.verifyTime is the
444 * time at which a cert is being evaluated.
445 */
446CSSM_RETURN TPCrlInfo::verifyWithContextNow(
447	TPVerifyContext		&tpVerifyContext,
448	TPCertInfo			*forCert,			// optional
449	bool				doCrlVerify)
450{
451	CSSM_TIMESTRING ctxTime = tpVerifyContext.verifyTime;
452	CSSM_RETURN crtn = verifyWithContext(tpVerifyContext, forCert, doCrlVerify);
453	tpVerifyContext.verifyTime = ctxTime;
454	return crtn;
455}
456
457/*
458 * Do I have the same issuer as the specified subject cert? Returns
459 * true if so.
460 */
461bool TPCrlInfo::hasSameIssuer(
462	const TPCertInfo	&subject)
463{
464	assert(subject.issuerName() != NULL);
465	if(tpCompareCssmData(issuerName(), subject.issuerName())) {
466		return true;
467	}
468	else {
469		return false;
470	}
471}
472
473/*
474 * Determine if specified cert has been revoked as of the
475 * provided time; a NULL timestring indicates "now".
476 *
477 * Assumes current CRL is verified good and that issuer names of
478 * the cert and CRL match.
479 *
480 * This duplicates similar logic in the CL, but to avoid re-parsing
481 * the subject cert (which we have parsed and cached), we just do it
482 * here.
483 *
484 * Possible errors are
485 *	CSSMERR_TP_CERT_REVOKED
486 * 	CSSMERR_TP_CERT_SUSPENDED
487 *  TBD
488 *
489 * Error status is added to subjectCert.
490 */
491CSSM_RETURN TPCrlInfo::isCertRevoked(
492	TPCertInfo &subjectCert,
493	CSSM_TIMESTRING verifyTime)
494{
495	assert(mVerifyState == CVS_Good);
496	CSSM_X509_TBS_CERTLIST_PTR tbs = &mX509Crl->tbsCertList;
497
498	/* trivial case - empty CRL */
499	if((tbs->revokedCertificates == NULL) ||
500	   (tbs->revokedCertificates->numberOfRevokedCertEntries == 0)) {
501	   tpCrlDebug("   isCertRevoked: empty CRL at index %u", index());
502	   return CSSM_OK;
503	}
504
505	/* is subject cert's serial number in this CRL? */
506	CSSM_DATA_PTR subjSerial = NULL;
507	CSSM_RETURN crtn;
508	crtn = subjectCert.fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial);
509	if(crtn) {
510		/* should never happen */
511		tpErrorLog("TPCrlInfo:isCertRevoked: error fetching serial number\n");
512		if(subjectCert.addStatusCode(crtn)) {
513			return crtn;
514		}
515		else {
516			/* allowed error - can't proceed; punt with success */
517			return CSSM_OK;
518		}
519	}
520	/* subsequent errors to errOut: */
521
522	uint32 numEntries = tbs->revokedCertificates->numberOfRevokedCertEntries;
523	CSSM_X509_REVOKED_CERT_ENTRY_PTR entries =
524		tbs->revokedCertificates->revokedCertEntry;
525	crtn = CSSM_OK;
526	CFDateRef cfRevokedTime = NULL;
527	CFDateRef cfVerifyTime = NULL;
528
529	for(uint32 dex=0; dex<numEntries; dex++) {
530		CSSM_X509_REVOKED_CERT_ENTRY_PTR entry = &entries[dex];
531		if(tpCompareCssmData(subjSerial, &entry->certificateSerialNumber)) {
532			/*
533			 * It's in there. Compare revocation time in the CRL to
534			 * our caller-specified verifyTime.
535			 */
536			CSSM_X509_TIME_PTR xTime = &entry->revocationDate;
537			int rtn;
538			rtn = timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length,
539				&cfRevokedTime);
540			if(rtn) {
541				tpErrorLog("fetchNotBeforeAfter: malformed revocationDate\n");
542			}
543			else {
544				if(verifyTime != NULL) {
545					rtn = timeStringToCfDate((char *)verifyTime, (unsigned)strlen(verifyTime),
546											 &cfVerifyTime);
547				}
548				else {
549					/* verify right now */
550					cfVerifyTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
551				}
552				if((rtn == 0) && cfVerifyTime != NULL) {
553					CFComparisonResult res = CFDateCompare(cfVerifyTime, cfRevokedTime, NULL);
554					if(res == kCFCompareLessThan) {
555						/* cfVerifyTime < cfRevokedTime; I guess this one's OK */
556						tpCrlDebug("   isCertRevoked: cert %u NOT YET REVOKED by CRL %u",
557								   subjectCert.index(), index());
558						break;
559					}
560				}
561			}
562
563			/*
564			 * REQUIRED TBD: parse the entry's extensions, specifically to
565			 * get a reason. This will entail a bunch of new TP/cert specific
566			 * CSSM_RETURNS.
567			 * For now, just flag it revoked.
568			 */
569			crtn = CSSMERR_TP_CERT_REVOKED;
570			tpCrlDebug("   isCertRevoked: cert %u REVOKED by CRL %u",
571				subjectCert.index(), index());
572			break;
573		}
574	}
575
576	subjectCert.freeField(&CSSMOID_X509V1SerialNumber, subjSerial);
577	if(crtn && !subjectCert.addStatusCode(crtn)) {
578		return CSSM_OK;
579	}
580	if(cfRevokedTime) {
581		CFRelease(cfRevokedTime);
582	}
583	if(cfVerifyTime) {
584		CFRelease(cfVerifyTime);
585	}
586	return crtn;
587}
588
589/***
590 *** TPCrlGroup class
591 ***/
592
593/* build empty group */
594TPCrlGroup::TPCrlGroup(
595	Allocator			&alloc,
596	TPGroupOwner		whoOwns) :
597		mAlloc(alloc),
598		mCrlInfo(NULL),
599		mNumCrls(0),
600		mSizeofCrlInfo(0),
601		mWhoOwns(whoOwns)
602{
603	/* nothing for now */
604}
605
606/*
607 * Construct from unordered, untrusted CSSM_CRLGROUP. Resulting
608 * TPCrlInfos are more or less in the same order as the incoming
609 * CRLs, though incoming CRLs are discarded if they don't parse.
610 * No verification of any sort is performed.
611 */
612TPCrlGroup::TPCrlGroup(
613	const CSSM_CRLGROUP 	*cssmCrlGroup,			// optional
614	CSSM_CL_HANDLE 			clHand,
615	CSSM_CSP_HANDLE 		cspHand,
616	Allocator				&alloc,
617	const char				*verifyTime,			// may be NULL
618	TPGroupOwner			whoOwns) :
619		mAlloc(alloc),
620		mCrlInfo(NULL),
621		mNumCrls(0),
622		mSizeofCrlInfo(0),
623		mWhoOwns(whoOwns)
624{
625	/* verify input args */
626	if((cssmCrlGroup == NULL) || (cssmCrlGroup->NumberOfCrls == 0)) {
627		return;
628	}
629	if(cspHand == CSSM_INVALID_HANDLE) {
630		CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE);
631	}
632	if(clHand == CSSM_INVALID_HANDLE)	{
633		CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE);
634	}
635	if(cssmCrlGroup->CrlGroupType != CSSM_CRLGROUP_DATA) {
636		CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP);
637	}
638	switch(cssmCrlGroup->CrlType) {
639		case CSSM_CRL_TYPE_X_509v1:
640		case CSSM_CRL_TYPE_X_509v2:
641			break;
642		default:
643			CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
644	}
645	switch(cssmCrlGroup->CrlEncoding) {
646		case CSSM_CRL_ENCODING_BER:
647		case CSSM_CRL_ENCODING_DER:
648			break;
649		default:
650			CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
651	}
652
653	/*
654	 * Add remaining input certs to mCrlInfo.
655	 */
656	TPCrlInfo *crlInfo = NULL;
657	for(unsigned crlDex=0; crlDex<cssmCrlGroup->NumberOfCrls; crlDex++) {
658		try {
659			crlInfo = new TPCrlInfo(clHand,
660				cspHand,
661				&cssmCrlGroup->GroupCrlList.CrlList[crlDex],
662				TIC_NoCopy,			// don't copy data
663				verifyTime);
664		}
665		catch (...) {
666			/* just ignore this CRL */
667			continue;
668		}
669		crlInfo->index(crlDex);
670		appendCrl(*crlInfo);
671	}
672}
673
674/*
675 * Deletes all TPCrlInfo's if appropriate.
676 */
677TPCrlGroup::~TPCrlGroup()
678{
679	if(mWhoOwns == TGO_Group) {
680		unsigned i;
681		for(i=0; i<mNumCrls; i++) {
682			delete mCrlInfo[i];
683		}
684	}
685	mAlloc.free(mCrlInfo);
686}
687
688/* add/remove/access TPTCrlInfo's. */
689/*
690 * NOTE: I am aware that most folks would just use an array<> here, but
691 * gdb is so lame that it doesn't even let one examine the contents
692 * of an array<> (or just about anything else in the STL). I prefer
693 * debuggability over saving a few lines of trivial code.
694 */
695void TPCrlGroup::appendCrl(
696	TPCrlInfo			&crlInfo)
697{
698	if(mNumCrls == mSizeofCrlInfo) {
699		if(mSizeofCrlInfo == 0) {
700			/* appending to empty array */
701			mSizeofCrlInfo = 1;
702		}
703		else {
704			mSizeofCrlInfo *= 2;
705		}
706		mCrlInfo = (TPCrlInfo **)mAlloc.realloc(mCrlInfo,
707			mSizeofCrlInfo * sizeof(TPCrlInfo *));
708	}
709	mCrlInfo[mNumCrls++] = &crlInfo;
710}
711
712TPCrlInfo *TPCrlGroup::crlAtIndex(
713	unsigned			index)
714{
715	if(index > (mNumCrls - 1)) {
716		CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
717	}
718	return mCrlInfo[index];
719}
720
721TPCrlInfo &TPCrlGroup::removeCrlAtIndex(
722	unsigned			index)				// doesn't delete the cert, just
723											// removes it from our list
724{
725	if(index > (mNumCrls - 1)) {
726		CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
727	}
728	TPCrlInfo &rtn = *mCrlInfo[index];
729
730	/* removed requested element and compact remaining array */
731	unsigned i;
732	for(i=index; i<(mNumCrls - 1); i++) {
733		mCrlInfo[i] = mCrlInfo[i+1];
734	}
735	mNumCrls--;
736	return rtn;
737}
738
739void TPCrlGroup::removeCrl(
740	TPCrlInfo			&crlInfo)
741{
742	for(unsigned dex=0; dex<mNumCrls; dex++) {
743		if(mCrlInfo[dex] == &crlInfo) {
744			removeCrlAtIndex(dex);
745			return;
746		}
747	}
748	tpErrorLog("TPCrlGroup::removeCrl: CRL NOT FOUND\n");
749	assert(0);
750}
751
752TPCrlInfo *TPCrlGroup::firstCrl()
753{
754	if(mNumCrls == 0) {
755		/* the caller really should not do this... */
756		CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
757	}
758	else {
759		return mCrlInfo[0];
760	}
761}
762
763TPCrlInfo *TPCrlGroup::lastCrl()
764{
765	if(mNumCrls == 0) {
766		/* the caller really should not do this... */
767		CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
768	}
769	else {
770		return mCrlInfo[mNumCrls - 1];
771	}
772}
773
774	/*
775	 * Find a CRL whose issuer matches specified subject cert.
776	 * Returned CRL has not necessarily been verified.
777	 */
778TPCrlInfo *TPCrlGroup::findCrlForCert(
779		TPCertInfo			&subject)
780{
781	for(unsigned dex=0; dex<mNumCrls; dex++) {
782		TPCrlInfo *crl = mCrlInfo[dex];
783		if(crl->hasSameIssuer(subject)) {
784			return crl;
785		}
786	}
787	return NULL;
788}
789