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