]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecImportExportAgg.cpp
96b0034123e4adc4f999e83778dd4429117fa88f
[apple/security.git] / OSX / libsecurity_keychain / lib / 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 /* Get digest of this cert's public key */
551 ortn = SecCertificateGetData(importedCertRef, &certData);
552 if(ortn) {
553 SecImpExpDbg("SecCertificateGetData error");
554 goto loopEnd;
555 }
556 ortn = CSSM_CL_CertGetKeyInfo(clHand, &certData, &pubKey);
557 if(ortn) {
558 SecImpExpDbg("SecCertificateGetData error");
559 goto loopEnd;
560 }
561 ortn = impExpKeyDigest(rawCspHand, pubKey, &pubKeyDigest);
562 if(ortn) {
563 goto loopEnd;
564 }
565
566 /*
567 * Now search for a private key with this same digest
568 */
569 numKeys = CFArrayGetCount(privKeys);
570 for(CFIndex privDex=0; privDex<numKeys; privDex++) {
571 privKey = (CSSM_KEY_PTR)CFArrayGetValueAtIndex(privKeys, privDex);
572 assert(privKey != NULL);
573 ortn = impExpKeyDigest(cspHand, privKey, &privKeyDigest);
574 if(ortn) {
575 goto loopEnd;
576 }
577 CSSM_BOOL digestMatch = cuCompareCssmData(&pubKeyDigest, &privKeyDigest);
578 impExpFreeCssmMemory(cspHand, privKeyDigest.Data);
579 if(digestMatch) {
580 /*
581 * MATCH: try to cook up Identity.
582 * TBD: I expect some work will be needed here when
583 * Sec layer can handle floating keys. One thing
584 * that would be nice would be if we could create an identity
585 * FROM a given SecCertRef and a SecKeyRef, even if
586 * the SecKeyRef is floating.
587 *
588 * NOTE: you might think that we could do a
589 * SecIdentityCreateWithCertificate() before, or even without,
590 * doing a digest match....but that could "work" without
591 * us having imported any keys at all, if the appropriate
592 * private key were already there. Doing the digest match
593 * guarantees the uniqueness of the key item in the DB.
594 */
595 SecIdentityRef idRef = NULL;
596 ortn = SecIdentityCreateWithCertificate(importKeychain,
597 importedCertRef, &idRef);
598 if(ortn == errSecSuccess) {
599 /*
600 * Got one!
601 *
602 * -- add Identity to outArray
603 * -- remove privKey from privKeys array
604 * -- skip to next cert
605 */
606 SecImpExpDbg("P12Import: generating a SecIdentityRef");
607 assert(outArray != NULL);
608 CFArrayAppendValue(outArray, idRef);
609 CFRelease(idRef); // array holds only ref
610 idRef = NULL;
611 CFArrayRemoveValueAtIndex(privKeys, privDex);
612 foundIdentity = true;
613 goto loopEnd;
614 } /* ID create worked, else try next key */
615 } /* digest match */
616 } /* searching thru privKeys */
617 loopEnd:
618 /* free resources allocated in this loop */
619 assert(certRef != NULL);
620 if(!foundIdentity ) {
621 /* No private key for this cert: give to caller */
622 assert(outArray != NULL);
623 CFArrayAppendValue(outArray, certRef);
624 }
625 CFRelease(certRef); // outArray holds only ref
626 certRef = NULL;
627 if (importedCertRef) {
628 CFRelease(importedCertRef);
629 importedCertRef = NULL;
630 }
631 if(pubKey != NULL) {
632 /* technically invalid, the CL used some CSP handle we
633 * don't have access to to get this... */
634 CSSM_FreeKey(rawCspHand, NULL, pubKey, CSSM_FALSE);
635 impExpFreeCssmMemory(clHand, pubKey);
636 pubKey = NULL;
637 }
638 if(pubKeyDigest.Data != NULL) {
639 impExpFreeCssmMemory(rawCspHand, pubKeyDigest.Data);
640 pubKeyDigest.Data = NULL;
641 }
642 if(ortn) {
643 goto errOut;
644 }
645 }
646
647 errOut:
648 /*
649 * One last thing: pass any remaining (non-identity) keys to caller.
650 * For now, the keys are CSSM_KEYs owned by the P12 coder object, we
651 * don't have to release them. When P12 can vend SecKeyRefs, we release the
652 * keys here.
653 */
654
655 /*
656 The code below has no net effect, except for generating a leak. This was
657 found while investigating
658 <rdar://problem/8799913> SecItemImport() leaking
659 Code like this will need to be added when we return SecIdentityRefs in
660 the "in memory" case (destKeychain = NULL). Currently, when importing to
661 a physical keychain, the returned item array contains SecIdentityRefs,
662 whereas the "in memory" case returns SecCertificateRefs. See
663 <rdar://problem/8862809> ER: SecItemImport should return SecIdentityRefs in the "in memory" case
664
665 */
666 #if 0
667 if(privKeys) {
668 if(ortn == errSecSuccess) { // TBD OR keys are SecKeyRefs
669 numKeys = CFArrayGetCount(privKeys);
670 for(dex=0; dex<numKeys; dex++) {
671 SecKeyRef keyRef;
672 CSSM_KEY_PTR privKey =
673 (CSSM_KEY_PTR)CFArrayGetValueAtIndex(privKeys, dex);
674 assert(privKey != NULL);
675 if(ortn == errSecSuccess) {
676 /* only do this on complete success so far */
677 ortn = SecKeyCreateWithCSSMKey(privKey, &keyRef);
678 if(ortn) {
679 SecImpExpDbg("SecKeyCreateWithCSSMKey error");
680 }
681 /* keep going for CFRelease */
682 if (keyRef)
683 CFRelease(keyRef);
684 }
685 /* TBD CFRelease the SecKeyRef */
686 } /* for each privKey */
687 } /* success so far */
688 }
689 #endif
690
691 SecPkcs12CoderRelease(p12Coder);
692 if(passKey != NULL) {
693 CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
694 free(passKey);
695 }
696 if(privKeys != NULL) {
697 CFRelease(privKeys);
698 }
699 if(clHand != 0) {
700 cuClDetachUnload(clHand);
701 }
702 if(rawCspHand != 0) {
703 cuCspDetachUnload(rawCspHand, CSSM_TRUE);
704 }
705 return ortn;
706 }
707
708 OSStatus impExpPkcs7Import(
709 CFDataRef inData,
710 SecItemImportExportFlags flags,
711 const SecKeyImportExportParameters *keyParams, // optional
712 SecKeychainRef importKeychain, // optional
713 CFMutableArrayRef outArray) // optional, append here
714 {
715 SecCmsDecoderRef decoderContext;
716 SecCmsMessageRef cmsMessage = NULL;
717 SecCmsContentInfoRef contentInfo;
718 SecCmsSignedDataRef signedData;
719 int contentLevelCount;
720 int i;
721 SECOidTag contentTypeTag;
722 OSStatus result;
723 OSStatus ourRtn = errSecSuccess;
724
725 /* decode the message */
726 result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext);
727 if (result != 0) {
728 ourRtn = result;
729 goto errOut;
730 }
731 result = SecCmsDecoderUpdate(decoderContext, CFDataGetBytePtr(inData),
732 CFDataGetLength(inData));
733 if (result != 0) {
734 /* any useful status here? */
735 SecImpExpDbg("SecCmsDecoderUpdate error");
736 ourRtn = errSecUnknownFormat;
737 SecCmsDecoderDestroy(decoderContext);
738 goto errOut;
739 }
740
741 ourRtn = SecCmsDecoderFinish(decoderContext, &cmsMessage);
742 if (ourRtn) {
743 SecImpExpDbg("SecCmsDecoderFinish error");
744 ourRtn = errSecUnknownFormat;
745 goto errOut;
746 }
747
748 // process the results
749 contentLevelCount = SecCmsMessageContentLevelCount (cmsMessage);
750
751 for (i = 0; i < contentLevelCount; ++i)
752 {
753 // get content information
754 contentInfo = SecCmsMessageContentLevel (cmsMessage, i);
755 contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
756
757 switch (contentTypeTag)
758 {
759 case SEC_OID_PKCS7_SIGNED_DATA:
760 {
761 /* I guess this the only interesting field */
762 signedData =
763 (SecCmsSignedDataRef) SecCmsContentInfoGetContent (contentInfo);
764 if (signedData == NULL) {
765 SecImpExpDbg("SecCmsContentInfoGetContent returned NULL");
766 ourRtn = errSecUnknownFormat;
767 goto errOut;
768 }
769
770 // import the certificates
771 CSSM_DATA **outCerts = SecCmsSignedDataGetCertificateList(signedData);
772 if(outCerts == NULL) {
773 SecImpExpDbg("SecCmsSignedDataGetCertificateList returned NULL");
774 ourRtn = errSecUnknownFormat;
775 goto errOut;
776 }
777
778 /* Returned value is NULL-terminated array */
779 unsigned count = 0;
780 CSSM_DATA **array = outCerts;
781 if (array) {
782 while (*array++) {
783 count++;
784 }
785 }
786 if(count == 0) {
787 SecImpExpDbg("No certs found in apparently good PKCS7 blob");
788 goto errOut;
789 }
790
791 for(unsigned dex=0; dex<count; dex++) {
792 ourRtn = impExpImportCertCommon(outCerts[dex], importKeychain,
793 outArray);
794 if(ourRtn) {
795 goto errOut;
796 }
797 }
798 break;
799 }
800 default:
801 break;
802 }
803 }
804 errOut:
805 if(cmsMessage) {
806 SecCmsMessageDestroy(cmsMessage);
807 }
808 return ourRtn;
809
810 }
811
812 /*
813 * Import a netscape-cert-sequence. Suitable for low-cost guessing when neither
814 * importKeychain nor outArray is specified.
815 */
816 OSStatus impExpNetscapeCertImport(
817 CFDataRef inData,
818 SecItemImportExportFlags flags,
819 const SecKeyImportExportParameters *keyParams, // optional
820 SecKeychainRef importKeychain, // optional
821 CFMutableArrayRef outArray) // optional, append here
822 {
823 SecNssCoder coder;
824 NetscapeCertSequence certSeq;
825
826 /* DER-decode */
827 memset(&certSeq, 0, sizeof(certSeq));
828 PRErrorCode perr = coder.decode(CFDataGetBytePtr(inData),
829 CFDataGetLength(inData),
830 NetscapeCertSequenceTemplate,
831 &certSeq);
832 if(perr) {
833 SecImpExpDbg("impExpNetscapeCertImport: DER decode failure");
834 return errSecUnknownFormat;
835 }
836
837 /* verify (contentType == netscape-cert-sequence) */
838 if(!cuCompareOid(&CSSMOID_NetscapeCertSequence, &certSeq.contentType)) {
839 SecImpExpDbg("impExpNetscapeCertImport: OID mismatch");
840 return errSecUnknownFormat;
841 }
842
843 /* Extract certs in CSSM_DATA form, return to caller */
844 unsigned numCerts = nssArraySize((const void **)certSeq.certs);
845 for(unsigned i=0; i<numCerts; i++) {
846 CSSM_DATA *cert = certSeq.certs[i];
847 OSStatus ortn = impExpImportCertCommon(cert, importKeychain, outArray);
848 if(ortn) {
849 return ortn;
850 }
851 }
852 return errSecSuccess;
853 }
854
855 #pragma mark --- Utilities ---
856
857 OSStatus impExpImportCertCommon(
858 const CSSM_DATA *cdata,
859 SecKeychainRef importKeychain, // optional
860 CFMutableArrayRef outArray) // optional, append here
861 {
862 OSStatus ortn = errSecSuccess;
863 SecCertificateRef certRef;
864
865 if (!cdata)
866 return errSecUnsupportedFormat;
867
868 CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)cdata->Data, (CFIndex)cdata->Length, kCFAllocatorNull);
869 /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */
870 if (!data)
871 return errSecUnsupportedFormat;
872
873 certRef = SecCertificateCreateWithData(kCFAllocatorDefault, data);
874 CFRelease(data); /* certRef has its own copy of the data now */
875 if(!certRef) {
876 SecImpExpDbg("impExpHandleCert error\n");
877 return errSecUnsupportedFormat;
878 }
879
880 if(importKeychain != NULL) {
881 ortn = SecCertificateAddToKeychain(certRef, importKeychain);
882 if(ortn) {
883 SecImpExpDbg("SecCertificateAddToKeychain error\n");
884 CFRelease(certRef);
885 return ortn;
886 }
887 }
888 if(outArray != NULL) {
889 CFArrayAppendValue(outArray, certRef);
890 }
891 CFRelease(certRef);
892 return ortn;
893 }
894