1/* Copyright (c) 2002-2006 Apple Computer, Inc.
2 *
3 * dbTool.cpp - DL/DB tool.
4 */
5#include <stdlib.h>
6#include <stdio.h>
7#include <time.h>
8#include <strings.h>
9#include <ctype.h>
10#include <Security/cssm.h>
11#include <Security/cssmapple.h>
12#include <Security/cssmapplePriv.h>
13#include "cspwrap.h"
14#include "common.h"
15#include "dbAttrs.h"
16#include "dbCert.h"
17#include "cspdlTesting.h"
18
19
20static void usage(char **argv)
21{
22	printf("usage: %s dbFileName command [options]\n", argv[0]);
23	printf("Commands:\n");
24	printf("   r   Dump Schema Relations\n");
25	printf("   k   Dump all keys\n");
26	printf("   c   Dump certs\n");
27	printf("   a   Dump all records\n");
28	printf("   d   Delete records (interactively)\n");
29	printf("   D   Delete records (noninteractively, requires really arg)\n");
30	printf("   i   Import bad cert and its (good) private key\n");
31	printf("Options:\n");
32	printf("   v   verbose\n");
33	printf("   q   quiet\n");
34	printf("   R   really! (for D command)\n");
35	printf("   d   dump data\n");
36	printf("   c=certFile\n");
37	printf("   k=keyFile\n");
38	exit(1);
39}
40
41
42static unsigned indentVal = 0;
43static void indentIncr()
44{
45	indentVal += 3;
46}
47
48static void indentDecr()
49{
50	if(indentVal) {
51		indentVal -= 3;
52	}
53}
54
55static void doIndent()
56{
57	unsigned i;
58	for(i=0; i<indentVal; i++) {
59		printf(" ");
60	}
61}
62
63#define NORM_KEY_LEN	20
64
65/* print an attribute name, padding out to NORM_KEY_LEN columns */
66static void printName(
67	const CSSM_DB_ATTRIBUTE_INFO *attrInfo)
68{
69	switch(attrInfo->AttributeNameFormat) {
70		case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
71		{
72			char *attrName = attrInfo->Label.AttributeName;
73			printf("%s", attrName);
74			int len = strlen(attrName);
75			if(len > NORM_KEY_LEN) {
76				return;
77			}
78			int numSpaces = NORM_KEY_LEN - len;
79			for(int i=0; i<numSpaces; i++) {
80				putchar(' ');
81			}
82			break;
83		}
84		case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
85		{
86			/* OSType, endian dependent... */
87			char *cp = (char *)&(attrInfo->Label.AttributeID);
88			for(unsigned i=0; i<4; i++) {
89				putchar(*cp++);
90			}
91			printf("                ");
92			break;
93		}
94		default:
95			printf("Unknown attribute name format (%u)\n",
96				(unsigned)attrInfo->AttributeNameFormat);
97			break;
98	}
99}
100
101/*
102 * Attempt to print a numeric value as a string, per a NameValuePair table.
103 * If the value is in fact a collection of legal values (per the nameValues
104 * array), the value will just be printed in hex.
105 */
106static void printValueAsString(
107	unsigned val,
108	const NameValuePair *nameValues)
109{
110	if(nameValues != NULL) {
111		while(nameValues->name != NULL) {
112			if(nameValues->value == val) {
113				printf("%s", nameValues->name);
114				return;
115			}
116			nameValues++;
117		}
118	}
119	/* Oh well */
120	printf("0x%x", val);
121}
122
123static void safePrint(
124	uint8 *cp,
125	uint32 len)
126{
127	for(unsigned i=0; i<len; i++) {
128		printf("%c", *cp++);
129	}
130}
131
132/* See if a blob is printable. Used for BLOB and UINT32 types, the latter of
133 * which is sometimes used for OSType representation of attr name. */
134bool isPrintable(
135	const CSSM_DATA *dp)
136{
137	bool printable = true;
138	uint8 *cp = dp->Data;
139	for(unsigned i=0; i<dp->Length; i++) {
140		if(*cp == 0) {
141			if(i != (dp->Length - 1)) {
142				/* data contains NULL character before end */
143				printable = false;
144			}
145			/* else end of string */
146			break;
147		}
148		if(!isprint(*cp)) {
149			printable = false;
150			break;
151		}
152		cp++;
153	}
154	return printable;
155}
156
157#define MAX_BLOB_TO_PRINT	12
158static void printBlob(
159	const CSSM_DATA *data)
160{
161	unsigned toPrint = data->Length;
162	if(toPrint > MAX_BLOB_TO_PRINT) {
163		toPrint = MAX_BLOB_TO_PRINT;
164	}
165	for(unsigned i=0; i<toPrint; i++) {
166		unsigned dat = data->Data[i];
167		printf("%02X ", dat);
168	}
169	if(toPrint < data->Length) {
170		printf("...");
171	}
172}
173
174static void printAttrData(
175	const CSSM_DB_ATTRIBUTE_INFO *attrInfo,
176	const CSSM_DATA *attrData,
177	const NameValuePair *nameValues)		// optional
178{
179	void *data = attrData->Data;
180
181	switch(attrInfo->AttributeFormat) {
182
183		case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
184			putchar('\'');
185			safePrint(attrData->Data, attrData->Length);
186			putchar('\'');
187			break;
188		case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
189		case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
190		{
191			unsigned val = *(unsigned *)data;
192			printValueAsString(val, nameValues);
193			break;
194		}
195		case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
196		{
197			printf("BLOB length %u : ", (unsigned)attrData->Length);
198			/* see if it happens to be a printable string */
199			if(isPrintable(attrData)) {
200				putchar('\'');
201				safePrint(attrData->Data, attrData->Length);
202				putchar('\'');
203			}
204			else {
205				printBlob(attrData);
206			}
207			break;
208		}
209		case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
210		{
211			printf("multi_int[");
212			uint32 numInts = attrData->Length / sizeof(uint32);
213			uint32 *uip = (uint32 *)data;
214			for(unsigned i=0; i<numInts; i++) {
215				if(i > 0) {
216					printf(", ");
217				}
218				printValueAsString(*uip++, nameValues);
219			}
220			printf("]");
221			break;
222		}
223		case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
224			putchar('\'');
225			safePrint(attrData->Data, attrData->Length);
226			putchar('\'');
227			break;
228
229		default:
230			printf("***UNKNOWN FORMAT (%u), Length %u",
231				(unsigned)attrInfo->AttributeFormat, (unsigned)attrData->Length);
232			break;
233	}
234}
235
236/* free attribute(s) allocated by DL */
237static void freeAttrs(
238	CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs)
239{
240	unsigned i;
241
242	for(i=0; i<recordAttrs->NumberOfAttributes; i++) {
243		CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &recordAttrs->AttributeData[i];
244		if(attrData == NULL) {
245			/* fault of caller, who allocated the CSSM_DB_ATTRIBUTE_DATA */
246			printf("***freeAttrs screwup: NULL attrData\n");
247			return;
248		}
249		unsigned j;
250		for(j=0; j<attrData->NumberOfValues; j++) {
251			CSSM_DATA_PTR data = &attrData->Value[j];
252			if(data == NULL) {
253				/* fault of MDS, who said there was a value here */
254				printf("***freeAttrs screwup: NULL data\n");
255				return;
256			}
257			CSSM_FREE(data->Data);
258			data->Data = NULL;
259			data->Length = 0;
260		}
261		CSSM_FREE(attrData->Value);
262		attrData->Value = NULL;
263	}
264}
265
266static void dumpDataBlob(
267	const CSSM_DATA *datap)
268{
269	doIndent();
270	printf("Record data length %lu ", datap->Length);
271	if(datap->Length != 0) {
272		printf(" : ");
273		printBlob(datap);
274	}
275	printf("\n");
276}
277
278static void dumpRecordAttrs(
279	const CSSM_DB_RECORD_ATTRIBUTE_DATA *recordAttrs,
280	const NameValuePair 				**nameValues,		// parallel to recordAttrs
281	const CSSM_DATA						*recordData = NULL)	// optional data
282{
283	unsigned valNum;
284	unsigned dex;
285
286	for(dex=0; dex<recordAttrs->NumberOfAttributes; dex++) {
287		const CSSM_DB_ATTRIBUTE_DATA *attrData = &recordAttrs->AttributeData[dex];
288		doIndent();
289		printName(&attrData->Info);
290		printf(": ");
291		if(attrData->NumberOfValues == 0) {
292			printf("<<not present>>\n");
293			continue;
294		}
295		for(valNum=0; valNum<attrData->NumberOfValues; valNum++) {
296			printAttrData(&attrData->Info, &attrData->Value[valNum], nameValues[dex]);
297			if(valNum < (attrData->NumberOfValues - 1)) {
298				printf(", ");
299			}
300		}
301		printf("\n");
302	}
303	if(recordData) {
304		dumpDataBlob(recordData);
305	}
306}
307
308static void dumpRelation(
309	CSSM_DL_DB_HANDLE 		dlDbHand,
310	const RelationInfo		*relInfo,
311	CSSM_BOOL				dumpData)
312{
313	CSSM_QUERY						query;
314	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
315	CSSM_RETURN						crtn;
316	CSSM_HANDLE						resultHand;
317	CSSM_DB_ATTRIBUTE_DATA			*attrs;
318	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
319	unsigned 						attrDex;
320	unsigned 						recNum = 0;
321	uint32							numAttrs = relInfo->NumberOfAttributes;
322	CSSM_DATA						data = {0, NULL};
323	CSSM_DATA_PTR					datap = NULL;
324
325	if(dumpData) {
326		datap = &data;
327	}
328
329	/* build an attr array from known schema */
330	attrs = (CSSM_DB_ATTRIBUTE_DATA *)CSSM_MALLOC(
331		sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
332	memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
333	for(attrDex=0; attrDex<numAttrs; attrDex++) {
334		attrs[attrDex].Info = relInfo->AttributeInfo[attrDex];
335	}
336	recordAttrs.DataRecordType = relInfo->DataRecordType;
337	recordAttrs.NumberOfAttributes = numAttrs;
338	recordAttrs.AttributeData = attrs;
339
340	/* just search by recordType, no predicates */
341	query.RecordType = relInfo->DataRecordType;
342	query.Conjunctive = CSSM_DB_NONE;
343	query.NumSelectionPredicates = 0;
344	query.SelectionPredicate = NULL;
345	query.QueryLimits.TimeLimit = 0;			// FIXME - meaningful?
346	query.QueryLimits.SizeLimit = 1;			// FIXME - meaningful?
347	query.QueryFlags = 0;		// CSSM_QUERY_RETURN_DATA...FIXME - used?
348
349	crtn = CSSM_DL_DataGetFirst(dlDbHand,
350		&query,
351		&resultHand,
352		&recordAttrs,
353		datap,
354		&record);
355	switch(crtn) {
356		case CSSM_OK:
357			break;		// proceed
358		case CSSMERR_DL_ENDOFDATA:
359			printf("%s: no record found\n", relInfo->relationName);
360			CSSM_FREE(attrs);
361			return;
362		default:
363			printError("DataGetFirst", crtn);
364			CSSM_FREE(attrs);
365			return;
366	}
367	printf("%s:\n", relInfo->relationName);
368	printf("   record %d; numAttrs %d:\n",
369		recNum++, (int)recordAttrs.NumberOfAttributes);
370	indentIncr();
371
372	dumpRecordAttrs(&recordAttrs, relInfo->nameValues, datap);
373	CSSM_DL_FreeUniqueRecord(dlDbHand, record);
374	freeAttrs(&recordAttrs);
375	if(datap) {
376		CSSM_FREE(datap->Data);
377	}
378
379	/* now the rest of them */
380	/* hopefully we don't have to re-init the recordAttr array */
381	for(;;) {
382		crtn = CSSM_DL_DataGetNext(dlDbHand,
383			resultHand,
384			&recordAttrs,
385			datap,
386			&record);
387		switch(crtn) {
388			case CSSM_OK:
389				printf("   record %d; numAttrs %d:\n",
390					recNum++, (int)recordAttrs.NumberOfAttributes);
391				dumpRecordAttrs(&recordAttrs, relInfo->nameValues, datap);
392				CSSM_DL_FreeUniqueRecord(dlDbHand, record);
393				freeAttrs(&recordAttrs);
394				if(datap) {
395					CSSM_FREE(datap->Data);
396				}
397				break;		// and go again
398			case CSSMERR_DL_ENDOFDATA:
399				/* normal termination */
400				break;
401			default:
402				printError("DataGetNext", crtn);
403				break;
404		}
405		if(crtn != CSSM_OK) {
406			break;
407		}
408	}
409	indentDecr();
410	CSSM_FREE(attrs);
411}
412
413/*
414 * Given a record type and a CSSM_DB_UNIQUE_RECORD, fetch and parse all the
415 * attributes we can.
416 */
417static void fetchParseRecord(
418	CSSM_DL_DB_HANDLE				dlDbHand,
419	CSSM_DB_RECORD_ATTRIBUTE_DATA	*inRecordAttrs,
420	CSSM_DB_UNIQUE_RECORD_PTR		record,
421	const CSSM_DATA_PTR				datap,
422	CSSM_BOOL						dumpData)
423{
424	const RelationInfo *relInfo = NULL;
425
426	/* infer RelationInfo from recordType */
427	switch(inRecordAttrs->DataRecordType) {
428		case CSSM_DL_DB_RECORD_PUBLIC_KEY:
429		case CSSM_DL_DB_RECORD_PRIVATE_KEY:
430		case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
431			relInfo = &allKeysRelation;
432			break;
433		case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
434		case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
435		case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
436			relInfo = &genericKcRelation;
437			break;
438		case CSSM_DL_DB_RECORD_CERT:
439			relInfo = &certRecordRelation;
440			break;
441		case CSSM_DL_DB_RECORD_X509_CERTIFICATE:
442			relInfo = &x509CertRecordRelation;
443			break;
444		case CSSM_DL_DB_RECORD_X509_CRL:
445			relInfo = &x509CrlRecordRelation;
446			break;
447		case CSSM_DL_DB_RECORD_USER_TRUST:
448			relInfo = &userTrustRelation;
449			break;
450		case CSSM_DL_DB_RECORD_UNLOCK_REFERRAL:
451			relInfo = &referralRecordRelation;
452			break;
453		case CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE:
454			relInfo = &extendedAttrRelation;
455			break;
456		case DBBlobRelationID:
457			relInfo = NULL;
458			doIndent();
459			printf("--- No attributes ---\n");
460			if(dumpData) {
461				dumpDataBlob(datap);
462			}
463			return;
464		default:
465			doIndent();
466			printf("<<unparsed>>\n");
467			if(dumpData) {
468				doIndent();
469				printf("Record blob (length %ld): ", datap->Length);
470				printBlob(datap);
471				printf("\n");
472			}
473			return;
474	}
475
476	CSSM_DB_ATTRIBUTE_DATA			*attrs = NULL;
477	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
478	unsigned 						attrDex;
479	uint32							numAttrs = relInfo->NumberOfAttributes;
480	CSSM_RETURN						crtn;
481	CSSM_DATA 						recordData = {0, NULL};
482	CSSM_DATA_PTR					recordDataP = dumpData ? &recordData : NULL;
483
484	/* build an attr array from known schema */
485	attrs = (CSSM_DB_ATTRIBUTE_DATA *)CSSM_MALLOC(
486		sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
487	memset(attrs, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * numAttrs);
488	for(attrDex=0; attrDex<numAttrs; attrDex++) {
489		attrs[attrDex].Info = relInfo->AttributeInfo[attrDex];
490	}
491
492	/* from inRecordAttrs, not the relInfo, which could be a typeless template */
493	recordAttrs.DataRecordType = relInfo->DataRecordType;
494	recordAttrs.NumberOfAttributes = numAttrs;
495	recordAttrs.AttributeData = attrs;
496
497	crtn = 	CSSM_DL_DataGetFromUniqueRecordId(dlDbHand,
498		record,
499		&recordAttrs,
500		recordDataP);
501	if(crtn) {
502		printError("CSSM_DL_DataGetFromUniqueRecordId", crtn);
503		goto abort;
504	}
505	dumpRecordAttrs(&recordAttrs, relInfo->nameValues, recordDataP);
506	freeAttrs(&recordAttrs);
507	if(recordData.Data) {
508		CSSM_FREE(recordData.Data);
509	}
510abort:
511	if(attrs) {
512		CSSM_FREE(attrs);
513	}
514	return;
515}
516
517static void deleteRecord(
518	CSSM_DL_DB_HANDLE 			dlDbHand,
519	CSSM_DB_UNIQUE_RECORD_PTR	record,
520	CSSM_BOOL					interact)
521{
522	if(interact) {
523		fpurge(stdin);
524		printf("\nDelete this record [y/anything] ? ");
525		char resp = getchar();
526		if(resp != 'y') {
527			return;
528		}
529	}
530	CSSM_RETURN crtn;
531	crtn = CSSM_DL_DataDelete(dlDbHand, record);
532	if(crtn) {
533		printError("CSSM_DL_DataDelete", crtn);
534	}
535	else if(interact) {
536		printf("...record deleted\n\n");
537	}
538}
539
540/*
541 * In this case we search for CSSM_DL_DB_RECORD_ANY. The current schema results
542 * in no single attribute which all interesting records have in common, so we
543 * can't grab any attributes at GetFirst/GetNext time. Instead we have
544 * to deal with the returned record per its record type.
545 */
546static void dumpAllRecords(
547	CSSM_DL_DB_HANDLE 		dlDbHand,
548	CSSM_BOOL				deleteAll,
549	CSSM_BOOL				interact,
550	CSSM_BOOL				dumpData)
551{
552	CSSM_QUERY						query;
553	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
554	CSSM_RETURN						crtn;
555	CSSM_HANDLE						resultHand;
556	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
557	CSSM_DATA						data = {0, NULL};
558	CSSM_DATA_PTR					datap = NULL;
559
560	if(dumpData) {
561		datap = &data;
562	}
563
564	recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_ANY;
565	recordAttrs.NumberOfAttributes = 0;
566	recordAttrs.AttributeData = NULL;
567
568	/* just search by recordType, no predicates */
569	query.RecordType = CSSM_DL_DB_RECORD_ANY;
570	query.Conjunctive = CSSM_DB_NONE;
571	query.NumSelectionPredicates = 0;
572	query.SelectionPredicate = NULL;
573	query.QueryLimits.TimeLimit = 0;			// FIXME - meaningful?
574	query.QueryLimits.SizeLimit = 1;			// FIXME - meaningful?
575	query.QueryFlags = 0;		// CSSM_QUERY_RETURN_DATA...FIXME - used?
576
577	crtn = CSSM_DL_DataGetFirst(dlDbHand,
578		&query,
579		&resultHand,
580		&recordAttrs,
581		datap,
582		&record);
583	switch(crtn) {
584		case CSSM_OK:
585			break;		// proceed
586		case CSSMERR_DL_ENDOFDATA:
587			printf("CSSM_DL_DB_RECORD_ANY: no record found\n");
588			return;
589		default:
590			printError("DataGetFirst", crtn);
591			return;
592	}
593
594	/* could be anything; check it out */
595	if(interact) {
596		doIndent();
597		printValueAsString(recordAttrs.DataRecordType, recordTypeNames);
598		printf("\n");
599		indentIncr();
600		fetchParseRecord(dlDbHand, &recordAttrs, record, datap, dumpData);
601		indentDecr();
602	}
603	if(deleteAll && (recordAttrs.DataRecordType != DBBlobRelationID)) {
604		/* NEVER delete a DBBlob */
605		deleteRecord(dlDbHand, record, interact);
606	}
607	CSSM_DL_FreeUniqueRecord(dlDbHand, record);
608
609	/* now the rest of them */
610	/* hopefully we don't have to re-init the recordAttr array */
611	for(;;) {
612		crtn = CSSM_DL_DataGetNext(dlDbHand,
613			resultHand,
614			&recordAttrs,
615			datap,
616			&record);
617		switch(crtn) {
618			case CSSM_OK:
619				if(interact) {
620					doIndent();
621					printValueAsString(recordAttrs.DataRecordType, recordTypeNames);
622					printf("\n");
623					indentIncr();
624					fetchParseRecord(dlDbHand, &recordAttrs, record, datap, dumpData);
625					indentDecr();
626				}
627				if(deleteAll && (recordAttrs.DataRecordType != DBBlobRelationID)) {
628					/* NEVER delete a DBBlob */
629					deleteRecord(dlDbHand, record, interact);
630				}
631				CSSM_DL_FreeUniqueRecord(dlDbHand, record);
632				break;		// and go again
633			case CSSMERR_DL_ENDOFDATA:
634				/* normal termination */
635				break;
636			default:
637				printError("DataGetNext", crtn);
638				break;
639		}
640		if(crtn != CSSM_OK) {
641			break;
642		}
643	}
644}
645
646int main(
647	int argc,
648	char **argv)
649{
650	int					arg;
651	char				*argp;
652	char				*dbFileName;
653	char				cmd;
654	CSSM_DL_DB_HANDLE	dlDbHand;
655	CSSM_BOOL			verbose = CSSM_FALSE;
656	CSSM_BOOL			quiet = CSSM_FALSE;
657	char				*certFile = NULL;
658	char				*keyFile = NULL;
659	CSSM_BOOL			interact = CSSM_TRUE;
660	CSSM_BOOL			dumpData = CSSM_FALSE;
661
662	/* should be cmd line opts */
663	CSSM_ALGORITHMS		keyAlg = CSSM_ALGID_RSA;
664	CSSM_BOOL			pemFormat = CSSM_FALSE;
665	CSSM_KEYBLOB_FORMAT	keyFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
666	CSSM_RETURN 		crtn = CSSM_OK;
667
668	if(argc < 3) {
669		usage(argv);
670	}
671	dbFileName = argv[1];
672	cmd = argv[2][0];
673
674	for(arg=3; arg<argc; arg++) {
675		argp = argv[arg];
676		switch(argp[0]) {
677			case 'v':
678				verbose = CSSM_TRUE;
679				break;
680			case 'q':
681				quiet = CSSM_TRUE;
682				break;
683			case 'R':
684				if(cmd == 'D') {
685					interact = CSSM_FALSE;
686				}
687				break;
688			case 'd':
689				dumpData = CSSM_TRUE;
690				break;
691			case 'c':
692				certFile = &argp[2];
693				break;
694			case 'k':
695				keyFile = &argp[2];
696				break;
697		    case 'h':
698		    default:
699				usage(argv);
700		}
701	}
702
703	dlDbHand.DLHandle = dlStartup();
704	if(dlDbHand.DLHandle == 0) {
705		exit(1);
706	}
707	if(cmd == 'i') {
708		crtn = importBadCert(dlDbHand.DLHandle, dbFileName, certFile,
709			keyFile, keyAlg, pemFormat, keyFormat, verbose);
710		goto done;
711	}
712	crtn = dbCreateOpen(dlDbHand.DLHandle, dbFileName,
713		CSSM_FALSE, CSSM_FALSE, NULL, &dlDbHand.DBHandle);
714	if(crtn) {
715		exit(1);
716	}
717	switch(cmd) {
718		case 'r':
719			dumpRelation(dlDbHand, &schemaInfoRelation, dumpData);
720			break;
721		case 'k':
722			dumpRelation(dlDbHand, &allKeysRelation, dumpData);
723			break;
724		case 'c':
725			dumpRelation(dlDbHand, &x509CertRecordRelation, dumpData);
726			break;
727		case 'a':
728			dumpAllRecords(dlDbHand, CSSM_FALSE, CSSM_TRUE, dumpData);
729			break;
730		case 'd':
731		case 'D':
732			dumpAllRecords(dlDbHand, CSSM_TRUE, interact, dumpData);
733			if(!interact) {
734				/* we ignored errors.... */
735				if(!quiet) {
736					printf("...DB %s wiped clean\n", dbFileName);
737				}
738			}
739			break;
740		default:
741			usage(argv);
742	}
743	CSSM_DL_DbClose(dlDbHand);
744done:
745	CSSM_ModuleDetach(dlDbHand.DLHandle);
746	return crtn;
747}
748