1/*
2 * p12ImportExport.cpp - high-level libnsspkcs12 exerciser
3 */
4
5#include <security_pkcs12/SecPkcs12.h>
6#include <security_cdsa_utils/cuFileIo.h>
7#include <Security/Security.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <security_cdsa_utilities/KeySchema.h>
11#include <security_cdsa_utils/cuCdsaUtils.h>
12#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
13#include "p12GetPassKey.h"
14
15static void printOsError(
16	const char *op,
17	OSStatus ortn)
18{
19	char *errStr = NULL;
20	switch(ortn) {
21		case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
22			errStr = "CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA"; break;
23		case CSSMERR_DL_DATASTORE_DOESNOT_EXIST:
24			errStr = "CSSMERR_DL_DATASTORE_DOESNOT_EXIST"; break;
25		case errSecDuplicateItem:
26			errStr = "errSecDuplicateItem"; break;
27		case errSecNotAvailable:
28			errStr = "errSecNotAvailable"; break;
29		case errSecAuthFailed:
30			errStr = "errSecAuthFailed"; break;
31		case errSecItemNotFound:
32			errStr = "errSecItemNotFound"; break;
33		case errSecInvalidItemRef:
34			errStr = "errSecInvalidItemRef"; break;
35		default:
36			break;
37	}
38	if(errStr) {
39		printf("%s returned %s\n", op, errStr);
40	}
41	else {
42		printf("%s returned %d\n", op, (int)ortn);
43	}
44}
45
46/*
47 * For now we assume "import everything"
48 */
49int p12Import(
50	const char *pfxFile,
51	const char *kcName,
52	CFStringRef pwd,			// explicit passphrase, mutually exclusive with...
53	bool usePassKey,			// use SECURE_PASSPHRASE key
54	const char *kcPwd)			// optional
55{
56	OSStatus 		ortn;
57	unsigned char 	*pfx;
58	unsigned 		pfxLen;
59	CSSM_KEY		passKey;
60
61	/* get the PFX */
62	if(readFile(pfxFile, &pfx, &pfxLen)) {
63		printf("***Error reading pfx from %s. Aborting.\n", pfxFile);
64		return 1;
65	}
66	CFDataRef cfd = CFDataCreate(NULL, pfx, pfxLen);
67
68	/* import to keychain specified by kcName */
69	SecKeychainRef kcRef = NULL;
70	ortn = SecKeychainOpen(kcName, &kcRef);
71	if(ortn) {
72		printOsError("SecKeychainOpen", ortn);
73		return ortn;
74	}
75
76	if(kcPwd) {
77		ortn = SecKeychainUnlock(kcRef, strlen(kcPwd), (void *)kcPwd, true);
78		if(ortn) {
79			printOsError("SecKeychainUnlock", ortn);
80		}
81	}
82
83	/* set up a pkcs12 coder for import */
84	SecPkcs12CoderRef coder;
85	ortn = SecPkcs12CoderCreate(&coder);
86	if(ortn) {
87		printOsError("SecPkcs12CoderCreate", ortn);
88		return ortn;
89	}
90
91	ortn = SecPkcs12SetKeychain(coder, kcRef);
92	if(ortn) {
93		printOsError("SecPkcs12SetKeychain", ortn);
94		return ortn;
95	}
96
97	if(usePassKey) {
98		CSSM_CSP_HANDLE cspHand;
99		ortn =	SecKeychainGetCSPHandle(kcRef, &cspHand);
100		if(ortn) {
101			printOsError("SecPkcs12SetKeychain", ortn);
102			return ortn;
103		}
104		ortn = p12GetPassKey(cspHand, GPK_Decode, false, &passKey);
105		if(ortn) {
106			return ortn;
107		}
108		ortn = SecPkcs12SetMACPassKey(coder, &passKey);
109		if(ortn) {
110			printOsError("SecPkcs12SetMACPassKey", ortn);
111			return ortn;
112		}
113	}
114	else {
115		ortn = SecPkcs12SetMACPassphrase(coder, pwd);
116		if(ortn) {
117			printOsError("SecPkcs12SetMACPassphrase", ortn);
118			return ortn;
119		}
120	}
121
122	/*
123	 * For now we assume "import everything"
124	 */
125	ortn = SecPkcs12SetImportToKeychain(coder,
126		kSecImportCertificates |
127		kSecImportCRLs |
128		kSecImportKeys);
129	if(ortn) {
130		printOsError("SecPkcs12SetImportFromKeychain", ortn);
131		return ortn;
132	}
133
134	/* Go! */
135	ortn = SecPkcs12Decode(coder, cfd);
136	if(ortn) {
137		printOsError("SecPkcs12Decode", ortn);
138		return ortn;
139	}
140
141	/* report how many of each item got imported */
142	CFIndex num;
143	SecPkcs12CertificateCount(coder, &num);
144	printf("...%d certs imported\n", (int)num);
145	SecPkcs12CrlCount(coder, &num);
146	printf("...%d CRLs imported\n", (int)num);
147	SecPkcs12PrivateKeyCount(coder, &num);
148	printf("...%d private keys imported\n", (int)num);
149
150	SecPkcs12CoderRelease(coder);
151	CFRelease(cfd);
152	free(pfx);			// mallocd by readFile()
153	return 0;
154}
155
156/*
157 * Use the kludge from hell to get the name-as-int form of a specified
158 * "known" name-as-string for the Key Schema.
159 */
160OSStatus attrNameToInt(
161	const char *name,
162	uint32 *attrInt)
163{
164	const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *attrList =
165		KeySchema::KeySchemaAttributeList;
166	unsigned numAttrs = KeySchema::KeySchemaAttributeCount;
167	for(unsigned dex=0; dex<numAttrs; dex++) {
168		const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *info = &attrList[dex];
169		if(!strcmp(name, info->AttributeName)) {
170			*attrInt = info->AttributeId;
171			return noErr;
172		}
173	}
174	return paramErr;
175}
176
177static int p12AddExportedItem(
178	SecKeychainItemRef item,
179	CFMutableArrayRef itemArray,
180	bool noPrompt)
181{
182	if(noPrompt) {
183		CFArrayAppendValue(itemArray, item);
184		return 1;
185	}
186
187	CFTypeID itemId = CFGetTypeID(item);
188	OSStatus ortn;
189
190	/* the printable name attr */
191	UInt32 nameAttr = 0;
192	char *itemClass = "";
193	if(itemId == SecCertificateGetTypeID()) {
194		itemClass = "Certificate";
195		nameAttr = kSecLabelItemAttr;
196	}
197	else if(itemId == SecKeyGetTypeID()) {
198		itemClass = "Private Key";
199		ortn = attrNameToInt("PrintName", &nameAttr);
200		if(ortn) {
201			/* out of sync with Sec layer? With KeySchema? */
202			printf("warning: attrNameToInt failure\n");
203			return 0;
204		}
205	}
206	else {
207		/* we don't know how to deal with this */
208		printf("p12AddExportedItem: internal screwup\n");
209		return 0;
210	}
211
212	/* get the printable name attr */
213	SecKeychainAttributeInfo attrInfo;
214	attrInfo.count = 1;
215	attrInfo.tag = &nameAttr;
216	attrInfo.format = NULL;	// ???
217
218	/* FIXME header says this is an IN/OUT param, but it's not */
219	SecKeychainAttributeList *attrList = NULL;
220
221	ortn = SecKeychainItemCopyAttributesAndData(
222		item,
223		&attrInfo,
224		NULL,			// itemClass
225		&attrList,
226		NULL,			// don't need the data
227		NULL);
228	if(ortn) {
229		printOsError("SecKeychainItemCopyAttributesAndData", ortn);
230		return 0;
231	}
232	if(attrList->count != 1) {
233		printf("***Unexpected attribute count (%u) for %s\n",
234			(unsigned)attrList->count, itemClass);
235		return 0;
236	}
237	SecKeychainAttribute *attr = attrList->attr;
238
239	/* it's a UTF8 string: use CFString to convert to C ASCII string */
240	CFStringRef cfStr = CFStringCreateWithBytes(NULL,
241		(UInt8 *)attr->data, attr->length,
242		kCFStringEncodingUTF8,	false);
243	SecKeychainItemFreeAttributesAndData(attrList, NULL);
244	if(cfStr == NULL) {
245		printf("***Error converting %s name to UTF CFSTring.\n",
246			itemClass);
247		return 0;
248	}
249
250	CFIndex strLen = CFStringGetLength(cfStr);
251	char *printName = (char *)malloc(strLen + 1);
252	if(!CFStringGetCString(cfStr, printName, strLen + 1, kCFStringEncodingASCII)) {
253		printf("***Error converting %s name to ASCII\n", itemClass);
254		return 0;
255	}
256	CFRelease(cfStr);
257
258	char *aliasCStr = NULL;
259	if((itemId == SecCertificateGetTypeID())) {
260		/* the alias attr, for cert email */
261		CFStringRef aliasCFStr = NULL;
262		nameAttr = kSecAlias;
263		attrInfo.count = 1;
264		attrInfo.tag = &nameAttr;
265		attrInfo.format = NULL;	// ???
266		attrList = NULL;
267
268		ortn = SecKeychainItemCopyAttributesAndData(
269			item,
270			&attrInfo,
271			NULL,			// itemClass
272			&attrList,
273			NULL,			// don't need the data
274			NULL);
275		if(ortn) {
276			printOsError("SecKeychainItemCopyAttributesAndData", ortn);
277			return 0;
278		}
279		if(attrList->count != 1) {
280			printf("***Unexpected attribute count (%u) for Alias\n",
281				(unsigned)attrList->count);
282			return 0;
283		}
284		attr = attrList->attr;
285
286		/* it's a UTF8 string: use CFString to convert to C ASCII string */
287		aliasCFStr = CFStringCreateWithBytes(NULL,
288			(UInt8 *)attr->data, attr->length,
289			kCFStringEncodingUTF8,	false);
290		if(aliasCFStr == NULL) {
291			printf("***Error converting Alias name to UTF CFSTring.\n");
292			return 0;
293		}
294
295		strLen = CFStringGetLength(aliasCFStr);
296		aliasCStr = (char *)malloc(strLen + 1);
297		if(!CFStringGetCString(aliasCFStr, aliasCStr, strLen + 1,
298				kCFStringEncodingASCII)) {
299			printf("***Error converting Alias name to ASCII\n");
300			return 0;
301		}
302		CFRelease(aliasCFStr);
303	}
304
305	int ourRtn = 0;
306	fpurge(stdin);
307	printf("Found %s\n", itemClass);
308	printf("   printable name : %s\n", printName);
309	if(aliasCStr != NULL) {
310		printf("   alias          : %s\n", aliasCStr);
311	}
312	printf("Export (y/anything)? ");
313	char c = getchar();
314	if(c == 'y') {
315		CFArrayAppendValue(itemArray, item);
316		ourRtn = 1;
317	}
318	free(printName);
319	if(aliasCStr) {
320		free(aliasCStr);
321	}
322	return ourRtn;
323}
324
325int p12Export(
326	const char *pfxFile,
327	const char *kcName,
328	CFStringRef pwd,			// explicit passphrase, mutually exclusive with...
329	bool usePassKey,			// use SECURE_PASSPHRASE key
330	const char *kcPwd,			// optional
331	bool noPrompt)				// true --> export all
332{
333	OSStatus ortn;
334	CSSM_KEY		passKey;
335
336	/* set up a pkcs12 coder for export */
337	SecPkcs12CoderRef coder;
338	ortn = SecPkcs12CoderCreate(&coder);
339	if(ortn) {
340		printOsError("SecPkcs12CoderCreate", ortn);
341		return ortn;
342	}
343
344	/*
345	�* Since we're not providing the SecPkcs12CoderRef with a
346	 * keychain, we have to provide the CSPDL handle
347	 */
348	CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_FALSE);
349	if(cspHand == 0) {
350		printf("***Error attaching to CSPDL. Aborting.\n");
351		return 1;
352	}
353
354	if(usePassKey) {
355		ortn = p12GetPassKey(cspHand, GPK_Encode, false, &passKey);
356		if(ortn) {
357			return ortn;
358		}
359		ortn = SecPkcs12SetMACPassKey(coder, &passKey);
360		if(ortn) {
361			printOsError("SecPkcs12SetMACPassKey", ortn);
362			return ortn;
363		}
364	}
365	else {
366		ortn = SecPkcs12SetMACPassphrase(coder, pwd);
367		if(ortn) {
368			printOsError("SecPkcs12SetMACPassphrase", ortn);
369			return ortn;
370		}
371	}
372
373	ortn = SecPkcs12SetCspHandle(coder, cspHand);
374	if(ortn) {
375		printOsError("SecPkcs12SetCspHandle", ortn);
376		return ortn;
377	}
378
379	/* the array of things we want to export */
380	CFMutableArrayRef items = CFArrayCreateMutable(NULL, 0, NULL);
381
382	/* export from keychain specified by kcName */
383	SecKeychainRef kcRef = NULL;
384	ortn = SecKeychainOpen(kcName, &kcRef);
385	if(ortn) {
386		printOsError("SecKeychainOpen", ortn);
387		return ortn;
388	}
389
390	if(kcPwd) {
391		ortn = SecKeychainUnlock(kcRef, strlen(kcPwd), (void *)kcPwd, true);
392		if(ortn) {
393			printOsError("SecKeychainUnlock", ortn);
394		}
395	}
396
397	/*
398	 * Prompt user for each known item - it would be nice if we
399	 * could search for anything, eh?
400	 * Certs first...
401	 */
402	SecKeychainSearchRef srchRef;
403	ortn = SecKeychainSearchCreateFromAttributes(kcRef,
404		kSecCertificateItemClass,
405		NULL,		// no attrs
406		&srchRef);
407	if(ortn) {
408		printOsError("SecKeychainSearchCreateFromAttributes", ortn);
409		return ortn;
410	}
411	int exported = 0;
412	for(;;) {
413		SecKeychainItemRef certRef;
414		ortn = SecKeychainSearchCopyNext(srchRef, &certRef);
415		if(ortn) {
416			break;
417		}
418		exported += p12AddExportedItem(certRef, items, noPrompt);
419	}
420	CFRelease(srchRef);
421
422	/* now private keys */
423	ortn = SecKeychainSearchCreateFromAttributes(kcRef,
424		CSSM_DL_DB_RECORD_PRIVATE_KEY,	// undocumented
425		NULL,							// no attrs
426		&srchRef);
427	if(ortn) {
428		printOsError("SecKeychainSearchCreateFromAttributes", ortn);
429		return ortn;
430	}
431	for(;;) {
432		SecKeychainItemRef keyRef;
433		ortn = SecKeychainSearchCopyNext(srchRef, &keyRef);
434		if(ortn) {
435			break;
436		}
437		exported += p12AddExportedItem(keyRef, items, noPrompt);
438	}
439
440	if(exported == 0) {
441		printf("...Hmmm, no items to export. Done.\n");
442		return 0;
443	}
444	ortn = SecPkcs12ExportKeychainItems(coder, items);
445	if(ortn) {
446		printOsError("SecPkcs12ExportKeychainItems", ortn);
447		return ortn;
448	}
449
450	/* go */
451	CFDataRef pfx;
452	ortn = SecPkcs12Encode(coder, &pfx);
453	if(ortn) {
454		printOsError("SecPkcs12ExportKeychainItems", ortn);
455		return ortn;
456	}
457
458	if(writeFile(pfxFile, CFDataGetBytePtr(pfx),
459				CFDataGetLength(pfx))) {
460		printf("***Error writing pfx to %s\n", pfxFile);
461		return 1;
462	}
463	printf("...%u items exported; %ld bytes written to %s\n",
464		exported, CFDataGetLength(pfx), pfxFile);
465
466	/* cleanup */
467	SecPkcs12CoderRelease(coder);
468	CFRelease(pfx);
469	CFRelease(srchRef);
470	CFRelease(kcRef);
471	return 0;
472}
473