1/*
2 * Copyright (c) 2003-2004 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 * pkcs12Keychain.h - P12Coder keychain-related functions.
26 */
27
28#include "pkcs12Coder.h"
29#include "pkcs12Templates.h"
30#include "pkcs12Utils.h"
31#include "pkcs12Debug.h"
32#include "pkcs12Crypto.h"
33#include <Security/cssmerr.h>
34#include <security_cdsa_utils/cuDbUtils.h>			// cuAddCrlToDb()
35#include <security_asn1/nssUtils.h>
36#include <security_cdsa_utilities/KeySchema.h>			/* private API */
37#include <security_keychain/SecImportExportCrypto.h>	/* private API */
38
39/*
40 * Store the results of a successful decode in app-specified
41 * keychain per mImportFlags. Also assign public key hash attributes to any
42 * private keys found.
43 */
44void P12Coder::storeDecodeResults()
45{
46	assert(mKeychain != NULL);
47	assert(mDlDbHand.DLHandle != 0);
48	if(mImportFlags & kSecImportKeys) {
49		setPrivateKeyHashes();
50	}
51	if(mImportFlags & kSecImportCertificates) {
52		for(unsigned dex=0; dex<numCerts(); dex++) {
53			P12CertBag *certBag = mCerts[dex];
54			SecCertificateRef secCert = certBag->getSecCert();
55			OSStatus ortn = SecCertificateAddToKeychain(secCert, mKeychain);
56			CFRelease(secCert);
57			switch(ortn) {
58				case errSecSuccess:					// normal
59					p12DecodeLog("cert added to keychain");
60					break;
61				case errSecDuplicateItem:	// dup cert, OK< skip
62					p12DecodeLog("skipping dup cert");
63					break;
64				default:
65					p12ErrorLog("SecCertificateAddToKeychain failure\n");
66					MacOSError::throwMe(ortn);
67			}
68		}
69	}
70
71	if(mImportFlags & kSecImportCRLs) {
72		for(unsigned dex=0; dex<numCrls(); dex++) {
73			P12CrlBag *crlBag = mCrls[dex];
74			CSSM_RETURN crtn = cuAddCrlToDb(mDlDbHand,
75				clHand(),
76				&crlBag->crlData(),
77				NULL);			// no URI known
78			switch(crtn) {
79				case CSSM_OK:								// normal
80					p12DecodeLog("CRL added to keychain");
81					break;
82				case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:	// dup, ignore
83					p12DecodeLog("skipping dup CRL");
84					break;
85				default:
86					p12LogCssmError("Error adding CRL to keychain", crtn);
87					CssmError::throwMe(crtn);
88			}
89		}
90	}
91
92	/* If all of that succeeded, post notification for imported keys */
93	if(mImportFlags & kSecImportKeys) {
94		notifyKeyImport();
95	}
96}
97
98/*
99 * Assign appropriate public key hash attribute to each
100 * private key.
101 */
102void P12Coder::setPrivateKeyHashes()
103{
104	CSSM_KEY_PTR newKey;
105
106	for(unsigned dex=0; dex<numKeys(); dex++) {
107		P12KeyBag *keyBag = mKeys[dex];
108
109		CSSM_DATA newLabel = {0, NULL};
110		CFStringRef friendlyName = keyBag->friendlyName();
111		newKey = NULL;
112		CSSM_RETURN crtn = p12SetPubKeyHash(mCspHand,
113			mDlDbHand,
114			keyBag->label(),
115			p12StringToUtf8(friendlyName, mCoder),
116			mCoder,
117			newLabel,
118			newKey);
119		if(friendlyName) {
120			CFRelease(friendlyName);
121		}
122		switch(crtn) {
123			case CSSM_OK:
124				/* update key's label in case we have to delete on error */
125				keyBag->setLabel(newLabel);
126				p12DecodeLog("set pub key hash for private key");
127				break;
128			case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
129				/*
130				 * Special case: update keyBag's CSSM_KEY and proceed without error
131				 */
132				p12DecodeLog("ignoring dup private key");
133				assert(newKey != NULL);
134				keyBag->setKey(newKey);
135				keyBag->dupKey(true);
136				/* update key's label in case we have to delete on error */
137				keyBag->setLabel(newLabel);
138				break;
139			default:
140				p12ErrorLog("p12SetPubKeyHash failure\n");
141				CssmError::throwMe(crtn);
142		}
143	}
144}
145
146/*
147 * Post keychain notification for imported keys.
148 */
149void P12Coder::notifyKeyImport()
150{
151	if(mKeychain == NULL) {
152		/* Can't notify if user only gave us DLDB */
153		return;
154	}
155	for(unsigned dex=0; dex<numKeys(); dex++) {
156		P12KeyBag *keyBag = mKeys[dex];
157		if(keyBag->dupKey()) {
158			/* no notification for keys we merely looked up */
159			continue;
160		}
161		CssmData &labelData = CssmData::overlay(keyBag->label());
162		OSStatus ortn = impExpKeyNotify(mKeychain, labelData, *keyBag->key());
163		if(ortn) {
164			p12ErrorLog("notifyKeyImport: impExpKeyNotify returned %ld\n", (unsigned long)ortn);
165			MacOSError::throwMe(ortn);
166		}
167	}
168}
169
170/*
171 * Given a P12KeyBag, find a matching P12CertBag. Keys and certs
172 * "match" if their localKeyIds match. Returns NULL if not found.
173 */
174P12CertBag *P12Coder::findCertForKey(
175	P12KeyBag *keyBag)
176{
177	assert(keyBag != NULL);
178	CSSM_DATA &keyKeyId = keyBag->localKeyIdCssm();
179
180	for(unsigned dex=0; dex<numCerts(); dex++) {
181		P12CertBag *certBag = mCerts[dex];
182		CSSM_DATA &certKeyId = certBag->localKeyIdCssm();
183		if(nssCompareCssmData(&keyKeyId, &certKeyId)) {
184			p12DecodeLog("findCertForKey SUCCESS");
185			return certBag;
186		}
187	}
188	p12DecodeLog("findCertForKey FAILURE");
189	return NULL;
190}
191
192/*
193 * Export items specified as SecKeychainItemRefs.
194 */
195void P12Coder::exportKeychainItems(
196	CFArrayRef				items)
197{
198	assert(items != NULL);
199	CFIndex numItems = CFArrayGetCount(items);
200	for(CFIndex dex=0; dex<numItems; dex++) {
201		const void *item = CFArrayGetValueAtIndex(items, dex);
202		if(item == NULL) {
203			p12ErrorLog("exportKeychainItems: NULL item\n");
204			MacOSError::throwMe(errSecParam);
205		}
206		CFTypeID itemType = CFGetTypeID(item);
207		if(itemType == SecCertificateGetTypeID()) {
208			addSecCert((SecCertificateRef)item);
209		}
210		else if(itemType == SecKeyGetTypeID()) {
211			addSecKey((SecKeyRef)item);
212		}
213		else {
214			p12ErrorLog("exportKeychainItems: unknown item\n");
215			MacOSError::throwMe(errSecParam);
216		}
217	}
218}
219
220/*
221 * Gross kludge to work around the fact that SecKeyRefs have no attributes which
222 * are visible at the Sec layer. Not only are the attribute names we happen
223 * to know about (Label, PrintName) not publically visible anywhere in the
224 * system, but the *format* of the attr names for SecKeyRefs differs from
225 * the format of all other SecKeychainItems (NAME_AS_STRING for SecKeys,
226 * NAME_AS_INTEGER for everything else).
227 *
228 * So. We use the privately accessible schema definition table for
229 * keys to map from the attr name strings we happen to know about to a
230 * totally private name-as-int index which we can then use in the
231 * SecKeychainItemCopyAttributesAndData mechanism.
232 *
233 * This will go away if SecKeyRef defines its actual attrs as strings, AND
234 * the SecKeychainSearch mechanism knows to specify attr names for SecKeyRefs
235 * as strings rather than integers.
236 */
237static OSStatus attrNameToInt(
238	const char *name,
239	UInt32 *attrInt)
240{
241	const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *attrList =
242		KeySchema::KeySchemaAttributeList;
243	unsigned numAttrs = KeySchema::KeySchemaAttributeCount;
244	for(unsigned dex=0; dex<numAttrs; dex++) {
245		const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *info = &attrList[dex];
246		if(!strcmp(name, info->AttributeName)) {
247			*attrInt = info->AttributeId;
248			return errSecSuccess;
249		}
250	}
251	return errSecParam;
252}
253
254void P12Coder::addSecKey(
255	SecKeyRef	keyRef)
256{
257	/* get the cert's attrs (not data) */
258
259	/*
260	 * Convert the attr name strings we happen to know about to
261	 * unknowable name-as-int values.
262	 */
263	UInt32 printNameTag;
264	OSStatus ortn = attrNameToInt(P12_KEY_ATTR_PRINT_NAME, &printNameTag);
265	if(ortn) {
266		p12ErrorLog("addSecKey: problem looking up key attr name\n");
267		MacOSError::throwMe(ortn);
268	}
269	UInt32 labelHashTag;
270	ortn = attrNameToInt(P12_KEY_ATTR_LABEL_AND_HASH, &labelHashTag);
271	if(ortn) {
272		p12ErrorLog("addSecKey: problem looking up key attr name\n");
273		MacOSError::throwMe(ortn);
274	}
275
276	UInt32 tags[2];
277	tags[0] = printNameTag;
278	tags[1] = labelHashTag;
279
280	/* I don't know what the format field is for */
281	SecKeychainAttributeInfo attrInfo;
282	attrInfo.count = 2;
283	attrInfo.tag = tags;
284	attrInfo.format = NULL;	// ???
285
286	/* FIXME header says this is an IN/OUT param, but it's not */
287	SecKeychainAttributeList *attrList = NULL;
288
289	ortn = SecKeychainItemCopyAttributesAndData(
290		(SecKeychainItemRef)keyRef,
291		&attrInfo,
292		NULL,			// itemClass
293		&attrList,
294		NULL,			// don't need the data
295		NULL);
296	if(ortn) {
297		p12ErrorLog("addSecKey: SecKeychainItemCopyAttributesAndData "
298			"error\n");
299		MacOSError::throwMe(ortn);
300	}
301
302	/* Snag the attrs, convert to something useful */
303	CFStringRef friendName = NULL;
304	CFDataRef localKeyId = NULL;
305	for(unsigned i=0; i<attrList->count; i++) {
306		SecKeychainAttribute *attr = &attrList->attr[i];
307		if(attr->tag == printNameTag) {
308			friendName = CFStringCreateWithBytes(NULL,
309				(UInt8 *)attr->data, attr->length,
310				kCFStringEncodingUTF8,	false);
311		}
312		else if(attr->tag == labelHashTag) {
313			localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
314		}
315		else {
316			p12ErrorLog("addSecKey: unexpected attr tag\n");
317			MacOSError::throwMe(errSecParam);
318
319		}
320	}
321
322	/*
323	 * Infer the CSP associated with this key.
324	 * FIXME: this should be an attribute of the SecKeyRef itself,
325	 * not inferred from the keychain it happens to be living on
326	 * (SecKeyRefs should not have to be attached to Keychains at
327	 * this point).
328	 */
329	SecKeychainRef kcRef;
330	ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
331	if(ortn) {
332		p12ErrorLog("addSecKey: SecKeychainItemCopyKeychain returned %d\n", (int)ortn);
333		MacOSError::throwMe(ortn);
334	}
335	CSSM_CSP_HANDLE cspHand;
336	ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
337	if(ortn) {
338		p12ErrorLog("addSecKey: SecKeychainGetCSPHandle returned %d\n", (int)ortn);
339		MacOSError::throwMe(ortn);
340	}
341	CFRelease(kcRef);
342
343	/* and the CSSM_KEY itself */
344	const CSSM_KEY *cssmKey;
345	ortn = SecKeyGetCSSMKey(keyRef, &cssmKey);
346	if(ortn) {
347		p12ErrorLog("addSecKey: SecKeyGetCSSMKey returned %d\n", (int)ortn);
348		MacOSError::throwMe(ortn);
349	}
350
351	/* Cook up a key bag and save it */
352	P12KeyBag *keyBag = new P12KeyBag(cssmKey,
353		cspHand,
354		friendName,	localKeyId,
355		NULL, 			// other attrs
356		mCoder,
357		keyRef);
358	addKey(keyBag);
359	SecKeychainItemFreeAttributesAndData(attrList, NULL);
360	if(friendName) {
361		CFRelease(friendName);
362	}
363	if(localKeyId) {
364		CFRelease(localKeyId);
365	}
366}
367
368void P12Coder::addSecCert(
369	SecCertificateRef	certRef)
370{
371	/* get the cert's attrs and data */
372	/* I don't know what the format field is for */
373	SecKeychainAttributeInfo attrInfo;
374	attrInfo.count = 2;
375	UInt32 tags[2] = {kSecLabelItemAttr, kSecPublicKeyHashItemAttr};
376	attrInfo.tag = tags;
377	attrInfo.format = NULL;	// ???
378
379	/* FIXME header says this is an IN/OUT param, but it's not */
380	SecKeychainAttributeList *attrList = NULL;
381	UInt32 certLen;
382	void *certData;
383
384	OSStatus ortn = SecKeychainItemCopyAttributesAndData(
385		(SecKeychainItemRef)certRef,
386		&attrInfo,
387		NULL,			// itemClass
388		&attrList,
389		&certLen,
390		&certData);
391	if(ortn) {
392		p12ErrorLog("addSecCert: SecKeychainItemCopyAttributesAndData "
393			"error\n");
394		MacOSError::throwMe(ortn);
395	}
396
397	/* Snag the attrs, convert to something useful */
398	CFStringRef friendName = NULL;
399	CFDataRef localKeyId = NULL;
400	for(unsigned i=0; i<attrList->count; i++) {
401		SecKeychainAttribute *attr = &attrList->attr[i];
402		switch(attr->tag) {
403			case kSecPublicKeyHashItemAttr:
404				localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
405				break;
406			case kSecLabelItemAttr:
407				/* FIXME: always in UTF8? */
408				friendName = CFStringCreateWithBytes(NULL,
409					(UInt8 *)attr->data, attr->length, kCFStringEncodingUTF8,
410					false);
411				break;
412			default:
413				p12ErrorLog("addSecCert: unexpected attr tag\n");
414				MacOSError::throwMe(errSecParam);
415
416		}
417	}
418
419	/* Cook up a cert bag and save it */
420	CSSM_DATA cData = {certLen, (uint8 *)certData};
421	P12CertBag *certBag = new P12CertBag(CT_X509, cData, friendName,
422		localKeyId, NULL, mCoder);
423	addCert(certBag);
424	SecKeychainItemFreeAttributesAndData(attrList, certData);
425	if(friendName) {
426		CFRelease(friendName);
427	}
428	if(localKeyId) {
429		CFRelease(localKeyId);
430	}
431}
432
433/*
434 * Delete anything stored in a keychain during decode, called on
435 * decode error.
436 * Currently the only thing we have to deal with is private keys,
437 * since certs and CRLs don't get stored until the end of a successful
438 * decode.
439 */
440void P12Coder::deleteDecodedItems()
441{
442	if(!(mImportFlags & kSecImportKeys)) {
443		/* no keys stored, done */
444		return;
445	}
446	if(mDlDbHand.DLHandle == 0) {
447		/* no keychain, done */
448		return;
449	}
450
451	unsigned nKeys = numKeys();
452	for(unsigned dex=0; dex<nKeys; dex++) {
453		P12KeyBag *keyBag = mKeys[dex];
454		p12DeleteKey(mDlDbHand, keyBag->label());
455	}
456
457}
458
459