1/*
2 * dotMacTool.cpp - .mac TP exerciser
3 */
4
5#include <Security/Security.h>
6#include <Security/SecKeyPriv.h>
7#include <security_cdsa_utils/cuCdsaUtils.h>
8#include <security_cdsa_utils/cuFileIo.h>
9#include <security_cdsa_utils/cuPem.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <unistd.h>
13//#include <security_dotmac_tp/dotMacTp.h>
14#include <dotMacTp.h>
15#include <security_cdsa_utils/cuPrintCert.h>
16
17#include "keyPicker.h"
18#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
19
20#define USER_DEFAULT		"dmitchtest@mac.com"
21#define PWD_DEF				"123456"
22
23static void usage(char **argv)
24{
25	printf("usage: %s op [options]\n", argv[0]);
26	printf("Op:\n");
27	printf("    g                generate identity cert\n");
28	printf("    G                generate email signing cert\n");
29	printf("    e                generate email encrypting cert\n");
30	printf("    l                lookup cert (requires -f)\n");
31	printf("    L                lookup identity cert (via -u)\n");
32	printf("    M                lookup email signing cert (via -u)\n");
33	printf("    N                lookup encrypting cert (via -u)\n");
34	printf("Options:\n");
35	printf("   -g                Generate keypair\n");
36	printf("   -p                pick key pair from existing\n");
37	printf("   -u username       Default = %s\n", USER_DEFAULT);
38	printf("   -Z password       Specify password immediately\n");
39	printf("   -z                Use default password %s\n", PWD_DEF);
40	printf("   -k keychain       Source/destination of keys and certs\n");
41	printf("   -c filename       Write CSR to filename\n");
42	printf("   -C filename       Use existing CSR (no keygen)\n");
43	printf("   -f refIdFile      RefId file for cert lookup\n");
44	printf("   -n                Do NOT post the CSR to the .mac server\n");
45	printf("   -H hostname       Alternate .mac server host name (default %s)\n",
46					DOT_MAC_SIGN_HOST_NAME);
47	printf("   -o outFile        Write output cert or refId (if any) to outFile\n");
48	printf("   -r                Renew (default is new)\n");
49	printf("   -M                Pause for MallocDebug\n");
50	printf("   -q                Quiet\n");
51	printf("   -v                Verbose\n");
52	printf("   -h                Usage\n");
53	exit(1);
54}
55
56static CSSM_VERSION vers = {2, 0};
57
58static CSSM_API_MEMORY_FUNCS memFuncs = {
59	cuAppMalloc,
60	cuAppFree,
61	cuAppRealloc,
62 	cuAppCalloc,
63 	NULL
64 };
65
66static CSSM_TP_HANDLE dotMacStartup()
67{
68	CSSM_TP_HANDLE tpHand;
69	CSSM_RETURN crtn;
70
71	if(cuCssmStartup() == CSSM_FALSE) {
72		return 0;
73	}
74	crtn = CSSM_ModuleLoad(&gGuidAppleDotMacTP,
75		CSSM_KEY_HIERARCHY_NONE,
76		NULL,			// eventHandler
77		NULL);			// AppNotifyCallbackCtx
78	if(crtn) {
79		cuPrintError("CSSM_ModuleLoad(DotMacTP)", crtn);
80		return 0;
81	}
82	crtn = CSSM_ModuleAttach (&gGuidAppleDotMacTP,
83		&vers,
84		&memFuncs,				// memFuncs
85		0,						// SubserviceID
86		CSSM_SERVICE_TP,		// SubserviceFlags
87		0,						// AttachFlags
88		CSSM_KEY_HIERARCHY_NONE,
89		NULL,					// FunctionTable
90		0,						// NumFuncTable
91		NULL,					// reserved
92		&tpHand);
93	if(crtn) {
94		cuPrintError("CSSM_ModuleAttach(DotMacTP)", crtn);
95		return 0;
96	}
97	else {
98		return tpHand;
99	}
100}
101
102/* print text, safely */
103static void snDumpText(
104	const unsigned char *rcvBuf,
105	unsigned len)
106{
107	char *cp = (char *)rcvBuf;
108	unsigned i;
109	char c;
110
111	for(i=0; i<len; i++) {
112		c = *cp++;
113		if(c == '\0') {
114			break;
115		}
116		switch(c) {
117			case '\n':
118				printf("\\n\n");	// graphic and liternal newline
119				break;
120			case '\r':
121				printf("\\r\n");
122				break;
123			default:
124				if(isprint(c) && (c != '\n')) {
125					printf("%c", c);
126				}
127				else {
128					printf("<%02X>", ((unsigned)c) & 0xff);
129				}
130			break;
131		}
132
133	}
134}
135
136static OSStatus genKeyPair(
137	SecKeychainRef  kcRef,		// NULL means the default list
138	SecKeyRef		*pubKey,	// RETURNED
139	SecKeyRef		*privKey)   // RETURNED
140{
141	OSStatus ortn;
142
143	ortn = SecKeyCreatePair(kcRef,
144		DOT_MAC_KEY_ALG,
145		DOT_MAC_KEY_SIZE,
146		0,						// context handle
147		/* public key usage and attrs */
148		CSSM_KEYUSE_ANY,
149		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE,
150		/* private key usage and attrs */
151		CSSM_KEYUSE_ANY,
152		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE |
153			CSSM_KEYATTR_SENSITIVE,
154		NULL,					// initial access
155		pubKey,
156		privKey);
157	if(ortn) {
158		cssmPerror("SecKeyCreatePair", ortn);
159	}
160	return ortn;
161}
162
163/* Lookup via ReferenceID, obtained from CSSM_TP_SubmitCredRequest() */
164OSStatus doLookupViaRefId(
165	CSSM_TP_HANDLE tpHand,
166	unsigned char *refId,
167	unsigned refIdLen,
168	char *outFile,
169	bool verbose)
170{
171	CSSM_DATA refIdData = { refIdLen, refId };
172	sint32 EstimatedTime;
173	CSSM_BOOL ConfirmationRequired;
174	CSSM_TP_RESULT_SET_PTR resultSet = NULL;
175	CSSM_RETURN crtn;
176
177	crtn = CSSM_TP_RetrieveCredResult(tpHand, &refIdData, NULL,
178		&EstimatedTime, &ConfirmationRequired, &resultSet);
179	if(crtn) {
180		cssmPerror("CSSM_TP_RetrieveCredResult", crtn);
181		return crtn;
182	}
183	if(resultSet == NULL) {
184		printf("***CSSM_TP_RetrieveCredResult OK, but no result set\n");
185		return -1;
186	}
187	if(resultSet->NumberOfResults != 1) {
188		printf("***CSSM_TP_RetrieveCredResult OK, NumberOfResults (%u)\n",
189			(unsigned)resultSet->NumberOfResults);
190		return -1;
191	}
192	if(resultSet->Results == NULL) {
193		printf("***CSSM_TP_RetrieveCredResult OK, but empty result set\n");
194		return -1;
195	}
196	CSSM_DATA_PTR certData = (CSSM_DATA_PTR)resultSet->Results;
197
198	printf("...cert retrieval complete\n");
199	if(outFile) {
200		if(!writeFile(outFile, certData->Data, certData->Length)) {
201			printf("...%lu bytes of cert data written to %s\n",
202					certData->Length, outFile);
203		}
204		else {
205			printf("***Error writing cert to %s\n", outFile);
206			crtn = ioErr;
207		}
208	}
209	else if(verbose) {
210		unsigned char *der;
211		unsigned derLen;
212		if(pemDecode(certData->Data, certData->Length, &der, &derLen)) {
213			printf("***Error PEM decoding returned cert\n");
214		}
215		else {
216			printCert(der, derLen, CSSM_FALSE);
217			free(der);
218		}
219	}
220	return noErr;
221}
222
223/*
224* Lookup via user name, a greatly simplified form of CSSM_TP_SubmitCredRequest()
225*/
226OSStatus doLookupViaUserName(
227	CSSM_TP_HANDLE tpHand,
228	const CSSM_OID *opOid,
229	const char *userName,
230	const char *hostName,		// optional
231	char *outFile,
232	bool verbose)
233{
234	CSSM_APPLE_DOTMAC_TP_CERT_REQUEST	certReq;
235	CSSM_TP_AUTHORITY_ID				tpAuthority;
236	CSSM_TP_AUTHORITY_ID				*tpAuthPtr = NULL;
237	CSSM_NET_ADDRESS					tpNetAddrs;
238	CSSM_TP_REQUEST_SET					reqSet;
239	CSSM_FIELD							policyField;
240	CSSM_DATA							certData = {0, NULL};
241	sint32								estTime;
242	CSSM_TP_CALLERAUTH_CONTEXT			callerAuth;
243
244	memset(&certReq, 0, sizeof(certReq));
245	certReq.userName.Data = (uint8 *)userName;
246	certReq.userName.Length = strlen(userName);
247	if(hostName != NULL) {
248		tpAuthority.AuthorityCert = NULL;
249		tpAuthority.AuthorityLocation = &tpNetAddrs;
250		tpNetAddrs.AddressType = CSSM_ADDR_NAME;
251		tpNetAddrs.Address.Data = (uint8 *)hostName;
252		tpNetAddrs.Address.Length = strlen(hostName);
253		tpAuthPtr = &tpAuthority;
254	};
255
256	certReq.version = CSSM_DOT_MAC_TP_REQ_VERSION;
257	reqSet.NumberOfRequests = 1;
258	reqSet.Requests = &certReq;
259	policyField.FieldOid = *opOid;
260	policyField.FieldValue.Data = NULL;
261	policyField.FieldValue.Length = 0;
262	memset(&callerAuth, 0, sizeof(callerAuth));
263	callerAuth.Policy.NumberOfPolicyIds = 1;
264	callerAuth.Policy.PolicyIds = &policyField;
265
266	CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest (tpHand,
267		tpAuthPtr,
268		CSSM_TP_AUTHORITY_REQUEST_CERTLOOKUP,
269		&reqSet,	// const CSSM_TP_REQUEST_SET *RequestInput,
270		&callerAuth,
271		&estTime,   // sint32 *EstimatedTime,
272		&certData);	// CSSM_DATA_PTR ReferenceIdentifier
273
274	if(crtn) {
275		cssmPerror("CSSM_TP_SubmitCredRequest(lookup)", crtn);
276		return crtn;
277	}
278
279	printf("...cert lookup complete\n");
280	if(outFile) {
281		if(!writeFile(outFile, certData.Data, certData.Length)) {
282			printf("...%lu bytes of cert data written to %s\n",
283					certData.Length, outFile);
284		}
285		else {
286			printf("***Error writing cert to %s\n", outFile);
287			crtn = ioErr;
288		}
289	}
290	if(verbose) {
291		/* This one returns the cert in DER format, we might revisit that */
292		printCert(certData.Data, certData.Length, CSSM_FALSE);
293	}
294	return crtn;
295}
296
297#define FULL_EMAIL_ADDRESS	1
298
299int main(int argc, char **argv)
300{
301	CSSM_RETURN							crtn;
302	CSSM_TP_AUTHORITY_ID				tpAuthority;
303	CSSM_TP_AUTHORITY_ID				*tpAuthPtr = NULL;
304	CSSM_NET_ADDRESS					tpNetAddrs;
305	CSSM_APPLE_DOTMAC_TP_CERT_REQUEST	certReq;
306	CSSM_TP_REQUEST_SET					reqSet;
307	CSSM_CSP_HANDLE						cspHand = 0;
308	CSSM_X509_TYPE_VALUE_PAIR			tvp;
309	char								pwdBuf[1000];
310	CSSM_TP_CALLERAUTH_CONTEXT			callerAuth;
311	sint32								estTime;
312	CSSM_DATA							refId = {0, NULL};
313	OSStatus							ortn;
314	SecKeyRef							pubKeyRef = NULL;
315	SecKeyRef							privKeyRef = NULL;
316	const CSSM_KEY						*privKey = NULL;
317	const CSSM_KEY						*pubKey = NULL;
318	SecKeychainRef						kcRef = NULL;
319	CSSM_FIELD							policyField;
320
321	/* user-spec'd variables */
322	bool genKeys = false;
323	bool pickKeys = false;
324	char *keychainName = NULL;
325	char *csrOutName = NULL;
326	char *csrInName = NULL;
327	const char *userName = USER_DEFAULT;
328	char *password = NULL;
329	char *hostName = NULL;
330	bool doNotPost = false;
331	bool doRenew = false;
332	const CSSM_OID *opOid = NULL;
333	char *outFile = NULL;
334	bool quiet = false;
335	bool verbose = false;
336	bool lookupViaRefId = false;
337	bool lookupViaUserName = false;
338	char *refIdFile = NULL;
339	bool doPause = false;
340
341	if(argc < 2) {
342		usage(argv);
343	}
344	switch(argv[1][0]) {
345		case 'L':
346			lookupViaUserName = true;
347			/* drop thru */
348		case 'g':
349			opOid = &CSSMOID_DOTMAC_CERT_REQ_IDENTITY;
350			break;
351
352		case 'M':
353			lookupViaUserName = true;
354			/* drop thru */
355		case 'G':
356			opOid = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_SIGN;
357			break;
358
359		case 'N':
360			lookupViaUserName = true;
361			/* drop thru */
362		case 'e':
363			opOid = &CSSMOID_DOTMAC_CERT_REQ_EMAIL_ENCRYPT;
364			break;
365
366		case 'l':
367			lookupViaRefId = true;
368			break;
369		default:
370			usage(argv);
371	}
372
373	extern char *optarg;
374	extern int optind;
375	optind = 2;
376	int arg;
377	while ((arg = getopt(argc, argv, "gpk:c:u:Z:H:nzrC:o:hf:Mqv")) != -1) {
378		switch (arg) {
379			case 'g':
380				genKeys = true;
381				break;
382			case 'p':
383				pickKeys = true;
384				break;
385			case 'u':
386				userName = optarg;
387				break;
388			case 'Z':
389				password = optarg;
390				break;
391			case 'z':
392				password = (char *)PWD_DEF;
393				break;
394			case 'k':
395				keychainName = optarg;
396				break;
397			case 'c':
398				csrOutName = optarg;
399				break;
400			case 'C':
401				csrInName = optarg;
402				break;
403			case 'H':
404				hostName = optarg;
405				break;
406			case 'n':
407				doNotPost = true;
408				break;
409			case 'r':
410				doRenew = true;
411				break;
412			case 'o':
413				outFile = optarg;
414				break;
415			case 'f':
416				refIdFile = optarg;
417				break;
418			case 'M':
419				doPause = true;
420				break;
421			case 'v':
422				verbose = true;
423				break;
424			case 'q':
425				quiet = true;
426				break;
427			case 'h':
428				usage(argv);
429		}
430	}
431
432	if(doPause) {
433		fpurge(stdin);
434		printf("Pausing for MallocDebug attach; CR to continue: ");
435		getchar();
436	}
437
438	CSSM_TP_HANDLE tpHand = dotMacStartup();
439	if(tpHand == 0) {
440		printf("Error attaching to the .mac TP. Check your MDS file.\n");
441		exit(1);
442	}
443
444	if(lookupViaRefId) {
445		if(refIdFile == NULL) {
446			printf("I need a refIdFile to do a lookup.\n");
447			usage(argv);
448		}
449		unsigned char *refId;
450		unsigned refIdLen;
451		int irtn = readFile(refIdFile, &refId, &refIdLen);
452		if(irtn) {
453			printf("***Error reading refId from %s. Aborting.\n", refIdFile);
454			exit(1);
455		}
456		ortn = doLookupViaRefId(tpHand, refId, refIdLen, outFile, verbose);
457		free(refId);
458		goto done;
459	}
460	if(lookupViaUserName) {
461		ortn = doLookupViaUserName(tpHand, opOid, userName, hostName, outFile, verbose);
462		goto done;
463	}
464	if(!pickKeys && !genKeys && (csrInName == NULL)) {
465		printf("***You must specify either the -p (pick keys) or -g (generate keys)"
466			" arguments, or provide a CSR (-C).\n");
467		exit(1);
468	}
469
470	memset(&certReq, 0, sizeof(certReq));
471
472	/* all of the subsequest argument are superfluous for lookupViaUserName, except for
473	 * the user name itself, which has a default */
474	if(keychainName != NULL) {
475		/* pick a keychain (optional) */
476		ortn = SecKeychainOpen(keychainName, &kcRef);
477		if(ortn) {
478			cssmPerror("SecKeychainOpen", ortn);
479			exit(1);
480		}
481
482		/* make sure it's there since a successful SecKeychainOpen proves nothing */
483		SecKeychainStatus kcStat;
484		ortn = SecKeychainGetStatus(kcRef, &kcStat);
485		if(ortn) {
486			cssmPerror("SecKeychainGetStatus", ortn);
487			goto done;
488		}
489	}
490
491	if(password == NULL) {
492		const char *pwdp = getpass("Enter .mac password: ");
493		if(pwdp == NULL) {
494			printf("Aboerting.\n");
495			ortn = paramErr;
496			goto done;
497		}
498		memmove(pwdBuf, pwdp, strlen(pwdp) + 1);
499		password = pwdBuf;
500	}
501	certReq.password.Data = (uint8 *)password;
502	certReq.password.Length = strlen(password);
503	certReq.userName.Data = (uint8 *)userName;
504	certReq.userName.Length = strlen(userName);
505
506	if(csrInName) {
507		unsigned len;
508		if(readFile(csrInName, &certReq.csr.Data, &len)) {
509			printf("***Error reading CSR from %s. Aborting.\n", csrInName);
510			exit(1);
511		}
512		certReq.csr.Length = len;
513		certReq.flags |= CSSM_DOTMAC_TP_EXIST_CSR;
514	}
515	else {
516		/*
517		 * All the stuff the TP needs to actually generate a CSR.
518		 *
519		 * Get a key pair, somehow.
520		 */
521		if(genKeys) {
522			ortn = genKeyPair(kcRef, &pubKeyRef, &privKeyRef);
523		}
524		else {
525			ortn = keyPicker(kcRef, &pubKeyRef, &privKeyRef);
526		}
527		if(ortn) {
528			printf("Can't proceed without a keypair. Aborting.\n");
529			exit(1);
530		}
531		ortn = SecKeyGetCSSMKey(pubKeyRef, &pubKey);
532		if(ortn) {
533			cssmPerror("SecKeyGetCSSMKey", ortn);
534			goto done;
535		}
536		ortn = SecKeyGetCSSMKey(privKeyRef, &privKey);
537		if(ortn) {
538			cssmPerror("SecKeyGetCSSMKey", ortn);
539			goto done;
540		}
541		ortn = SecKeyGetCSPHandle(privKeyRef, &cspHand);
542		if(ortn) {
543			cssmPerror("SecKeyGetCSPHandle", ortn);
544			goto done;
545		}
546
547		/* CSSM_X509_TYPE_VALUE_PAIR - one pair for now */
548		// tvp.type = CSSMOID_EmailAddress;
549		tvp.type = CSSMOID_CommonName;
550		tvp.valueType = BER_TAG_PRINTABLE_STRING;
551		#if FULL_EMAIL_ADDRESS
552		{
553			unsigned nameLen = strlen(userName);
554			tvp.value.Data = (uint8 *)malloc(nameLen + strlen("@mac.com") + 1);
555			strcpy((char *)tvp.value.Data, userName);
556			strcpy((char *)tvp.value.Data + nameLen, "@mac.com");
557			tvp.value.Length = strlen((char *)tvp.value.Data);
558		}
559		#else
560		tvp.value.Data = (uint8 *)userName;
561		tvp.value.Length = strlen(userName);
562		#endif
563	}
564	/* set up args for CSSM_TP_SubmitCredRequest */
565	if(hostName != NULL) {
566		tpAuthority.AuthorityCert = NULL;
567		tpAuthority.AuthorityLocation = &tpNetAddrs;
568		tpNetAddrs.AddressType = CSSM_ADDR_NAME;
569		tpNetAddrs.Address.Data = (uint8 *)hostName;
570		tpNetAddrs.Address.Length = strlen(hostName);
571		tpAuthPtr = &tpAuthority;
572	};
573
574	certReq.version = CSSM_DOT_MAC_TP_REQ_VERSION;
575	if(!csrInName) {
576		certReq.cspHand = cspHand;
577		certReq.clHand = cuClStartup();
578		certReq.numTypeValuePairs = 1;
579		certReq.typeValuePairs = &tvp;
580		certReq.publicKey = (CSSM_KEY_PTR)pubKey;
581		certReq.privateKey = (CSSM_KEY_PTR)privKey;
582	}
583	if(doNotPost) {
584		certReq.flags |= CSSM_DOTMAC_TP_DO_NOT_POST;
585	}
586	if(csrOutName != NULL) {
587		certReq.flags |= CSSM_DOTMAC_TP_RETURN_CSR;
588	}
589	if(doRenew) {
590		certReq.flags |= CSSM_DOTMAC_TP_SIGN_RENEW;
591	}
592
593	reqSet.NumberOfRequests = 1;
594	reqSet.Requests = &certReq;
595
596	policyField.FieldOid = *opOid;
597	policyField.FieldValue.Data = NULL;
598	policyField.FieldValue.Length = 0;
599	memset(&callerAuth, 0, sizeof(callerAuth));
600	callerAuth.Policy.NumberOfPolicyIds = 1;
601	callerAuth.Policy.PolicyIds = &policyField;
602	if(!csrInName) {
603		ortn = SecKeyGetCredentials(privKeyRef,
604			CSSM_ACL_AUTHORIZATION_SIGN,
605			kSecCredentialTypeDefault,
606			const_cast<const CSSM_ACCESS_CREDENTIALS **>(&callerAuth.CallerCredentials));
607		if(ortn) {
608			cssmPerror("SecKeyGetCredentials", crtn);
609			goto done;
610		}
611	}
612
613	crtn = CSSM_TP_SubmitCredRequest (tpHand,
614		tpAuthPtr,
615		CSSM_TP_AUTHORITY_REQUEST_CERTISSUE,	// CSSM_TP_AUTHORITY_REQUEST_TYPE
616		&reqSet,	// const CSSM_TP_REQUEST_SET *RequestInput,
617		&callerAuth,
618		&estTime,   // sint32 *EstimatedTime,
619		&refId);	// CSSM_DATA_PTR ReferenceIdentifier
620	switch(crtn) {
621		case CSSM_OK:
622		case CSSMERR_APPLE_DOTMAC_REQ_QUEUED:
623		{
624			/*
625			 * refId should be a cert or RefId
626			 */
627			const char *itemType = "Cert";
628			const char *statStr = "OK";
629			if(crtn != CSSM_OK) {
630				itemType = "RefId";
631				statStr = "Cert";
632			}
633			if((refId.Data == NULL) || (refId.Length == 0)) {
634				printf("CSSM_TP_SubmitCredRequest returned %s but no data\n", statStr);
635				break;
636			}
637			if(crtn == CSSM_OK) {
638				printf("...cert acquisition complete\n");
639			}
640			else {
641				printf("...Cert request QUEUED\n");
642			}
643			if(outFile) {
644				if(!writeFile(outFile, refId.Data, refId.Length)) {
645					if(!quiet) {
646						printf("...%lu bytes of %s written to %s\n",
647							refId.Length, itemType, outFile);
648					}
649				}
650				else {
651					printf("***Error writing %s to %s\n", itemType, outFile);
652					crtn = ioErr;
653				}
654			}
655			else if(verbose) {
656				if(crtn == CSSM_OK) {
657					unsigned char *der;
658					unsigned derLen;
659					if(pemDecode(refId.Data, refId.Length, &der, &derLen)) {
660						printf("***Error PEM decoding returned cert\n");
661					}
662					else {
663						printCert(der, derLen, CSSM_FALSE);
664						free(der);
665					}
666				}
667				else {
668					printf("RefId data:\n");
669					snDumpText(refId.Data, refId.Length);
670				}
671			}
672			break;
673		}
674		case CSSMERR_APPLE_DOTMAC_REQ_REDIRECT:
675			if((refId.Data == NULL) || (refId.Length == 0)) {
676				printf("CSSM_TP_SubmitCredRequest returned REDIRECT but no data\n");
677				break;
678			}
679			printf("...cert acquisition : REDIRECTED to: ");
680			snDumpText(refId.Data, refId.Length);
681			printf("\n");
682			break;
683		default:
684			cssmPerror("CSSM_TP_SubmitCredRequest", crtn);
685			break;
686	}
687	if(csrOutName) {
688		if((certReq.csr.Data == NULL) || (certReq.csr.Length == 0)) {
689			printf("***Asked for CSR but didn't get one\n");
690			ortn = paramErr;
691			goto done;
692		}
693		if(writeFile(csrOutName, certReq.csr.Data, certReq.csr.Length)) {
694			printf("***Error writing CSR to %s.\n", csrOutName);
695		}
696		else {
697			printf("...%lu bytes written as CSR to %s\n", certReq.csr.Length, csrOutName);
698		}
699	}
700done:
701	/* cleanup */
702	CSSM_ModuleDetach(tpHand);
703	if(certReq.clHand) {
704		CSSM_ModuleDetach(certReq.clHand);
705	}
706	if(kcRef) {
707		CFRelease(kcRef);
708	}
709	if(csrInName) {
710		free(certReq.csr.Data);
711	}
712	if(privKeyRef) {
713		CFRelease(privKeyRef);
714	}
715	if(pubKeyRef) {
716		CFRelease(pubKeyRef);
717	}
718	if(refId.Data) {
719		cuAppFree(refId.Data, NULL);
720	}
721	if(doPause) {
722		fpurge(stdin);
723		printf("Pausing for MallocDebug measurement; CR to continue: ");
724		getchar();
725	}
726
727	return ortn;
728}
729