]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/SecImportExportAgg.cpp
2f1bafad2d3df6e078ad432c890df6f381482a59
[apple/security.git] / libsecurity_keychain / lib / SecImportExportAgg.cpp
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 <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
33 #include <security_pkcs12/SecPkcs12.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
46 using namespace Security;
47 using namespace KeychainCore;
48
49 #pragma mark --- Aggregate Export routines ---
50
51 OSStatus 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 = noErr;
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
154 errOut:
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
172 OSStatus 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 noErr;
191 }
192
193 /* create the message object */
194 SecCmsMessageRef cmsg = SecCmsMessageCreate(NULL);
195 if(cmsg == NULL) {
196 SecImpExpDbg("impExpPkcs7Export: SecCmsMessageCreate failure");
197 return internalComponentErr;
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 = paramErr;
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 = internalComponentErr;
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 = paramErr;
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 = internalComponentErr;
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 = internalComponentErr;
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
285 errOut:
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 */
301 OSStatus 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 = paramErr;
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 == noErr) {
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
634 errOut:
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 == noErr) { // 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 == noErr) {
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
695 OSStatus 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 = noErr;
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 }
791 errOut:
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 */
803 OSStatus 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 noErr;
840 }
841
842 #pragma mark --- Utilities ---
843
844 OSStatus impExpImportCertCommon(
845 const CSSM_DATA *cdata,
846 SecKeychainRef importKeychain, // optional
847 CFMutableArrayRef outArray) // optional, append here
848 {
849 OSStatus ortn = noErr;
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