cuCdsaUtils.cpp   [plain text]


/*
 * Copyright (c) 2001-2003 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please 
 * obtain a copy of the License at http://www.apple.com/publicsource and 
 * read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
 * Please see the License for the specific language governing rights and 
 * limitations under the License.
 */
 
/*
	File:		 cuCdsaUtils.cpp
	
	Description: common CDSA access utilities

	Author:		 dmitch
*/

#include "cuCdsaUtils.h"
#include <stdlib.h>
#include <stdio.h>
#include <Security/SecCertificate.h>
#include <Security/cssmapple.h>				/* for cssmPerror() */
#include <Security/oidsalg.h>
#include <Security/TrustSettingsSchema.h>
#include <strings.h>

static CSSM_VERSION vers = {2, 0};
static const CSSM_GUID testGuid = { 0xFADE, 0, 0, { 1,2,3,4,5,6,7,0 }};

/*
 * Standard app-level memory functions required by CDSA.
 */
void * cuAppMalloc (CSSM_SIZE size, void *allocRef) {
	return( malloc(size) );
}

void cuAppFree (void *mem_ptr, void *allocRef) {
	free(mem_ptr);
 	return;
}

void * cuAppRealloc (void *ptr, CSSM_SIZE size, void *allocRef) {
	return( realloc( ptr, size ) );
}

void * cuAppCalloc (uint32 num, CSSM_SIZE size, void *allocRef) {
	return( calloc( num, size ) );
}

static CSSM_API_MEMORY_FUNCS memFuncs = {
	cuAppMalloc,
	cuAppFree,
	cuAppRealloc,
 	cuAppCalloc,
 	NULL
 };
 
CSSM_BOOL cuCompareCssmData(const CSSM_DATA *d1,
	const CSSM_DATA *d2)
{	
	if(d1->Length != d2->Length) {
		return CSSM_FALSE;
	}
	if(memcmp(d1->Data, d2->Data, d1->Length)) {
		return CSSM_FALSE;
	}
	return CSSM_TRUE;	
}

/*
 * Init CSSM; returns CSSM_FALSE on error. Reusable.
 */
static CSSM_BOOL cssmInitd = CSSM_FALSE;

CSSM_BOOL cuCssmStartup()
{
	CSSM_RETURN  crtn;
    CSSM_PVC_MODE pvcPolicy = CSSM_PVC_NONE;
	
	if(cssmInitd) {
		return CSSM_TRUE;
	}  
	crtn = CSSM_Init (&vers, 
		CSSM_PRIVILEGE_SCOPE_NONE,
		&testGuid,
		CSSM_KEY_HIERARCHY_NONE,
		&pvcPolicy,
		NULL /* reserved */);
	if(crtn != CSSM_OK) 
	{
		cuPrintError("CSSM_Init", crtn);
		return CSSM_FALSE;
	}
	else {
		cssmInitd = CSSM_TRUE;
		return CSSM_TRUE;
	}
}

/*
 * Attach to CSP. Returns zero on error.
 */
CSSM_CSP_HANDLE cuCspStartup(
	CSSM_BOOL bareCsp)		// true ==> CSP, false ==> CSP/DL
{
	CSSM_CSP_HANDLE cspHand;
	CSSM_RETURN		crtn;
	const CSSM_GUID *guid;
	
	/* common CSSM init */
	if(cuCssmStartup() == CSSM_FALSE) {
		return 0;
	}
	if(bareCsp) {
		guid = &gGuidAppleCSP;
	}
	else {
		guid = &gGuidAppleCSPDL;
	}
	crtn = CSSM_ModuleLoad(guid,
		CSSM_KEY_HIERARCHY_NONE,
		NULL,			// eventHandler
		NULL);			// AppNotifyCallbackCtx
	if(crtn) {
		cuPrintError("CSSM_ModuleLoad()", crtn);
		return 0;
	}
	crtn = CSSM_ModuleAttach (guid,
		&vers,
		&memFuncs,			// memFuncs
		0,					// SubserviceID
		CSSM_SERVICE_CSP,	
		0,					// AttachFlags
		CSSM_KEY_HIERARCHY_NONE,
		NULL,				// FunctionTable
		0,					// NumFuncTable
		NULL,				// reserved
		&cspHand);
	if(crtn) {
		cuPrintError("CSSM_ModuleAttach()", crtn);
		return 0;
	}
	return cspHand;
}

/* Attach to DL side of CSPDL */
CSSM_DL_HANDLE cuDlStartup()
{
	CSSM_DL_HANDLE 	dlHand = 0;
	CSSM_RETURN		crtn;
	
	if(cuCssmStartup() == CSSM_FALSE) {
		return 0;
	}
	crtn = CSSM_ModuleLoad(&gGuidAppleCSPDL,
		CSSM_KEY_HIERARCHY_NONE,
		NULL,			// eventHandler
		NULL);			// AppNotifyCallbackCtx
	if(crtn) {
		cuPrintError("CSSM_ModuleLoad(Apple CSPDL)", crtn);
		return 0;
	}
	crtn = CSSM_ModuleAttach (&gGuidAppleCSPDL,
		&vers,
		&memFuncs,			// memFuncs
		0,					// SubserviceID
		CSSM_SERVICE_DL,	
		0,					// AttachFlags
		CSSM_KEY_HIERARCHY_NONE,
		NULL,				// FunctionTable
		0,					// NumFuncTable
		NULL,				// reserved
		&dlHand);
	if(crtn) {
		cuPrintError("CSSM_ModuleAttach(Apple CSPDL)", crtn);
		return 0;
	}
	return dlHand;
}

CSSM_CL_HANDLE cuClStartup()
{
	CSSM_CL_HANDLE clHand;
	CSSM_RETURN crtn;
	
	if(cuCssmStartup() == CSSM_FALSE) {
		return 0;
	}
	crtn = CSSM_ModuleLoad(&gGuidAppleX509CL,
		CSSM_KEY_HIERARCHY_NONE,
		NULL,			// eventHandler
		NULL);			// AppNotifyCallbackCtx
	if(crtn) {
		cuPrintError("CSSM_ModuleLoad(AppleCL)", crtn);
		return 0;
	}
	crtn = CSSM_ModuleAttach (&gGuidAppleX509CL,
		&vers,
		&memFuncs,				// memFuncs
		0,						// SubserviceID
		CSSM_SERVICE_CL,		// SubserviceFlags - Where is this used?
		0,						// AttachFlags
		CSSM_KEY_HIERARCHY_NONE,
		NULL,					// FunctionTable
		0,						// NumFuncTable
		NULL,					// reserved
		&clHand);
	if(crtn) {
		cuPrintError("CSSM_ModuleAttach(AppleCL)", crtn);
		return 0;
	}
	else {
		return clHand;
	}
}

CSSM_TP_HANDLE cuTpStartup()
{
	CSSM_TP_HANDLE tpHand;
	CSSM_RETURN crtn;
	
	if(cuCssmStartup() == CSSM_FALSE) {
		return 0;
	}
	crtn = CSSM_ModuleLoad(&gGuidAppleX509TP,
		CSSM_KEY_HIERARCHY_NONE,
		NULL,			// eventHandler
		NULL);			// AppNotifyCallbackCtx
	if(crtn) {
		cuPrintError("CSSM_ModuleLoad(AppleTP)", crtn);
		return 0;
	}
	crtn = CSSM_ModuleAttach (&gGuidAppleX509TP,
		&vers,
		&memFuncs,				// memFuncs
		0,						// SubserviceID
		CSSM_SERVICE_TP,		// SubserviceFlags
		0,						// AttachFlags
		CSSM_KEY_HIERARCHY_NONE,
		NULL,					// FunctionTable
		0,						// NumFuncTable
		NULL,					// reserved
		&tpHand);
	if(crtn) {
		cuPrintError("CSSM_ModuleAttach(AppleTP)", crtn);
		return 0;
	}
	else {
		return tpHand;
	}
}

/* detach and unload */
CSSM_RETURN cuCspDetachUnload(
	CSSM_CSP_HANDLE cspHand,
	CSSM_BOOL bareCsp)					// true ==> CSP, false ==> CSP/DL
{
	CSSM_RETURN crtn = CSSM_ModuleDetach(cspHand);
	if(crtn) {
		return crtn;
	}
	const CSSM_GUID *guid;
	if(bareCsp) {
		guid = &gGuidAppleCSP;
	}
	else {
		guid = &gGuidAppleCSPDL;
	}
	return CSSM_ModuleUnload(guid, NULL, NULL);
}

CSSM_RETURN cuClDetachUnload(
	CSSM_CL_HANDLE  clHand)
{
	CSSM_RETURN crtn = CSSM_ModuleDetach(clHand);
	if(crtn) {
		return crtn;
	}
	return CSSM_ModuleUnload(&gGuidAppleX509CL, NULL, NULL);

}

CSSM_RETURN cuDlDetachUnload(
	CSSM_DL_HANDLE  dlHand)
{
	CSSM_RETURN crtn = CSSM_ModuleDetach(dlHand);
	if(crtn) {
		return crtn;
	}
	return CSSM_ModuleUnload(&gGuidAppleCSPDL, NULL, NULL);

}
CSSM_RETURN cuTpDetachUnload(
	CSSM_TP_HANDLE  tpHand)
{
	CSSM_RETURN crtn = CSSM_ModuleDetach(tpHand);
	if(crtn) {
		return crtn;
	}
	return CSSM_ModuleUnload(&gGuidAppleX509TP, NULL, NULL);

}

/*
 * open a DB, ensure it's empty.
 */
CSSM_DB_HANDLE cuDbStartup(
	CSSM_DL_HANDLE		dlHand,			// from dlStartup()
	const char 			*dbName)
{
	CSSM_DB_HANDLE				dbHand = 0;
	CSSM_RETURN					crtn;
	CSSM_DBINFO					dbInfo;
	
	/* first delete possible existing DB, ignore error */
	crtn = CSSM_DL_DbDelete(dlHand, dbName, NULL, NULL);
	switch(crtn) {
		/* only allowed error is "no such file" */
		case CSSM_OK:
		case CSSMERR_DL_DATASTORE_DOESNOT_EXIST:
			break;
		default:
			cuPrintError("CSSM_DL_DbDelete", crtn);
			return 0;
	}
	
	memset(&dbInfo, 0, sizeof(CSSM_DBINFO));
	
	/* now create it */
	crtn = CSSM_DL_DbCreate(dlHand, 
		dbName,
		NULL,						// DbLocation
		&dbInfo,
		// &Security::KeychainCore::Schema::DBInfo,
		CSSM_DB_ACCESS_PRIVILEGED,
		NULL,						// CredAndAclEntry
		NULL,						// OpenParameters
		&dbHand);
	if(crtn) {
		cuPrintError("CSSM_DL_DbCreate", crtn);
	}
	return dbHand;
}

/*
 * Attach to existing DB or create an empty new one.
 */
CSSM_DB_HANDLE cuDbStartupByName(CSSM_DL_HANDLE dlHand,
	char 		*dbName,
	CSSM_BOOL 	doCreate,
	CSSM_BOOL	quiet)
{
	CSSM_RETURN 	crtn;
	CSSM_DB_HANDLE	dbHand;
	
	/* try open existing DB in either case */
	
	crtn = CSSM_DL_DbOpen(dlHand,
		dbName, 
		NULL,			// DbLocation
		CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE,
		NULL, 			// CSSM_ACCESS_CREDENTIALS *AccessCred
		NULL,			// void *OpenParameters
		&dbHand);
	if(crtn == CSSM_OK) {
		return dbHand;
	}
	if(!doCreate) {
		if(!quiet) {
			printf("***no such data base (%s)\n", dbName);
			cuPrintError("CSSM_DL_DbOpen", crtn);
		}
		return 0;
	}
	/* have to create one */
	return cuDbStartup(dlHand, dbName);
}

/*
 * Given a context specified via a CSSM_CC_HANDLE, add a new
 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
 * AttributeLength, and an untyped pointer.
 */
CSSM_RETURN cuAddContextAttribute(CSSM_CC_HANDLE CCHandle,
	uint32 AttributeType,
	uint32 AttributeLength,
	const void *AttributePtr)
{
	CSSM_CONTEXT_ATTRIBUTE		newAttr;	
	CSSM_RETURN					crtn;
	
	newAttr.AttributeType     = AttributeType;
	newAttr.AttributeLength   = AttributeLength;
	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
	crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
	if(crtn) {
		cuPrintError("CSSM_UpdateContextAttributes", crtn);
	}
	return crtn;
}


/*
 * Derive symmetric key.
 * Note in the X CSP, we never return an IV. 
 */
CSSM_RETURN cuCspDeriveKey(CSSM_CSP_HANDLE cspHand,
		uint32				keyAlg,			// CSSM_ALGID_RC5, etc.
		const char 			*keyLabel,
		unsigned 			keyLabelLen,
		uint32 				keyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
		uint32 				keySizeInBits,
		CSSM_DATA_PTR		password,		// in PKCS-5 lingo
		CSSM_DATA_PTR		salt,			// ditto
		uint32				iterationCnt,	// ditto
		CSSM_KEY_PTR		key)
{
	CSSM_RETURN					crtn;
	CSSM_CC_HANDLE 				ccHand;
	uint32						keyAttr;
	CSSM_DATA					dummyLabel;
	CSSM_PKCS5_PBKDF2_PARAMS 	pbeParams;
	CSSM_DATA					pbeData;
	CSSM_ACCESS_CREDENTIALS		creds;
	
	memset(key, 0, sizeof(CSSM_KEY));
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
		CSSM_ALGID_PKCS5_PBKDF2,
		keyAlg,
		keySizeInBits,
		&creds,
		NULL,			// BaseKey
		iterationCnt,
		salt,
		NULL,			// seed
		&ccHand);
	if(crtn) {
		cuPrintError("CSSM_CSP_CreateDeriveKeyContext", crtn);
		return crtn;
	}
	keyAttr = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF | 
			  CSSM_KEYATTR_SENSITIVE;
	dummyLabel.Length = keyLabelLen;
	dummyLabel.Data = (uint8 *)keyLabel;
	
	/* passing in password is pretty strange....*/
	pbeParams.Passphrase = *password;
	pbeParams.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
	pbeData.Data = (uint8 *)&pbeParams;
	pbeData.Length = sizeof(pbeParams);
	crtn = CSSM_DeriveKey(ccHand,
		&pbeData,
		keyUsage,
		keyAttr,
		&dummyLabel,
		NULL,			// cred and acl
		key);
	if(crtn) {
		cuPrintError("CSSM_DeriveKey", crtn);
		return crtn;
	}
	crtn = CSSM_DeleteContext(ccHand);
	if(crtn) {
		cuPrintError("CSSM_DeleteContext", crtn);
	}
	return crtn;
}

/*
 * Generate key pair of arbitrary algorithm. 
 */
 
/* CSP DL currently does not perform DSA generate params; let CSP do it implicitly */
#define DO_DSA_GEN_PARAMS		0

CSSM_RETURN cuCspGenKeyPair(CSSM_CSP_HANDLE cspHand,
	CSSM_DL_DB_HANDLE *dlDbHand,	// optional
	uint32 algorithm,
	const char *keyLabel,
	unsigned keyLabelLen,
	uint32 keySize,					// in bits
	CSSM_KEY_PTR pubKey,			// mallocd by caller
	CSSM_KEYUSE pubKeyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
	CSSM_KEYATTR_FLAGS pubAttrs,	// CSSM_KEYATTR_EXTRACTABLE, etc. 
	CSSM_KEY_PTR privKey,			// mallocd by caller
	CSSM_KEYUSE privKeyUsage,		// CSSM_KEYUSE_DECRYPT, etc.
	CSSM_KEYATTR_FLAGS privAttrs)	// CSSM_KEYATTR_EXTRACTABLE, etc. 
{
	CSSM_RETURN				crtn;
	CSSM_RETURN				ocrtn;
	CSSM_CC_HANDLE 			ccHand;
	CSSM_DATA				keyLabelData;
	
	keyLabelData.Data        = (uint8 *)keyLabel,
	keyLabelData.Length      = keyLabelLen;
	memset(pubKey, 0, sizeof(CSSM_KEY));
	memset(privKey, 0, sizeof(CSSM_KEY));
	
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
		algorithm,
		keySize,
		NULL,					// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		NULL,					// Params
		&ccHand);
	if(crtn) {
		cuPrintError("CSSM_CSP_CreateKeyGenContext", crtn);
		return crtn;
	}

	/* post-context-create algorithm-specific stuff */
	switch(algorithm) {		 
		#if DO_DSA_GEN_PARAMS
		case CSSM_ALGID_DSA:
			/* 
			 * extra step - generate params - this just adds some
			 * info to the context
			 */
			{
				CSSM_DATA dummy = {0, NULL};
				crtn = CSSM_GenerateAlgorithmParams(ccHand, 
					keySize, &dummy);
				if(crtn) {
					cuPrintError("CSSM_GenerateAlgorithmParams", crtn);
					CSSM_DeleteContext(ccHand);
					return crtn;
				}
				cuAppFree(dummy.Data, NULL);
			}
			break;
		#endif	/* DO_DSA_GEN_PARAMS */
		default:
			break;
	}
	
	/* optionally specify DL/DB storage location */
	if(dlDbHand) {
		crtn = cuAddContextAttribute(ccHand, 
			CSSM_ATTRIBUTE_DL_DB_HANDLE,
			sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE),
			dlDbHand);
		if(crtn) {
			CSSM_DeleteContext(ccHand);
			return crtn;
		}
	}
	ocrtn = CSSM_GenerateKeyPair(ccHand,
		pubKeyUsage,
		pubAttrs,
		&keyLabelData,
		pubKey,
		privKeyUsage,
		privAttrs,
		&keyLabelData,			// same labels
		NULL,					// CredAndAclEntry
		privKey);
	if(ocrtn) {
		cuPrintError("CSSM_GenerateKeyPair", ocrtn);
	}
	crtn = CSSM_DeleteContext(ccHand);
	if(crtn) {
		cuPrintError("CSSM_DeleteContext", crtn);
		if(ocrtn == CSSM_OK) {
			/* error on CSSM_GenerateKeyPair takes precedence */
			ocrtn = crtn;
		}
	}
	return ocrtn;
}


/*
 * Add a certificate to an open Keychain.
 */
CSSM_RETURN cuAddCertToKC(
	SecKeychainRef		keychain,
	const CSSM_DATA		*cert,
	CSSM_CERT_TYPE		certType,
	CSSM_CERT_ENCODING	certEncoding,
	const char			*printName,		// C string
	const CSSM_DATA		*keyLabel)		// ??
{
	SecCertificateRef certificate;
	
	OSStatus rslt = SecCertificateCreateFromData(cert, certType, certEncoding, &certificate);
	if (!rslt)
	{
		rslt = SecCertificateAddToKeychain(certificate, keychain);
		CFRelease(certificate);
	}

	return rslt;
}

/*
 * Convert a CSSM_DATA_PTR, referring to a DER-encoded int, to an
 * unsigned.
 */
unsigned cuDER_ToInt(const CSSM_DATA *DER_Data)
{
	uint32		rtn = 0;
	unsigned	i = 0;

	while(i < DER_Data->Length) {
		rtn |= DER_Data->Data[i];
		if(++i == DER_Data->Length) {
			break;
		}
		rtn <<= 8;
	}
	return rtn;
}

/*
 * Log CSSM error.
 */
void cuPrintError(const char *op, CSSM_RETURN err)
{
	cssmPerror(op, err);
}

/*
 * Verify a CRL against system anchors and intermediate certs. 
 */
CSSM_RETURN cuCrlVerify(
	CSSM_TP_HANDLE			tpHand, 
	CSSM_CL_HANDLE 			clHand,
	CSSM_CSP_HANDLE 		cspHand,
	const CSSM_DATA			*crlData,
	CSSM_DL_DB_HANDLE_PTR	certKeychain,	// intermediate certs
	const CSSM_DATA 		*anchors,		// optional - if NULL, use Trust Settings
	uint32 					anchorCount)
{
	/* main job is building a CSSM_TP_VERIFY_CONTEXT and its components */
	CSSM_TP_VERIFY_CONTEXT			vfyCtx;
	CSSM_TP_CALLERAUTH_CONTEXT		authCtx;
	
	memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT));
	memset(&authCtx, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
	
	/* CSSM_TP_CALLERAUTH_CONTEXT components */
	/* 
		typedef struct cssm_tp_callerauth_context {
			CSSM_TP_POLICYINFO Policy;
			CSSM_TIMESTRING VerifyTime;
			CSSM_TP_STOP_ON VerificationAbortOn;
			CSSM_TP_VERIFICATION_RESULTS_CALLBACK CallbackWithVerifiedCert;
			uint32 NumberOfAnchorCerts;
			CSSM_DATA_PTR AnchorCerts;
			CSSM_DL_DB_LIST_PTR DBList;
			CSSM_ACCESS_CREDENTIALS_PTR CallerCredentials;
		} CSSM_TP_CALLERAUTH_CONTEXT, *CSSM_TP_CALLERAUTH_CONTEXT_PTR;
	*/
	CSSM_FIELD	policyId;
	CSSM_APPLE_TP_CRL_OPTIONS crlOpts;
	policyId.FieldOid = CSSMOID_APPLE_TP_REVOCATION_CRL;
	policyId.FieldValue.Data = (uint8 *)&crlOpts;
	policyId.FieldValue.Length = sizeof(crlOpts);
	crlOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
	/* perhaps this should be user-specifiable */
	crlOpts.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET;
	crlOpts.crlStore = NULL;

	authCtx.Policy.NumberOfPolicyIds = 1;
	authCtx.Policy.PolicyIds = &policyId;
	authCtx.Policy.PolicyControl = NULL;
	
	authCtx.VerifyTime = NULL;
	authCtx.VerificationAbortOn = CSSM_TP_STOP_ON_POLICY;
	authCtx.CallbackWithVerifiedCert = NULL;
	
	/* anchors */
	authCtx.NumberOfAnchorCerts = anchorCount;
	authCtx.AnchorCerts = const_cast<CSSM_DATA_PTR>(anchors);
	
	/* DBList of intermediate certs, plus possible System.keychain and 
   	 * system roots */
	CSSM_DL_DB_HANDLE handles[3];
	unsigned numDbs = 0;
	CSSM_DL_HANDLE dlHand = 0;
	if(certKeychain != NULL) {
		handles[0] = *certKeychain;
		numDbs++;
	}
	if(anchors == NULL) {
		/* Trust Settings requires two more DBs */
		if(numDbs == 0) {
			/* new DL handle */
			dlHand = cuDlStartup();
			handles[numDbs].DLHandle = dlHand;
			handles[numDbs + 1].DLHandle = dlHand;
		}
		else {
			/* use the same one passed in for certKeychain */
			handles[numDbs].DLHandle = handles[0].DLHandle;
			handles[numDbs + 1].DLHandle = handles[0].DLHandle;
		}
		handles[numDbs].DBHandle = cuDbStartupByName(handles[numDbs].DLHandle,
			(char*) ADMIN_CERT_STORE_PATH, CSSM_FALSE, CSSM_TRUE);
		numDbs++;
		
		handles[numDbs].DBHandle = cuDbStartupByName(handles[numDbs].DLHandle,
			(char*) SYSTEM_ROOT_STORE_PATH, CSSM_FALSE, CSSM_TRUE);
		numDbs++;
	}
	CSSM_DL_DB_LIST dlDbList;
	dlDbList.DLDBHandle = handles;
	dlDbList.NumHandles = numDbs;
	
	authCtx.DBList = &dlDbList; 
	authCtx.CallerCredentials = NULL;
	
	/* CSSM_TP_VERIFY_CONTEXT */
	vfyCtx.ActionData.Data = NULL;
	vfyCtx.ActionData.Length = 0;
	vfyCtx.Action = CSSM_TP_ACTION_DEFAULT;
	vfyCtx.Cred = &authCtx;

	/* CSSM_APPLE_TP_ACTION_DATA */
	CSSM_APPLE_TP_ACTION_DATA tpAction;
	if(anchors == NULL) {
		/* enable Trust Settings */
		tpAction.Version = CSSM_APPLE_TP_ACTION_VERSION;
		tpAction.ActionFlags = CSSM_TP_ACTION_TRUST_SETTINGS;
		vfyCtx.ActionData.Data   = (uint8 *)&tpAction;
		vfyCtx.ActionData.Length = sizeof(tpAction);
	}
	
	/* cook up CSSM_ENCODED_CRL */
	CSSM_ENCODED_CRL encCrl;
	encCrl.CrlType = CSSM_CRL_TYPE_X_509v2;
	encCrl.CrlEncoding = CSSM_CRL_ENCODING_DER;
	encCrl.CrlBlob = *crlData;
	
	/* CDSA API requires a SignerCertGroup; for us, all the certs are in
	 * certKeyChain... */
	CSSM_CERTGROUP certGroup;
	certGroup.CertType = CSSM_CERT_X_509v1;
	certGroup.CertEncoding = CSSM_CERT_ENCODING_DER;
	certGroup.NumCerts = 0;
	certGroup.GroupList.CertList = NULL;
	certGroup.CertGroupType = CSSM_CERTGROUP_DATA;
	
	CSSM_RETURN crtn = CSSM_TP_CrlVerify(tpHand,
		clHand,
		cspHand,
		&encCrl,
		&certGroup,
		&vfyCtx,
		NULL);			// RevokerVerifyResult
	if(crtn) {
		cuPrintError("CSSM_TP_CrlVerify", crtn);
	}
	if(anchors == NULL) {
		/* close the DBs and maybe the DL we opened */
		unsigned dexToClose = (certKeychain == NULL) ? 0 : 1;
		CSSM_DL_DbClose(handles[dexToClose++]);
		CSSM_DL_DbClose(handles[dexToClose]);
		if(dlHand != 0) {
			cuDlDetachUnload(dlHand);
		}
	}
	return crtn;
}