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