mdsLookup.cpp   [plain text]


/*
 * mdsLookup.cpp - demonstrate some MDS lookups
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <Security/mds.h>
#include <Security/mds_schema.h>
#include <Security/oidsalg.h>		// for TP OIDs
#include "common.h"
#include <strings.h>

/* the memory functions themselves are in utilLib/common.c. */
static CSSM_MEMORY_FUNCS memFuncs = {
	appMalloc,
	appFree,
	appRealloc,
 	appCalloc,
 	NULL
 };
 
static void usage(char **argv)
{
	printf("Usage: %s [options]\n", argv[0]);
	printf("Options:\n");
	printf("   k   keep connected and go again\n");
	exit(1);
}

#define NORM_KEY_LEN	10

/* print a key name, padding out to NORM_KEY_LEN columns */
static void printName(
	const char *attrName)
{
	printf("   %s", attrName);
	int len = strlen(attrName);
	if(len > NORM_KEY_LEN) {
		return;
	}
	int numSpaces = NORM_KEY_LEN - len;
	for(int i=0; i<numSpaces; i++) {
		putchar(' ');
	}

}

/* print value string, surrounded by single quotes, then a newline */
static void printValue(
	const CSSM_DATA *attrValue)
{
	printf("'");
	for(uint32 dex=0; dex<attrValue->Length; dex++) {
		printf("%c", attrValue->Data[dex]);
	} 
	printf("'\n");
}

/* Print one attribute value */
static void dumpAttr(
	CSSM_DB_ATTRIBUTE_FORMAT attrForm,
	const CSSM_DATA *attrData)
{
	if((attrData == NULL) || (attrData->Data == NULL)) {
		printf("<NULL DATA>\n");
		return;
	}
	void *data = attrData->Data;
	switch(attrForm) {
		case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
			printValue(attrData);
			break;
		case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:	// not really supported in MDS
		case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
		{
			unsigned val = *(unsigned *)data;
			printf("0x%x\n", val);
			break;
		}
		case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
		{
			printf("BLOB length %u : ", (unsigned)attrData->Length);
			for(unsigned i=0; i<attrData->Length; i++) {
				unsigned dat = attrData->Data[i];
				printf("%02X ", dat);
			}
			printf("\n");
			break;
		}
		case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
		{
			printf("multi_int[");
			uint32 numInts = attrData->Length / sizeof(uint32);
			uint32 *uip = (uint32 *)data;
			for(unsigned i=0; i<numInts; i++) {
				if(i > 0) {
					printf(", ");
				}
				printf("0x%x", (unsigned)*uip++);
			}
			printf("]\n");
			break;
		}
		default:
			printf("***UNKNOWN FORMAT (%u), Length %u\n",
				(unsigned)attrForm, (unsigned)attrData->Length);
			break;
	}
}

/*
 * Vanilla "dump one record" routine. Assumes format of all attribute labels 
 * as string.  
 */
static void dumpRecord(
	const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs)
{
	unsigned dex;
	for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) {
		const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex];
		if(attrData->Info.AttributeNameFormat != 
				CSSM_DB_ATTRIBUTE_NAME_AS_STRING) {
			printf("***BAD ATTR_NAME FORMAT (%u)\n", 
				(unsigned)attrData->Info.AttributeNameFormat);
				continue;
		}
		const char *attrName = attrData->Info.Label.AttributeName;
		printName(attrName);
		printf(": ");
		for(unsigned attrNum=0; attrNum<attrData->NumberOfValues; attrNum++) {
			dumpAttr(attrData->Info.AttributeFormat, 	
				&attrData->Value[attrNum]);
		}
		if(attrData->NumberOfValues == 0) {
			printf("<<no values present>>\n");
		}
	}
}

/* free attribute(s) allocated by MDS */
static void freeAttrs(
	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR	recordAttrs)
{
	unsigned i;
	
	for(i=0; i<recordAttrs->NumberOfAttributes; i++) {
		CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i];
		if(attrData == NULL) {
			/* fault of caller, who allocated the CSSM_DB_ATTRIBUTE_DATA */
			printf("***freeAttrs screwup: NULL attrData\n");
			return;
		}
		unsigned j;
		for(j=0; j<attrData->NumberOfValues; j++) {
			CSSM_DATA_PTR data = &attrData->Value[j];
			if(data == NULL) {
				/* fault of MDS, who said there was a value here */
				printf("***freeAttrs screwup: NULL data\n");
				return;
			}
			appFree(data->Data, NULL);
			data->Data = NULL;
			data->Length = 0;
		}
		appFree(attrData->Value, NULL);
		attrData->Value = NULL;
	}
}

/*
 * Core MDS lookup routine. Used in two situations. It's called by main() to perform
 * a lookup in the CDSA Directory Database based one one key/value pair; this
 * call fetches one attribute from the associated record - the GUID ("ModuleID" 
 * in MDS lingo). Then the function calls itself to do a lookup in the Object DB,
 * based on that GUID, in order to fetch the path of the module associated with
 * that GUID. The first call (from main()) corresponds to an application's
 * typical use of MDS. The recursive call, which does a lookup in the Object
 * DB, corresponds to CSSM's typical use of MDS, which is to map a GUID to a 
 * bundle path.
 *
 * The ModuleID and Path of all modules satisfying the initial search criteria
 * are displayed on stdout. 
 *
 * Caller specifies one search attribute, by name, value,Êand value format. 
 * Whether this is the first or second (recursive) call is indicated by the 
 * cdsaLookup argument. That determines both the DB to search and the attribute
 * to fetch (ModuleID or Path).
 */
static void doLookup(
	MDS_FUNCS 					*mdsFuncs,
	
	/* Two DBs and a flag indicating which one to use */
	MDS_DB_HANDLE 				objDlDb,
	MDS_DB_HANDLE 				cdsaDlDb,
	bool						cdsaLookup,	// true - use cdsaDlDb; false - objDlDb
	
	/* Record type, a.k.a. Relation, e.g. MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE */
	CSSM_DB_RECORDTYPE			recordType,	
	
	/* key, value, valForm, and valOp are the thing we search on */
	/* Note CSSM_DB_ATTRIBUTE_NAME_FORMAT - the format of the attribute name - 
	 *    is always CSSM_DB_ATTRIBUTE_NAME_AS_STRING for MDS. */
	const char					*key,		// e.g. "AlgType"
	const void					*valPtr,	
	unsigned					valLen,
	CSSM_DB_ATTRIBUTE_FORMAT	valForm,	// CSSM_DB_ATTRIBUTE_FORMAT_STRING, etc.
	CSSM_DB_OPERATOR			valOp,		// normally CSSM_DB_EQUAL
	
	/* for display only */
	const char					*srchStr)
{
	CSSM_QUERY						query;
	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
	CSSM_HANDLE						resultHand;
	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
	CSSM_SELECTION_PREDICATE		predicate;
	CSSM_DATA						predData;
	CSSM_DB_ATTRIBUTE_DATA			outAttr;
	CSSM_DB_ATTRIBUTE_INFO_PTR		attrInfo;
	CSSM_RETURN						crtn;
	MDS_DB_HANDLE					dlDb;
	const char 						*attrName;
	
	if(cdsaLookup) {
		/* first call, fetching guid from the CDSA Directory DB */
		dlDb = cdsaDlDb;
		attrName = "ModuleID";
	}
	else {
		/* recursive call, fetching path from Object DB */
		dlDb = objDlDb;
		attrName = "Path";
	}
	
	/* We want one attributes back, name and format specified by caller */
	recordAttrs.DataRecordType = recordType;
	recordAttrs.SemanticInformation = 0;
	recordAttrs.NumberOfAttributes = 1;
	recordAttrs.AttributeData = &outAttr;

	memset(&outAttr, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA));
	attrInfo = &outAttr.Info;
	attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	attrInfo->Label.AttributeName = (char *)attrName;
	attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;

	/* one predicate - the caller's key and CSSM_DB_OPERATOR */
	predicate.DbOperator = valOp;
	predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	predicate.Attribute.Info.Label.AttributeName = (char *)key;
	predicate.Attribute.Info.AttributeFormat = valForm;
	predData.Data = (uint8 *)valPtr;
	predData.Length = valLen;
	predicate.Attribute.Value = &predData;
	predicate.Attribute.NumberOfValues = 1;
	
	query.RecordType = recordType;
	query.Conjunctive = CSSM_DB_NONE;
	query.NumSelectionPredicates = 1;
	query.SelectionPredicate = &predicate;
	query.QueryLimits.TimeLimit = 0;			// FIXME - meaningful?
	query.QueryLimits.SizeLimit = 1;			// FIXME - meaningful?
	query.QueryFlags = 0;		// CSSM_QUERY_RETURN_DATA...FIXME - used?

	crtn = mdsFuncs->DataGetFirst(dlDb,
		&query,
		&resultHand,
		&recordAttrs,
		NULL,			// No data
		&record);
	switch(crtn) {
		case CSSM_OK:
			break;		// proceed
		case CSSMERR_DL_ENDOFDATA:
			printf("%s: no record found\n", srchStr);
			return;
		default:
			printError("DataGetFirst", crtn);
			return;
	}
	/* dump this record, one attribute */
	if(srchStr) {
		/* not done on recursive call */
		printf("%s found:\n", srchStr);		
	}
	dumpRecord(&recordAttrs);
	mdsFuncs->FreeUniqueRecord(dlDb, record);
	
	if(srchStr != NULL) {
		/* 
		 * Now do a lookup in Object DB of this guid, looking for path. 
		 * Apps normally don't do this; this is what CSSM does when given
		 * the GUID of a module.
		 */
		if(outAttr.Value == NULL) {
			printf("***Screwup: DataGetFirst worked, but no outAttr\n");
			return;
		}
		doLookup(mdsFuncs,
			objDlDb,
			cdsaDlDb,
			false,			 		// use objDlDb
			MDS_OBJECT_RECORDTYPE,
			"ModuleID",				// key
			outAttr.Value->Data,	// valPtr, ModuleID, as string
			outAttr.Value->Length,	// valLen
			CSSM_DB_ATTRIBUTE_FORMAT_STRING,
			CSSM_DB_EQUAL,
			NULL);					// srchStr
	}
	freeAttrs(&recordAttrs);

	/* now the rest of them */
	for(;;) {
		crtn = mdsFuncs->DataGetNext(dlDb,
			resultHand, 
			&recordAttrs,
			NULL,
			&record);
		switch(crtn) {
			case CSSM_OK:
				dumpRecord(&recordAttrs);
				mdsFuncs->FreeUniqueRecord(cdsaDlDb, record);
				if(srchStr != NULL) {
					if(outAttr.Value == NULL) {
						printf("***Screwup: DataGetNext worked, but no outAttr\n");
						return;
					}
					doLookup(mdsFuncs,
						objDlDb,
						cdsaDlDb,
						false,			 		// use objDlDb
						MDS_OBJECT_RECORDTYPE,
						"ModuleID",				// key
						outAttr.Value->Data,	// valPtr, ModuleID, as string
						outAttr.Value->Length,	// valLen
						CSSM_DB_ATTRIBUTE_FORMAT_STRING,
						CSSM_DB_EQUAL,
						NULL);					// srchStr
				}
				freeAttrs(&recordAttrs);
				break;		// and go again 
			case CSSMERR_DL_ENDOFDATA:
				/* normal termination */
				break;
			default:
				printError("DataGetNext", crtn);
				break;
		}
		if(crtn != CSSM_OK) {
			break;
		}
	}
}

int main(int argc, char **argv)
{
	MDS_FUNCS 			mdsFuncs;
	MDS_HANDLE 			mdsHand;
	CSSM_RETURN 		crtn;
	int 				arg;
	CSSM_DB_HANDLE		dbHand = 0;
	MDS_DB_HANDLE		objDlDb;
	MDS_DB_HANDLE		cdsaDlDb;
	bool				keepConnected = false;
	uint32				val;
	
	for(arg=2; arg<argc; arg++) {
		switch(argv[arg][0]) {
			case 'k':
				keepConnected = true;
				break;
			default:
				usage(argv);
		}
	}
	crtn = MDS_Initialize(NULL,		// callerGuid
		&memFuncs,
		&mdsFuncs,
		&mdsHand);
	if(crtn) {
		printError("MDS_Initialize", crtn);
		exit(1);
	}

	do {
		/* 
		 * Open both MDS DBs - apps normally only have to open 
		 * MDS_CDSA_DIRECTORY_NAME.
		 */
		crtn = mdsFuncs.DbOpen(mdsHand,
			MDS_OBJECT_DIRECTORY_NAME,
			NULL,				// DbLocation
			CSSM_DB_ACCESS_READ,
			NULL,				// AccessCred - hopefully optional 
			NULL,				// OpenParameters
			&dbHand);
		if(crtn) {
			printError("DbOpen(MDS_OBJECT_DIRECTORY_NAME)", crtn);
			exit(1);
		}
		objDlDb.DLHandle = mdsHand;
		objDlDb.DBHandle = dbHand;
		
		crtn = mdsFuncs.DbOpen(mdsHand,
			MDS_CDSA_DIRECTORY_NAME,
			NULL,				// DbLocation
			CSSM_DB_ACCESS_READ,
			NULL,				// AccessCred - hopefully optional 
			NULL,				// OpenParameters
			&dbHand);
		if(crtn) {
			printError("DbOpen(MDS_CDSA_DIRECTORY_NAME)", crtn);
			exit(1);
		}
		cdsaDlDb.DLHandle = mdsHand;
		cdsaDlDb.DBHandle = dbHand;
		
		/* 
		 * Do some typical lookups.
		 */

		/* a CSP which can do SHA1 digest */
		val = CSSM_ALGID_SHA1;
		doLookup(&mdsFuncs,
			objDlDb,
			cdsaDlDb,
			true,
			MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE,
			"AlgType",
			&val,
			sizeof(uint32),
			CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
			CSSM_DB_EQUAL,
			"CSP for SHA1 digest");
		
		/* a TP which can do iSign verification */
		doLookup(&mdsFuncs,
			objDlDb,
			cdsaDlDb,
			true,
			MDS_CDSADIR_TP_OIDS_RECORDTYPE,
			"OID",
			CSSMOID_APPLE_ISIGN.Data,
			CSSMOID_APPLE_ISIGN.Length,
			CSSM_DB_ATTRIBUTE_FORMAT_BLOB,
			CSSM_DB_EQUAL,
			"TP for CSSMOID_APPLE_ISIGN policy");

		/* an X509-savvy CL */
		/* Very weird data form - two fields in one 32-bit word */
		val = (CSSM_CERT_X_509v3 << 16) | CSSM_CERT_ENCODING_DER;
		doLookup(&mdsFuncs,
			objDlDb,
			cdsaDlDb,
			true,
			MDS_CDSADIR_CL_PRIMARY_RECORDTYPE,
			"CertTypeFormat",
			&val,
			sizeof(uint32),
			CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
			CSSM_DB_EQUAL,
			"X509 CL");

		/* A DL which can do CSSM_DB_AND */
		val = CSSM_DB_AND;
		doLookup(&mdsFuncs,
			objDlDb,
			cdsaDlDb,
			true,
			MDS_CDSADIR_DL_PRIMARY_RECORDTYPE,
			"ConjunctiveOps",
			&val,
			sizeof(uint32),
			CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32,
			/* This is a multi-uint32, meaning we want to search for a
			 * ConjunctiveOps which contains the specified value */
			CSSM_DB_CONTAINS,
			"DL with ConjunctiveOp CSSM_DB_AND");

		/* a CSP which can do CSSM_ALGID_IDEA, should fail */
		val = CSSM_ALGID_IDEA;
		doLookup(&mdsFuncs,
			objDlDb,
			cdsaDlDb,
			true,
			MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE,
			"AlgType",
			&val,
			sizeof(uint32),
			CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
			CSSM_DB_EQUAL,
			"CSP for CSSM_ALGID_BLOWFISH, expect failure");

		/* a TP which can obtain a .mac signing certificate */
		doLookup(&mdsFuncs,
			objDlDb,
			cdsaDlDb,
			true,
			MDS_CDSADIR_TP_OIDS_RECORDTYPE,
			"OID",
			CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Data,
			CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Length,
			CSSM_DB_ATTRIBUTE_FORMAT_BLOB,
			CSSM_DB_EQUAL,
			"TP for .mac signing certificate policy");

		crtn = mdsFuncs.DbClose(objDlDb);
		if(crtn) {
			printError("DbClose(objDlDb)", crtn);
		}
		crtn = mdsFuncs.DbClose(cdsaDlDb);
		if(crtn) {
			printError("DbClose(cdsaDlDb)", crtn);
		}
		if(keepConnected) {
			printf("\n");
			fpurge(stdin);
			printf("Enter CR to go again: ");
			getchar();
		}
	} while(keepConnected);
	crtn = MDS_Terminate(mdsHand);
	if(crtn) {
		printError("MDS_Terminate", crtn);
	}
	return 0;
}