1/* 2 * Copyright (c) 2003-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 24/* 25 * pkcs12Keychain.h - P12Coder keychain-related functions. 26 */ 27 28#include "pkcs12Coder.h" 29#include "pkcs12Templates.h" 30#include "pkcs12Utils.h" 31#include "pkcs12Debug.h" 32#include "pkcs12Crypto.h" 33#include <Security/cssmerr.h> 34#include <security_cdsa_utils/cuDbUtils.h> // cuAddCrlToDb() 35#include <security_asn1/nssUtils.h> 36#include <security_cdsa_utilities/KeySchema.h> /* private API */ 37#include <security_keychain/SecImportExportCrypto.h> /* private API */ 38 39/* 40 * Store the results of a successful decode in app-specified 41 * keychain per mImportFlags. Also assign public key hash attributes to any 42 * private keys found. 43 */ 44void P12Coder::storeDecodeResults() 45{ 46 assert(mKeychain != NULL); 47 assert(mDlDbHand.DLHandle != 0); 48 if(mImportFlags & kSecImportKeys) { 49 setPrivateKeyHashes(); 50 } 51 if(mImportFlags & kSecImportCertificates) { 52 for(unsigned dex=0; dex<numCerts(); dex++) { 53 P12CertBag *certBag = mCerts[dex]; 54 SecCertificateRef secCert = certBag->getSecCert(); 55 OSStatus ortn = SecCertificateAddToKeychain(secCert, mKeychain); 56 CFRelease(secCert); 57 switch(ortn) { 58 case errSecSuccess: // normal 59 p12DecodeLog("cert added to keychain"); 60 break; 61 case errSecDuplicateItem: // dup cert, OK< skip 62 p12DecodeLog("skipping dup cert"); 63 break; 64 default: 65 p12ErrorLog("SecCertificateAddToKeychain failure\n"); 66 MacOSError::throwMe(ortn); 67 } 68 } 69 } 70 71 if(mImportFlags & kSecImportCRLs) { 72 for(unsigned dex=0; dex<numCrls(); dex++) { 73 P12CrlBag *crlBag = mCrls[dex]; 74 CSSM_RETURN crtn = cuAddCrlToDb(mDlDbHand, 75 clHand(), 76 &crlBag->crlData(), 77 NULL); // no URI known 78 switch(crtn) { 79 case CSSM_OK: // normal 80 p12DecodeLog("CRL added to keychain"); 81 break; 82 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: // dup, ignore 83 p12DecodeLog("skipping dup CRL"); 84 break; 85 default: 86 p12LogCssmError("Error adding CRL to keychain", crtn); 87 CssmError::throwMe(crtn); 88 } 89 } 90 } 91 92 /* If all of that succeeded, post notification for imported keys */ 93 if(mImportFlags & kSecImportKeys) { 94 notifyKeyImport(); 95 } 96} 97 98/* 99 * Assign appropriate public key hash attribute to each 100 * private key. 101 */ 102void P12Coder::setPrivateKeyHashes() 103{ 104 CSSM_KEY_PTR newKey; 105 106 for(unsigned dex=0; dex<numKeys(); dex++) { 107 P12KeyBag *keyBag = mKeys[dex]; 108 109 CSSM_DATA newLabel = {0, NULL}; 110 CFStringRef friendlyName = keyBag->friendlyName(); 111 newKey = NULL; 112 CSSM_RETURN crtn = p12SetPubKeyHash(mCspHand, 113 mDlDbHand, 114 keyBag->label(), 115 p12StringToUtf8(friendlyName, mCoder), 116 mCoder, 117 newLabel, 118 newKey); 119 if(friendlyName) { 120 CFRelease(friendlyName); 121 } 122 switch(crtn) { 123 case CSSM_OK: 124 /* update key's label in case we have to delete on error */ 125 keyBag->setLabel(newLabel); 126 p12DecodeLog("set pub key hash for private key"); 127 break; 128 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: 129 /* 130 * Special case: update keyBag's CSSM_KEY and proceed without error 131 */ 132 p12DecodeLog("ignoring dup private key"); 133 assert(newKey != NULL); 134 keyBag->setKey(newKey); 135 keyBag->dupKey(true); 136 /* update key's label in case we have to delete on error */ 137 keyBag->setLabel(newLabel); 138 break; 139 default: 140 p12ErrorLog("p12SetPubKeyHash failure\n"); 141 CssmError::throwMe(crtn); 142 } 143 } 144} 145 146/* 147 * Post keychain notification for imported keys. 148 */ 149void P12Coder::notifyKeyImport() 150{ 151 if(mKeychain == NULL) { 152 /* Can't notify if user only gave us DLDB */ 153 return; 154 } 155 for(unsigned dex=0; dex<numKeys(); dex++) { 156 P12KeyBag *keyBag = mKeys[dex]; 157 if(keyBag->dupKey()) { 158 /* no notification for keys we merely looked up */ 159 continue; 160 } 161 CssmData &labelData = CssmData::overlay(keyBag->label()); 162 OSStatus ortn = impExpKeyNotify(mKeychain, labelData, *keyBag->key()); 163 if(ortn) { 164 p12ErrorLog("notifyKeyImport: impExpKeyNotify returned %ld\n", (unsigned long)ortn); 165 MacOSError::throwMe(ortn); 166 } 167 } 168} 169 170/* 171 * Given a P12KeyBag, find a matching P12CertBag. Keys and certs 172 * "match" if their localKeyIds match. Returns NULL if not found. 173 */ 174P12CertBag *P12Coder::findCertForKey( 175 P12KeyBag *keyBag) 176{ 177 assert(keyBag != NULL); 178 CSSM_DATA &keyKeyId = keyBag->localKeyIdCssm(); 179 180 for(unsigned dex=0; dex<numCerts(); dex++) { 181 P12CertBag *certBag = mCerts[dex]; 182 CSSM_DATA &certKeyId = certBag->localKeyIdCssm(); 183 if(nssCompareCssmData(&keyKeyId, &certKeyId)) { 184 p12DecodeLog("findCertForKey SUCCESS"); 185 return certBag; 186 } 187 } 188 p12DecodeLog("findCertForKey FAILURE"); 189 return NULL; 190} 191 192/* 193 * Export items specified as SecKeychainItemRefs. 194 */ 195void P12Coder::exportKeychainItems( 196 CFArrayRef items) 197{ 198 assert(items != NULL); 199 CFIndex numItems = CFArrayGetCount(items); 200 for(CFIndex dex=0; dex<numItems; dex++) { 201 const void *item = CFArrayGetValueAtIndex(items, dex); 202 if(item == NULL) { 203 p12ErrorLog("exportKeychainItems: NULL item\n"); 204 MacOSError::throwMe(errSecParam); 205 } 206 CFTypeID itemType = CFGetTypeID(item); 207 if(itemType == SecCertificateGetTypeID()) { 208 addSecCert((SecCertificateRef)item); 209 } 210 else if(itemType == SecKeyGetTypeID()) { 211 addSecKey((SecKeyRef)item); 212 } 213 else { 214 p12ErrorLog("exportKeychainItems: unknown item\n"); 215 MacOSError::throwMe(errSecParam); 216 } 217 } 218} 219 220/* 221 * Gross kludge to work around the fact that SecKeyRefs have no attributes which 222 * are visible at the Sec layer. Not only are the attribute names we happen 223 * to know about (Label, PrintName) not publically visible anywhere in the 224 * system, but the *format* of the attr names for SecKeyRefs differs from 225 * the format of all other SecKeychainItems (NAME_AS_STRING for SecKeys, 226 * NAME_AS_INTEGER for everything else). 227 * 228 * So. We use the privately accessible schema definition table for 229 * keys to map from the attr name strings we happen to know about to a 230 * totally private name-as-int index which we can then use in the 231 * SecKeychainItemCopyAttributesAndData mechanism. 232 * 233 * This will go away if SecKeyRef defines its actual attrs as strings, AND 234 * the SecKeychainSearch mechanism knows to specify attr names for SecKeyRefs 235 * as strings rather than integers. 236 */ 237static OSStatus attrNameToInt( 238 const char *name, 239 UInt32 *attrInt) 240{ 241 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *attrList = 242 KeySchema::KeySchemaAttributeList; 243 unsigned numAttrs = KeySchema::KeySchemaAttributeCount; 244 for(unsigned dex=0; dex<numAttrs; dex++) { 245 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *info = &attrList[dex]; 246 if(!strcmp(name, info->AttributeName)) { 247 *attrInt = info->AttributeId; 248 return errSecSuccess; 249 } 250 } 251 return errSecParam; 252} 253 254void P12Coder::addSecKey( 255 SecKeyRef keyRef) 256{ 257 /* get the cert's attrs (not data) */ 258 259 /* 260 * Convert the attr name strings we happen to know about to 261 * unknowable name-as-int values. 262 */ 263 UInt32 printNameTag; 264 OSStatus ortn = attrNameToInt(P12_KEY_ATTR_PRINT_NAME, &printNameTag); 265 if(ortn) { 266 p12ErrorLog("addSecKey: problem looking up key attr name\n"); 267 MacOSError::throwMe(ortn); 268 } 269 UInt32 labelHashTag; 270 ortn = attrNameToInt(P12_KEY_ATTR_LABEL_AND_HASH, &labelHashTag); 271 if(ortn) { 272 p12ErrorLog("addSecKey: problem looking up key attr name\n"); 273 MacOSError::throwMe(ortn); 274 } 275 276 UInt32 tags[2]; 277 tags[0] = printNameTag; 278 tags[1] = labelHashTag; 279 280 /* I don't know what the format field is for */ 281 SecKeychainAttributeInfo attrInfo; 282 attrInfo.count = 2; 283 attrInfo.tag = tags; 284 attrInfo.format = NULL; // ??? 285 286 /* FIXME header says this is an IN/OUT param, but it's not */ 287 SecKeychainAttributeList *attrList = NULL; 288 289 ortn = SecKeychainItemCopyAttributesAndData( 290 (SecKeychainItemRef)keyRef, 291 &attrInfo, 292 NULL, // itemClass 293 &attrList, 294 NULL, // don't need the data 295 NULL); 296 if(ortn) { 297 p12ErrorLog("addSecKey: SecKeychainItemCopyAttributesAndData " 298 "error\n"); 299 MacOSError::throwMe(ortn); 300 } 301 302 /* Snag the attrs, convert to something useful */ 303 CFStringRef friendName = NULL; 304 CFDataRef localKeyId = NULL; 305 for(unsigned i=0; i<attrList->count; i++) { 306 SecKeychainAttribute *attr = &attrList->attr[i]; 307 if(attr->tag == printNameTag) { 308 friendName = CFStringCreateWithBytes(NULL, 309 (UInt8 *)attr->data, attr->length, 310 kCFStringEncodingUTF8, false); 311 } 312 else if(attr->tag == labelHashTag) { 313 localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length); 314 } 315 else { 316 p12ErrorLog("addSecKey: unexpected attr tag\n"); 317 MacOSError::throwMe(errSecParam); 318 319 } 320 } 321 322 /* 323 * Infer the CSP associated with this key. 324 * FIXME: this should be an attribute of the SecKeyRef itself, 325 * not inferred from the keychain it happens to be living on 326 * (SecKeyRefs should not have to be attached to Keychains at 327 * this point). 328 */ 329 SecKeychainRef kcRef; 330 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef); 331 if(ortn) { 332 p12ErrorLog("addSecKey: SecKeychainItemCopyKeychain returned %d\n", (int)ortn); 333 MacOSError::throwMe(ortn); 334 } 335 CSSM_CSP_HANDLE cspHand; 336 ortn = SecKeychainGetCSPHandle(kcRef, &cspHand); 337 if(ortn) { 338 p12ErrorLog("addSecKey: SecKeychainGetCSPHandle returned %d\n", (int)ortn); 339 MacOSError::throwMe(ortn); 340 } 341 CFRelease(kcRef); 342 343 /* and the CSSM_KEY itself */ 344 const CSSM_KEY *cssmKey; 345 ortn = SecKeyGetCSSMKey(keyRef, &cssmKey); 346 if(ortn) { 347 p12ErrorLog("addSecKey: SecKeyGetCSSMKey returned %d\n", (int)ortn); 348 MacOSError::throwMe(ortn); 349 } 350 351 /* Cook up a key bag and save it */ 352 P12KeyBag *keyBag = new P12KeyBag(cssmKey, 353 cspHand, 354 friendName, localKeyId, 355 NULL, // other attrs 356 mCoder, 357 keyRef); 358 addKey(keyBag); 359 SecKeychainItemFreeAttributesAndData(attrList, NULL); 360 if(friendName) { 361 CFRelease(friendName); 362 } 363 if(localKeyId) { 364 CFRelease(localKeyId); 365 } 366} 367 368void P12Coder::addSecCert( 369 SecCertificateRef certRef) 370{ 371 /* get the cert's attrs and data */ 372 /* I don't know what the format field is for */ 373 SecKeychainAttributeInfo attrInfo; 374 attrInfo.count = 2; 375 UInt32 tags[2] = {kSecLabelItemAttr, kSecPublicKeyHashItemAttr}; 376 attrInfo.tag = tags; 377 attrInfo.format = NULL; // ??? 378 379 /* FIXME header says this is an IN/OUT param, but it's not */ 380 SecKeychainAttributeList *attrList = NULL; 381 UInt32 certLen; 382 void *certData; 383 384 OSStatus ortn = SecKeychainItemCopyAttributesAndData( 385 (SecKeychainItemRef)certRef, 386 &attrInfo, 387 NULL, // itemClass 388 &attrList, 389 &certLen, 390 &certData); 391 if(ortn) { 392 p12ErrorLog("addSecCert: SecKeychainItemCopyAttributesAndData " 393 "error\n"); 394 MacOSError::throwMe(ortn); 395 } 396 397 /* Snag the attrs, convert to something useful */ 398 CFStringRef friendName = NULL; 399 CFDataRef localKeyId = NULL; 400 for(unsigned i=0; i<attrList->count; i++) { 401 SecKeychainAttribute *attr = &attrList->attr[i]; 402 switch(attr->tag) { 403 case kSecPublicKeyHashItemAttr: 404 localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length); 405 break; 406 case kSecLabelItemAttr: 407 /* FIXME: always in UTF8? */ 408 friendName = CFStringCreateWithBytes(NULL, 409 (UInt8 *)attr->data, attr->length, kCFStringEncodingUTF8, 410 false); 411 break; 412 default: 413 p12ErrorLog("addSecCert: unexpected attr tag\n"); 414 MacOSError::throwMe(errSecParam); 415 416 } 417 } 418 419 /* Cook up a cert bag and save it */ 420 CSSM_DATA cData = {certLen, (uint8 *)certData}; 421 P12CertBag *certBag = new P12CertBag(CT_X509, cData, friendName, 422 localKeyId, NULL, mCoder); 423 addCert(certBag); 424 SecKeychainItemFreeAttributesAndData(attrList, certData); 425 if(friendName) { 426 CFRelease(friendName); 427 } 428 if(localKeyId) { 429 CFRelease(localKeyId); 430 } 431} 432 433/* 434 * Delete anything stored in a keychain during decode, called on 435 * decode error. 436 * Currently the only thing we have to deal with is private keys, 437 * since certs and CRLs don't get stored until the end of a successful 438 * decode. 439 */ 440void P12Coder::deleteDecodedItems() 441{ 442 if(!(mImportFlags & kSecImportKeys)) { 443 /* no keys stored, done */ 444 return; 445 } 446 if(mDlDbHand.DLHandle == 0) { 447 /* no keychain, done */ 448 return; 449 } 450 451 unsigned nKeys = numKeys(); 452 for(unsigned dex=0; dex<nKeys; dex++) { 453 P12KeyBag *keyBag = mKeys[dex]; 454 p12DeleteKey(mDlDbHand, keyBag->label()); 455 } 456 457} 458 459