1/*
2 * Copyright (c) 2004-2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * keyPicker.cpp - select a key pair from a keychain
26 */
27
28#include "keyPicker.h"
29#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
30#include <Security/Security.h>
31#include <stdexcept>
32#include <ctype.h>
33#include <clAppUtils/identPicker.h>		/* for kcFileName() */
34#include <vector>
35
36/*
37 * Obtain either public key hash or PrintName for a given SecKeychainItem. Works on public keys,
38 * private keys, identities, and certs. Caller must release the returned result.
39 */
40OSStatus getKcItemAttr(
41	SecKeychainItemRef kcItem,
42	WhichAttr whichAttr,
43	CFDataRef *rtnAttr)			/* RETURNED */
44{
45	/* main job is to figure out which attrType to ask for, and from what Sec item */
46	SecKeychainItemRef attrFromThis;
47	SecKeychainAttrType	attrType = 0;
48	OSStatus ortn;
49	bool releaseKcItem = false;
50
51	CFTypeID cfId = CFGetTypeID(kcItem);
52	if(cfId == SecIdentityGetTypeID()) {
53		/* switch over to cert */
54		ortn = SecIdentityCopyCertificate((SecIdentityRef)kcItem,
55			(SecCertificateRef *)&attrFromThis);
56		if(ortn)
57			cssmPerror("SecIdentityCopyCertificate", ortn);
58			return ortn;
59		kcItem = attrFromThis;
60		releaseKcItem = true;
61		cfId = SecCertificateGetTypeID();
62	}
63
64	if(cfId == SecCertificateGetTypeID()) {
65		switch(whichAttr) {
66			case WA_Hash:
67				attrType = kSecPublicKeyHashItemAttr;
68				break;
69			case WA_PrintName:
70				attrType = kSecLabelItemAttr;
71				break;
72			default:
73				printf("getKcItemAttr: WhichAttr\n");
74				return paramErr;
75		}
76	}
77	else if(cfId == SecKeyGetTypeID()) {
78		switch(whichAttr) {
79			case WA_Hash:
80				attrType = kSecKeyLabel;
81				break;
82			case WA_PrintName:
83				attrType = kSecKeyPrintName;
84				break;
85			default:
86				printf("getKcItemAttr: WhichAttr\n");
87				return paramErr;
88		}
89	}
90
91	SecKeychainAttributeInfo attrInfo;
92	attrInfo.count = 1;
93	attrInfo.tag = &attrType;
94	attrInfo.format = NULL;	// ???
95	SecKeychainAttributeList *attrList = NULL;
96
97	ortn = SecKeychainItemCopyAttributesAndData(
98		kcItem,
99		&attrInfo,
100		NULL,			// itemClass
101		&attrList,
102		NULL,			// don't need the data
103		NULL);
104	if(releaseKcItem) {
105		CFRelease(kcItem);
106	}
107	if(ortn) {
108		cssmPerror("SecKeychainItemCopyAttributesAndData", ortn);
109		return paramErr;
110	}
111	SecKeychainAttribute *attr = attrList->attr;
112	*rtnAttr = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
113	SecKeychainItemFreeAttributesAndData(attrList, NULL);
114	return noErr;
115}
116
117/*
118 * Class representing one key in the keychain.
119 */
120class PickerKey
121{
122public:
123	PickerKey(SecKeyRef keyRef);
124	~PickerKey();
125
126	bool		isUsed()					{ return mIsUsed;}
127	void		isUsed(bool u)				{ mIsUsed = u; }
128	bool		isPrivate()					{ return mIsPrivate; }
129	CFDataRef	getPrintName()				{ return mPrintName; }
130	CFDataRef	getPubKeyHash()				{ return mPubKeyHash; }
131	SecKeyRef	keyRef()					{ return mKeyRef; }
132
133	PickerKey	*partnerKey()				{ return mPartner; }
134	void		partnerKey(PickerKey *pk)	{ mPartner = pk; }
135	char		*kcFile()					{ return mKcFile; }
136
137private:
138	SecKeyRef	mKeyRef;
139	CFDataRef	mPrintName;
140	CFDataRef	mPubKeyHash;
141	bool		mIsPrivate;		// private/public key
142	bool		mIsUsed;		// has been spoken for
143	PickerKey	*mPartner;		// other member of public/private pair
144	char		*mKcFile;		// file name of keychain this lives on
145};
146
147PickerKey::PickerKey(SecKeyRef keyRef)
148	:	mKeyRef(NULL),
149		mPrintName(NULL),
150		mPubKeyHash(NULL),
151		mIsPrivate(false),
152		mIsUsed(false),
153		mPartner(NULL),
154		mKcFile(NULL)
155{
156	if(CFGetTypeID(keyRef) != SecKeyGetTypeID()) {
157		throw std::invalid_argument("not a key");
158	}
159
160	OSStatus ortn = getKcItemAttr((SecKeychainItemRef)keyRef, WA_Hash, &mPubKeyHash);
161	if(ortn) {
162		throw std::invalid_argument("pub key hash not available");
163	}
164	ortn = getKcItemAttr((SecKeychainItemRef)keyRef, WA_PrintName, &mPrintName);
165	if(ortn) {
166		throw std::invalid_argument("pub key hash not available");
167	}
168
169	const CSSM_KEY *cssmKey;
170	ortn = SecKeyGetCSSMKey(keyRef, &cssmKey);
171	if(ortn) {
172		/* should never happen */
173		cssmPerror("SecKeyGetCSSMKey", ortn);
174		throw std::invalid_argument("SecKeyGetCSSMKey error");
175	}
176	if(cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
177		mIsPrivate = true;
178	}
179
180	/* stash name of the keychain this lives on */
181	SecKeychainRef kcRef;
182	ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
183	if(ortn) {
184		cssmPerror("SecKeychainItemCopyKeychain", ortn);
185		mKcFile = strdup("Unnamed keychain");
186	}
187	else {
188		mKcFile = kcFileName(kcRef);
189	}
190
191	mKeyRef = keyRef;
192	CFRetain(mKeyRef);
193}
194
195PickerKey::~PickerKey()
196{
197	if(mKeyRef) {
198		CFRelease(mKeyRef);
199	}
200	if(mPubKeyHash) {
201		CFRelease(mPubKeyHash);
202	}
203	if(mPrintName) {
204		CFRelease(mPrintName);
205	}
206	if(mKcFile) {
207		free(mKcFile);
208	}
209}
210
211typedef std::vector<PickerKey *> KeyVector;
212
213/*
214 * add PickerKey objects of specified type to a KeyVector.
215 */
216static void getPickerKeys(
217	SecKeychainRef	kcRef,
218	SecItemClass	itemClass,	// actually CSSM_DL_DB_RECORD_{PRIVATE,PRIVATE}_KEY for now
219	KeyVector		&keyVector)
220{
221	SecKeychainSearchRef 	srchRef = NULL;
222	SecKeychainItemRef		kcItem;
223
224	OSStatus ortn = SecKeychainSearchCreateFromAttributes(kcRef,
225		itemClass,
226		NULL,			// any attrs
227		&srchRef);
228	if(ortn) {
229		cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
230		return;
231	}
232	do {
233		ortn = SecKeychainSearchCopyNext(srchRef, &kcItem);
234		if(ortn) {
235			break;
236		}
237		try {
238			PickerKey *pickerKey = new PickerKey((SecKeyRef)kcItem);
239			keyVector.push_back(pickerKey);
240		}
241		catch(...) {
242			printf("**** key item that failed PickerKey construct ***\n");
243			/* but keep going */
244		}
245	} while(ortn == noErr);
246	CFRelease(srchRef);
247}
248
249/*
250 * Print contents of a CFData assuming it's printable
251 */
252static void printCfData(CFDataRef cfd)
253{
254	CFIndex len = CFDataGetLength(cfd);
255	const UInt8 *cp = CFDataGetBytePtr(cfd);
256	for(CFIndex dex=0; dex<len; dex++) {
257		char c = cp[dex];
258		if(isprint(c)) {
259			putchar(c);
260		}
261		else {
262			printf(".%02X.", c);
263		}
264	}
265}
266
267OSStatus keyPicker(
268	SecKeychainRef  kcRef,		// NULL means the default list
269	SecKeyRef		*pubKey,	// RETURNED
270	SecKeyRef		*privKey)   // RETURNED
271{
272
273	/* First create a arrays of all of the keys, parsed and ready for use */
274
275	std::vector<PickerKey *> privKeys;
276	std::vector<PickerKey *> pubKeys;
277	getPickerKeys(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, privKeys);
278	getPickerKeys(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY,  pubKeys);
279
280	/* now interate thru private keys, looking for a partner for each one */
281	int numPairs = 0;
282	unsigned numPrivKeys = privKeys.size();
283	unsigned numPubKeys  = pubKeys.size();
284
285	for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
286		PickerKey *privPk = privKeys[privDex];
287		CFDataRef privHash = privPk->getPubKeyHash();
288		for(unsigned pubDex=0; pubDex<numPubKeys; pubDex++) {
289			PickerKey *pubPk = pubKeys[pubDex];
290			if(pubPk->isUsed()) {
291				/* already spoken for */
292				continue;
293			}
294			if(!CFEqual(privHash, pubPk->getPubKeyHash())) {
295				/* public key hashes don't match */
296				continue;
297			}
298
299			/* got a match */
300			pubPk->partnerKey(privPk);
301			privPk->partnerKey(pubPk);
302			pubPk->isUsed(true);
303			privPk->isUsed(true);
304
305			/* display */
306			printf("[%d] privKey  : ", numPairs); printCfData(privPk->getPrintName()); printf("\n");
307			printf("    pubKey   : ");  printCfData(pubPk->getPrintName());printf("\n");
308			printf("    keychain : %s\n", privPk->kcFile());
309
310			numPairs++;
311		}
312	}
313
314	if(numPairs == 0) {
315		printf("*** keyPicker: no key pairs found.\n");
316		return paramErr;
317	}
318
319	OSStatus ortn = noErr;
320	int ires;
321	while(1) {
322		fpurge(stdin);
323		printf("\nEnter key pair number or CR to quit : ");
324		fflush(stdout);
325		char resp[64];
326		getString(resp, sizeof(resp));
327		if(resp[0] == '\0') {
328			ortn = CSSMERR_CSSM_USER_CANCELED;
329			break;
330		}
331		ires = atoi(resp);
332		if((ires < 0) || (ires >= numPairs)) {
333			printf("***Invalid entry. Type a number between 0 and %d\n", numPairs-1);
334			continue;
335		}
336		break;
337	}
338
339	if(ortn == noErr) {
340		/* find the ires'th partnered private key */
341		int goodOnes = 0;
342		for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
343			PickerKey *privPk = privKeys[privDex];
344			if(!privPk->isUsed()) {
345				continue;
346			}
347			if(goodOnes == ires) {
348				/* this is it */
349				*privKey = privPk->keyRef();
350				*pubKey = privPk->partnerKey()->keyRef();
351			}
352			goodOnes++;
353		}
354	}
355
356	/* clean out PickerKey arrays */
357	for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
358		delete privKeys[privDex];
359	}
360	for(unsigned pubDex=0; pubDex<numPubKeys; pubDex++) {
361		delete pubKeys[pubDex];
362	}
363	return ortn;
364}
365
366