1/*
2 * mdsLookup.cpp - demonstrate some MDS lookups
3 */
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <Security/mds.h>
8#include <Security/mds_schema.h>
9#include <Security/oidsalg.h>		// for TP OIDs
10#include "common.h"
11#include <strings.h>
12
13/* the memory functions themselves are in utilLib/common.c. */
14static CSSM_MEMORY_FUNCS memFuncs = {
15	appMalloc,
16	appFree,
17	appRealloc,
18 	appCalloc,
19 	NULL
20 };
21
22static void usage(char **argv)
23{
24	printf("Usage: %s [options]\n", argv[0]);
25	printf("Options:\n");
26	printf("   k   keep connected and go again\n");
27	exit(1);
28}
29
30#define NORM_KEY_LEN	10
31
32/* print a key name, padding out to NORM_KEY_LEN columns */
33static void printName(
34	const char *attrName)
35{
36	printf("   %s", attrName);
37	int len = strlen(attrName);
38	if(len > NORM_KEY_LEN) {
39		return;
40	}
41	int numSpaces = NORM_KEY_LEN - len;
42	for(int i=0; i<numSpaces; i++) {
43		putchar(' ');
44	}
45
46}
47
48/* print value string, surrounded by single quotes, then a newline */
49static void printValue(
50	const CSSM_DATA *attrValue)
51{
52	printf("'");
53	for(uint32 dex=0; dex<attrValue->Length; dex++) {
54		printf("%c", attrValue->Data[dex]);
55	}
56	printf("'\n");
57}
58
59/* Print one attribute value */
60static void dumpAttr(
61	CSSM_DB_ATTRIBUTE_FORMAT attrForm,
62	const CSSM_DATA *attrData)
63{
64	if((attrData == NULL) || (attrData->Data == NULL)) {
65		printf("<NULL DATA>\n");
66		return;
67	}
68	void *data = attrData->Data;
69	switch(attrForm) {
70		case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
71			printValue(attrData);
72			break;
73		case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:	// not really supported in MDS
74		case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
75		{
76			unsigned val = *(unsigned *)data;
77			printf("0x%x\n", val);
78			break;
79		}
80		case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
81		{
82			printf("BLOB length %u : ", (unsigned)attrData->Length);
83			for(unsigned i=0; i<attrData->Length; i++) {
84				unsigned dat = attrData->Data[i];
85				printf("%02X ", dat);
86			}
87			printf("\n");
88			break;
89		}
90		case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
91		{
92			printf("multi_int[");
93			uint32 numInts = attrData->Length / sizeof(uint32);
94			uint32 *uip = (uint32 *)data;
95			for(unsigned i=0; i<numInts; i++) {
96				if(i > 0) {
97					printf(", ");
98				}
99				printf("0x%x", (unsigned)*uip++);
100			}
101			printf("]\n");
102			break;
103		}
104		default:
105			printf("***UNKNOWN FORMAT (%u), Length %u\n",
106				(unsigned)attrForm, (unsigned)attrData->Length);
107			break;
108	}
109}
110
111/*
112 * Vanilla "dump one record" routine. Assumes format of all attribute labels
113 * as string.
114 */
115static void dumpRecord(
116	const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs)
117{
118	unsigned dex;
119	for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) {
120		const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex];
121		if(attrData->Info.AttributeNameFormat !=
122				CSSM_DB_ATTRIBUTE_NAME_AS_STRING) {
123			printf("***BAD ATTR_NAME FORMAT (%u)\n",
124				(unsigned)attrData->Info.AttributeNameFormat);
125				continue;
126		}
127		const char *attrName = attrData->Info.Label.AttributeName;
128		printName(attrName);
129		printf(": ");
130		for(unsigned attrNum=0; attrNum<attrData->NumberOfValues; attrNum++) {
131			dumpAttr(attrData->Info.AttributeFormat,
132				&attrData->Value[attrNum]);
133		}
134		if(attrData->NumberOfValues == 0) {
135			printf("<<no values present>>\n");
136		}
137	}
138}
139
140/* free attribute(s) allocated by MDS */
141static void freeAttrs(
142	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR	recordAttrs)
143{
144	unsigned i;
145
146	for(i=0; i<recordAttrs->NumberOfAttributes; i++) {
147		CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i];
148		if(attrData == NULL) {
149			/* fault of caller, who allocated the CSSM_DB_ATTRIBUTE_DATA */
150			printf("***freeAttrs screwup: NULL attrData\n");
151			return;
152		}
153		unsigned j;
154		for(j=0; j<attrData->NumberOfValues; j++) {
155			CSSM_DATA_PTR data = &attrData->Value[j];
156			if(data == NULL) {
157				/* fault of MDS, who said there was a value here */
158				printf("***freeAttrs screwup: NULL data\n");
159				return;
160			}
161			appFree(data->Data, NULL);
162			data->Data = NULL;
163			data->Length = 0;
164		}
165		appFree(attrData->Value, NULL);
166		attrData->Value = NULL;
167	}
168}
169
170/*
171 * Core MDS lookup routine. Used in two situations. It's called by main() to perform
172 * a lookup in the CDSA Directory Database based one one key/value pair; this
173 * call fetches one attribute from the associated record - the GUID ("ModuleID"
174 * in MDS lingo). Then the function calls itself to do a lookup in the Object DB,
175 * based on that GUID, in order to fetch the path of the module associated with
176 * that GUID. The first call (from main()) corresponds to an application's
177 * typical use of MDS. The recursive call, which does a lookup in the Object
178 * DB, corresponds to CSSM's typical use of MDS, which is to map a GUID to a
179 * bundle path.
180 *
181 * The ModuleID and Path of all modules satisfying the initial search criteria
182 * are displayed on stdout.
183 *
184 * Caller specifies one search attribute, by name, value,�and value format.
185 * Whether this is the first or second (recursive) call is indicated by the
186 * cdsaLookup argument. That determines both the DB to search and the attribute
187 * to fetch (ModuleID or Path).
188 */
189static void doLookup(
190	MDS_FUNCS 					*mdsFuncs,
191
192	/* Two DBs and a flag indicating which one to use */
193	MDS_DB_HANDLE 				objDlDb,
194	MDS_DB_HANDLE 				cdsaDlDb,
195	bool						cdsaLookup,	// true - use cdsaDlDb; false - objDlDb
196
197	/* Record type, a.k.a. Relation, e.g. MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE */
198	CSSM_DB_RECORDTYPE			recordType,
199
200	/* key, value, valForm, and valOp are the thing we search on */
201	/* Note CSSM_DB_ATTRIBUTE_NAME_FORMAT - the format of the attribute name -
202	 *    is always CSSM_DB_ATTRIBUTE_NAME_AS_STRING for MDS. */
203	const char					*key,		// e.g. "AlgType"
204	const void					*valPtr,
205	unsigned					valLen,
206	CSSM_DB_ATTRIBUTE_FORMAT	valForm,	// CSSM_DB_ATTRIBUTE_FORMAT_STRING, etc.
207	CSSM_DB_OPERATOR			valOp,		// normally CSSM_DB_EQUAL
208
209	/* for display only */
210	const char					*srchStr)
211{
212	CSSM_QUERY						query;
213	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
214	CSSM_HANDLE						resultHand;
215	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
216	CSSM_SELECTION_PREDICATE		predicate;
217	CSSM_DATA						predData;
218	CSSM_DB_ATTRIBUTE_DATA			outAttr;
219	CSSM_DB_ATTRIBUTE_INFO_PTR		attrInfo;
220	CSSM_RETURN						crtn;
221	MDS_DB_HANDLE					dlDb;
222	const char 						*attrName;
223
224	if(cdsaLookup) {
225		/* first call, fetching guid from the CDSA Directory DB */
226		dlDb = cdsaDlDb;
227		attrName = "ModuleID";
228	}
229	else {
230		/* recursive call, fetching path from Object DB */
231		dlDb = objDlDb;
232		attrName = "Path";
233	}
234
235	/* We want one attributes back, name and format specified by caller */
236	recordAttrs.DataRecordType = recordType;
237	recordAttrs.SemanticInformation = 0;
238	recordAttrs.NumberOfAttributes = 1;
239	recordAttrs.AttributeData = &outAttr;
240
241	memset(&outAttr, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA));
242	attrInfo = &outAttr.Info;
243	attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
244	attrInfo->Label.AttributeName = (char *)attrName;
245	attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
246
247	/* one predicate - the caller's key and CSSM_DB_OPERATOR */
248	predicate.DbOperator = valOp;
249	predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
250	predicate.Attribute.Info.Label.AttributeName = (char *)key;
251	predicate.Attribute.Info.AttributeFormat = valForm;
252	predData.Data = (uint8 *)valPtr;
253	predData.Length = valLen;
254	predicate.Attribute.Value = &predData;
255	predicate.Attribute.NumberOfValues = 1;
256
257	query.RecordType = recordType;
258	query.Conjunctive = CSSM_DB_NONE;
259	query.NumSelectionPredicates = 1;
260	query.SelectionPredicate = &predicate;
261	query.QueryLimits.TimeLimit = 0;			// FIXME - meaningful?
262	query.QueryLimits.SizeLimit = 1;			// FIXME - meaningful?
263	query.QueryFlags = 0;		// CSSM_QUERY_RETURN_DATA...FIXME - used?
264
265	crtn = mdsFuncs->DataGetFirst(dlDb,
266		&query,
267		&resultHand,
268		&recordAttrs,
269		NULL,			// No data
270		&record);
271	switch(crtn) {
272		case CSSM_OK:
273			break;		// proceed
274		case CSSMERR_DL_ENDOFDATA:
275			printf("%s: no record found\n", srchStr);
276			return;
277		default:
278			printError("DataGetFirst", crtn);
279			return;
280	}
281	/* dump this record, one attribute */
282	if(srchStr) {
283		/* not done on recursive call */
284		printf("%s found:\n", srchStr);
285	}
286	dumpRecord(&recordAttrs);
287	mdsFuncs->FreeUniqueRecord(dlDb, record);
288
289	if(srchStr != NULL) {
290		/*
291		 * Now do a lookup in Object DB of this guid, looking for path.
292		 * Apps normally don't do this; this is what CSSM does when given
293		 * the GUID of a module.
294		 */
295		if(outAttr.Value == NULL) {
296			printf("***Screwup: DataGetFirst worked, but no outAttr\n");
297			return;
298		}
299		doLookup(mdsFuncs,
300			objDlDb,
301			cdsaDlDb,
302			false,			 		// use objDlDb
303			MDS_OBJECT_RECORDTYPE,
304			"ModuleID",				// key
305			outAttr.Value->Data,	// valPtr, ModuleID, as string
306			outAttr.Value->Length,	// valLen
307			CSSM_DB_ATTRIBUTE_FORMAT_STRING,
308			CSSM_DB_EQUAL,
309			NULL);					// srchStr
310	}
311	freeAttrs(&recordAttrs);
312
313	/* now the rest of them */
314	for(;;) {
315		crtn = mdsFuncs->DataGetNext(dlDb,
316			resultHand,
317			&recordAttrs,
318			NULL,
319			&record);
320		switch(crtn) {
321			case CSSM_OK:
322				dumpRecord(&recordAttrs);
323				mdsFuncs->FreeUniqueRecord(cdsaDlDb, record);
324				if(srchStr != NULL) {
325					if(outAttr.Value == NULL) {
326						printf("***Screwup: DataGetNext worked, but no outAttr\n");
327						return;
328					}
329					doLookup(mdsFuncs,
330						objDlDb,
331						cdsaDlDb,
332						false,			 		// use objDlDb
333						MDS_OBJECT_RECORDTYPE,
334						"ModuleID",				// key
335						outAttr.Value->Data,	// valPtr, ModuleID, as string
336						outAttr.Value->Length,	// valLen
337						CSSM_DB_ATTRIBUTE_FORMAT_STRING,
338						CSSM_DB_EQUAL,
339						NULL);					// srchStr
340				}
341				freeAttrs(&recordAttrs);
342				break;		// and go again
343			case CSSMERR_DL_ENDOFDATA:
344				/* normal termination */
345				break;
346			default:
347				printError("DataGetNext", crtn);
348				break;
349		}
350		if(crtn != CSSM_OK) {
351			break;
352		}
353	}
354}
355
356int main(int argc, char **argv)
357{
358	MDS_FUNCS 			mdsFuncs;
359	MDS_HANDLE 			mdsHand;
360	CSSM_RETURN 		crtn;
361	int 				arg;
362	CSSM_DB_HANDLE		dbHand = 0;
363	MDS_DB_HANDLE		objDlDb;
364	MDS_DB_HANDLE		cdsaDlDb;
365	bool				keepConnected = false;
366	uint32				val;
367
368	for(arg=2; arg<argc; arg++) {
369		switch(argv[arg][0]) {
370			case 'k':
371				keepConnected = true;
372				break;
373			default:
374				usage(argv);
375		}
376	}
377	crtn = MDS_Initialize(NULL,		// callerGuid
378		&memFuncs,
379		&mdsFuncs,
380		&mdsHand);
381	if(crtn) {
382		printError("MDS_Initialize", crtn);
383		exit(1);
384	}
385
386	do {
387		/*
388		 * Open both MDS DBs - apps normally only have to open
389		 * MDS_CDSA_DIRECTORY_NAME.
390		 */
391		crtn = mdsFuncs.DbOpen(mdsHand,
392			MDS_OBJECT_DIRECTORY_NAME,
393			NULL,				// DbLocation
394			CSSM_DB_ACCESS_READ,
395			NULL,				// AccessCred - hopefully optional
396			NULL,				// OpenParameters
397			&dbHand);
398		if(crtn) {
399			printError("DbOpen(MDS_OBJECT_DIRECTORY_NAME)", crtn);
400			exit(1);
401		}
402		objDlDb.DLHandle = mdsHand;
403		objDlDb.DBHandle = dbHand;
404
405		crtn = mdsFuncs.DbOpen(mdsHand,
406			MDS_CDSA_DIRECTORY_NAME,
407			NULL,				// DbLocation
408			CSSM_DB_ACCESS_READ,
409			NULL,				// AccessCred - hopefully optional
410			NULL,				// OpenParameters
411			&dbHand);
412		if(crtn) {
413			printError("DbOpen(MDS_CDSA_DIRECTORY_NAME)", crtn);
414			exit(1);
415		}
416		cdsaDlDb.DLHandle = mdsHand;
417		cdsaDlDb.DBHandle = dbHand;
418
419		/*
420		 * Do some typical lookups.
421		 */
422
423		/* a CSP which can do SHA1 digest */
424		val = CSSM_ALGID_SHA1;
425		doLookup(&mdsFuncs,
426			objDlDb,
427			cdsaDlDb,
428			true,
429			MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE,
430			"AlgType",
431			&val,
432			sizeof(uint32),
433			CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
434			CSSM_DB_EQUAL,
435			"CSP for SHA1 digest");
436
437		/* a TP which can do iSign verification */
438		doLookup(&mdsFuncs,
439			objDlDb,
440			cdsaDlDb,
441			true,
442			MDS_CDSADIR_TP_OIDS_RECORDTYPE,
443			"OID",
444			CSSMOID_APPLE_ISIGN.Data,
445			CSSMOID_APPLE_ISIGN.Length,
446			CSSM_DB_ATTRIBUTE_FORMAT_BLOB,
447			CSSM_DB_EQUAL,
448			"TP for CSSMOID_APPLE_ISIGN policy");
449
450		/* an X509-savvy CL */
451		/* Very weird data form - two fields in one 32-bit word */
452		val = (CSSM_CERT_X_509v3 << 16) | CSSM_CERT_ENCODING_DER;
453		doLookup(&mdsFuncs,
454			objDlDb,
455			cdsaDlDb,
456			true,
457			MDS_CDSADIR_CL_PRIMARY_RECORDTYPE,
458			"CertTypeFormat",
459			&val,
460			sizeof(uint32),
461			CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
462			CSSM_DB_EQUAL,
463			"X509 CL");
464
465		/* A DL which can do CSSM_DB_AND */
466		val = CSSM_DB_AND;
467		doLookup(&mdsFuncs,
468			objDlDb,
469			cdsaDlDb,
470			true,
471			MDS_CDSADIR_DL_PRIMARY_RECORDTYPE,
472			"ConjunctiveOps",
473			&val,
474			sizeof(uint32),
475			CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32,
476			/* This is a multi-uint32, meaning we want to search for a
477			 * ConjunctiveOps which contains the specified value */
478			CSSM_DB_CONTAINS,
479			"DL with ConjunctiveOp CSSM_DB_AND");
480
481		/* a CSP which can do CSSM_ALGID_IDEA, should fail */
482		val = CSSM_ALGID_IDEA;
483		doLookup(&mdsFuncs,
484			objDlDb,
485			cdsaDlDb,
486			true,
487			MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE,
488			"AlgType",
489			&val,
490			sizeof(uint32),
491			CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
492			CSSM_DB_EQUAL,
493			"CSP for CSSM_ALGID_BLOWFISH, expect failure");
494
495		/* a TP which can obtain a .mac signing certificate */
496		doLookup(&mdsFuncs,
497			objDlDb,
498			cdsaDlDb,
499			true,
500			MDS_CDSADIR_TP_OIDS_RECORDTYPE,
501			"OID",
502			CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Data,
503			CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN.Length,
504			CSSM_DB_ATTRIBUTE_FORMAT_BLOB,
505			CSSM_DB_EQUAL,
506			"TP for .mac signing certificate policy");
507
508		crtn = mdsFuncs.DbClose(objDlDb);
509		if(crtn) {
510			printError("DbClose(objDlDb)", crtn);
511		}
512		crtn = mdsFuncs.DbClose(cdsaDlDb);
513		if(crtn) {
514			printError("DbClose(cdsaDlDb)", crtn);
515		}
516		if(keepConnected) {
517			printf("\n");
518			fpurge(stdin);
519			printf("Enter CR to go again: ");
520			getchar();
521		}
522	} while(keepConnected);
523	crtn = MDS_Terminate(mdsHand);
524	if(crtn) {
525		printError("MDS_Terminate", crtn);
526	}
527	return 0;
528}
529