1/*
2 * mdsdump.cpp - dump contents of system MDS databases
3 */
4
5 /**** FIXME this uses a private API not currently exported in any way from
6  **** Security project
7  ****/
8
9#include <CoreFoundation/CoreFoundation.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <Security/mds.h>
13#include <common.h>
14#include <Security/mds_schema.h>
15#include "MDSSchema.h"
16#include <strings.h>
17
18#define MAX_MDS_ATTRS	32
19
20static CSSM_MEMORY_FUNCS memFuncs = {
21	appMalloc,
22	appFree,
23	appRealloc,
24 	appCalloc,
25 	NULL
26 };
27
28static void showInfoTypes()
29{
30	printf("   o   Object records\n");
31	printf("   C   CSSM records\n");
32	printf("   p   Plugin Common records\n");
33	printf("   c   CSP records\n");
34	printf("   l   CL records\n");
35	printf("   t   TP records\n");
36	printf("   d   DL records\n");
37	printf("   a   All records from Object DB\n");
38	printf("   A   All records from CDSA directory DB\n");
39}
40
41static void usage(char **argv)
42{
43	printf("Usage: %s info_type [options...]\n", argv[0]);
44	printf("info_type values:\n");
45	showInfoTypes();
46	printf("   h   help\n");
47	printf("Options:\n");
48	printf("   i   perform MDS_Install()\n");
49	printf("   v   verbose\n");
50	printf("   k   keep connected and go again\n");
51	exit(1);
52}
53
54#define NORM_KEY_LEN	20
55
56/* print a key name, padding out to NORM_KEY_LEN columns */
57static void printName(
58	const char *attrName)
59{
60	printf("      %s", attrName);
61	int len = strlen(attrName);
62	if(len > NORM_KEY_LEN) {
63		return;
64	}
65	int numSpaces = NORM_KEY_LEN - len;
66	for(int i=0; i<numSpaces; i++) {
67		putchar(' ');
68	}
69
70}
71
72#if 0
73/*
74 * Attempt to print a numeric value as a string, per a MDSNameValuePair table.
75 * Of course this can not deal with OR'd together values; the MDSNameValuePair
76 * mechanism does not indicate on a per-field basis which fields are OR-able.
77 * If the value is in fact a collection of legal values (per the nameValues
78 * array), the value will just be printed in hex.
79 */
80static void printValueAsString(
81	unsigned val,
82	const Security::MDSNameValuePair *nameValues)		// optional
83{
84	if(nameValues != NULL) {
85		while(nameValues->name != NULL) {
86			if(nameValues->value == val) {
87				printf("%s", nameValues->name);
88				return;
89			}
90			nameValues++;
91		}
92	}
93	/* Oh well */
94	printf("0x%x", val);
95}
96#endif
97
98/* print value string, surrounded by single quotes, then a newline */
99static void printValue(
100	const CSSM_DATA *attrValue)
101{
102	printf("'");
103	for(uint32 dex=0; dex<attrValue->Length; dex++) {
104		printf("%c", attrValue->Data[dex]);
105	}
106	printf("'\n");
107}
108
109/* Print one attribute value */
110static void dumpAttr(
111	CSSM_DB_ATTRIBUTE_FORMAT attrForm,
112	const CSSM_DATA *attrData)
113{
114	if((attrData == NULL) || (attrData->Data == NULL)) {
115		printf("<NULL DATA>\n");
116		return;
117	}
118	void *data = attrData->Data;
119	switch(attrForm) {
120		case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
121			printValue(attrData);
122			break;
123		case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:	// not really supported in MDS
124		case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
125		{
126			unsigned val = *(unsigned *)data;
127			printf("0x%x\n", val);
128			break;
129		}
130		case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
131		{
132			printf("BLOB length %u : ", (unsigned)attrData->Length);
133			for(unsigned i=0; i<attrData->Length; i++) {
134				unsigned dat = attrData->Data[i];
135				printf("%02X ", dat);
136			}
137			printf("\n");
138			break;
139		}
140		case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
141		{
142			printf("multi_int[");
143			uint32 numInts = attrData->Length / sizeof(uint32);
144			uint32 *uip = (uint32 *)data;
145			for(unsigned i=0; i<numInts; i++) {
146				if(i > 0) {
147					printf(", ");
148				}
149				printf("0x%x", (unsigned)(*uip++));
150			}
151			printf("]\n");
152			break;
153		}
154		default:
155			printf("***UNKNOWN FORMAT (%u), Length %u\n",
156				(unsigned)attrForm, (unsigned)attrData->Length);
157			break;
158	}
159}
160
161/*
162 * Vanilla "dump one record" routine. Assumes format of all attribute labels
163 * as string. Uses a MDSNameValuePair ptr array in parallel to the attributes
164 * themselves to facilitate displaying numeric values as strings (e.g.
165 * "CSSM_ALGID_SHA1") where possible.
166 */
167static void dumpRecord(
168	const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs)
169{
170	unsigned dex;
171	for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) {
172		const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex];
173		if(attrData->Info.AttributeNameFormat != CSSM_DB_ATTRIBUTE_NAME_AS_STRING) {
174			printf("***BAD ATTR_NAME FORMAT (%u)\n",
175				(unsigned)attrData->Info.AttributeNameFormat);
176				continue;
177		}
178		const char *attrName = attrData->Info.Label.AttributeName;
179		printName(attrName);
180		printf(": ");
181		/* note currently in MDS NumberOfValues is always one or zero */
182		for(unsigned attrNum=0; attrNum<attrData->NumberOfValues; attrNum++) {
183			dumpAttr(attrData->Info.AttributeFormat,
184				&attrData->Value[attrNum]);
185		}
186		if(attrData->NumberOfValues == 0) {
187			printf("<<no values present>>\n");
188		}
189	}
190}
191
192/* free attribute(s) allocated by MDS */
193static void freeAttrs(
194	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR	recordAttrs)
195{
196	unsigned i;
197
198	for(i=0; i<recordAttrs->NumberOfAttributes; i++) {
199		CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i];
200		if(attrData == NULL) {
201			/* fault of caller, who allocated the CSSM_DB_ATTRIBUTE_DATA */
202			printf("***freeAttrs screwup: NULL attrData\n");
203			return;
204		}
205		unsigned j;
206		for(j=0; j<attrData->NumberOfValues; j++) {
207			CSSM_DATA_PTR data = &attrData->Value[j];
208			if(data == NULL) {
209				/* fault of MDS, who said there was a value here */
210				printf("***freeAttrs screwup: NULL data\n");
211				return;
212			}
213			appFree(data->Data, NULL);
214			data->Data = NULL;
215			data->Length = 0;
216		}
217		appFree(attrData->Value, NULL);
218		attrData->Value = NULL;
219	}
220}
221
222/*
223 * Fetch and display all records of specified CSSM_DB_RECORDTYPE.
224 */
225static void fetchAllAttrs(
226	MDS_FUNCS *mdsFuncs,
227	MDS_DB_HANDLE dlDb,
228	CSSM_DB_RECORDTYPE recordType)
229{
230	CSSM_QUERY						query;
231	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
232	CSSM_RETURN						crtn;
233	CSSM_HANDLE						resultHand;
234	CSSM_DB_ATTRIBUTE_DATA			attrs[MAX_MDS_ATTRS];
235	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
236	const RelationInfo				*relInfo;
237
238	relInfo = MDSRecordTypeToRelation(recordType);
239	if(relInfo == NULL) {
240		printf("***UNKNOWN recordType %d\n", (int)recordType);
241		return;
242	}
243
244	/* build an attr array from schema so we get all known attrs */
245	memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * MAX_MDS_ATTRS);
246	unsigned attrDex;
247	for(attrDex=0; attrDex<relInfo->NumberOfAttributes; attrDex++) {
248		attrs[attrDex].Info = relInfo->AttributeInfo[attrDex];
249	}
250	recordAttrs.DataRecordType = recordType;
251	recordAttrs.NumberOfAttributes = relInfo->NumberOfAttributes;
252	recordAttrs.AttributeData = attrs;
253
254	/* just search by recordType, no predicates */
255	query.RecordType = recordType;
256	query.Conjunctive = CSSM_DB_NONE;
257	query.NumSelectionPredicates = 0;
258	query.SelectionPredicate = NULL;
259	query.QueryLimits.TimeLimit = 0;			// FIXME - meaningful?
260	query.QueryLimits.SizeLimit = 1;			// FIXME - meaningful?
261	query.QueryFlags = 0;		// CSSM_QUERY_RETURN_DATA...FIXME - used?
262
263	crtn = mdsFuncs->DataGetFirst(dlDb,
264		&query,
265		&resultHand,
266		&recordAttrs,
267		NULL,			// No data
268		&record);
269	switch(crtn) {
270		case CSSM_OK:
271			break;		// proceed
272		case CSSMERR_DL_ENDOFDATA:
273			printf("%s: no record found\n", relInfo->relationName);
274			return;
275		default:
276			printError("DataGetFirst", crtn);
277			return;
278	}
279	unsigned recNum = 0;
280	printf("%s:\n", relInfo->relationName);
281	printf("   record %d; numAttrs %d:\n",
282		recNum++, (int)recordAttrs.NumberOfAttributes);
283
284	dumpRecord(&recordAttrs);
285	mdsFuncs->FreeUniqueRecord(dlDb, record);
286	freeAttrs(&recordAttrs);
287
288	/* now the rest of them */
289	/* hopefully we don't have to re-init the recordAttr array */
290	for(;;) {
291		crtn = mdsFuncs->DataGetNext(dlDb,
292			resultHand,
293			&recordAttrs,
294			NULL,
295			&record);
296		switch(crtn) {
297			case CSSM_OK:
298				printf("   record %d; numAttrs %d:\n",
299					recNum++, (int)recordAttrs.NumberOfAttributes);
300				dumpRecord(&recordAttrs);
301				mdsFuncs->FreeUniqueRecord(dlDb, record);
302				freeAttrs(&recordAttrs);
303				break;		// and go again
304			case CSSMERR_DL_ENDOFDATA:
305				/* normal termination */
306				break;
307			default:
308				printError("DataGetNext", crtn);
309				break;
310		}
311		if(crtn != CSSM_OK) {
312			break;
313		}
314	}
315}
316
317/*
318 * This is different - it's schema-independent. Fetch all records from specified
319 * DlDb which contain a ModuleID attribute.
320 */
321static void fetchAllRecords(
322	MDS_FUNCS *mdsFuncs,
323	MDS_DB_HANDLE dlDb)
324{
325	CSSM_QUERY						query;
326	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
327	CSSM_RETURN						crtn;
328	CSSM_HANDLE						resultHand;
329	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
330	CSSM_DB_ATTRIBUTE_DATA			theAttr;
331	CSSM_DB_ATTRIBUTE_INFO_PTR		attrInfo = &theAttr.Info;
332	CSSM_DATA						attrValue = {0, NULL};
333
334	recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY;
335	recordAttrs.SemanticInformation = 0;
336	recordAttrs.NumberOfAttributes = 1;
337	recordAttrs.AttributeData = &theAttr;
338
339	attrInfo->AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
340	attrInfo->Label.AttributeName = (char *)"ModuleID";
341	attrInfo->AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
342
343	theAttr.NumberOfValues = 1;
344	theAttr.Value = &attrValue;
345
346	/* just search by recordType, no predicates */
347	query.RecordType = CSSM_DL_DB_RECORD_ANY;
348	query.Conjunctive = CSSM_DB_NONE;
349	query.NumSelectionPredicates = 0;
350	query.SelectionPredicate = NULL;
351	query.QueryLimits.TimeLimit = 0;			// FIXME - meaningful?
352	query.QueryLimits.SizeLimit = 1;			// FIXME - meaningful?
353	query.QueryFlags = 0;		// CSSM_QUERY_RETURN_DATA...FIXME - used?
354
355	crtn = mdsFuncs->DataGetFirst(dlDb,
356		&query,
357		&resultHand,
358		&recordAttrs,
359		NULL,			// No data
360		&record);
361	switch(crtn) {
362		case CSSM_OK:
363			break;		// proceed
364		case CSSMERR_DL_ENDOFDATA:
365			printf("no record found\n");
366			return;
367		default:
368			printError("DataGetFirst", crtn);
369			return;
370	}
371	unsigned recNum = 0;
372	printf("Records containing a ModuleID attribute:\n");
373	printf("   record %d:\n", recNum++);
374
375	dumpRecord(&recordAttrs);
376	mdsFuncs->FreeUniqueRecord(dlDb, record);
377	freeAttrs(&recordAttrs);
378
379	/* now the rest of them */
380	/* hopefully we don't have to re-init the recordAttr array */
381	for(;;) {
382		crtn = mdsFuncs->DataGetNext(dlDb,
383			resultHand,
384			&recordAttrs,
385			NULL,
386			&record);
387		switch(crtn) {
388			case CSSM_OK:
389				printf("   record %d:\n", recNum++);
390				dumpRecord(&recordAttrs);
391				mdsFuncs->FreeUniqueRecord(dlDb, record);
392				freeAttrs(&recordAttrs);
393				break;		// and go again
394			case CSSMERR_DL_ENDOFDATA:
395				/* normal termination */
396				break;
397			default:
398				printError("DataGetNext", crtn);
399				break;
400		}
401		if(crtn != CSSM_OK) {
402			break;
403		}
404	}
405}
406
407static void doInstall(
408	MDS_HANDLE mdsHand)
409{
410	CFAbsoluteTime start, end;
411
412	start = CFAbsoluteTimeGetCurrent();
413	CSSM_RETURN crtn = MDS_Install(mdsHand);
414	end = CFAbsoluteTimeGetCurrent();
415	if(crtn) {
416		printError("MDS_Install", crtn);
417	}
418	else {
419		printf("MDS_Install took %gs\n", end - start);
420	}
421}
422
423int main(int argc, char **argv)
424{
425	MDS_FUNCS 			mdsFuncs;
426	MDS_HANDLE 			mdsHand;
427	CSSM_RETURN 		crtn;
428	int 				arg;
429	char 				op;
430	char 				*dbName;
431	CSSM_DB_HANDLE		dbHand = 0;
432	MDS_DB_HANDLE		dlDb;
433	bool 				verbose = 0;
434	bool				keepConnected = false;
435	bool				install = false;
436
437	if(argc < 2) {
438		usage(argv);
439	}
440	op = argv[1][0];
441	if(op == 'h') {
442		usage(argv);
443	}
444	for(arg=2; arg<argc; arg++) {
445		switch(argv[arg][0]) {
446			case 'v':
447				verbose = true;
448				break;
449			case 'i':
450				install = true;
451				break;
452			case 'k':
453				keepConnected = true;
454				break;
455			default:
456				usage(argv);
457		}
458	}
459	if(verbose) {
460		printf("..calling MDS_Initialize\n");
461	}
462	crtn = MDS_Initialize(NULL,		// callerGuid
463		&memFuncs,
464		&mdsFuncs,
465		&mdsHand);
466	if(crtn) {
467		printError("MDS_Initialize", crtn);
468		exit(1);
469	}
470	if(install) {
471		doInstall(mdsHand);
472	}
473	do {
474		/* open one or the other DB */
475		switch(op) {
476			case 'o':
477			case 'a':
478				dbName = (char *)MDS_OBJECT_DIRECTORY_NAME;
479				break;
480			default:
481				dbName = (char *)MDS_CDSA_DIRECTORY_NAME;
482				break;
483		}
484		crtn = mdsFuncs.DbOpen(mdsHand,
485			dbName,
486			NULL,				// DbLocation
487			CSSM_DB_ACCESS_READ,
488			NULL,				// AccessCred - hopefully optional
489			NULL,				// OpenParameters
490			&dbHand);
491		if(crtn) {
492			printError("DbOpen", crtn);
493			exit(1);
494		}
495		dlDb.DLHandle = mdsHand;
496		dlDb.DBHandle = dbHand;
497
498		/* go for it */
499		switch(op) {
500			case 'o':
501				fetchAllAttrs(&mdsFuncs, dlDb, MDS_OBJECT_RECORDTYPE);
502				break;
503			case 'C':
504				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSSM_RECORDTYPE);
505				break;
506			case 'p':
507				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_COMMON_RECORDTYPE);
508				break;
509			case 'c':
510				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSP_PRIMARY_RECORDTYPE);
511				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CSP_CAPABILITY_RECORDTYPE);
512				if(verbose) {
513					fetchAllAttrs(&mdsFuncs, dlDb,
514							MDS_CDSADIR_CSP_ENCAPSULATED_PRODUCT_RECORDTYPE);
515					fetchAllAttrs(&mdsFuncs, dlDb,
516							MDS_CDSADIR_CSP_SC_INFO_RECORDTYPE);
517				}
518				break;
519			case 'l':
520				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_CL_PRIMARY_RECORDTYPE);
521				break;
522			case 't':
523				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_TP_PRIMARY_RECORDTYPE);
524				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_TP_OIDS_RECORDTYPE);
525				if(verbose) {
526					fetchAllAttrs(&mdsFuncs, dlDb,
527						MDS_CDSADIR_TP_ENCAPSULATED_PRODUCT_RECORDTYPE);
528				}
529				break;
530			case 'd':
531				fetchAllAttrs(&mdsFuncs, dlDb, MDS_CDSADIR_DL_PRIMARY_RECORDTYPE);
532				break;
533			case 'a':
534			case 'A':
535				fetchAllRecords(&mdsFuncs, dlDb);
536				break;
537			default:
538				usage(argv);
539		}
540
541		crtn = mdsFuncs.DbClose(dlDb);
542		if(crtn) {
543			printError("DbClose", crtn);
544		}
545		if(keepConnected) {
546			printf("\n");
547			showInfoTypes();
548			fpurge(stdin);
549			printf("Enter new info type: ");
550			op = getchar();
551		}
552	} while(keepConnected);
553	crtn = MDS_Terminate(mdsHand);
554	if(crtn) {
555		printError("MDS_Terminate", crtn);
556	}
557	return 0;
558}
559