1/*
2 * Copyright (c) 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 * SecImportExportAgg.cpp - private routines used by SecImportExport.h for
24 *						    aggregate (PKCS12 and PKCS7) conversion.
25 */
26
27#include "SecImportExportAgg.h"
28#include "SecExternalRep.h"
29#include "SecImportExportUtils.h"
30#include "SecNetscapeTemplates.h"
31#include "Certificate.h"
32#include <security_pkcs12/SecPkcs12.h>
33#include <Security/SecBase.h>
34#include <Security/SecCmsDecoder.h>
35#include <Security/SecCmsEncoder.h>
36#include <Security/SecCmsMessage.h>
37#include <Security/SecCmsContentInfo.h>
38#include <Security/SecCmsSignedData.h>
39#include <security_asn1/SecNssCoder.h>
40#include <security_asn1/nssUtils.h>
41#include <security_cdsa_utils/cuCdsaUtils.h>
42#include <security_keychain/Globals.h>
43#include <Security/SecIdentityPriv.h>
44#include <Security/SecKeyPriv.h>
45
46using namespace Security;
47using namespace KeychainCore;
48
49#pragma mark --- Aggregate Export routines ---
50
51OSStatus impExpPkcs12Export(
52	CFArrayRef							exportReps,		// SecExportReps
53	SecItemImportExportFlags			flags,			// kSecItemPemArmour, etc.
54	const SecKeyImportExportParameters	*keyParams,		// optional
55	CFMutableDataRef					outData)		// output appended here
56{
57	SecPkcs12CoderRef   p12Coder;
58	OSStatus			ortn = errSecSuccess;
59	CFMutableArrayRef   exportItems;			// SecKeychainItemRefs
60	CFDataRef			tmpData = NULL;
61	CSSM_CSP_HANDLE		cspHand;
62	CSSM_KEY			*passKey = NULL;
63	CFStringRef			phraseStr = NULL;
64
65	if( (keyParams == NULL) ||
66	    ( (keyParams->passphrase == NULL) &&
67		  !(keyParams->flags & kSecKeySecurePassphrase) ) ) {
68		/* passphrase mandatory */
69		return errSecPassphraseRequired;
70	}
71	CFIndex numReps = CFArrayGetCount(exportReps);
72	if(numReps == 0) {
73		SecImpExpDbg("impExpPkcs12Export: no items to export");
74		return errSecItemNotFound;
75	}
76
77	/*
78	 * Build an array of SecKeychainItemRefs.
79	 *
80	 * Keychain is inferred from the objects to be exported. Some may be
81	 * floating certs with no keychain.
82	 */
83	exportItems = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
84	SecKeychainRef kcRef = nil;
85	for(CFIndex dex=0; dex<numReps; dex++) {
86		SecExportRep *exportRep =
87			(SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
88		SecKeychainItemRef kcItemRef = (SecKeychainItemRef)exportRep->kcItem();
89		CFArrayAppendValue(exportItems, kcItemRef);
90		if(kcRef == nil) {
91			SecKeychainItemCopyKeychain(kcItemRef, &kcRef);
92			/* ignore error - we do this 'til we get a kcRef */
93		}
94	}
95
96	/* Set up a PKCS12 encoder */
97	ortn = SecPkcs12CoderCreate(&p12Coder);
98	if(ortn) {
99		return ortn;
100	}
101	/* subsequent errors to errOut: */
102
103	ortn = SecPkcs12SetKeychain(p12Coder, kcRef);
104	if(ortn) {
105		goto errOut;
106	}
107
108	/* We need a CSPDL handle for possible secure passphrase acquisition */
109	ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
110	if(ortn) {
111		SecImpExpDbg("SecKeychainGetCSPHandle error");
112		goto errOut;
113	}
114
115	/* explicit passphrase, or get one ourself? */
116	ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_String,
117		VP_Export, (CFTypeRef *)&phraseStr, &passKey);
118	if(ortn) {
119		goto errOut;
120	}
121	if(phraseStr != NULL) {
122		ortn = SecPkcs12SetMACPassphrase(p12Coder, phraseStr);
123		CFRelease(phraseStr);
124		if(ortn) {
125			SecImpExpDbg("SecPkcs12SetMACPassphrase error");
126			goto errOut;
127		}
128	}
129	else {
130		assert(passKey != NULL);
131		ortn = SecPkcs12SetMACPassKey(p12Coder, passKey);
132		if(ortn) {
133			SecImpExpDbg("SecPkcs12SetMACPassphrase error");
134			goto errOut;
135		}
136	}
137
138	ortn = SecPkcs12ExportKeychainItems(p12Coder, exportItems);
139	if(ortn) {
140		SecImpExpDbg("impExpPkcs12Export: SecPkcs12ExportKeychainItems failure");
141		goto errOut;
142	}
143
144	/* GO */
145	ortn = SecPkcs12Encode(p12Coder, &tmpData);
146	if(ortn) {
147		SecImpExpDbg("impExpPkcs12Export: SecPkcs12Encode failure");
148		goto errOut;
149	}
150
151	/* append encoded data to output */
152	CFDataAppendBytes(outData, CFDataGetBytePtr(tmpData), CFDataGetLength(tmpData));
153
154errOut:
155	SecPkcs12CoderRelease(p12Coder);
156	if(passKey != NULL) {
157		CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
158		free(passKey);
159	}
160	if(kcRef) {
161		CFRelease(kcRef);
162	}
163	if(exportItems) {
164		CFRelease(exportItems);
165	}
166	if(tmpData) {
167		CFRelease(tmpData);
168	}
169	return ortn;
170}
171
172OSStatus impExpPkcs7Export(
173	CFArrayRef							exportReps,		// SecExportReps
174	SecItemImportExportFlags			flags,			// kSecItemPemArmour, etc.
175	const SecKeyImportExportParameters	*keyParams,		// optional
176	CFMutableDataRef					outData)		// output appended here
177{
178	SecCmsSignedDataRef	sigd = NULL;
179	SecCertificateRef   certRef;
180	OSStatus			ortn;
181	CFIndex				numCerts = CFArrayGetCount(exportReps);
182	SecExportRep		*exportRep;
183	SecCmsContentInfoRef cinfo = NULL;
184	SecArenaPoolRef     arena = NULL;
185	SecCmsEncoderRef    ecx;
186	CSSM_DATA			output = { 0, NULL };
187
188	if(numCerts == 0) {
189		SecImpExpDbg("impExpPkcs7Export: no certs to export");
190		return errSecSuccess;
191	}
192
193    /* create the message object */
194    SecCmsMessageRef cmsg = SecCmsMessageCreate(NULL);
195    if(cmsg == NULL) {
196		SecImpExpDbg("impExpPkcs7Export: SecCmsMessageCreate failure");
197		return errSecInternalComponent;
198	}
199
200	/* get first cert */
201	exportRep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
202	assert(exportRep != NULL);
203	if(exportRep->externType() != kSecItemTypeCertificate) {
204		SecImpExpDbg("impExpPkcs7Export: non-cert item");
205		ortn = errSecParam;
206		goto errOut;
207	}
208	certRef = (SecCertificateRef)exportRep->kcItem();
209
210    /* build chain of objects: message->signedData->data */
211    sigd = SecCmsSignedDataCreateCertsOnly(cmsg, certRef, false);
212	if(sigd == NULL) {
213		SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataCreateCertsOnly failure");
214		ortn = errSecInternalComponent;
215		goto errOut;
216	}
217
218    for (CFIndex dex=1; dex<numCerts; dex++) {
219		exportRep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
220		assert(exportRep != NULL);
221		if(exportRep->externType() != kSecItemTypeCertificate) {
222			SecImpExpDbg("impExpPkcs7Export: non-cert item");
223			ortn = errSecParam;
224			goto errOut;
225		}
226		certRef = (SecCertificateRef)exportRep->kcItem();
227        ortn = SecCmsSignedDataAddCertChain(sigd, certRef);
228		if(ortn) {
229			SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataAddCertChain error");
230			goto errOut;
231		}
232    }
233
234    cinfo = SecCmsMessageGetContentInfo(cmsg);
235	if(cinfo == NULL) {
236		SecImpExpDbg("impExpPkcs7Export: SecCmsMessageGetContentInfo returned NULL");
237		ortn = errSecInternalComponent;
238		goto errOut;
239	}
240    ortn = SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd);
241	if(ortn) {
242		SecImpExpDbg("impExpPkcs7Export: SecCmsContentInfoSetContentSignedData error");
243		goto errOut;
244    }
245    cinfo = SecCmsSignedDataGetContentInfo(sigd);
246	if(cinfo == NULL) {
247		SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataGetContentInfo returned NULL");
248		ortn = errSecInternalComponent;
249		goto errOut;
250	}
251    ortn = SecCmsContentInfoSetContentData(cmsg, cinfo, NULL,
252				false /* FIXME - what's this? */);
253	if(ortn) {
254		SecImpExpDbg("impExpPkcs7Export: SecCmsContentInfoSetContentData error");
255		goto errOut;
256    }
257
258	/* Now encode it */
259    ortn = SecArenaPoolCreate(1024, &arena);
260	if(ortn) {
261		SecImpExpDbg("impExpPkcs7Export: SecArenaPoolCreate error");
262		goto errOut;
263	}
264	ortn = SecCmsEncoderCreate(cmsg,
265		   NULL, NULL,		/* DER output callback  */
266		   &output, arena,  /* destination storage  */
267		   NULL, NULL,		/* password callback    */
268		   NULL, NULL,		/* decrypt key callback */
269		   NULL, NULL,
270           &ecx );	/* detached digests    */
271	if(ortn) {
272		SecImpExpDbg("impExpPkcs7Export: SecCmsEncoderCreate error");
273		goto errOut;
274	}
275	ortn = SecCmsEncoderFinish(ecx);
276	if(ortn) {
277		SecImpExpDbg("impExpPkcs7Export: SecCmsEncoderFinish returned NULL");
278		goto errOut;
279	}
280
281	/* append encoded data to output */
282	CFDataAppendBytes(outData, output.Data, output.Length);
283
284
285errOut:
286    if(cmsg != NULL) {
287        SecCmsMessageDestroy(cmsg);
288	}
289	if(arena != NULL) {
290		SecArenaPoolFree(arena, false);
291	}
292	return ortn;
293}
294
295#pragma mark --- Aggregate Import routines ---
296
297/*
298 * For all of these, if a cspHand is specified instead of a keychain,
299 * the cspHand MUST be a CSPDL handle, not a raw CSP handle.
300 */
301OSStatus impExpPkcs12Import(
302	CFDataRef							inData,
303	SecItemImportExportFlags			flags,
304	const SecKeyImportExportParameters	*keyParams,			// optional
305	ImpPrivKeyImportState				&keyImportState,	// IN/OUT
306
307	/* caller must supply one of these */
308	SecKeychainRef						importKeychain, // optional
309	CSSM_CSP_HANDLE						cspHand,		// required
310	CFMutableArrayRef					outArray)		// optional, append here
311{
312	SecPkcs12CoderRef   p12Coder = NULL;
313	OSStatus			ortn;
314	CFIndex				numCerts;
315	CFIndex				numKeys;
316	CFIndex				dex;
317	CFMutableArrayRef	privKeys = NULL;
318	CSSM_KEY			*passKey = NULL;
319	CFStringRef			phraseStr = NULL;
320
321	/*
322	 * Optional private key attrs.
323	 * Although the PKCS12 library has its own defaults for these, we'll
324	 * set them explicitly to the defaults specified in our API if the
325	 * caller doesn't specify any.
326	 */
327	CSSM_KEYUSE keyUsage = CSSM_KEYUSE_ANY;
328	CSSM_KEYATTR_FLAGS keyAttrs = CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE |
329		CSSM_KEYATTR_RETURN_REF;
330
331	if( (keyParams == NULL) ||
332	    ( (keyParams->passphrase == NULL) &&
333		  !(keyParams->flags & kSecKeySecurePassphrase) ) ) {
334		/* passphrase mandatory */
335		return errSecPassphraseRequired;
336	}
337
338	/*
339	 * Set up a P12 decoder.
340	 */
341	ortn = SecPkcs12CoderCreate(&p12Coder);
342	if(ortn) {
343		SecImpExpDbg("SecPkcs12CoderCreate error");
344		return ortn;
345	}
346	/* subsequent errors to errOut: */
347
348	CSSM_CL_HANDLE clHand = cuClStartup();
349	CSSM_CSP_HANDLE rawCspHand = cuCspStartup(CSSM_TRUE);   // for CL
350	if((clHand == 0) || (rawCspHand == 0)) {
351		return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
352	}
353
354	assert(cspHand != CSSM_INVALID_HANDLE);
355	if(importKeychain != NULL) {
356		ortn = SecPkcs12SetKeychain(p12Coder, importKeychain);
357		if(ortn) {
358			SecImpExpDbg("SecPkcs12SetKeychain error");
359			goto errOut;
360		}
361	}
362	else {
363		if(cspHand == CSSM_INVALID_HANDLE) {
364			ortn = errSecParam;
365			goto errOut;
366		}
367		ortn = SecPkcs12SetCspHandle(p12Coder, cspHand);
368		if(ortn) {
369			SecImpExpDbg("SecPkcs12SetCspHandle error");
370			goto errOut;
371		}
372	}
373
374	/* explicit passphrase, or get one ourself? */
375	ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_String,
376		VP_Import, (CFTypeRef *)&phraseStr, &passKey);
377	if(ortn) {
378		goto errOut;
379	}
380	if(phraseStr != NULL) {
381		ortn = SecPkcs12SetMACPassphrase(p12Coder, phraseStr);
382		CFRelease(phraseStr);
383		if(ortn) {
384			SecImpExpDbg("SecPkcs12SetMACPassphrase error");
385			goto errOut;
386		}
387	}
388	else {
389		assert(passKey != NULL);
390		ortn = SecPkcs12SetMACPassKey(p12Coder, passKey);
391		if(ortn) {
392			SecImpExpDbg("SecPkcs12SetMACPassphrase error");
393			goto errOut;
394		}
395	}
396
397	if(keyImportState != PIS_NoLimit) {
398		bool foundOneKey = false;
399
400		/* allow either zero or one more private key */
401		if(keyImportState == PIS_NoMore) {
402			foundOneKey = true;
403		}
404		ortn = SecPkcs12LimitPrivateKeyImport(p12Coder, foundOneKey);
405		if(ortn) {
406			SecImpExpDbg("SecPkcs12LimitPrivateKeyImport error");
407			goto errOut;
408		}
409	}
410
411	if(keyParams != NULL) {
412		if(keyParams->keyUsage != 0) {
413			keyUsage = keyParams->keyUsage;
414		}
415		if(keyParams->keyAttributes != 0) {
416			keyAttrs = keyParams->keyAttributes;
417		}
418		if(keyParams->flags & kSecKeyNoAccessControl) {
419			ortn = SecPkcs12SetAccess(p12Coder, NULL);
420			if(ortn) {
421				SecImpExpDbg("SecPkcs12SetAccess error");
422				goto errOut;
423			}
424		}
425		else if(keyParams->accessRef != NULL) {
426			ortn = SecPkcs12SetAccess(p12Coder, keyParams->accessRef);
427			if(ortn) {
428				SecImpExpDbg("SecPkcs12SetAccess error");
429				goto errOut;
430			}
431		}
432		/* else default ACL */
433	}
434	ortn = SecPkcs12SetKeyUsage(p12Coder, keyUsage);
435	if(ortn) {
436		SecImpExpDbg("SecPkcs12SetKeyUsage error");
437		goto errOut;
438	}
439	ortn = SecPkcs12SetKeyAttrs(p12Coder, keyAttrs);
440	if(ortn) {
441		SecImpExpDbg("SecPkcs12SetKeyAttrs error");
442		goto errOut;
443	}
444
445	/* GO */
446	ortn = SecPkcs12Decode(p12Coder, inData);
447	if(ortn) {
448		SecImpExpDbg("SecPkcs12Decode error");
449		goto errOut;
450	}
451
452	/*
453	 * About to process SecKeychainItemRefs.
454	 * This whole mess is irrelevant if the caller doesn't
455	 * want an array of SecKeychainItemRefs.
456	 */
457	if(outArray == NULL) {
458		goto errOut;
459	}
460
461	ortn = SecPkcs12CertificateCount(p12Coder, &numCerts);
462	if(ortn) {
463		SecImpExpDbg("SecPkcs12CertificateCount error");
464		goto errOut;
465	}
466	ortn = SecPkcs12PrivateKeyCount(p12Coder, &numKeys);
467	if(ortn) {
468		SecImpExpDbg("SecPkcs12PrivateKeyCount error");
469		goto errOut;
470	}
471
472	/*
473	 * Match up certs and keys to create SecIdentityRefs.
474	 * First create a temporary array of the private keys
475	 * found by the P12 module.
476	 *
477	 * FIXME we're working with a P12 module which can not
478	 * vend SecKeyRefs.....this will hopefully, eventually,
479	 * change.
480	 */
481	privKeys = CFArrayCreateMutable(NULL, numKeys, NULL);
482	for(dex=0; dex<numKeys; dex++) {
483		CSSM_KEY_PTR privKey;
484		ortn = SecPkcs12GetCssmPrivateKey(p12Coder,
485			dex, &privKey, NULL, NULL, NULL);
486		CFArrayAppendValue(privKeys, privKey);
487	}
488
489	/*
490	 * Now go through all certs, searching for a matching private
491	 * key. When we find a match we try to create an identity from the
492	 * cert, which might fail for a number of reasons, currently including
493	 * the fact that there is no way to create an identity with a key
494	 * which does not reside on a keychain. (Such is the case here when
495	 * caller has not specified a keychain.) If that works we skip the
496	 * cert, delete that key from the privKeys array, and append the
497	 * indentity to outArray. If no identity is found we append the
498	 * cert to outArray. At the end of this loop, remaining
499	 * items in privKeys (of which there will typically be none) are
500	 * also appended to outArray.
501	 */
502	for(dex=0; dex<numCerts; dex++) {
503		SecCertificateRef   certRef = NULL;				// created by P12
504        SecCertificateRef   importedCertRef = NULL;		// owned by Sec layer
505		CSSM_KEY_PTR		pubKey = NULL;				// mallocd by CL
506		CSSM_KEY_PTR		privKey = NULL;				// owned by P12
507		CSSM_DATA			certData;					// owned by Sec layer
508		CSSM_DATA			pubKeyDigest = {0, NULL};   // mallocd by CSP
509		CSSM_DATA			privKeyDigest = {0, NULL};  // mallocd by CSP
510		bool				foundIdentity = false;
511
512		ortn = SecPkcs12CopyCertificate(p12Coder, dex, &certRef,
513			NULL, NULL, NULL);
514		if(ortn) {
515			/* should never happen */
516			SecImpExpDbg("SecPkcs12CopyCertificate error");
517			goto errOut;
518		}
519		/* subsequent errors in this loop to loopEnd: */
520
521		if(importKeychain == NULL) {
522			/* Skip the Identity match - just return keys and certs */
523			goto loopEnd;
524		}
525
526        /* the SecPkcs12CopyCertificate function returns a floating
527         * certificate without a keychain. We must update it now that
528         * it has been added to importKeychain.
529         */
530        {
531        StorageManager::KeychainList keychains;
532        globals().storageManager.optionalSearchList(importKeychain, keychains);
533        SecPointer<Certificate> cert = Certificate::required(certRef);
534        importedCertRef = cert->findInKeychain(keychains)->handle();
535        }
536
537		/* Get digest of this cert's public key */
538		ortn = SecCertificateGetData(importedCertRef, &certData);
539		if(ortn) {
540			SecImpExpDbg("SecCertificateGetData error");
541			goto loopEnd;
542		}
543		ortn = CSSM_CL_CertGetKeyInfo(clHand, &certData, &pubKey);
544		if(ortn) {
545			SecImpExpDbg("SecCertificateGetData error");
546			goto loopEnd;
547		}
548		ortn = impExpKeyDigest(rawCspHand, pubKey, &pubKeyDigest);
549		if(ortn) {
550			goto loopEnd;
551		}
552
553		/*
554		 * Now search for a private key with this same digest
555		 */
556		numKeys = CFArrayGetCount(privKeys);
557		for(CFIndex privDex=0; privDex<numKeys; privDex++) {
558			privKey = (CSSM_KEY_PTR)CFArrayGetValueAtIndex(privKeys, privDex);
559			assert(privKey != NULL);
560			ortn = impExpKeyDigest(cspHand, privKey, &privKeyDigest);
561			if(ortn) {
562				goto loopEnd;
563			}
564			CSSM_BOOL digestMatch = cuCompareCssmData(&pubKeyDigest, &privKeyDigest);
565			impExpFreeCssmMemory(cspHand, privKeyDigest.Data);
566			if(digestMatch) {
567				/*
568				 * MATCH: try to cook up Identity.
569				 * TBD: I expect some work will be needed here when
570				 * Sec layer can handle floating keys. One thing
571				 * that would be nice would be if we could create an identity
572				 * FROM a given SecCertRef and a SecKeyRef, even if
573				 * the SecKeyRef is floating.
574				 *
575				 * NOTE: you might think that we could do a
576				 * SecIdentityCreateWithCertificate() before, or even without,
577				 * doing a digest match....but that could "work" without
578				 * us having imported any keys at all, if the appropriate
579				 * private key were already there. Doing the digest match
580				 * guarantees the uniqueness of the key item in the DB.
581				 */
582				SecIdentityRef idRef = NULL;
583				ortn = SecIdentityCreateWithCertificate(importKeychain,
584					importedCertRef, &idRef);
585				if(ortn == errSecSuccess) {
586					/*
587					 * Got one!
588					 *
589					 * -- add Identity to outArray
590					 * -- remove privKey from privKeys array
591					 * -- skip to next cert
592					 */
593					SecImpExpDbg("P12Import: generating a SecIdentityRef");
594					assert(outArray != NULL);
595					CFArrayAppendValue(outArray, idRef);
596					CFRelease(idRef);		// array holds only ref
597					idRef = NULL;
598					CFArrayRemoveValueAtIndex(privKeys, privDex);
599					foundIdentity = true;
600					goto loopEnd;
601				}   /* ID create worked, else try next key */
602			}		/* digest match */
603		}			/* searching thru privKeys */
604	loopEnd:
605		/* free resources allocated in this loop */
606		assert(certRef != NULL);
607		if(!foundIdentity ) {
608			/* No private key for this cert: give to caller */
609			assert(outArray != NULL);
610			CFArrayAppendValue(outArray, certRef);
611		}
612		CFRelease(certRef);				// outArray holds only ref
613		certRef = NULL;
614        if (importedCertRef) {
615            CFRelease(importedCertRef);
616            importedCertRef = NULL;
617        }
618		if(pubKey != NULL) {
619			/* technically invalid, the CL used some CSP handle we
620			 * don't have access to to get this... */
621			CSSM_FreeKey(rawCspHand, NULL, pubKey, CSSM_FALSE);
622			impExpFreeCssmMemory(clHand, pubKey);
623			pubKey = NULL;
624		}
625		if(pubKeyDigest.Data != NULL) {
626			impExpFreeCssmMemory(rawCspHand, pubKeyDigest.Data);
627			pubKeyDigest.Data = NULL;
628		}
629		if(ortn) {
630			goto errOut;
631		}
632	}
633
634errOut:
635	/*
636	 * One last thing: pass any remaining (non-identity) keys to caller.
637	 * For now, the keys are CSSM_KEYs owned by the P12 coder object, we
638	 * don't have to release them. When P12 can vend SecKeyRefs, we release the
639	 * keys here.
640	 */
641
642	 /*
643		The code below has no net effect, except for generating a leak. This was
644		found while investigating
645			<rdar://problem/8799913> SecItemImport() leaking
646		Code like this will need to be added when we return SecIdentityRefs in
647		the "in memory" case (destKeychain = NULL). Currently, when importing to
648		a physical keychain, the returned item array contains SecIdentityRefs,
649		whereas the "in memory" case returns SecCertificateRefs. See
650			<rdar://problem/8862809> ER: SecItemImport should return SecIdentityRefs in the "in memory" case
651
652	*/
653#if 0
654	if(privKeys) {
655		if(ortn == errSecSuccess) {		// TBD OR keys are SecKeyRefs
656			numKeys = CFArrayGetCount(privKeys);
657			for(dex=0; dex<numKeys; dex++) {
658				SecKeyRef keyRef;
659				CSSM_KEY_PTR privKey =
660					(CSSM_KEY_PTR)CFArrayGetValueAtIndex(privKeys, dex);
661				assert(privKey != NULL);
662				if(ortn == errSecSuccess) {
663					/* only do this on complete success so far */
664					ortn = SecKeyCreateWithCSSMKey(privKey, &keyRef);
665					if(ortn) {
666						SecImpExpDbg("SecKeyCreateWithCSSMKey error");
667					}
668					/* keep going for CFRelease */
669					if (keyRef)
670						CFRelease(keyRef);
671				}
672				/* TBD CFRelease the SecKeyRef */
673			}   /* for each privKey */
674		}		/* success so far */
675	}
676#endif
677
678	SecPkcs12CoderRelease(p12Coder);
679	if(passKey != NULL) {
680		CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
681		free(passKey);
682	}
683	if(privKeys != NULL) {
684		CFRelease(privKeys);
685	}
686	if(clHand != 0) {
687		cuClDetachUnload(clHand);
688	}
689	if(rawCspHand != 0) {
690		cuCspDetachUnload(rawCspHand, CSSM_TRUE);
691	}
692	return ortn;
693}
694
695OSStatus impExpPkcs7Import(
696	CFDataRef							inData,
697	SecItemImportExportFlags			flags,
698	const SecKeyImportExportParameters	*keyParams,		// optional
699	SecKeychainRef						importKeychain, // optional
700	CFMutableArrayRef					outArray)		// optional, append here
701{
702    SecCmsDecoderRef        decoderContext;
703    SecCmsMessageRef        cmsMessage = NULL;
704    SecCmsContentInfoRef    contentInfo;
705    SecCmsSignedDataRef		signedData;
706    int						contentLevelCount;
707    int						i;
708    SECOidTag				contentTypeTag;
709    OSStatus				result;
710	OSStatus				ourRtn = errSecSuccess;
711
712    /* decode the message */
713    result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext);
714    if (result != 0) {
715        ourRtn = result;
716        goto errOut;
717    }
718    result = SecCmsDecoderUpdate(decoderContext, CFDataGetBytePtr(inData),
719		CFDataGetLength(inData));
720	if (result != 0) {
721		/* any useful status here? */
722		SecImpExpDbg("SecCmsDecoderUpdate error");
723		ourRtn = errSecUnknownFormat;
724        SecCmsDecoderDestroy(decoderContext);
725		goto errOut;
726	}
727
728    ourRtn = SecCmsDecoderFinish(decoderContext, &cmsMessage);
729    if (ourRtn) {
730		SecImpExpDbg("SecCmsDecoderFinish error");
731		ourRtn = errSecUnknownFormat;
732		goto errOut;
733	}
734
735    // process the results
736    contentLevelCount = SecCmsMessageContentLevelCount (cmsMessage);
737
738    for (i = 0; i < contentLevelCount; ++i)
739    {
740        // get content information
741        contentInfo = SecCmsMessageContentLevel (cmsMessage, i);
742        contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
743
744        switch (contentTypeTag)
745        {
746            case SEC_OID_PKCS7_SIGNED_DATA:
747            {
748				/* I guess this the only interesting field */
749                signedData =
750					(SecCmsSignedDataRef) SecCmsContentInfoGetContent (contentInfo);
751                if (signedData == NULL) {
752					SecImpExpDbg("SecCmsContentInfoGetContent returned NULL");
753					ourRtn = errSecUnknownFormat;
754					goto errOut;
755				}
756
757                // import the certificates
758                CSSM_DATA **outCerts = SecCmsSignedDataGetCertificateList(signedData);
759				if(outCerts == NULL) {
760					SecImpExpDbg("SecCmsSignedDataGetCertificateList returned NULL");
761					ourRtn = errSecUnknownFormat;
762					goto errOut;
763				}
764
765				/* Returned value is NULL-terminated array */
766				unsigned count = 0;
767				CSSM_DATA **array = outCerts;
768				if (array) {
769					while (*array++) {
770						count++;
771					}
772				}
773				if(count == 0) {
774					SecImpExpDbg("No certs found in apparently good PKCS7 blob");
775					goto errOut;
776				}
777
778				for(unsigned dex=0; dex<count; dex++) {
779					ourRtn = impExpImportCertCommon(outCerts[dex], importKeychain,
780						outArray);
781					if(ourRtn) {
782						goto errOut;
783					}
784				}
785                break;
786            }
787            default:
788                break;
789        }
790    }
791errOut:
792	if(cmsMessage) {
793		SecCmsMessageDestroy(cmsMessage);
794	}
795    return ourRtn;
796
797}
798
799/*
800 * Import a netscape-cert-sequence. Suitable for low-cost guessing when neither
801 * importKeychain nor outArray is specified.
802 */
803OSStatus impExpNetscapeCertImport(
804	CFDataRef							inData,
805	SecItemImportExportFlags			flags,
806	const SecKeyImportExportParameters	*keyParams,		// optional
807	SecKeychainRef						importKeychain, // optional
808	CFMutableArrayRef					outArray)		// optional, append here
809{
810	SecNssCoder coder;
811	NetscapeCertSequence certSeq;
812
813	/* DER-decode */
814	memset(&certSeq, 0, sizeof(certSeq));
815	PRErrorCode perr = coder.decode(CFDataGetBytePtr(inData),
816		CFDataGetLength(inData),
817		NetscapeCertSequenceTemplate,
818		&certSeq);
819	if(perr) {
820		SecImpExpDbg("impExpNetscapeCertImport: DER decode failure");
821		return errSecUnknownFormat;
822	}
823
824	/* verify (contentType == netscape-cert-sequence) */
825	if(!cuCompareOid(&CSSMOID_NetscapeCertSequence, &certSeq.contentType)) {
826		SecImpExpDbg("impExpNetscapeCertImport: OID mismatch");
827		return errSecUnknownFormat;
828	}
829
830	/* Extract certs in CSSM_DATA form, return to caller */
831	unsigned numCerts = nssArraySize((const void **)certSeq.certs);
832	for(unsigned i=0; i<numCerts; i++) {
833		CSSM_DATA *cert = certSeq.certs[i];
834		OSStatus ortn = impExpImportCertCommon(cert, importKeychain, outArray);
835		if(ortn) {
836			return ortn;
837		}
838	}
839	return errSecSuccess;
840}
841
842#pragma mark --- Utilities ---
843
844OSStatus impExpImportCertCommon(
845	const CSSM_DATA		*cdata,
846	SecKeychainRef		importKeychain, // optional
847	CFMutableArrayRef	outArray)		// optional, append here
848{
849	OSStatus ortn = errSecSuccess;
850	SecCertificateRef certRef;
851
852	if (!cdata)
853		return errSecUnsupportedFormat;
854
855	CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)cdata->Data, (CFIndex)cdata->Length, kCFAllocatorNull);
856	/* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */
857	if (!data)
858		return errSecUnsupportedFormat;
859
860	certRef = SecCertificateCreateWithData(kCFAllocatorDefault, data);
861	CFRelease(data); /* certRef has its own copy of the data now */
862	if(!certRef) {
863		SecImpExpDbg("impExpHandleCert error\n");
864		return errSecUnsupportedFormat;
865	}
866
867	if(importKeychain != NULL) {
868		ortn = SecCertificateAddToKeychain(certRef, importKeychain);
869		if(ortn) {
870			SecImpExpDbg("SecCertificateAddToKeychain error\n");
871			CFRelease(certRef);
872			return ortn;
873		}
874	}
875	if(outArray != NULL) {
876		CFArrayAppendValue(outArray, certRef);
877	}
878	CFRelease(certRef);
879	return ortn;
880}
881
882