1/*
2 * kcExport.cpp - export keychain items using SecKeychainItemExport
3 */
4
5#include <Security/Security.h>
6#include <Security/SecImportExport.h>
7#include <security_cdsa_utils/cuFileIo.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12#include <utilLib/common.h>
13#include <clAppUtils/identPicker.h>
14
15static void usage(char **argv)
16{
17    printf("Usage: %s infile [option ...]\n", argv[0]);
18	printf("Options:\n");
19	printf("   -k keychain     Keychain to import into\n");
20	printf("   -z passphrase   For PKCS12 and wrapped keys only\n");
21	printf("   -w              Private keys are wrapped\n");
22	printf("   -d              Display Imported Items\n");
23	printf("   -i              Interactive: displays items before possible import\n");
24	printf("   -v              Verbose display\n");
25	printf("   -l              Loop & Pause for MallocDebug\n");
26	printf("   -q              Quiet\n");
27	printf("Import type/format options:\n");
28	printf("   -t <type>       type = pub|priv|session|cert|agg\n");
29	printf("   -f <format>     format = openssl|openssh1|openssh2|bsafe|\n"
30	       "                            raw|pkcs7|pkcs8|pkcs12|netscape|pemseq\n");
31	printf("Private key options:\n");
32	printf("   -a appPath      Add appPath to list of trusted apps in private key's ACL\n");
33	printf("   -e              Allow private keys to be extractable in the clear\n");
34	printf("   -n              Private keys have *NO* ACL\n");
35	printf("   -s              Private keys can sign (only)\n");
36	printf("Secure passphrase options:\n");
37	printf("   -Z              Get secure passphrase\n");
38	printf("   -L title\n");
39	printf("   -p prompt\n");
40	printf("Verification options:\n");
41	printf("   -C numCerts     Verify number of certificates\n");
42	printf("   -K numKeys      Verify number of keys\n");
43	printf("   -I numIdents    Verify number of identities\n");
44	printf("   -F rtnFormat    Returned format = "
45					"openssl|openssh1|openssh2|bsafe|raw|pkcs7|pkcs12|netscape|pemseq\n");
46	printf("   -T <type>       Returned type = pub|priv|session|cert|agg\n");
47	printf("   -m              Set ImportOnlyOneKey bit\n");
48	printf("   -M              Set ImportOnlyOneKey bit and expect errSecMultiplePrivKeys\n");
49	exit(1);
50}
51
52const char *secExtFormatStr(
53	SecExternalFormat format)
54{
55	switch(format) {
56		case kSecFormatUnknown:			return "kSecFormatUnknown";
57		case kSecFormatOpenSSL:			return "kSecFormatOpenSSL";
58		case kSecFormatSSH:				return "kSecFormatSSH";
59		case kSecFormatBSAFE:			return "kSecFormatBSAFE";
60		case kSecFormatRawKey:			return "kSecFormatRawKey";
61		case kSecFormatWrappedPKCS8:	return "kSecFormatWrappedPKCS8";
62		case kSecFormatWrappedOpenSSL:  return "kSecFormatWrappedOpenSSL";
63		case kSecFormatWrappedSSH:		return "kSecFormatWrappedSSH";
64		case kSecFormatWrappedLSH:		return "kSecFormatWrappedLSH";
65		case kSecFormatX509Cert:		return "kSecFormatX509Cert";
66		case kSecFormatPEMSequence:		return "kSecFormatPEMSequence";
67		case kSecFormatPKCS7:			return "kSecFormatPKCS7";
68		case kSecFormatPKCS12:			return "kSecFormatPKCS12";
69		case kSecFormatNetscapeCertSequence:  return "kSecFormatNetscapeCertSequence";
70		case kSecFormatSSHv2:			return "kSecFormatSSHv2";
71		default:						return "UNKNOWN FORMAT ENUM";
72	}
73}
74
75const char *secExtItemTypeStr(
76	SecExternalItemType itemType)
77{
78	switch(itemType) {
79		case kSecItemTypeUnknown:		return "kSecItemTypeUnknown";
80		case kSecItemTypePrivateKey:	return "kSecItemTypePrivateKey";
81		case kSecItemTypePublicKey:		return "kSecItemTypePublicKey";
82		case kSecItemTypeSessionKey:	return "kSecItemTypeSessionKey";
83		case kSecItemTypeCertificate:   return "kSecItemTypeCertificate";
84		case kSecItemTypeAggregate:		return "kSecItemTypeAggregate";
85		default:						return "UNKNOWN ITEM TYPE ENUM";
86	}
87}
88
89static OSStatus processItem(
90	SecKeychainRef keychain,
91	SecKeychainItemRef item,
92	int *numCerts,				// IN/OUT
93	int *numKeys,
94	int *numIds,
95	bool interactive,			// unimplemented
96	bool verbose)
97{
98	char *kcName = NULL;
99	CFTypeID itemType = CFGetTypeID(item);
100	if(itemType == SecIdentityGetTypeID()) {
101		(*numIds)++;
102		if(verbose) {
103			/* identities don't have keychains, only their components do */
104			SecCertificateRef certRef = NULL;
105			OSStatus ortn;
106			ortn = SecIdentityCopyCertificate((SecIdentityRef)item, &certRef);
107			if(ortn) {
108				cssmPerror("SecIdentityCopyCertificate", ortn);
109				return ortn;
110			}
111			kcName = kcItemKcFileName((SecKeychainItemRef)certRef);
112			printf(" identity : cert keychain name %s\n", kcName ? kcName : "<none>");
113			CFRelease(certRef);
114
115			SecKeyRef keyRef = NULL;
116			ortn = SecIdentityCopyPrivateKey((SecIdentityRef)item, &keyRef);
117			if(ortn) {
118				cssmPerror("SecIdentityCopyPrivateKey", ortn);
119				return ortn;
120			}
121			free(kcName);
122			kcName = kcItemKcFileName((SecKeychainItemRef)keyRef);
123			printf(" identity : key  keychain name %s\n", kcName ? kcName : "<none>");
124			CFRelease(keyRef);
125		}
126	}
127	else if(itemType == SecCertificateGetTypeID()) {
128		(*numCerts)++;
129		if(verbose) {
130			kcName = kcItemKcFileName(item);
131			printf(" cert     : keychain name %s\n", kcName ? kcName : "<none>");
132		}
133	}
134	else if(itemType == SecKeyGetTypeID()) {
135		(*numKeys)++;
136		if(verbose) {
137			kcName = kcItemKcFileName(item);
138			printf(" key      : keychain name %s\n", kcName ? kcName : "<none>");
139		}
140	}
141	/* FIX display attr info, at least names, eventually */
142	else {
143		printf("***Unknown type returned from SecKeychainItemImport()\n");
144		return errSecUnknownFormat;
145	}
146	if(kcName) {
147		free(kcName);
148	}
149	return noErr;
150}
151
152static OSStatus	processItems(
153	SecKeychainRef keychain,
154	CFArrayRef outArray,
155	int expectNumCerts,				// -1 means don't check
156	int expectNumKeys,
157	int expectNumIds,
158	bool interactive,
159	bool displayItems,
160	bool verbose)
161{
162	int numCerts = 0;
163	int numKeys = 0;
164	int numIds = 0;
165	OSStatus ortn;
166
167	CFIndex numItems = CFArrayGetCount(outArray);
168	for(CFIndex dex=0; dex<numItems; dex++) {
169		ortn = processItem(keychain,
170			(SecKeychainItemRef)CFArrayGetValueAtIndex(outArray, dex),
171			&numCerts, &numKeys, &numIds, interactive, verbose);
172		if(ortn) {
173			break;
174		}
175	}
176	if(ortn) {
177		return ortn;
178	}
179
180	if(displayItems) {
181		printf("Certs  found : %d\n", numCerts);
182		printf("Keys   found : %d\n", numKeys);
183		printf("Idents found : %d\n", numIds);
184	}
185	if(expectNumCerts >= 0) {
186		if(expectNumCerts != numCerts) {
187			printf("***Expected %d certs, got %d\n",
188				expectNumCerts, numCerts);
189			ortn = -1;
190		}
191	}
192	if(expectNumKeys >= 0) {
193		if(expectNumKeys != numKeys) {
194			printf("***Expected %d keys, got %d\n",
195				expectNumKeys, numKeys);
196			ortn = -1;
197		}
198	}
199	if(expectNumIds >= 0) {
200		if(expectNumIds != numIds) {
201			printf("***Expected %d certs, got %d\n",
202				expectNumIds, numIds);
203			ortn = -1;
204		}
205	}
206	return ortn;
207}
208
209/*
210 * Parse cmd-line format specifier into an SecExternalFormat.
211 * Returns true if it works.
212 */
213static bool formatFromString(
214	const char *formStr,
215	SecExternalFormat *externFormat)
216{
217	if(!strcmp("openssl", formStr)) {
218		*externFormat = kSecFormatOpenSSL;
219	}
220	else if(!strcmp("openssh1", formStr)) {
221		*externFormat = kSecFormatSSH;
222	}
223	else if(!strcmp("openssh2", formStr)) {
224		*externFormat = kSecFormatSSHv2;
225	}
226	else if(!strcmp("bsafe", formStr)) {
227		*externFormat = kSecFormatBSAFE;
228	}
229	else if(!strcmp("raw", formStr)) {
230		*externFormat = kSecFormatRawKey;
231	}
232	else if(!strcmp("pkcs7", formStr)) {
233		*externFormat = kSecFormatPKCS7;
234	}
235	else if(!strcmp("pkcs8", formStr)) {
236		*externFormat = kSecFormatWrappedPKCS8;
237	}
238	else if(!strcmp("pkcs12", formStr)) {
239		*externFormat = kSecFormatPKCS12;
240	}
241	else if(!strcmp("netscape", formStr)) {
242		*externFormat = kSecFormatNetscapeCertSequence;
243	}
244	else if(!strcmp("pemseq", formStr)) {
245		*externFormat = kSecFormatPEMSequence;
246	}
247	else {
248		return false;
249	}
250	return true;
251}
252
253 /*
254 * Parse cmd-line type specifier into an SecExternalItemType.
255 * Returns true if it works.
256 */
257static bool itemTypeFromString(
258	const char *typeStr,
259	SecExternalItemType *itemType)
260{
261	if(!strcmp("pub", typeStr)) {
262		*itemType = kSecItemTypePublicKey;
263	}
264	else if(!strcmp("priv", typeStr)) {
265		*itemType = kSecItemTypePrivateKey;
266	}
267	else if(!strcmp("session", typeStr)) {
268		*itemType = kSecItemTypeSessionKey;
269	}
270	else if(!strcmp("cert", typeStr)) {
271		*itemType = kSecItemTypeCertificate;
272	}
273	else if(!strcmp("agg", typeStr)) {
274		*itemType = kSecItemTypeAggregate;
275	}
276	else {
277		return false;
278	}
279	return true;
280}
281
282int main(int argc, char **argv)
283{
284	if(argc < 2) {
285		usage(argv);
286	}
287
288	const char *inFileName = argv[1];
289
290	extern int optind;
291	extern char *optarg;
292	int arg;
293	optind = 2;
294	int ourRtn = 0;
295	SecAccessRef accessRef = NULL;
296	OSStatus ortn;
297
298	/* optional args */
299	const char *keychainName = NULL;
300	CFStringRef passphrase = NULL;
301	bool interactive = false;
302	bool securePassphrase = false;
303	SecExternalFormat externFormat = kSecFormatUnknown;
304	SecExternalItemType itemType = kSecItemTypeUnknown;
305	bool doWrap = false;
306	bool allowClearExtract = false;
307	int expectNumCerts = -1;		// >=0 means verify per user spec
308	int expectNumKeys = -1;
309	int expectNumIds = -1;
310	bool processOutput = false;
311	bool displayItems = false;
312	SecExternalFormat expectFormat = kSecFormatUnknown; // otherwise verify
313	SecExternalItemType expectItemType = kSecItemTypeUnknown;   // ditto
314	bool quiet = false;
315	bool noACL = false;
316	char *alertTitle = NULL;
317	char *alertPrompt = NULL;
318	bool setAllowOnlyOne = false;
319	bool expectMultiKeysError = false;
320	CFMutableArrayRef trustedAppList = NULL;
321	bool loopPause = false;
322	bool signOnly = false;
323	bool verbose = false;
324
325	while ((arg = getopt(argc, argv, "k:iZz:wet:f:C:K:I:F:T:qnL:p:mMa:dlsv")) != -1) {
326		switch (arg) {
327			case 'k':
328				keychainName = optarg;
329				break;
330			case 'i':
331				interactive = true;
332				processOutput = true;
333				break;
334			case 'Z':
335				securePassphrase = true;
336				break;
337			case 'z':
338				passphrase = CFStringCreateWithCString(NULL, optarg,
339					kCFStringEncodingASCII);
340				break;
341			case 'w':
342				doWrap = true;
343				break;
344			case 'e':
345				allowClearExtract = true;
346				break;
347			case 't':
348				if(!itemTypeFromString(optarg, &itemType)) {
349					usage(argv);
350				}
351				break;
352			case 'T':
353				if(!itemTypeFromString(optarg, &expectItemType)) {
354					usage(argv);
355				}
356				break;
357			case 'f':
358				if(!formatFromString(optarg, &externFormat)) {
359					usage(argv);
360				}
361				break;
362			case 'F':
363				if(!formatFromString(optarg, &expectFormat)) {
364					usage(argv);
365				}
366				break;
367			case 'C':
368				expectNumCerts = atoi(optarg);
369				processOutput = true;
370				break;
371			case 'K':
372				expectNumKeys = atoi(optarg);
373				processOutput = true;
374				break;
375			case 'I':
376				expectNumIds = atoi(optarg);
377				processOutput = true;
378				break;
379			case 'd':
380				displayItems = true;
381				processOutput = true;
382				break;
383			case 'q':
384				quiet = true;
385				break;
386			case 'n':
387				noACL = true;
388				break;
389			case 'L':
390				alertTitle = optarg;
391				break;
392			case 'p':
393				alertPrompt = optarg;
394				break;
395			case 'm':
396				setAllowOnlyOne = true;
397				break;
398			case 'M':
399				setAllowOnlyOne = true;
400				expectMultiKeysError = true;
401				break;
402			case 'a':
403			{
404				if(trustedAppList == NULL) {
405					trustedAppList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
406				}
407				SecTrustedApplicationRef appRef;
408				ortn = SecTrustedApplicationCreateFromPath(optarg, &appRef);
409				if(ortn) {
410					cssmPerror("SecTrustedApplicationCreateFromPath", ortn);
411					exit(1);
412				}
413				CFArrayAppendValue(trustedAppList, appRef);
414				break;
415			}
416			case 's':
417				signOnly = true;
418				break;
419			case 'l':
420				loopPause = true;
421				break;
422			case 'v':
423				verbose = true;
424				break;
425			default:
426			case '?':
427				usage(argv);
428		}
429	}
430	if(optind != argc) {
431		/* getopt does not return '?' */
432		usage(argv);
433	}
434	if(doWrap) {
435		switch(externFormat) {
436			case kSecFormatOpenSSL:
437			case kSecFormatUnknown:		// i.e., use default
438				externFormat = kSecFormatWrappedOpenSSL;
439				break;
440			case kSecFormatSSH:
441				externFormat = kSecFormatWrappedSSH;
442				break;
443			case kSecFormatSSHv2:
444				/* there is no wrappedSSHv2 */
445				externFormat = kSecFormatWrappedOpenSSL;
446				break;
447			case kSecFormatWrappedPKCS8:
448				/* proceed */
449				break;
450			default:
451				printf("Don't know how to wrap in specified format/type.\n");
452				exit(1);
453		}
454	}
455
456	CFArrayRef outArray = NULL;
457	CFArrayRef *outArrayP = NULL;
458	if(processOutput) {
459		outArrayP = &outArray;
460	}
461
462	SecKeychainRef kcRef = NULL;
463	if(keychainName) {
464		OSStatus ortn = SecKeychainOpen(keychainName, &kcRef);
465		if(ortn) {
466			cssmPerror("SecKeychainOpen", ortn);
467			exit(1);
468		}
469		/* why is this failing later */
470		CSSM_DL_DB_HANDLE dlDbHandle;
471		ortn = SecKeychainGetDLDBHandle(kcRef, &dlDbHandle);
472		if(ortn) {
473			cssmPerror("SecKeychainGetDLDBHandle", ortn);
474			exit(1);
475		}
476
477	}
478
479	unsigned char *inFile = NULL;
480	unsigned inFileLen = 0;
481	if(readFile(inFileName, &inFile, &inFileLen)) {
482		printf("***Error reading input file %s. Aborting.\n", inFileName);
483		exit(1);
484	}
485	CFDataRef inFileRef = CFDataCreate(NULL, inFile, inFileLen);
486	CFStringRef fileNameStr = CFStringCreateWithCString(NULL, inFileName,
487			kCFStringEncodingASCII);
488
489loopTop:
490	SecKeyImportExportParameters keyParams;
491	SecKeyImportExportParameters *keyParamPtr = NULL;
492
493	if(passphrase || securePassphrase || allowClearExtract || noACL ||
494					 setAllowOnlyOne || trustedAppList || signOnly) {
495		keyParamPtr = &keyParams;
496		memset(&keyParams, 0, sizeof(keyParams));
497		if(securePassphrase) {
498			/* give this precedence */
499			keyParams.flags |= kSecKeySecurePassphrase;
500			if(alertTitle) {
501				keyParams.alertTitle =
502					CFStringCreateWithCString(NULL, alertTitle, kCFStringEncodingASCII);
503			}
504			if(alertPrompt) {
505				keyParams.alertPrompt =
506					CFStringCreateWithCString(NULL, alertPrompt, kCFStringEncodingASCII);
507			}
508		}
509		else if(passphrase) {
510			keyParams.passphrase = passphrase;
511		}
512		if(noACL) {
513			keyParams.flags |= kSecKeyNoAccessControl;
514		}
515		if(setAllowOnlyOne) {
516			keyParams.flags |= kSecKeyImportOnlyOne;
517		}
518		keyParams.keyAttributes = ( CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE);
519		if(!allowClearExtract) {
520			keyParams.keyAttributes |= CSSM_KEYATTR_SENSITIVE;
521		}
522		if(kcRef) {
523			keyParams.keyAttributes |= CSSM_KEYATTR_PERMANENT;
524		}
525		if(signOnly) {
526			keyParams.keyUsage = CSSM_KEYUSE_SIGN;
527		}
528		else {
529			keyParams.keyUsage = CSSM_KEYUSE_ANY;
530		}
531		if(trustedAppList) {
532			ortn = SecAccessCreate(CFSTR("Imported Private Key"), trustedAppList, &accessRef);
533			if(ortn) {
534				cssmPerror("SecAccessCreate", ortn);
535				exit(1);
536			}
537			keyParams.accessRef = accessRef;
538		}
539		/* TBD other stuff - usage? Other? */
540	}
541
542	/* GO */
543	ortn = SecKeychainItemImport(inFileRef,
544		fileNameStr,
545		&externFormat,
546		&itemType,
547		0,			// flags
548		keyParamPtr,
549		kcRef,
550		outArrayP);
551	ourRtn = 0;
552	if(ortn) {
553		if(expectMultiKeysError && (ortn == errSecMultiplePrivKeys)) {
554			if(!quiet) {
555				printf("...errSecMultiplePrivKeys error seen as expected\n");
556			}
557		}
558		else {
559			cssmPerror("SecKeychainItemImport", ortn);
560			ourRtn = -1;
561		}
562	}
563	else if(expectMultiKeysError) {
564		printf("***errSecMultiplePrivKeys expected but no error seen\n");
565		ourRtn = -1;
566	}
567	if(ortn == noErr) {
568		if(!quiet) {
569			printf("...import successful. Returned format %s\n",
570				secExtFormatStr(externFormat));
571		}
572		if(expectFormat != kSecFormatUnknown) {
573			if(expectFormat != externFormat) {
574				printf("***Expected format %s, got %s\n",
575					secExtFormatStr(expectFormat),
576					secExtFormatStr(externFormat));
577				ourRtn = -1;
578			}
579		}
580		if(expectItemType != kSecItemTypeUnknown) {
581			if(expectItemType != itemType) {
582				printf("***Expected itemType %s, got %s\n",
583					secExtItemTypeStr(expectItemType),
584					secExtItemTypeStr(itemType));
585				ourRtn = -1;
586			}
587		}
588		if(processOutput) {
589			ourRtn |= processItems(kcRef, outArray, expectNumCerts,
590				expectNumKeys, expectNumIds, interactive, displayItems, verbose);
591		}
592	}
593	if(loopPause) {
594		fflush(stdin);
595		printf("Pausing for MallocDebug; CR to continue: ");
596		getchar();
597		goto loopTop;
598	}
599	return ourRtn;
600}
601