1/* 2 * Copyright (c) 2006-2013 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 24#include "SecBridge.h" 25#include "SecInternal.h" 26#include <CoreFoundation/CoreFoundation.h> 27#include <security_utilities/cfutilities.h> 28#include <Security/SecBase.h> 29#include <Security/SecKeychainItem.h> 30#include <Security/SecCertificate.h> 31#include <sys/param.h> 32#include "cssmdatetime.h" 33#include "SecItem.h" 34#include "SecItemPriv.h" 35#include "SecIdentitySearchPriv.h" 36#include "SecKeychainPriv.h" 37#include "SecCertificatePriv.h" 38#include "SecCertificatePrivP.h" 39#include "TrustAdditions.h" 40 41#include <AssertMacros.h> 42#include <syslog.h> 43 44#include <Security/SecTrustedApplication.h> 45#include <Security/SecTrustedApplicationPriv.h> 46#include <Security/SecCode.h> 47#include <Security/SecCodePriv.h> 48#include <Security/SecRequirement.h> 49 50const uint8_t kUUIDStringLength = 36; 51 52OSStatus SecItemAdd_osx(CFDictionaryRef attributes, CFTypeRef *result); 53OSStatus SecItemCopyMatching_osx(CFDictionaryRef query, CFTypeRef *result); 54OSStatus SecItemUpdate_osx(CFDictionaryRef query, CFDictionaryRef attributesToUpdate); 55OSStatus SecItemDelete_osx(CFDictionaryRef query); 56 57extern "C" { 58OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result); 59OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result); 60OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate); 61OSStatus SecItemDelete_ios(CFDictionaryRef query); 62 63CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes); 64CFTypeRef SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios); 65OSStatus SecItemValidateAppleApplicationGroupAccess(CFStringRef group); 66CFDictionaryRef SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, 67 bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess); 68} 69 70static void secitemlog(int priority, const char *format, ...) 71{ 72#ifndef NDEBUG 73 // log everything 74#else 75 if (priority < LOG_NOTICE) // log warnings and errors 76#endif 77 { 78 va_list list; 79 va_start(list, format); 80 vsyslog(priority, format, list); 81 va_end(list); 82 } 83} 84 85static void secitemshow(CFTypeRef obj, const char *context) 86{ 87#ifndef NDEBUG 88 CFStringRef desc = CFCopyDescription(obj); 89 if (!desc) return; 90 91 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc), kCFStringEncodingUTF8) + 1; 92 char* buffer = (char*) malloc(length); 93 if (buffer) { 94 Boolean converted = CFStringGetCString(desc, buffer, length, kCFStringEncodingUTF8); 95 if (converted) { 96 const char *prefix = (context) ? context : ""; 97 const char *separator = (context) ? " " : ""; 98 secitemlog(LOG_NOTICE, "%s%s%s", prefix, separator, buffer); 99 } 100 free(buffer); 101 } 102 CFRelease(desc); 103#endif 104} 105 106 107#define CFDataGetBytePtrVoid CFDataGetBytePtr 108 109#pragma mark SecItem private utility functions 110 111/******************************************************************************/ 112 113struct ProtocolAttributeInfo { 114 const CFTypeRef *protocolValue; 115 SecProtocolType protocolType; 116}; 117 118static ProtocolAttributeInfo gProtocolTypes[] = { 119 { &kSecAttrProtocolFTP, kSecProtocolTypeFTP }, 120 { &kSecAttrProtocolFTPAccount, kSecProtocolTypeFTPAccount }, 121 { &kSecAttrProtocolHTTP, kSecProtocolTypeHTTP }, 122 { &kSecAttrProtocolIRC, kSecProtocolTypeIRC }, 123 { &kSecAttrProtocolNNTP, kSecProtocolTypeNNTP }, 124 { &kSecAttrProtocolPOP3, kSecProtocolTypePOP3 }, 125 { &kSecAttrProtocolSMTP, kSecProtocolTypeSMTP }, 126 { &kSecAttrProtocolSOCKS, kSecProtocolTypeSOCKS }, 127 { &kSecAttrProtocolIMAP, kSecProtocolTypeIMAP }, 128 { &kSecAttrProtocolLDAP, kSecProtocolTypeLDAP }, 129 { &kSecAttrProtocolAppleTalk, kSecProtocolTypeAppleTalk }, 130 { &kSecAttrProtocolAFP, kSecProtocolTypeAFP }, 131 { &kSecAttrProtocolTelnet, kSecProtocolTypeTelnet }, 132 { &kSecAttrProtocolSSH, kSecProtocolTypeSSH }, 133 { &kSecAttrProtocolFTPS, kSecProtocolTypeFTPS }, 134 { &kSecAttrProtocolHTTPS, kSecProtocolTypeHTTPS }, 135 { &kSecAttrProtocolHTTPProxy, kSecProtocolTypeHTTPProxy }, 136 { &kSecAttrProtocolHTTPSProxy, kSecProtocolTypeHTTPSProxy }, 137 { &kSecAttrProtocolFTPProxy, kSecProtocolTypeFTPProxy }, 138 { &kSecAttrProtocolSMB, kSecProtocolTypeSMB }, 139 { &kSecAttrProtocolRTSP, kSecProtocolTypeRTSP }, 140 { &kSecAttrProtocolRTSPProxy, kSecProtocolTypeRTSPProxy }, 141 { &kSecAttrProtocolDAAP, kSecProtocolTypeDAAP }, 142 { &kSecAttrProtocolEPPC, kSecProtocolTypeEPPC }, 143 { &kSecAttrProtocolIPP, kSecProtocolTypeIPP }, 144 { &kSecAttrProtocolNNTPS, kSecProtocolTypeNNTPS }, 145 { &kSecAttrProtocolLDAPS, kSecProtocolTypeLDAPS }, 146 { &kSecAttrProtocolTelnetS, kSecProtocolTypeTelnetS }, 147 { &kSecAttrProtocolIMAPS, kSecProtocolTypeIMAPS }, 148 { &kSecAttrProtocolIRCS, kSecProtocolTypeIRCS }, 149 { &kSecAttrProtocolPOP3S, kSecProtocolTypePOP3S } 150}; 151 152static const int kNumberOfProtocolTypes = sizeof(gProtocolTypes) / sizeof(ProtocolAttributeInfo); 153 154/* 155 * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType. 156 */ 157static SecProtocolType 158_SecProtocolTypeForSecAttrProtocol( 159 CFTypeRef protocol) 160{ 161 SecProtocolType result = kSecProtocolTypeAny; 162 163 if (protocol != NULL) { 164 CFIndex count; 165 for (count=0; count<kNumberOfProtocolTypes; count++) { 166 if (CFEqual(protocol, *(gProtocolTypes[count].protocolValue))) { 167 result = gProtocolTypes[count].protocolType; 168 break; 169 } 170 } 171 } 172 173 return result; 174} 175 176/* 177 * _SecAttrProtocolForSecProtocolType converts a SecProtocolType to a SecAttrProtocol. 178 */ 179static CFTypeRef 180_SecAttrProtocolForSecProtocolType( 181 SecProtocolType protocolType) 182{ 183 CFTypeRef result = NULL; 184 CFIndex count; 185 for (count=0; count<kNumberOfProtocolTypes; count++) { 186 if (gProtocolTypes[count].protocolType == protocolType) { 187 result = *(gProtocolTypes[count].protocolValue); 188 break; 189 } 190 } 191 192 return result; 193} 194 195 196/******************************************************************************/ 197 198struct AuthenticationAttributeInfo { 199 const CFTypeRef *authValue; 200 SecAuthenticationType authType; 201}; 202 203static AuthenticationAttributeInfo gAuthTypes[] = { 204 { &kSecAttrAuthenticationTypeNTLM, kSecAuthenticationTypeNTLM }, 205 { &kSecAttrAuthenticationTypeMSN, kSecAuthenticationTypeMSN }, 206 { &kSecAttrAuthenticationTypeDPA, kSecAuthenticationTypeDPA }, 207 { &kSecAttrAuthenticationTypeRPA, kSecAuthenticationTypeRPA }, 208 { &kSecAttrAuthenticationTypeHTTPBasic, kSecAuthenticationTypeHTTPBasic }, 209 { &kSecAttrAuthenticationTypeHTTPDigest, kSecAuthenticationTypeHTTPDigest }, 210 { &kSecAttrAuthenticationTypeHTMLForm, kSecAuthenticationTypeHTMLForm }, 211 { &kSecAttrAuthenticationTypeDefault, kSecAuthenticationTypeDefault } 212}; 213 214static const int kNumberOfAuthenticationTypes = sizeof(gAuthTypes) / sizeof(AuthenticationAttributeInfo); 215 216/* 217 * _SecAuthenticationTypeForSecAttrAuthenticationType converts a 218 * SecAttrAuthenticationType to a SecAuthenticationType. 219 */ 220static SecAuthenticationType 221_SecAuthenticationTypeForSecAttrAuthenticationType( 222 CFTypeRef authenticationType) 223{ 224 SecAuthenticationType result = kSecAuthenticationTypeAny; 225 226 if (authenticationType != NULL) { 227 CFIndex count; 228 for (count=0; count<kNumberOfAuthenticationTypes; count++) { 229 if (CFEqual(authenticationType, *(gAuthTypes[count].authValue))) { 230 result = gAuthTypes[count].authType; 231 break; 232 } 233 } 234 } 235 236 return result; 237} 238 239/* 240 * _SecAttrAuthenticationTypeForSecAuthenticationType converts a SecAuthenticationType 241 * to a SecAttrAuthenticationType. 242 */ 243static CFTypeRef 244_SecAttrAuthenticationTypeForSecAuthenticationType( 245 SecAuthenticationType authenticationType) 246{ 247 CFTypeRef result = NULL; 248 CFIndex count; 249 for (count=0; count<kNumberOfAuthenticationTypes; count++) { 250 if (gAuthTypes[count].authType == authenticationType) { 251 result = *(gAuthTypes[count].authValue); 252 break; 253 } 254 } 255 256 return result; 257} 258 259 260/******************************************************************************/ 261 262struct KeyAlgorithmInfo { 263 const CFTypeRef *keyType; 264 UInt32 keyValue; 265}; 266 267static KeyAlgorithmInfo gKeyTypes[] = { 268 { &kSecAttrKeyTypeRSA, CSSM_ALGID_RSA }, 269 { &kSecAttrKeyTypeDSA, CSSM_ALGID_DSA }, 270 { &kSecAttrKeyTypeAES, CSSM_ALGID_AES }, 271 { &kSecAttrKeyTypeDES, CSSM_ALGID_DES }, 272 { &kSecAttrKeyType3DES, CSSM_ALGID_3DES }, 273 { &kSecAttrKeyTypeRC4, CSSM_ALGID_RC4 }, 274 { &kSecAttrKeyTypeRC2, CSSM_ALGID_RC2 }, 275 { &kSecAttrKeyTypeCAST, CSSM_ALGID_CAST }, 276 { &kSecAttrKeyTypeECDSA, CSSM_ALGID_ECDSA }, 277 { &kSecAttrKeyTypeEC, CSSM_ALGID_ECDSA } 278}; 279 280static const int kNumberOfKeyTypes = sizeof(gKeyTypes) / sizeof (KeyAlgorithmInfo); 281 282 283static UInt32 _SecAlgorithmTypeFromSecAttrKeyType( 284 CFTypeRef keyTypeRef) 285{ 286 UInt32 keyAlgValue = 0; 287 if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef)) 288 return keyAlgValue; 289 290 int ix; 291 for (ix=0; ix<kNumberOfKeyTypes; ix++) { 292 if (CFEqual(keyTypeRef, *(gKeyTypes[ix].keyType))) { 293 keyAlgValue = gKeyTypes[ix].keyValue; 294 return keyAlgValue; 295 } 296 } 297 298 //%%%TODO try to convert the input string to a number here 299 300 return keyAlgValue; 301} 302 303 304enum ItemRepresentation 305{ 306 kStringRepresentation, 307 kDataRepresentation, 308 kNumberRepresentation, 309 kBooleanRepresentation, 310 kDateRepresentation 311}; 312 313 314struct InternalAttributeListInfo 315{ 316 UInt32 oldItemType; 317 const CFTypeRef *newItemType; 318 ItemRepresentation itemRepresentation; 319}; 320 321 322static InternalAttributeListInfo gGenericPasswordAttributes[] = 323{ 324 { kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation }, 325 { kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation }, 326 { kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation }, 327 { kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation }, 328 { kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode 329 { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode 330 { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation }, 331 { kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation }, 332 { kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation }, 333 { kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation }, 334 { kSecServiceItemAttr, &kSecAttrService, kStringRepresentation }, 335 { kSecGenericItemAttr, &kSecAttrGeneric, kDataRepresentation } 336}; 337 338static const int kNumberOfGenericPasswordAttributes = sizeof(gGenericPasswordAttributes) / sizeof (InternalAttributeListInfo); 339 340#if 0 341static InternalAttributeListInfo gInternetPasswordAttributes[] = 342{ 343 { kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation }, 344 { kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation }, 345 { kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation }, 346 { kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation }, 347 { kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode 348 { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode 349 { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation }, 350 { kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation }, 351 { kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation }, 352 { kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation }, 353 { kSecSecurityDomainItemAttr, &kSecAttrSecurityDomain, kStringRepresentation }, 354 { kSecServerItemAttr, &kSecAttrServer, kStringRepresentation }, 355 { kSecAuthenticationTypeItemAttr, &kSecAttrAuthenticationType, kStringRepresentation }, // maps from UInt32 value to string constant 356 { kSecPortItemAttr, &kSecAttrPort, kNumberRepresentation }, 357 { kSecPathItemAttr, &kSecAttrPath, kStringRepresentation } 358}; 359 360static const int kNumberOfInternetPasswordAttributes = sizeof(gInternetPasswordAttributes) / sizeof (InternalAttributeListInfo); 361#endif 362 363static InternalAttributeListInfo gCertificateAttributes[] = 364{ 365 { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation }, 366 { kSecSubjectItemAttr, &kSecAttrSubject, kDataRepresentation }, 367 { kSecIssuerItemAttr, &kSecAttrIssuer, kDataRepresentation }, 368 { kSecSerialNumberItemAttr, &kSecAttrSerialNumber, kDataRepresentation }, 369 { kSecPublicKeyHashItemAttr, &kSecAttrPublicKeyHash, kDataRepresentation }, 370 { kSecSubjectKeyIdentifierItemAttr, &kSecAttrSubjectKeyID, kDataRepresentation }, 371 { kSecCertTypeItemAttr, &kSecAttrCertificateType, kDataRepresentation }, 372 { kSecCertEncodingItemAttr, &kSecAttrCertificateEncoding, kDataRepresentation } 373}; 374 375static const int kNumberOfCertificateAttributes = sizeof(gCertificateAttributes) / sizeof(InternalAttributeListInfo); 376 377 378static InternalAttributeListInfo gKeyAttributes[] = 379{ 380 { kSecKeyKeyClass, &kSecAttrKeyClass, kStringRepresentation }, // key class maps from UInt32 value to string constant 381 { kSecKeyPrintName, &kSecAttrLabel, kStringRepresentation }, // note that "print name" maps to the user-visible label 382// { kSecKeyAlias, /* not yet exposed by SecItem */, kDataRepresentation }, 383 { kSecKeyPermanent, &kSecAttrIsPermanent, kBooleanRepresentation }, 384// { kSecKeyPrivate, /* not yet exposed by SecItem */, kBooleanRepresentation }, 385// { kSecKeyModifiable, /* not yet exposed by SecItem */, kBooleanRepresentation }, 386 { kSecKeyLabel, &kSecAttrApplicationLabel, kDataRepresentation }, // this contains the hash of the key (or the public key hash, if asymmetric) as a CFData. Legacy keys may contain a UUID as a CFString 387 { kSecKeyApplicationTag, &kSecAttrApplicationTag, kDataRepresentation }, 388// { kSecKeyKeyCreator, /* not yet exposed by SecItem */, kStringRepresentation }, // this is the GUID of the CSP that owns this key 389 { kSecKeyKeyType, &kSecAttrKeyType, kStringRepresentation }, // algorithm type is given as a string constant (e.g. kSecAttrKeyTypeAES) 390 { kSecKeyKeySizeInBits, &kSecAttrKeySizeInBits, kNumberRepresentation }, 391 { kSecKeyEffectiveKeySize, &kSecAttrEffectiveKeySize, kNumberRepresentation }, 392// { kSecKeyStartDate, /* not yet exposed by SecItem */, kDateRepresentation }, 393// { kSecKeyEndDate, /* not yet exposed by SecItem */, kDateRepresentation }, 394// { kSecKeySensitive, /* not yet exposed by SecItem */, kBooleanRepresentation }, 395// { kSecKeyAlwaysSensitive, /* not yet exposed by SecItem */, kBooleanRepresentation }, 396// { kSecKeyExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation }, 397// { kSecKeyNeverExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation }, 398 { kSecKeyEncrypt, &kSecAttrCanEncrypt, kBooleanRepresentation }, 399 { kSecKeyDecrypt, &kSecAttrCanDecrypt, kBooleanRepresentation }, 400 { kSecKeyDerive, &kSecAttrCanDerive, kBooleanRepresentation }, 401 { kSecKeySign, &kSecAttrCanSign, kBooleanRepresentation }, 402 { kSecKeyVerify, &kSecAttrCanVerify, kBooleanRepresentation }, 403// { kSecKeySignRecover, /* not yet exposed by SecItem */, kBooleanRepresentation }, 404// { kSecKeyVerifyRecover, /* not yet exposed by SecItem */, kBooleanRepresentation }, 405 { kSecKeyWrap, &kSecAttrCanWrap, kBooleanRepresentation }, 406 { kSecKeyUnwrap, &kSecAttrCanUnwrap, kBooleanRepresentation } 407}; 408 409static const int kNumberOfKeyAttributes = sizeof(gKeyAttributes) / sizeof(InternalAttributeListInfo); 410 411 412static void* CloneDataByType(ItemRepresentation type, CFTypeRef value, UInt32& length) 413{ 414 switch (type) 415 { 416 case kStringRepresentation: 417 { 418 if (CFStringGetTypeID() != CFGetTypeID(value)) { 419 length = 0; 420 return NULL; 421 } 422 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef) value), kCFStringEncodingUTF8) + 1; 423 char* buffer = (char*) malloc(maxLength); 424 Boolean converted = CFStringGetCString((CFStringRef) value, buffer, maxLength, kCFStringEncodingUTF8); 425 if (converted) { 426 length = (UInt32)strlen(buffer); 427 } 428 else { 429 length = 0; 430 free(buffer); 431 buffer = NULL; 432 } 433 return buffer; 434 } 435 436 case kDataRepresentation: 437 { 438 if (CFStringGetTypeID() == CFGetTypeID(value)) { 439 // We may have a string here, since the key label may be a GUID for the symmetric keys 440 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef) value), kCFStringEncodingUTF8) + 1; 441 char* buffer = (char*) malloc(maxLength); 442 Boolean converted = CFStringGetCString((CFStringRef) value, buffer, maxLength, kCFStringEncodingUTF8); 443 if (converted) { 444 length = (UInt32)strlen(buffer); 445 } 446 else { 447 length = 0; 448 free(buffer); 449 buffer = NULL; 450 } 451 return buffer; 452 } 453 454 if (CFDataGetTypeID() != CFGetTypeID(value)) { 455 length = 0; 456 return NULL; 457 } 458 length = (UInt32)CFDataGetLength((CFDataRef) value); 459 uint8_t* buffer = (uint8_t*) malloc(length); 460 CFDataGetBytes((CFDataRef) value, CFRangeMake(0, length), buffer); 461 return buffer; 462 } 463 464 case kNumberRepresentation: 465 { 466 if (CFNumberGetTypeID() != CFGetTypeID(value)) { 467 length = 0; 468 return NULL; 469 } 470 uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t)); 471 Boolean converted = CFNumberGetValue((CFNumberRef) value, kCFNumberSInt32Type, buffer); 472 if (converted) { 473 length = sizeof(uint32_t); 474 } 475 else { 476 length = 0; 477 free(buffer); 478 buffer = NULL; 479 } 480 return buffer; 481 } 482 483 case kBooleanRepresentation: 484 { 485 if (CFBooleanGetTypeID() != CFGetTypeID(value)) { 486 length = 0; 487 return NULL; 488 } 489 uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t)); 490 *buffer = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 491 length = sizeof(uint32_t); 492 return buffer; 493 } 494 495 case kDateRepresentation: 496 { 497 if (CFDateGetTypeID() != CFGetTypeID(value)) { 498 length = 0; 499 return NULL; 500 } 501 char* buffer = (char*) calloc(1, 32); // max length of a CSSM date string 502 CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef) value, buffer); 503 length = (UInt32)strlen(buffer); 504 return buffer; 505 } 506 507 default: 508 { 509 length = 0; 510 return NULL; 511 } 512 } 513} 514 515 516static OSStatus 517_ConvertNewFormatToOldFormat( 518 CFAllocatorRef allocator, 519 const InternalAttributeListInfo* info, 520 int infoNumItems, 521 CFDictionaryRef dictionaryRef, 522 SecKeychainAttributeList* &attrList 523 ) 524{ 525 // get the keychain attributes array from the data item 526 // here's the problem. On the one hand, we have a dictionary that is purported to contain 527 // attributes for our type. On the other hand, the dictionary may contain items we don't support, 528 // and we therefore don't know how many attributes we will have unless we count them first 529 530 // setup the return 531 attrList = (SecKeychainAttributeList*) calloc(1, sizeof(SecKeychainAttributeList)); 532 533 // make storage to extract the dictionary items 534 CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef); 535 CFTypeRef keys[itemsInDictionary]; 536 CFTypeRef values[itemsInDictionary]; 537 538 CFTypeRef *keysPtr = keys; 539 CFTypeRef *valuesPtr = values; 540 541 CFDictionaryGetKeysAndValues(dictionaryRef, keys, values); 542 543 // count the number of items we are interested in 544 CFIndex count = 0; 545 CFIndex i; 546 547 // since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that 548 // we don't pay the price for this twice 549 SecKeychainAttrType tags[itemsInDictionary]; 550 ItemRepresentation types[itemsInDictionary]; 551 552 for (i = 0; i < itemsInDictionary; ++i) 553 { 554 CFTypeRef key = keysPtr[i]; 555 556 int j; 557 for (j = 0; j < infoNumItems; ++j) 558 { 559 if (CFEqual(*(info[j].newItemType), key)) 560 { 561 tags[i] = info[j].oldItemType; 562 types[i] = info[j].itemRepresentation; 563 count += 1; 564 break; 565 } 566 } 567 568 if (j >= infoNumItems) 569 { 570 // if we got here, we aren't interested in this item. 571 valuesPtr[i] = NULL; 572 } 573 } 574 575 // now we can make the result array 576 attrList->count = (UInt32)count; 577 attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count); 578 579 // fill out the array 580 int resultPointer = 0; 581 for (i = 0; i < itemsInDictionary; ++i) 582 { 583 if (values[i] != NULL) 584 { 585 attrList->attr[resultPointer].tag = tags[i]; 586 587 // we have to clone the data pointer. The caller will need to make sure to throw these away 588 // with _FreeAttrList when it is done... 589 attrList->attr[resultPointer].data = CloneDataByType(types[i], valuesPtr[i], attrList->attr[resultPointer].length); 590 resultPointer += 1; 591 } 592 } 593 594 return errSecSuccess; 595} 596 597 598 599static OSStatus 600_ConvertOldFormatToNewFormat( 601 CFAllocatorRef allocator, 602 const InternalAttributeListInfo* info, 603 int infoNumItems, 604 SecKeychainItemRef itemRef, 605 CFMutableDictionaryRef& dictionaryRef) 606{ 607 SecKeychainAttributeList list; 608 list.count = infoNumItems; 609 list.attr = (SecKeychainAttribute*) calloc(infoNumItems, sizeof(SecKeychainAttribute)); 610 611 // fill out the array. We only need to fill in the tags, since calloc zeros what it returns 612 int i; 613 for (i = 0; i < infoNumItems; ++i) 614 { 615 list.attr[i].tag = info[i].oldItemType; 616 } 617 618 OSStatus result = SecKeychainItemCopyContent(itemRef, NULL, &list, NULL, NULL); 619 if (result != errSecSuccess) 620 { 621 dictionaryRef = NULL; 622 free(list.attr); 623 return result; 624 } 625 626 // create the dictionary 627 dictionaryRef = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 628 629 // add the pairs 630 for (i = 0; i < infoNumItems; ++i) 631 { 632 if (list.attr[i].data == NULL) 633 continue; 634 635 switch (info[i].itemRepresentation) 636 { 637 case kStringRepresentation: 638 { 639 CFStringRef stringRef; 640 if (info[i].oldItemType == kSecKeyKeyClass) { 641 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant 642 uint32_t keyRecordValue = *((uint32_t*)list.attr[i].data); 643 bool retainString = true; 644 switch (keyRecordValue) { 645 case CSSM_DL_DB_RECORD_PUBLIC_KEY : 646 stringRef = (CFStringRef) kSecAttrKeyClassPublic; 647 break; 648 case CSSM_DL_DB_RECORD_PRIVATE_KEY: 649 stringRef = (CFStringRef) kSecAttrKeyClassPrivate; 650 break; 651 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY: 652 stringRef = (CFStringRef) kSecAttrKeyClassSymmetric; 653 break; 654 default: 655 stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue); 656 break; 657 } 658 if (stringRef) { 659 if (retainString) CFRetain(stringRef); 660 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef); 661 CFRelease(stringRef); 662 } 663 } 664 else if (info[i].oldItemType == kSecKeyKeyType) { 665 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant 666 uint32_t keyAlgValue = *((uint32_t*)list.attr[i].data); 667 bool retainString = true; 668 switch (keyAlgValue) { 669 case CSSM_ALGID_RSA : 670 stringRef = (CFStringRef) kSecAttrKeyTypeRSA; 671 break; 672 case CSSM_ALGID_DSA : 673 stringRef = (CFStringRef) kSecAttrKeyTypeDSA; 674 break; 675 case CSSM_ALGID_AES : 676 stringRef = (CFStringRef) kSecAttrKeyTypeAES; 677 break; 678 case CSSM_ALGID_DES : 679 stringRef = (CFStringRef) kSecAttrKeyTypeDES; 680 break; 681 case CSSM_ALGID_3DES : 682 stringRef = (CFStringRef) kSecAttrKeyType3DES; 683 break; 684 case CSSM_ALGID_RC4 : 685 stringRef = (CFStringRef) kSecAttrKeyTypeRC4; 686 break; 687 case CSSM_ALGID_RC2 : 688 stringRef = (CFStringRef) kSecAttrKeyTypeRC2; 689 break; 690 case CSSM_ALGID_CAST : 691 stringRef = (CFStringRef) kSecAttrKeyTypeCAST; 692 break; 693 case CSSM_ALGID_ECDSA : 694 stringRef = (CFStringRef) kSecAttrKeyTypeEC; 695 break; 696 default : 697 stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyAlgValue); 698 retainString = false; 699 break; 700 } 701 if (stringRef) { 702 if (retainString) CFRetain(stringRef); 703 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef); 704 CFRelease(stringRef); 705 } 706 } 707 else { 708 // normal case: attribute contains a string 709 stringRef = CFStringCreateWithBytes(allocator, (UInt8*)list.attr[i].data, list.attr[i].length, kCFStringEncodingUTF8, FALSE); 710 if (stringRef == NULL) 711 stringRef = (CFStringRef) CFRetain(kCFNull); 712 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef); 713 CFRelease(stringRef); 714 } 715 } 716 break; 717 718 case kDataRepresentation: 719 { 720 if ((info[i].oldItemType == kSecKeyLabel) && (list.attr[i].length == kUUIDStringLength)) { 721 // It's possible that there could be a string here because the key label may have a UUID 722 CFStringRef stringRef = CFStringCreateWithBytes(allocator, (UInt8*)list.attr[i].data, list.attr[i].length, kCFStringEncodingUTF8, FALSE); 723 if (stringRef == NULL) 724 stringRef = (CFStringRef) CFRetain(kCFNull); 725 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef); 726 CFRelease(stringRef); 727 break; 728 } 729 CFDataRef dataRef = CFDataCreate(allocator, (UInt8*) list.attr[i].data, list.attr[i].length); 730 if (dataRef == NULL) 731 dataRef = (CFDataRef) CFRetain(kCFNull); 732 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dataRef); 733 CFRelease(dataRef); 734 } 735 break; 736 737 case kNumberRepresentation: 738 { 739 CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, list.attr[i].data); 740 if (numberRef == NULL) 741 numberRef = (CFNumberRef) CFRetain(kCFNull); 742 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), numberRef); 743 CFRelease(numberRef); 744 } 745 break; 746 747 case kBooleanRepresentation: 748 { 749 uint32_t value = *((uint32_t*)list.attr[i].data); 750 CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse; 751 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), boolRef); 752 } 753 break; 754 755 case kDateRepresentation: 756 { 757 CFDateRef dateRef = NULL; 758 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list.attr[i].data, list.attr[i].length, &dateRef); 759 if (dateRef == NULL) 760 dateRef = (CFDateRef) CFRetain(kCFNull); 761 CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dateRef); 762 CFRelease(dateRef); 763 } 764 break; 765 } 766 } 767 768 // cleanup 769 SecKeychainItemFreeContent(&list, NULL); 770 free(list.attr); 771 772 return result; 773} 774 775 776 777// 778/* 779 * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the 780 * attributes of item. 781 */ 782static OSStatus 783_CreateAttributesDictionaryFromGenericPasswordItem( 784 CFAllocatorRef allocator, 785 SecKeychainItemRef item, 786 CFDictionaryRef *dictionary) 787{ 788 // do the basic allocations 789 CFMutableDictionaryRef dict = NULL; 790 OSStatus result = _ConvertOldFormatToNewFormat(allocator, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, item, dict); 791 if (result == errSecSuccess) // did we complete OK 792 { 793 CFDictionaryAddValue(dict, kSecClass, kSecClassGenericPassword); 794 } 795 796 *dictionary = dict; 797 798 return result; 799} 800 801 802 803/* 804 * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the 805 * attributes of item. 806 */ 807static OSStatus 808_CreateAttributesDictionaryFromCertificateItem( 809 CFAllocatorRef allocator, 810 SecKeychainItemRef item, 811 CFDictionaryRef *dictionary) 812{ 813 // do the basic allocations 814 CFMutableDictionaryRef dict = NULL; 815 OSStatus result = _ConvertOldFormatToNewFormat(allocator, gCertificateAttributes, kNumberOfCertificateAttributes, item, dict); 816 if (result == errSecSuccess) // did we complete OK 817 { 818 CFDictionaryAddValue(dict, kSecClass, kSecClassCertificate); 819 } 820 821 *dictionary = dict; 822 823 return errSecSuccess; 824} 825 826/* 827 * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the 828 * attributes of item. 829 */ 830static OSStatus 831_CreateAttributesDictionaryFromKeyItem( 832 CFAllocatorRef allocator, 833 SecKeychainItemRef item, 834 CFDictionaryRef *dictionary) 835{ 836#if 0 837//%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails. 838// Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and 839// SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent. 840 841 if (status) { 842 goto error_exit; // unable to get the attribute info (i.e. database schema) 843 } 844 845 status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL); 846 847 // do the basic allocations 848 CFMutableDictionaryRef dict = NULL; 849 OSStatus result = _ConvertOldFormatToNewFormat(allocator, gKeyAttributes, kNumberOfKeyAttributes, item, dict); 850 if (result == errSecSuccess) // did we complete OK 851 { 852 CFDictionaryAddValue(dict, kSecClass, kSecClassKey); 853 } 854 855 *dictionary = dict; 856 857 return errSecSuccess; 858#endif 859 860 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 861 unsigned int ix; 862 SecItemClass itemClass = 0; 863 UInt32 itemID; 864 SecKeychainAttributeList *attrList = NULL; 865 SecKeychainAttributeInfo *info = NULL; 866 SecKeychainRef keychain = NULL; 867 868 OSStatus status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL); 869 if (status) { 870 goto error_exit; // item must have an itemClass 871 } 872 873 switch (itemClass) 874 { 875 case kSecInternetPasswordItemClass: 876 itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD; 877 break; 878 case kSecGenericPasswordItemClass: 879 itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD; 880 break; 881 case 'ashp': /* kSecAppleSharePasswordItemClass */ 882 itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD; 883 break; 884 default: 885 itemID = itemClass; 886 break; 887 } 888 889 status = SecKeychainItemCopyKeychain(item, &keychain); 890 if (status) { 891 goto error_exit; // item must have a keychain, so we can get the attribute info for it 892 } 893 894 status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info); 895 if (status) { 896 goto error_exit; // unable to get the attribute info (i.e. database schema) 897 } 898 899 status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL); 900 if (status) { 901 goto error_exit; // unable to get the attribute info (i.e. database schema) 902 } 903 904 for (ix = 0; ix < info->count; ++ix) 905 { 906 SecKeychainAttribute *attribute = &attrList->attr[ix]; 907 if (!attribute->length && !attribute->data) 908 continue; 909 910 UInt32 j, count = kNumberOfKeyAttributes; 911 InternalAttributeListInfo *intInfo = NULL; 912 for (j=0; j<count; j++) { 913 if (gKeyAttributes[j].oldItemType == info->tag[ix]) { 914 intInfo = &gKeyAttributes[j]; 915 break; 916 } 917 } 918 if (!intInfo) 919 continue; 920 921 switch (intInfo->itemRepresentation) 922 { 923 case kStringRepresentation: 924 { 925 CFStringRef stringRef; 926 if (intInfo->oldItemType == kSecKeyKeyClass) { 927 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant 928 UInt32 keyRecordValue = *((UInt32*)attribute->data); 929 bool retainString = true; 930 switch (keyRecordValue) { 931 case CSSM_DL_DB_RECORD_PUBLIC_KEY : 932 stringRef = (CFStringRef) kSecAttrKeyClassPublic; 933 break; 934 case CSSM_DL_DB_RECORD_PRIVATE_KEY: 935 stringRef = (CFStringRef) kSecAttrKeyClassPrivate; 936 break; 937 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY: 938 stringRef = (CFStringRef) kSecAttrKeyClassSymmetric; 939 break; 940 default: 941 stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue); 942 break; 943 } 944 if (stringRef) { 945 if (retainString) CFRetain(stringRef); 946 CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef); 947 CFRelease(stringRef); 948 } 949 } 950 else if (intInfo->oldItemType == kSecKeyKeyType) { 951 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant 952 UInt32 keyAlgValue = *((UInt32*)attribute->data); 953 bool retainString = true; 954 switch (keyAlgValue) { 955 case CSSM_ALGID_RSA : 956 stringRef = (CFStringRef) kSecAttrKeyTypeRSA; 957 break; 958 case CSSM_ALGID_DSA : 959 stringRef = (CFStringRef) kSecAttrKeyTypeDSA; 960 break; 961 case CSSM_ALGID_AES : 962 stringRef = (CFStringRef) kSecAttrKeyTypeAES; 963 break; 964 case CSSM_ALGID_DES : 965 stringRef = (CFStringRef) kSecAttrKeyTypeDES; 966 break; 967 case CSSM_ALGID_3DES : 968 stringRef = (CFStringRef) kSecAttrKeyType3DES; 969 break; 970 case CSSM_ALGID_RC4 : 971 stringRef = (CFStringRef) kSecAttrKeyTypeRC4; 972 break; 973 case CSSM_ALGID_RC2 : 974 stringRef = (CFStringRef) kSecAttrKeyTypeRC2; 975 break; 976 case CSSM_ALGID_CAST : 977 stringRef = (CFStringRef) kSecAttrKeyTypeCAST; 978 break; 979 case CSSM_ALGID_ECDSA : 980 stringRef = (CFStringRef) kSecAttrKeyTypeEC; 981 break; 982 default : 983 stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyAlgValue); 984 retainString = false; 985 break; 986 } 987 if (stringRef) { 988 if (retainString) CFRetain(stringRef); 989 CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef); 990 CFRelease(stringRef); 991 } 992 } 993 else { 994 // normal case: attribute contains a string 995 stringRef = CFStringCreateWithBytes(allocator, (UInt8*)attribute->data, attribute->length, kCFStringEncodingUTF8, FALSE); 996 if (stringRef == NULL) 997 stringRef = (CFStringRef) CFRetain(kCFNull); 998 CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef); 999 CFRelease(stringRef); 1000 } 1001 } 1002 break; 1003 1004 case kDataRepresentation: 1005 { 1006 if ((intInfo->oldItemType == kSecKeyLabel) && (attribute->length == kUUIDStringLength)) { 1007 // It's possible that there could be a string here because the key label may have a UUID 1008 CFStringRef stringRef = CFStringCreateWithBytes(allocator, (UInt8*)attribute->data, attribute->length, kCFStringEncodingUTF8, FALSE); 1009 if (stringRef == NULL) 1010 stringRef = (CFStringRef) CFRetain(kCFNull); 1011 CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef); 1012 CFRelease(stringRef); 1013 break; 1014 } 1015 1016 CFDataRef dataRef = CFDataCreate(allocator, (UInt8*)attribute->data, attribute->length); 1017 if (dataRef == NULL) 1018 dataRef = (CFDataRef) CFRetain(kCFNull); 1019 CFDictionaryAddValue(dict, *(intInfo->newItemType), dataRef); 1020 CFRelease(dataRef); 1021 } 1022 break; 1023 1024 case kNumberRepresentation: 1025 { 1026 CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attribute->data); 1027 if (numberRef == NULL) 1028 numberRef = (CFNumberRef) CFRetain(kCFNull); 1029 CFDictionaryAddValue(dict, *(intInfo->newItemType), numberRef); 1030 CFRelease(numberRef); 1031 } 1032 break; 1033 1034 case kBooleanRepresentation: 1035 { 1036 UInt32 value = *((UInt32*)attribute->data); 1037 CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse; 1038 CFDictionaryAddValue(dict, *(intInfo->newItemType), boolRef); 1039 } 1040 break; 1041 1042 case kDateRepresentation: 1043 { 1044 //%%% FIXME need to convert from a CSSM date string to a CFDateRef here 1045 CFDateRef dateRef = NULL; 1046 if (dateRef == NULL) 1047 dateRef = (CFDateRef) CFRetain(kCFNull); 1048 CFDictionaryAddValue(dict, *(intInfo->newItemType), dateRef); 1049 CFRelease(dateRef); 1050 } 1051 break; 1052 } 1053 } 1054 1055 CFDictionaryAddValue(dict, kSecClass, kSecClassKey); 1056 1057error_exit: 1058 1059 if (attrList) 1060 SecKeychainItemFreeAttributesAndData(attrList, NULL); 1061 1062 if (info) 1063 SecKeychainFreeAttributeInfo(info); 1064 1065 if (keychain) 1066 CFRelease(keychain); 1067 1068 *dictionary = dict; 1069 1070 return status; 1071} 1072 1073 1074/* 1075 * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the 1076 * attributes of item. 1077 */ 1078static OSStatus 1079_CreateAttributesDictionaryFromInternetPasswordItem( 1080 CFAllocatorRef allocator, 1081 SecKeychainItemRef item, 1082 CFDictionaryRef *dictionary) 1083{ 1084 OSStatus status; 1085 SecKeychainAttribute attr[] = { 1086 { kSecServerItemAttr, 0, NULL }, /* [0] server */ 1087 { kSecSecurityDomainItemAttr, 0, NULL }, /* [1] securityDomain */ 1088 { kSecAccountItemAttr, 0, NULL }, /* [2] account */ 1089 { kSecPathItemAttr, 0, NULL }, /* [3] path */ 1090 { kSecPortItemAttr, 0, NULL }, /* [4] port */ 1091 { kSecProtocolItemAttr, 0, NULL }, /* [5] protocol */ 1092 { kSecAuthenticationTypeItemAttr, 0, NULL }, /* [6] authenticationType */ 1093 { kSecCommentItemAttr, 0, NULL }, /* [7] comment */ 1094 { kSecDescriptionItemAttr, 0, NULL }, /* [8] description */ 1095 { kSecLabelItemAttr, 0, NULL }, /* [9] label */ 1096 { kSecCreationDateItemAttr, 0, NULL }, /* [10] creation date */ 1097 { kSecModDateItemAttr, 0, NULL }, /* [11] modification date */ 1098 { kSecCreatorItemAttr, 0, NULL }, /* [12] creator */ 1099 { kSecTypeItemAttr, 0, NULL }, /* [13] type */ 1100 { kSecInvisibleItemAttr, 0, NULL }, /* [14] invisible */ 1101 { kSecNegativeItemAttr, 0, NULL }, /* [15] negative */ 1102 }; 1103 SecKeychainAttributeList attrList = { sizeof(attr) / sizeof(SecKeychainAttribute), attr }; 1104 CFIndex numValues; 1105 CFIndex index; 1106 CFTypeRef keys[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2]; 1107 CFTypeRef values[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2]; 1108 1109 *dictionary = NULL; 1110 1111 // copy the item's attributes 1112 status = SecKeychainItemCopyContent(item, NULL, &attrList, NULL, NULL); 1113 require_noerr(status, SecKeychainItemCopyContent_failed); 1114 1115 numValues = 0; 1116 1117 // add kSecClass 1118 keys[numValues] = kSecClass; 1119 values[numValues] = kSecClassInternetPassword; 1120 ++numValues; 1121 1122 // add kSecAttrServer 1123 if ( attrList.attr[0].length > 0 ) { 1124 keys[numValues] = kSecAttrServer; 1125 values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[0].data, attrList.attr[0].length, kCFStringEncodingUTF8, FALSE); 1126 if ( values[numValues] != NULL ) { 1127 ++numValues; 1128 } 1129 } 1130 1131 // add kSecAttrSecurityDomain 1132 if ( attrList.attr[1].length > 0 ) { 1133 keys[numValues] = kSecAttrSecurityDomain; 1134 values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[1].data, attrList.attr[1].length, kCFStringEncodingUTF8, FALSE); 1135 if ( values[numValues] != NULL ) { 1136 ++numValues; 1137 } 1138 } 1139 1140 // add kSecAttrAccount 1141 if ( attrList.attr[2].length > 0 ) { 1142 keys[numValues] = kSecAttrAccount; 1143 values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[2].data, attrList.attr[2].length, kCFStringEncodingUTF8, FALSE); 1144 if ( values[numValues] != NULL ) { 1145 ++numValues; 1146 } 1147 } 1148 1149 // add kSecAttrPath 1150 if ( attrList.attr[3].length > 0 ) { 1151 keys[numValues] = kSecAttrPath; 1152 values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[3].data, attrList.attr[3].length, kCFStringEncodingUTF8, FALSE); 1153 if ( values[numValues] != NULL ) { 1154 ++numValues; 1155 } 1156 } 1157 1158 // add kSecAttrPort 1159 if ( attrList.attr[4].length > 0 ) { 1160 keys[numValues] = kSecAttrPort; 1161 values[numValues] = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[4].data); 1162 if ( values[numValues] != NULL ) { 1163 ++numValues; 1164 } 1165 } 1166 1167 // add kSecAttrProtocol 1168 if ( attrList.attr[5].length > 0 ) { 1169 keys[numValues] = kSecAttrProtocol; 1170 values[numValues] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType*)attrList.attr[5].data); 1171 if ( values[numValues] != NULL ) { 1172 CFRetain(values[numValues]); 1173 ++numValues; 1174 } 1175 } 1176 1177 // add kSecAttrAuthenticationType 1178 if ( attrList.attr[6].length > 0 ) { 1179 keys[numValues] = kSecAttrAuthenticationType; 1180 values[numValues] = _SecAttrAuthenticationTypeForSecAuthenticationType(*(SecProtocolType*)attrList.attr[6].data); 1181 if ( values[numValues] != NULL ) { 1182 CFRetain(values[numValues]); 1183 ++numValues; 1184 } 1185 } 1186 1187 // add kSecAttrComment 1188 if ( attrList.attr[7].length > 0 ) { 1189 keys[numValues] = kSecAttrComment; 1190 values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[7].data, attrList.attr[7].length, kCFStringEncodingUTF8, FALSE); 1191 if ( values[numValues] != NULL ) { 1192 ++numValues; 1193 } 1194 } 1195 1196 // add kSecAttrDescription 1197 if ( attrList.attr[8].length > 0 ) { 1198 keys[numValues] = kSecAttrDescription; 1199 values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[8].data, attrList.attr[8].length, kCFStringEncodingUTF8, FALSE); 1200 if ( values[numValues] != NULL ) { 1201 ++numValues; 1202 } 1203 } 1204 1205 // add kSecAttrLabel 1206 if ( attrList.attr[9].length > 0 ) { 1207 keys[numValues] = kSecAttrLabel; 1208 values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[9].data, attrList.attr[9].length, kCFStringEncodingUTF8, FALSE); 1209 if ( values[numValues] != NULL ) { 1210 ++numValues; 1211 } 1212 } 1213 1214 // add kSecAttrCreationDate 1215 if ( attrList.attr[10].length > 0 ) { 1216 CFDateRef creationDate = NULL; 1217 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[10].data, attrList.attr[10].length, &creationDate); 1218 keys[numValues] = kSecAttrCreationDate; 1219 values[numValues] = creationDate; 1220 if ( values[numValues] != NULL ) { 1221 ++numValues; 1222 } 1223 } 1224 1225 // add kSecAttrModificationDate 1226 if ( attrList.attr[11].length > 0 ) { 1227 CFDateRef modDate = NULL; 1228 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[11].data, attrList.attr[11].length, &modDate); 1229 keys[numValues] = kSecAttrModificationDate; 1230 values[numValues] = modDate; 1231 if ( values[numValues] != NULL ) { 1232 ++numValues; 1233 } 1234 } 1235 1236 // add kSecCreatorItemAttr 1237 if ( attrList.attr[12].length > 0 ) { 1238 CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[12].data); 1239 keys[numValues] = kSecAttrCreator; 1240 values[numValues] = numberRef; 1241 if ( values[numValues] != NULL ) { 1242 CFRetain(values[numValues]); 1243 ++numValues; 1244 } 1245 } 1246 1247 // add kSecTypeItemAttr 1248 if ( attrList.attr[13].length > 0 ) { 1249 CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[13].data); 1250 keys[numValues] = kSecAttrType; 1251 values[numValues] = numberRef; 1252 if ( values[numValues] != NULL ) { 1253 CFRetain(values[numValues]); 1254 ++numValues; 1255 } 1256 } 1257 1258 // add kSecInvisibleItemAttr 1259 if ( attrList.attr[14].length > 0 ) { 1260 uint32_t value = *((uint32_t*)attrList.attr[14].data); 1261 CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse; 1262 keys[numValues] = kSecAttrIsInvisible; 1263 values[numValues] = boolRef; 1264 if ( values[numValues] != NULL ) { 1265 CFRetain(values[numValues]); 1266 ++numValues; 1267 } 1268 } 1269 1270 // add kSecNegativeItemAttr 1271 if ( attrList.attr[15].length > 0 ) { 1272 uint32_t value = *((uint32_t*)attrList.attr[15].data); 1273 CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse; 1274 keys[numValues] = kSecAttrIsNegative; 1275 values[numValues] = boolRef; 1276 if ( values[numValues] != NULL ) { 1277 CFRetain(values[numValues]); 1278 ++numValues; 1279 } 1280 } 1281 1282 // create the dictionary 1283 *dictionary = CFDictionaryCreate(allocator, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1284 1285 // release the values added to the dictionary 1286 for ( index = 0; index < numValues; ++index ) 1287 { 1288 CFRelease(values[index]); 1289 } 1290 1291 // and free the attributes 1292 (void) SecKeychainItemFreeContent(&attrList, NULL); 1293 1294SecKeychainItemCopyContent_failed: 1295 1296 return ( status ); 1297} 1298 1299 1300/* 1301 * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the 1302 * attributes of the specified item class and item. 1303 */ 1304static OSStatus 1305_CreateAttributesDictionaryFromItem( 1306 CFAllocatorRef allocator, 1307 SecItemClass itemClass, 1308 SecKeychainItemRef item, 1309 CFDictionaryRef *dictionary) 1310{ 1311 switch (itemClass) 1312 { 1313 case kSecInternetPasswordItemClass: 1314 return _CreateAttributesDictionaryFromInternetPasswordItem(allocator, item, dictionary); 1315 1316 case kSecGenericPasswordItemClass: 1317 return _CreateAttributesDictionaryFromGenericPasswordItem(allocator, item, dictionary); 1318 1319 case kSecCertificateItemClass: 1320 return _CreateAttributesDictionaryFromCertificateItem(allocator, item, dictionary); 1321 1322 case kSecPublicKeyItemClass: 1323 case kSecPrivateKeyItemClass: 1324 case kSecSymmetricKeyItemClass: 1325 return _CreateAttributesDictionaryFromKeyItem(allocator, item, dictionary); 1326 1327 default: 1328 *dictionary = NULL; 1329 break; 1330 } 1331 return errSecParam; 1332} 1333 1334 1335/* 1336 * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList 1337 * by the _CreateSecKeychainAttributeListFromDictionary function. 1338 */ 1339static void 1340_FreeAttrList( 1341 SecKeychainAttributeList *attrListPtr) 1342{ 1343 UInt32 index; 1344 1345 if ( attrListPtr != NULL ) { 1346 if ( attrListPtr->attr != NULL ) { 1347 // free any attribute data 1348 for ( index = 0; index < attrListPtr->count; ++index ) { 1349 free(attrListPtr->attr[index].data); 1350 } 1351 // free the attribute array 1352 free(attrListPtr->attr); 1353 } 1354 // free the attribute list 1355 free(attrListPtr); 1356 } 1357} 1358 1359/* 1360 * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by 1361 * attr using the data and tag parameters. 1362 * 1363 * The memory for the SecKeychainAttribute's data field is allocated with malloc 1364 * and must be released by the caller (this is normally done by calling _FreeAttrList). 1365 */ 1366static OSStatus 1367_CFDataCreateAttribute( 1368 CFDataRef data, 1369 SecKeychainAttrType tag, 1370 SecKeychainAttributePtr attr) 1371{ 1372 OSStatus status = errSecSuccess; 1373 CFRange range; 1374 1375 // set the attribute tag 1376 attr->tag = tag; 1377 1378 // determine the attribute length 1379 attr->length = (UInt32) CFDataGetLength(data); 1380 range = CFRangeMake(0, (CFIndex)attr->length); 1381 1382 // allocate memory for the attribute bytes 1383 attr->data = malloc(attr->length); 1384 require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall); 1385 1386 // get the attribute bytes 1387 CFDataGetBytes(data, range, (UInt8 *)attr->data); 1388 1389malloc_failed: 1390 1391 return ( status ); 1392} 1393 1394/* 1395 * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by 1396 * attr using the string and tag parameters. 1397 * 1398 * The memory for the SecKeychainAttribute's data field is allocated with malloc 1399 * and must be released by the caller (this is normally done by calling _FreeAttrList). 1400 */ 1401static OSStatus 1402_CFStringCreateAttribute( 1403 CFStringRef string, 1404 SecKeychainAttrType tag, 1405 SecKeychainAttributePtr attr) 1406{ 1407 OSStatus status = errSecSuccess; 1408 CFRange range; 1409 1410 // set the attribute tag 1411 attr->tag = tag; 1412 1413 // determine the attribute length 1414 range = CFRangeMake(0, CFStringGetLength(string)); 1415 CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, NULL, 0, (CFIndex *)&attr->length); 1416 1417 // allocate memory for the attribute bytes 1418 attr->data = malloc(attr->length); 1419 require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall); 1420 1421 // get the attribute bytes 1422 CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, (UInt8 *)attr->data, attr->length, NULL); 1423 1424malloc_failed: 1425 1426 return ( status ); 1427} 1428 1429 1430/* 1431 * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList 1432 * from the attribute key/values in attrDictionary. 1433 * 1434 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList 1435 * must be freed by the caller with _FreeAttrList() 1436 */ 1437static OSStatus 1438_CreateSecKeychainGenericPasswordAttributeListFromDictionary( 1439 CFDictionaryRef attrDictionary, 1440 SecKeychainAttributeList **attrList) 1441{ 1442 return _ConvertNewFormatToOldFormat(NULL, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, attrDictionary, *attrList); 1443} 1444 1445 1446/* 1447 * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList 1448 * from the attribute key/values in attrDictionary. 1449 * 1450 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList 1451 * must be freed by the caller with _FreeAttrList() 1452 */ 1453static OSStatus 1454_CreateSecKeychainCertificateAttributeListFromDictionary( 1455 CFDictionaryRef attrDictionary, 1456 SecKeychainAttributeList **attrList) 1457{ 1458 return _ConvertNewFormatToOldFormat(NULL, gCertificateAttributes, kNumberOfCertificateAttributes, attrDictionary, *attrList); 1459} 1460 1461 1462/* 1463 * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList 1464 * from the attribute key/values in attrDictionary. 1465 * 1466 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList 1467 * must be freed by the caller with _FreeAttrList() 1468 */ 1469static OSStatus 1470_CreateSecKeychainKeyAttributeListFromDictionary( 1471 CFDictionaryRef attrDictionary, 1472 SecKeychainAttributeList **attrList) 1473{ 1474#if 0 1475 //%%%FIXME this function should work for key attributes, but currently doesn't; need to debug 1476 return _ConvertNewFormatToOldFormat(NULL, gKeyAttributes, kNumberOfKeyAttributes, attrDictionary, *attrList); 1477#else 1478 // explicitly build attribute list for supported key attributes 1479 // NOTE: this code supports only MaxSecKeyAttributes (15) attributes 1480 const int MaxSecKeyAttributes = 15; 1481 1482 OSStatus status; 1483 CFTypeRef value; 1484 SecKeychainAttributeList *attrListPtr; 1485 1486 attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList)); 1487 require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall); 1488 1489 attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeyAttributes, sizeof(SecKeychainAttribute)); 1490 require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall); 1491 1492 // [0] get the kSecKeyKeyClass value 1493 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyClass, (const void **)&value) && value) { 1494 UInt32 keyRecordValue = 0; 1495 if (CFEqual(kSecAttrKeyClassPublic, value)) 1496 keyRecordValue = CSSM_DL_DB_RECORD_PUBLIC_KEY; 1497 else if (CFEqual(kSecAttrKeyClassPrivate, value)) 1498 keyRecordValue = CSSM_DL_DB_RECORD_PRIVATE_KEY; 1499 else if (CFEqual(kSecAttrKeyClassSymmetric, value)) 1500 keyRecordValue = CSSM_DL_DB_RECORD_SYMMETRIC_KEY; 1501 1502 // only use this attribute if we recognize the value! 1503 if (keyRecordValue != 0) { 1504 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1505 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1506 1507 attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyClass; 1508 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1509 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyRecordValue; 1510 1511 ++attrListPtr->count; 1512 } 1513 } 1514 1515 // [1] get the kSecKeyPrintName string 1516 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) && value) { 1517 status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyPrintName, &attrListPtr->attr[attrListPtr->count]); 1518 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1519 1520 ++attrListPtr->count; 1521 } 1522 1523 // [2] get the kSecKeyPermanent boolean 1524 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsPermanent, (const void **)&value) && value) { 1525 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1526 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1527 1528 attrListPtr->attr[attrListPtr->count].tag = kSecKeyPermanent; 1529 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1530 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1531 1532 ++attrListPtr->count; 1533 } 1534 1535 // [3] get the kSecKeyLabel string 1536 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationLabel, (const void **)&value) && value) { 1537 if (CFStringGetTypeID() == CFGetTypeID(value)) 1538 status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyLabel, &attrListPtr->attr[attrListPtr->count]); 1539 else if (CFDataGetTypeID() == CFGetTypeID(value)) 1540 status = _CFDataCreateAttribute((CFDataRef)value, kSecKeyLabel, &attrListPtr->attr[attrListPtr->count]); 1541 else 1542 status = errSecParam; 1543 1544 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1545 1546 ++attrListPtr->count; 1547 } 1548 1549 // [4] get the kSecKeyApplicationTag data 1550 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationTag, (const void **)&value) && value) { 1551 if (CFStringGetTypeID() == CFGetTypeID(value)) 1552 status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]); 1553 else if (CFDataGetTypeID() == CFGetTypeID(value)) 1554 status = _CFDataCreateAttribute((CFDataRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]); 1555 else 1556 status = errSecParam; 1557 1558 require_noerr_quiet(status, CFDataCreateAttribute_failed); 1559 ++attrListPtr->count; 1560 } 1561 1562 // [5] get the kSecKeyKeyType number 1563 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyType, (const void **)&value) && value) { 1564 UInt32 keyAlgValue = _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType); 1565 if (keyAlgValue != 0) { 1566 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1567 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1568 1569 attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyType; 1570 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1571 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyAlgValue; 1572 1573 ++attrListPtr->count; 1574 } 1575 } 1576 1577 // [6] get the kSecKeyKeySizeInBits number 1578 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeySizeInBits, (const void **)&value) && value) { 1579 if (CFNumberGetTypeID() == CFGetTypeID(value)) { 1580 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1581 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1582 1583 attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeySizeInBits; 1584 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1585 CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data); 1586 1587 ++attrListPtr->count; 1588 } 1589 } 1590 1591 // [7] get the kSecKeyEffectiveKeySize number 1592 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrEffectiveKeySize, (const void **)&value) && value) { 1593 if (CFNumberGetTypeID() == CFGetTypeID(value)) { 1594 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1595 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1596 1597 attrListPtr->attr[attrListPtr->count].tag = kSecKeyEffectiveKeySize; 1598 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1599 CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data); 1600 1601 ++attrListPtr->count; 1602 } 1603 } 1604 1605 // [8] get the kSecKeyEncrypt boolean 1606 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanEncrypt, (const void **)&value) && value) { 1607 if (CFBooleanGetTypeID() == CFGetTypeID(value)) { 1608 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1609 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1610 1611 attrListPtr->attr[attrListPtr->count].tag = kSecKeyEncrypt; 1612 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1613 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1614 1615 ++attrListPtr->count; 1616 } 1617 } 1618 1619 // [9] get the kSecKeyDecrypt boolean 1620 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDecrypt, (const void **)&value) && value) { 1621 if (CFBooleanGetTypeID() == CFGetTypeID(value)) { 1622 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1623 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1624 1625 attrListPtr->attr[attrListPtr->count].tag = kSecKeyDecrypt; 1626 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1627 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1628 1629 ++attrListPtr->count; 1630 } 1631 } 1632 1633 // [10] get the kSecKeyDerive boolean 1634 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDerive, (const void **)&value) && value) { 1635 if (CFBooleanGetTypeID() == CFGetTypeID(value)) { 1636 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1637 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1638 1639 attrListPtr->attr[attrListPtr->count].tag = kSecKeyDerive; 1640 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1641 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1642 1643 ++attrListPtr->count; 1644 } 1645 } 1646 1647 // [11] get the kSecKeySign boolean 1648 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanSign, (const void **)&value) && value) { 1649 if (CFBooleanGetTypeID() == CFGetTypeID(value)) { 1650 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1651 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1652 1653 attrListPtr->attr[attrListPtr->count].tag = kSecKeySign; 1654 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1655 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1656 1657 ++attrListPtr->count; 1658 } 1659 } 1660 1661 // [12] get the kSecKeyVerify boolean 1662 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanVerify, (const void **)&value) && value) { 1663 if (CFBooleanGetTypeID() == CFGetTypeID(value)) { 1664 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1665 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1666 1667 attrListPtr->attr[attrListPtr->count].tag = kSecKeyVerify; 1668 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1669 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1670 1671 ++attrListPtr->count; 1672 } 1673 } 1674 1675 // [13] get the kSecKeyWrap boolean 1676 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanWrap, (const void **)&value) && value) { 1677 if (CFBooleanGetTypeID() == CFGetTypeID(value)) { 1678 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1679 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1680 1681 attrListPtr->attr[attrListPtr->count].tag = kSecKeyWrap; 1682 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1683 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1684 1685 ++attrListPtr->count; 1686 } 1687 } 1688 1689 // [14] get the kSecKeyUnwrap boolean 1690 if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanUnwrap, (const void **)&value) && value) { 1691 if (CFBooleanGetTypeID() == CFGetTypeID(value)) { 1692 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1693 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); 1694 1695 attrListPtr->attr[attrListPtr->count].tag = kSecKeyUnwrap; 1696 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1697 *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0; 1698 1699 ++attrListPtr->count; 1700 } 1701 } 1702 1703 // return the pointer to the attrList 1704 *attrList = attrListPtr; 1705 1706 return ( errSecSuccess ); 1707 1708 /***************/ 1709 1710malloc_number_failed: 1711CFDataCreateAttribute_failed: 1712CFStringCreateAttribute_failed: 1713malloc_attrPtr_failed: 1714 1715 // free any attributes 1716 _FreeAttrList(attrListPtr); 1717 1718calloc_attrListPtr_failed: 1719 1720 return ( errSecBufferTooSmall ); 1721 1722#endif 1723} 1724 1725static CFTypeRef copyNumber(CFTypeRef obj) 1726{ 1727 if (!obj) 1728 return NULL; 1729 1730 CFTypeID tid = CFGetTypeID(obj); 1731 if (tid == CFNumberGetTypeID()) 1732 { 1733 CFRetain(obj); 1734 return obj; 1735 } 1736 1737 if (tid == CFBooleanGetTypeID()) 1738 { 1739 SInt32 value = CFBooleanGetValue((CFBooleanRef)obj); 1740 return CFNumberCreate(0, kCFNumberSInt32Type, &value); 1741 } 1742 1743 if (tid == CFStringGetTypeID()) 1744 { 1745 SInt32 value = CFStringGetIntValue((CFStringRef)obj); 1746 CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value); 1747 /* If a string converted to an int isn't equal to the int printed as 1748 a string, return a NULL instead. */ 1749 if (!CFEqual(t, obj)) 1750 { 1751 CFRelease(t); 1752 return NULL; 1753 } 1754 CFRelease(t); 1755 return CFNumberCreate(0, kCFNumberSInt32Type, &value); 1756 } 1757 return NULL; 1758} 1759 1760/* 1761 * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList 1762 * from the attribute key/values in attrDictionary. 1763 * 1764 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList 1765 * must be freed by the caller with _FreeAttrList() 1766 */ 1767static OSStatus 1768_CreateSecKeychainInternetPasswordAttributeListFromDictionary( 1769 CFDictionaryRef attrDictionary, 1770 SecKeychainAttributeList **attrList) 1771{ 1772 // explicitly build attribute list for supported key attributes 1773 // NOTE: this code supports only MaxSecKeychainAttributes (14) attributes 1774 const int MaxSecKeychainAttributes = 14; 1775 1776 OSStatus status; 1777 CFTypeRef value; 1778 SecKeychainAttributeList *attrListPtr; 1779 1780 attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList)); 1781 require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall); 1782 1783 attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeychainAttributes, sizeof(SecKeychainAttribute)); 1784 require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall); 1785 1786 1787 // [0] get the serverName string 1788 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrServer, (const void **)&value) ) { 1789 status = _CFStringCreateAttribute((CFStringRef)value, kSecServerItemAttr, &attrListPtr->attr[attrListPtr->count]); 1790 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1791 1792 ++attrListPtr->count; 1793 } 1794 1795 // [1] get the securityDomain string 1796 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrSecurityDomain, (const void **)&value) ) { 1797 status = _CFStringCreateAttribute((CFStringRef)value, kSecSecurityDomainItemAttr, &attrListPtr->attr[attrListPtr->count]); 1798 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1799 1800 ++attrListPtr->count; 1801 } 1802 1803 // [2] get the accountName string 1804 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAccount, (const void **)&value) ) { 1805 status = _CFStringCreateAttribute((CFStringRef)value, kSecAccountItemAttr, &attrListPtr->attr[attrListPtr->count]); 1806 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1807 1808 ++attrListPtr->count; 1809 } 1810 1811 // [3] get the path string 1812 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPath, (const void **)&value) ) { 1813 status = _CFStringCreateAttribute((CFStringRef)value, kSecPathItemAttr, &attrListPtr->attr[attrListPtr->count]); 1814 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1815 1816 ++attrListPtr->count; 1817 } 1818 1819 // [4] get the port number 1820 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPort, (const void **)&value) ) { 1821 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt16)); 1822 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall); 1823 1824 CFTypeRef num = copyNumber(value); 1825 require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam); 1826 attrListPtr->attr[attrListPtr->count].tag = kSecPortItemAttr; 1827 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt16); 1828 CFNumberGetValue((CFNumberRef)num, kCFNumberSInt16Type, attrListPtr->attr[attrListPtr->count].data); 1829 CFRelease(num); 1830 1831 ++attrListPtr->count; 1832 } 1833 1834 // [5] get the protocol 1835 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrProtocol, (const void **)&value) ) { 1836 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecProtocolType)); 1837 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_protocol_failed, status = errSecBufferTooSmall); 1838 1839 attrListPtr->attr[attrListPtr->count].tag = kSecProtocolItemAttr; 1840 attrListPtr->attr[attrListPtr->count].length = sizeof(SecProtocolType); 1841 *(SecProtocolType *)(attrListPtr->attr[attrListPtr->count].data) = _SecProtocolTypeForSecAttrProtocol(value); 1842 1843 ++attrListPtr->count; 1844 } 1845 1846 // [6] get the authenticationType 1847 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAuthenticationType, (const void **)&value) ) { 1848 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecAuthenticationType)); 1849 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_authenticationType_failed, status = errSecBufferTooSmall); 1850 1851 attrListPtr->attr[attrListPtr->count].tag = kSecAuthenticationTypeItemAttr; 1852 attrListPtr->attr[attrListPtr->count].length = sizeof(SecAuthenticationType); 1853 *(SecAuthenticationType *)(attrListPtr->attr[attrListPtr->count].data) = _SecAuthenticationTypeForSecAttrAuthenticationType(value); 1854 1855 ++attrListPtr->count; 1856 } 1857 1858 // [7] get the comment string 1859 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrComment, (const void **)&value) ) { 1860 status = _CFStringCreateAttribute((CFStringRef)value, kSecCommentItemAttr, &attrListPtr->attr[attrListPtr->count]); 1861 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1862 1863 ++attrListPtr->count; 1864 } 1865 1866 // [8] get the description string 1867 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrDescription, (const void **)&value) ) { 1868 status = _CFStringCreateAttribute((CFStringRef)value, kSecDescriptionItemAttr, &attrListPtr->attr[attrListPtr->count]); 1869 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1870 1871 ++attrListPtr->count; 1872 } 1873 1874 // [9] get the label string 1875 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) ) { 1876 status = _CFStringCreateAttribute((CFStringRef)value, kSecLabelItemAttr, &attrListPtr->attr[attrListPtr->count]); 1877 require_noerr_quiet(status, CFStringCreateAttribute_failed); 1878 1879 ++attrListPtr->count; 1880 } 1881 1882 // [10] get the creator code 1883 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCreator, (const void **)&value) ) { 1884 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1885 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall); 1886 1887 CFTypeRef num = copyNumber(value); 1888 require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam); 1889 attrListPtr->attr[attrListPtr->count].tag = kSecCreatorItemAttr; 1890 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1891 CFNumberGetValue((CFNumberRef)num, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data); 1892 CFRelease(num); 1893 1894 ++attrListPtr->count; 1895 } 1896 1897 // [11] get the type code 1898 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrType, (const void **)&value) ) { 1899 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1900 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall); 1901 1902 CFTypeRef num = copyNumber(value); 1903 require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam); 1904 attrListPtr->attr[attrListPtr->count].tag = kSecTypeItemAttr; 1905 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1906 CFNumberGetValue((CFNumberRef)num, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data); 1907 CFRelease(num); 1908 1909 ++attrListPtr->count; 1910 } 1911 1912 // [12] get the invisible flag 1913 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsInvisible, (const void **)&value) ) { 1914 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1915 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall); 1916 1917 attrListPtr->attr[attrListPtr->count].tag = kSecInvisibleItemAttr; 1918 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1919 *(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0; 1920 1921 ++attrListPtr->count; 1922 } 1923 1924 // [13] get the negative flag 1925 if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsNegative, (const void **)&value) ) { 1926 attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); 1927 require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall); 1928 1929 attrListPtr->attr[attrListPtr->count].tag = kSecNegativeItemAttr; 1930 attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32); 1931 *(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0; 1932 1933 ++attrListPtr->count; 1934 } 1935 1936 // return the pointer to the attrList 1937 *attrList = attrListPtr; 1938 1939 return ( errSecSuccess ); 1940 1941 /***************/ 1942 1943malloc_authenticationType_failed: 1944malloc_protocol_failed: 1945malloc_port_failed: 1946CFStringCreateAttribute_failed: 1947malloc_attrPtr_failed: 1948 1949 // free any attributes 1950 _FreeAttrList(attrListPtr); 1951 1952calloc_attrListPtr_failed: 1953 1954 return ( errSecBufferTooSmall ); 1955} 1956 1957 1958/* 1959 * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList 1960 * from the attribute key/values in attrDictionary for the specified item class. 1961 * 1962 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList 1963 * must be freed by the caller with _FreeAttrList() 1964 */ 1965static OSStatus 1966_CreateSecKeychainAttributeListFromDictionary( 1967 CFDictionaryRef attrDictionary, 1968 SecItemClass itemClass, 1969 SecKeychainAttributeList **attrList) 1970{ 1971 switch (itemClass) 1972 { 1973 case kSecInternetPasswordItemClass: 1974 return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary, attrList); 1975 1976 case kSecGenericPasswordItemClass: 1977 return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary, attrList); 1978 1979 case kSecCertificateItemClass: 1980 return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary, attrList); 1981 1982 case kSecPublicKeyItemClass: 1983 case kSecPrivateKeyItemClass: 1984 case kSecSymmetricKeyItemClass: 1985 return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary, attrList); 1986 1987 default: 1988 break; 1989 } 1990 return errSecParam; 1991} 1992 1993 1994/* 1995 * _AppNameFromSecTrustedApplication attempts to pull the name of the 1996 * application/tool from the SecTrustedApplicationRef. 1997 */ 1998static CFStringRef 1999_AppNameFromSecTrustedApplication( 2000 CFAllocatorRef alloc, 2001 SecTrustedApplicationRef appRef) 2002{ 2003 CFStringRef result; 2004 OSStatus status; 2005 CFDataRef appDataRef; 2006 2007 result = NULL; 2008 2009 // get the data for item's application/tool 2010 status = SecTrustedApplicationCopyData(appRef, &appDataRef); 2011 if ( status == errSecSuccess ) { 2012 CFStringRef path; 2013 2014 // convert it to a CFString potentially containing the path 2015 path = CFStringCreateWithCString(NULL, (char *)CFDataGetBytePtrVoid(appDataRef), kCFStringEncodingUTF8); 2016 if ( path != NULL ) { 2017 // the path has to start with a "/" and cannot contain "://" 2018 if ( CFStringHasPrefix(path, CFSTR("/")) && (CFStringFind(path, CFSTR("://"), 0).location == kCFNotFound) ) { 2019 CFRange nameRange, compRg; 2020 2021 nameRange = CFRangeMake(0, CFStringGetLength(path)); 2022 2023 // remove the trailing slashes (if any) 2024 while ( (nameRange.length > 0) && (CFStringGetCharacterAtIndex(path, nameRange.length - 1) == '/') ) { 2025 nameRange.length --; 2026 } 2027 2028 if ( nameRange.length > 0 ) { 2029 // find last slash and adjust nameRange be everything after it 2030 if ( CFStringFindWithOptions(path, CFSTR("/"), nameRange, kCFCompareBackwards, &compRg) ) { 2031 nameRange.length = nameRange.location + nameRange.length - (compRg.location + 1); 2032 nameRange.location = compRg.location + 1; 2033 } 2034 2035 result = CFStringCreateWithSubstring(alloc, path, nameRange); 2036 } 2037 } 2038 CFRelease(path); 2039 } 2040 CFRelease(appDataRef); 2041 } 2042 2043 return ( result ); 2044} 2045 2046/* (This function really belongs in SecIdentity.cpp!) 2047 * 2048 * Returns the public key item corresponding to the identity, if it exists in 2049 * the same keychain as the private key. Note that the public key might not 2050 * exist in the same keychain (e.g. if the identity was imported via PKCS12), 2051 * in which case it will not be found. 2052 */ 2053static OSStatus 2054_SecIdentityCopyPublicKey( 2055 SecIdentityRef identityRef, 2056 SecKeyRef *publicKeyRef) 2057{ 2058 OSStatus status; 2059 UInt32 count; 2060 SecKeychainAttribute attr = { kSecKeyLabel, 0, NULL }; 2061 SecKeychainAttributeList attrList = { 1, &attr }; 2062 SecKeychainAttributeList *keyAttrList = NULL; 2063 SecKeychainAttributeInfo *info = NULL; 2064 SecKeychainSearchRef search = NULL; 2065 SecKeychainRef keychain = NULL; 2066 SecKeychainItemRef privateKey = NULL; 2067 SecKeychainItemRef publicKey = NULL; 2068 2069 status = SecIdentityCopyPrivateKey(identityRef, (SecKeyRef *)&privateKey); 2070 if (status) { 2071 goto error_exit; // identity must have a private key 2072 } 2073 status = SecKeychainItemCopyKeychain(privateKey, &keychain); 2074 if (status) { 2075 goto error_exit; // private key must have a keychain, so we can get the attribute info for it 2076 } 2077 status = SecKeychainAttributeInfoForItemID(keychain, kSecPrivateKeyItemClass, &info); 2078 if (status) { 2079 goto error_exit; // unable to get the attribute info (i.e. database schema) for private keys 2080 } 2081 status = SecKeychainItemCopyAttributesAndData(privateKey, info, NULL, &keyAttrList, NULL, NULL); 2082 if (status) { 2083 goto error_exit; // unable to get the key label attribute for the private key 2084 } 2085 2086 // use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching 2087 for (count = 0; count < keyAttrList->count; count++) { 2088 if (keyAttrList->attr[count].tag == kSecKeyLabel) { 2089 attr.length = keyAttrList->attr[count].length; 2090 attr.data = keyAttrList->attr[count].data; 2091 break; 2092 } 2093 } 2094 if (!attr.length || !attr.data) { 2095 status = errSecNoSuchAttr; 2096 goto error_exit; // the private key didn't have the hash of the public key in its kSecKeyLabel 2097 } 2098 status = SecKeychainSearchCreateFromAttributes(keychain, kSecPublicKeyItemClass, &attrList, &search); 2099 if (status) { 2100 goto error_exit; // unable to create the search reference 2101 } 2102 status = SecKeychainSearchCopyNext(search, &publicKey); 2103 if (status) { 2104 goto error_exit; // unable to find the public key 2105 } 2106 2107 if (publicKeyRef) 2108 *publicKeyRef = (SecKeyRef)publicKey; 2109 else 2110 CFRelease(publicKey); 2111 2112error_exit: 2113 if (status != errSecSuccess) { 2114 if (publicKeyRef) 2115 *publicKeyRef = NULL; 2116 if (publicKey) 2117 CFRelease(publicKey); 2118 } 2119 if (search) 2120 CFRelease(search); 2121 2122 if (keyAttrList) 2123 SecKeychainItemFreeAttributesAndData(keyAttrList, NULL); 2124 2125 if (info) 2126 SecKeychainFreeAttributeInfo(info); 2127 2128 if (keychain) 2129 CFRelease(keychain); 2130 2131 if (privateKey) 2132 CFRelease(privateKey); 2133 2134 return status; 2135} 2136 2137 2138/* 2139 * Deletes a keychain item if the current application/tool is the only application/tool 2140 * with decrypt access to that keychain item. If more than one application/tool 2141 * has decrypt access to the keychain item, the item is left on the keychain. 2142 * 2143 * TBD: If more than one app/tool has access to the keychain item, we should remove 2144 * the current app/tool's decrypt access. There's no easy way to do that with 2145 * current keychain APIs without bringing up the security UI. 2146 */ 2147static OSStatus 2148_SafeSecKeychainItemDelete( 2149 SecKeychainItemRef itemRef) 2150{ 2151 OSStatus status; 2152 SecAccessRef access; 2153 CFArrayRef aclList; 2154 SecACLRef acl; 2155 CFArrayRef appList; 2156 CFStringRef description; 2157 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector; 2158 2159 SecItemClass itemClass; 2160 status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL); 2161 if (!status && (itemClass == kSecCertificateItemClass || itemClass == kSecPublicKeyItemClass)) { 2162 // the item doesn't have any access controls, so delete it normally 2163 return SecKeychainItemDelete(itemRef); 2164 } 2165 2166 // skip access control checking for web form passwords: <rdar://10957301> 2167 // This permits Safari to manage the removal of all web form passwords, 2168 // regardless of whether they are shared by multiple applications. 2169 if (itemClass == kSecInternetPasswordItemClass) { 2170 UInt32 tags[1] = { kSecAuthenticationTypeItemAttr }; 2171 SecKeychainAttributeInfo attrInfo = { 1, tags, NULL }; 2172 SecKeychainAttributeList *attrs = NULL; 2173 status = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrs, NULL, NULL); 2174 if (!status && attrs) { 2175 bool webFormPassword = (attrs->attr[0].length == 4 && (!memcmp(attrs->attr[0].data, "form", 4))); 2176 SecKeychainItemFreeAttributesAndData(attrs, NULL); 2177 if (webFormPassword) { 2178 return SecKeychainItemDelete(itemRef); 2179 } 2180 } 2181 } 2182 2183 // copy the access of the keychain item 2184 status = SecKeychainItemCopyAccess(itemRef, &access); 2185 require_noerr(status, SecKeychainItemCopyAccessFailed); 2186 2187 // copy the decrypt access control lists -- this is what has access to the keychain item 2188 status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList); 2189 require_noerr(status, SecAccessCopySelectedACLListFailed); 2190 require_quiet(aclList != NULL, noACLList); 2191 2192 // get the access control list 2193 acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0); 2194 require_quiet(acl != NULL, noACL); 2195 2196 // copy the application list, description, and CSSM prompt selector for a given access control list entry 2197 status = SecACLCopySimpleContents(acl, &appList, &description, &promptSelector); 2198 require_noerr(status, SecACLCopySimpleContentsFailed); 2199 require_quiet(appList != NULL, noAppList); 2200 2201 // does only a single application/tool have decrypt access to this item? 2202 if ( CFArrayGetCount(appList) == 1 ) { 2203 SecTrustedApplicationRef itemAppRef, currentAppRef; 2204 CFStringRef itemAppName, currentAppName; 2205 2206 // get SecTrustedApplicationRef for item's application/tool 2207 itemAppRef = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(appList, 0); 2208 require(itemAppRef != NULL, noItemAppRef); 2209 2210 // copy the name out 2211 itemAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), itemAppRef); 2212 require(itemAppName != NULL, noAppName); 2213 2214 // create SecTrustedApplicationRef for current application/tool 2215 status = SecTrustedApplicationCreateFromPath(NULL, ¤tAppRef); 2216 require((status == errSecSuccess) && (currentAppRef != NULL), SecTrustedApplicationCreateFromPathFailed); 2217 2218 // copy the name out 2219 currentAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), currentAppRef); 2220 require(currentAppName != NULL, noCurrentAppName); 2221 2222 // compare the current application/tool's name to this item's application/tool's name to see if we own the decrypt access 2223 if ( CFStringCompare(currentAppName, itemAppName, 0) == kCFCompareEqualTo ) { 2224 // delete the keychain item 2225 SecKeychainItemDelete(itemRef); 2226 } 2227 2228 CFRelease(currentAppName); 2229 noCurrentAppName: 2230 CFRelease(currentAppRef); 2231 SecTrustedApplicationCreateFromPathFailed: 2232 CFRelease(itemAppName); 2233 noAppName: 2234 noItemAppRef: 2235 ; 2236 } 2237 2238 if ( description ) { 2239 CFRelease(description); 2240 } 2241 CFRelease(appList); 2242noAppList: 2243SecACLCopySimpleContentsFailed: 2244noACL: 2245 CFRelease(aclList); 2246noACLList: 2247SecAccessCopySelectedACLListFailed: 2248 CFRelease(access); 2249SecKeychainItemCopyAccessFailed: 2250 2251 return status; 2252} 2253 2254static OSStatus 2255_UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes) 2256{ 2257 // This function updates a single keychain item, which may be specified as 2258 // a reference, persistent reference or attribute dictionary, with the 2259 // attributes provided. 2260 2261 OSStatus status = errSecSuccess; 2262 if (!item) { 2263 return errSecParam; 2264 } 2265 2266 SecItemClass itemClass; 2267 SecAccessRef access = NULL; 2268 SecKeychainAttributeList *changeAttrList = NULL; 2269 SecKeychainItemRef itemToUpdate = NULL; 2270 CFDataRef theData = NULL; 2271 CFTypeID itemType = CFGetTypeID(item); 2272 2273 // validate input item (must be convertible to a SecKeychainItemRef) 2274 if (SecKeychainItemGetTypeID() == itemType || 2275 SecCertificateGetTypeID() == itemType || 2276 SecKeyGetTypeID() == itemType) { 2277 // item is already a reference, retain it 2278 itemToUpdate = (SecKeychainItemRef) CFRetain(item); 2279 } 2280 else if (CFDataGetTypeID() == itemType) { 2281 // item is a persistent reference, must convert it 2282 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToUpdate); 2283 } 2284 else if (CFDictionaryGetTypeID() == itemType) { 2285 // item is a dictionary 2286 CFTypeRef value = NULL; 2287 if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) { 2288 // kSecValueRef value is a SecKeychainItemRef, retain it 2289 itemToUpdate = (SecKeychainItemRef) CFRetain(value); 2290 } 2291 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) { 2292 // kSecValuePersistentRef value is a persistent reference, must convert it 2293 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToUpdate); 2294 } 2295 } 2296 else if (SecIdentityGetTypeID() == itemType) { 2297 // item is a certificate + private key; since we can't really change the 2298 // certificate's attributes, assume we want to update the private key 2299 status = SecIdentityCopyPrivateKey((SecIdentityRef)item, (SecKeyRef*)&itemToUpdate); 2300 } 2301 require_action(itemToUpdate != NULL, update_failed, status = errSecInvalidItemRef); 2302 require_noerr(status, update_failed); 2303 2304 status = SecKeychainItemCopyContent(itemToUpdate, &itemClass, NULL, NULL, NULL); 2305 require_noerr(status, update_failed); 2306 2307 // build changeAttrList from changedAttributes dictionary 2308 switch (itemClass) 2309 { 2310 case kSecInternetPasswordItemClass: 2311 { 2312 status = _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList); 2313 require_noerr(status, update_failed); 2314 } 2315 break; 2316 2317 case kSecGenericPasswordItemClass: 2318 { 2319 status = _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList); 2320 require_noerr(status, update_failed); 2321 } 2322 break; 2323 2324 case kSecCertificateItemClass: 2325 { 2326 status = _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes, &changeAttrList); 2327 require_noerr(status, update_failed); 2328 } 2329 break; 2330 2331 case kSecPublicKeyItemClass: 2332 case kSecPrivateKeyItemClass: 2333 case kSecSymmetricKeyItemClass: 2334 { 2335 status = _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes, &changeAttrList); 2336 require_noerr(status, update_failed); 2337 } 2338 } 2339 2340 // get the password 2341 // (if the caller is not updating the password, this value will be NULL) 2342 theData = (CFDataRef)CFDictionaryGetValue(changedAttributes, kSecValueData); 2343 if (theData != NULL) { 2344 require_action(CFDataGetTypeID() == CFGetTypeID(theData), update_failed, status = errSecParam); 2345 } 2346 // update item 2347 status = SecKeychainItemModifyContent(itemToUpdate, 2348 (changeAttrList->count == 0) ? NULL : changeAttrList, 2349 (theData != NULL) ? (UInt32)CFDataGetLength(theData) : 0, 2350 (theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL); 2351 2352 // one more thing... update access? 2353 if (CFDictionaryGetValueIfPresent(changedAttributes, kSecAttrAccess, (const void **)&access)) { 2354 status = SecKeychainItemSetAccess(itemToUpdate, access); 2355 } 2356 2357update_failed: 2358 if (itemToUpdate) 2359 CFRelease(itemToUpdate); 2360 _FreeAttrList(changeAttrList); 2361 return status; 2362} 2363 2364static OSStatus 2365_DeleteKeychainItem(CFTypeRef item) 2366{ 2367 // This function deletes a single keychain item, which may be specified as 2368 // a reference, persistent reference or attribute dictionary. It will not 2369 // delete non-keychain items or aggregate items (such as a SecIdentityRef); 2370 // it is assumed that the caller will pass identity components separately. 2371 2372 OSStatus status = errSecSuccess; 2373 if (!item) { 2374 return errSecParam; 2375 } 2376 2377 SecKeychainItemRef itemToDelete = NULL; 2378 CFTypeID itemType = CFGetTypeID(item); 2379 if (SecKeychainItemGetTypeID() == itemType || 2380 SecCertificateGetTypeID() == itemType || 2381 SecKeyGetTypeID() == itemType) { 2382 // item is already a reference, retain it 2383 itemToDelete = (SecKeychainItemRef) CFRetain(item); 2384 } 2385 else if (CFDataGetTypeID() == itemType) { 2386 // item is a persistent reference, must convert it 2387 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToDelete); 2388 } 2389 else if (CFDictionaryGetTypeID() == itemType) { 2390 // item is a dictionary 2391 CFTypeRef value = NULL; 2392 if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) { 2393 // kSecValueRef value is a SecKeychainItemRef, retain it 2394 itemToDelete = (SecKeychainItemRef) CFRetain(value); 2395 } 2396 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) { 2397 // kSecValuePersistentRef value is a persistent reference, must convert it 2398 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToDelete); 2399 } 2400 } 2401 2402 if (itemToDelete) { 2403 if (!status) { 2404 status = _SafeSecKeychainItemDelete(itemToDelete); 2405 } 2406 CFRelease(itemToDelete); 2407 } 2408 2409 return status; 2410} 2411 2412static OSStatus 2413_DeleteIdentity(SecIdentityRef identity) 2414{ 2415 OSStatus status, result = errSecSuccess; 2416 SecKeyRef privateKey = NULL; 2417 SecCertificateRef certificate = NULL; 2418 2419 status = SecIdentityCopyPrivateKey(identity, &privateKey); 2420 if (!status) { 2421 SecKeyRef publicKey = NULL; 2422 status = _SecIdentityCopyPublicKey(identity, &publicKey); 2423 if (!status) { 2424 status = _DeleteKeychainItem(publicKey); 2425 CFRelease(publicKey); 2426 } 2427 status = _DeleteKeychainItem(privateKey); 2428 } 2429 2430 if (privateKey) CFRelease(privateKey); 2431 if (status) result = status; 2432 2433 status = SecIdentityCopyCertificate(identity, &certificate); 2434 if (!status) { 2435 status = _DeleteKeychainItem(certificate); 2436 } 2437 2438 if (certificate) CFRelease(certificate); 2439 if (status) result = status; 2440 2441 return result; 2442} 2443 2444static OSStatus 2445_UpdateAggregateStatus(OSStatus newStatus, OSStatus curStatus, OSStatus baseStatus) 2446{ 2447 // This function is used when atomically processing multiple items, 2448 // where an overall error result must be returned for the entire operation. 2449 // When newStatus is something other than errSecSuccess, we want to keep the "most 2450 // interesting" status (which usually will be newStatus, unless curStatus is 2451 // already set; in that case, newStatus can trump curStatus only by being 2452 // something different than baseStatus.) 2453 2454 OSStatus result = curStatus; 2455 2456 if (newStatus != errSecSuccess) { 2457 result = newStatus; 2458 if (curStatus != errSecSuccess) { 2459 result = (newStatus != baseStatus) ? newStatus : curStatus; 2460 } 2461 } 2462 return result; 2463} 2464 2465static void 2466_AddDictValueToOtherDict(const void *key, const void *value, void *context) 2467{ 2468 // CFDictionaryApplierFunction 2469 // This function just takes the given key/value pair, 2470 // and adds it to another dictionary supplied in the context argument. 2471 2472 CFMutableDictionaryRef dict = *((CFMutableDictionaryRef*) context); 2473 if (key && value) { 2474 CFDictionaryAddValue(dict, key, value); 2475 } 2476} 2477 2478static CFStringCompareFlags 2479_StringCompareFlagsFromQuery(CFDictionaryRef query) 2480{ 2481 CFTypeRef value; 2482 CFStringCompareFlags flags = 0; 2483 if (!query) return flags; 2484 2485 if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&value) || 2486 CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value)) 2487 flags |= kCFCompareAnchored; 2488 2489 if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value)) 2490 flags |= kCFCompareBackwards; 2491 2492 if (CFDictionaryGetValueIfPresent(query, kSecMatchCaseInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2493 flags |= kCFCompareCaseInsensitive; 2494 2495 if (CFDictionaryGetValueIfPresent(query, kSecMatchDiacriticInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2496 flags |= kCFCompareDiacriticInsensitive; 2497 2498 if (CFDictionaryGetValueIfPresent(query, kSecMatchWidthInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2499 flags |= kCFCompareWidthInsensitive; 2500 2501 return flags; 2502} 2503 2504static uint32 2505_CssmKeyUsageFromQuery(CFDictionaryRef query) 2506{ 2507 CFTypeRef value; 2508 uint32 keyUsage = 0; 2509 if (!query) return keyUsage; 2510 2511 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanEncrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2512 keyUsage |= CSSM_KEYUSE_ENCRYPT; 2513 2514 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDecrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2515 keyUsage |= CSSM_KEYUSE_DECRYPT; 2516 2517 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanSign, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2518 keyUsage |= CSSM_KEYUSE_SIGN; 2519 2520 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanVerify, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2521 keyUsage |= CSSM_KEYUSE_VERIFY; 2522 2523 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanWrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2524 keyUsage |= CSSM_KEYUSE_WRAP; 2525 2526 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanUnwrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2527 keyUsage |= CSSM_KEYUSE_UNWRAP; 2528 2529 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDerive, (const void **)&value) && CFEqual(kCFBooleanTrue, value)) 2530 keyUsage |= CSSM_KEYUSE_DERIVE; 2531 2532 return keyUsage; 2533} 2534 2535static SecItemClass 2536_ConvertItemClass(const void* item, const void* keyClass, Boolean *isIdentity) 2537{ 2538 SecItemClass itemClass = (SecItemClass) 0; 2539 if (isIdentity) *isIdentity = false; 2540 2541 if (CFEqual(item, kSecClassGenericPassword)) { 2542 itemClass = kSecGenericPasswordItemClass; 2543 } 2544 else if (CFEqual(item, kSecClassInternetPassword)) { 2545 itemClass = kSecInternetPasswordItemClass; 2546 } 2547 else if (CFEqual(item, kSecClassCertificate)) { 2548 itemClass = kSecCertificateItemClass; 2549 } 2550 else if (CFEqual(item, kSecClassIdentity)) { 2551 // will perform a certificate lookup 2552 itemClass = kSecCertificateItemClass; 2553 if (isIdentity) *isIdentity = true; 2554 } 2555 else if (CFEqual(item, kSecClassKey)) { 2556 // examine second parameter to determine type of key 2557 if (!keyClass || CFEqual(keyClass, kSecAttrKeyClassSymmetric)) { 2558 itemClass = kSecSymmetricKeyItemClass; 2559 } 2560 else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPublic)) { 2561 itemClass = kSecPublicKeyItemClass; 2562 } 2563 else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPrivate)) { 2564 itemClass = kSecPrivateKeyItemClass; 2565 } 2566 } 2567 2568 return itemClass; 2569} 2570 2571static SecItemClass 2572_ItemClassFromItemList(CFArrayRef itemList) 2573{ 2574 // Given a list of items (standard or persistent references), 2575 // determine whether they all have the same item class. Returns 2576 // the item class, or 0 if multiple classes in list. 2577 SecItemClass result = 0; 2578 CFIndex index, count = (itemList) ? CFArrayGetCount(itemList) : 0; 2579 for (index=0; index < count; index++) { 2580 CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(itemList, index); 2581 if (item) { 2582 SecKeychainItemRef itemRef = NULL; 2583 OSStatus status; 2584 if (CFGetTypeID(item) == CFDataGetTypeID()) { 2585 // persistent reference, resolve first 2586 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemRef); 2587 } 2588 else { 2589 itemRef = (SecKeychainItemRef) CFRetain(item); 2590 } 2591 if (itemRef) { 2592 SecItemClass itemClass = 0; 2593 CFTypeID itemTypeID = CFGetTypeID(itemRef); 2594 if (itemTypeID == SecIdentityGetTypeID() || itemTypeID == SecCertificateGetTypeID()) { 2595 // Identities and certificates have the same underlying item class 2596 itemClass = kSecCertificateItemClass; 2597 } 2598 else if (itemTypeID == SecKeychainItemGetTypeID()) { 2599 // Reference to item in a keychain 2600 status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL); 2601 } 2602 else if (itemTypeID == SecKeyGetTypeID()) { 2603 // SecKey that isn't stored in a keychain 2604 // %%% will need to change this code when SecKey is no longer CSSM-based %%% 2605 const CSSM_KEY *cssmKey; 2606 status = SecKeyGetCSSMKey((SecKeyRef)itemRef, &cssmKey); 2607 if (status == errSecSuccess) { 2608 if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY) 2609 itemClass = kSecPublicKeyItemClass; 2610 else if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) 2611 itemClass = kSecPrivateKeyItemClass; 2612 else 2613 itemClass = kSecSymmetricKeyItemClass; 2614 } 2615 } 2616 CFRelease(itemRef); 2617 if (itemClass != 0) { 2618 if (result != 0 && result != itemClass) { 2619 return 0; // different item classes in list; bail out 2620 } 2621 result = itemClass; 2622 } 2623 } 2624 } 2625 } 2626 return result; 2627} 2628 2629// SecItemParams contains a validated set of input parameters, as well as a 2630// search reference and attribute list built from those parameters. It is 2631// designed to be allocated with _CreateSecItemParamsFromDictionary, and 2632// freed with _FreeSecItemParams. 2633 2634struct SecItemParams { 2635 CFDictionaryRef query; // caller-supplied query 2636 int numResultTypes; // number of result types requested 2637 int maxMatches; // max number of matches to return 2638 uint32 keyUsage; // key usage(s) requested 2639 Boolean returningAttributes; // true if returning attributes dictionary 2640 Boolean returningData; // true if returning item's data 2641 Boolean returningRef; // true if returning item reference 2642 Boolean returningPersistentRef; // true if returing a persistent reference 2643 Boolean returnAllMatches; // true if we should return all matches 2644 Boolean returnIdentity; // true if we are returning a SecIdentityRef 2645 Boolean trustedOnly; // true if we only return trusted certs 2646 Boolean issuerAndSNToMatch; // true if both issuer and SN were provided 2647 SecItemClass itemClass; // item class for this query 2648 SecPolicyRef policy; // value for kSecMatchPolicy (may be NULL) 2649 SecKeychainRef keychain; // value for kSecUseKeychain (may be NULL) 2650 CFArrayRef useItems; // value for kSecUseItemList (may be NULL) 2651 CFArrayRef itemList; // value for kSecMatchItemList (may be NULL) 2652 CFTypeRef searchList; // value for kSecMatchSearchList (may be NULL) 2653 CFTypeRef matchLimit; // value for kSecMatchLimit (may be NULL) 2654 CFTypeRef emailAddrToMatch; // value for kSecMatchEmailAddressIfPresent (may be NULL) 2655 CFTypeRef validOnDate; // value for kSecMatchValidOnDate (may be NULL) 2656 CFTypeRef keyClass; // value for kSecAttrKeyClass (may be NULL) 2657 CFTypeRef service; // value for kSecAttrService (may be NULL) 2658 CFTypeRef issuer; // value for kSecAttrIssuer (may be NULL) 2659 CFTypeRef serialNumber; // value for kSecAttrSerialNumber (may be NULL) 2660 CFTypeRef search; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef) 2661 CFTypeRef assumedKeyClass; // if no kSecAttrKeyClass provided, holds the current class we're searching for 2662 CFIndex itemListIndex; // if no search reference but we have itemList, holds index of next item to return 2663 SecKeychainAttributeList *attrList; // attribute list for this query 2664 SecAccessRef access; // access reference (for SecItemAdd only, not used to find items) 2665 CFDataRef itemData; // item data (for SecItemAdd only, not used to find items) 2666 CFTypeRef itemRef; // item reference (to find, add, update or delete, depending on context) 2667 SecIdentityRef identityRef; // identity reference (input as kSecValueRef) 2668 CFDataRef itemPersistentRef; // item persistent reference (to find, add, update or delete, depending on context) 2669}; 2670 2671static OSStatus 2672_ValidateDictionaryEntry(CFDictionaryRef dict, CFTypeRef key, const void **value, CFTypeID expectedTypeID, CFTypeID altTypeID) 2673{ 2674 if (!dict || !key || !value || !expectedTypeID) 2675 return errSecParam; 2676 2677 if (!CFDictionaryGetValueIfPresent(dict, key, value)) { 2678 // value was not provided for this key (not an error!) 2679 *value = NULL; 2680 } 2681 else if (!(*value)) { 2682 // provided value is NULL (also not an error!) 2683 return errSecSuccess; 2684 } 2685 else { 2686 CFTypeID actualTypeID = CFGetTypeID(*value); 2687 if (!((expectedTypeID == actualTypeID) || (altTypeID && altTypeID == actualTypeID))) { 2688 // provided value does not have the expected (or alternate) CF type ID 2689 if ((expectedTypeID == SecKeychainItemGetTypeID()) && 2690 (actualTypeID == SecKeyGetTypeID() || actualTypeID == SecCertificateGetTypeID())) { 2691 // provided value is a "floating" reference which is not yet in a keychain 2692 CFRetain(*value); 2693 return errSecSuccess; 2694 } 2695 return errSecItemInvalidValue; 2696 } 2697 else { 2698 // provided value is OK; retain it 2699 CFRetain(*value); 2700 } 2701 } 2702 return errSecSuccess; 2703} 2704 2705static void 2706_FreeSecItemParams(SecItemParams *itemParams) 2707{ 2708 if (!itemParams) 2709 return; 2710 2711 if (itemParams->query) CFRelease(itemParams->query); 2712 if (itemParams->policy) CFRelease(itemParams->policy); 2713 if (itemParams->keychain) CFRelease(itemParams->keychain); 2714 if (itemParams->useItems) CFRelease(itemParams->useItems); 2715 if (itemParams->itemList) CFRelease(itemParams->itemList); 2716 if (itemParams->searchList) CFRelease(itemParams->searchList); 2717 if (itemParams->matchLimit) CFRelease(itemParams->matchLimit); 2718 if (itemParams->emailAddrToMatch) CFRelease(itemParams->emailAddrToMatch); 2719 if (itemParams->validOnDate) CFRelease(itemParams->validOnDate); 2720 if (itemParams->keyClass) CFRelease(itemParams->keyClass); 2721 if (itemParams->service) CFRelease(itemParams->service); 2722 if (itemParams->issuer) CFRelease(itemParams->issuer); 2723 if (itemParams->serialNumber) CFRelease(itemParams->serialNumber); 2724 if (itemParams->search) CFRelease(itemParams->search); 2725 if (itemParams->access) CFRelease(itemParams->access); 2726 if (itemParams->itemData) CFRelease(itemParams->itemData); 2727 if (itemParams->itemRef) CFRelease(itemParams->itemRef); 2728 if (itemParams->identityRef) CFRelease(itemParams->identityRef); 2729 if (itemParams->itemPersistentRef) CFRelease(itemParams->itemPersistentRef); 2730 2731 _FreeAttrList(itemParams->attrList); 2732 2733 free(itemParams); 2734} 2735 2736static SecItemParams* 2737_CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) 2738{ 2739 OSStatus status; 2740 CFTypeRef value = NULL; 2741 SecItemParams *itemParams = (SecItemParams *) malloc(sizeof(SecItemParams)); 2742 2743 require_action(itemParams != NULL, error_exit, status = errSecAllocate); 2744 require_action(dict && (CFDictionaryGetTypeID() == CFGetTypeID(dict)), error_exit, status = errSecParam); 2745 2746 memset(itemParams, 0, sizeof(SecItemParams)); 2747 itemParams->query = (CFDictionaryRef) CFRetain(dict); 2748 2749 // validate input search parameters 2750 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchPolicy, (const void **)&itemParams->policy, SecPolicyGetTypeID(), NULL), error_exit); 2751 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchSearchList, (const void **)&itemParams->searchList, CFArrayGetTypeID(), NULL), error_exit); 2752 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchItemList, (const void **)&itemParams->itemList, CFArrayGetTypeID(), NULL), error_exit); 2753 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchEmailAddressIfPresent, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit); 2754 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchValidOnDate, (const void **)&itemParams->validOnDate, CFDateGetTypeID(), CFNullGetTypeID()), error_exit); 2755 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchLimit, (const void **)&itemParams->matchLimit, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit); 2756 2757 require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseItemList, (const void **)&itemParams->useItems, CFArrayGetTypeID(), NULL), error_exit); 2758 require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseKeychain, (const void **)&itemParams->keychain, SecKeychainGetTypeID(), NULL), error_exit); 2759 2760 // validate a subset of input attributes (used to create an appropriate search reference) 2761 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrIssuer, (const void **)&itemParams->issuer, CFDataGetTypeID(), NULL), error_exit); 2762 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrSerialNumber, (const void **)&itemParams->serialNumber, CFDataGetTypeID(), NULL), error_exit); 2763 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrService, (const void **)&itemParams->service, CFStringGetTypeID(), NULL), error_exit); 2764 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrKeyClass, (const void **)&itemParams->keyClass, CFStringGetTypeID(), NULL), error_exit); 2765 2766 // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items 2767 require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueData, (const void **)&itemParams->itemData, CFDataGetTypeID(), CFStringGetTypeID()), error_exit); 2768 2769 // validate item references 2770 require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueRef, (const void **)&itemParams->itemRef, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit); 2771 if (itemParams->itemRef && (CFGetTypeID(itemParams->itemRef) == SecIdentityGetTypeID())) { 2772 itemParams->identityRef = (SecIdentityRef)itemParams->itemRef; 2773 itemParams->itemRef = NULL; 2774 SecIdentityCopyCertificate(itemParams->identityRef, (SecCertificateRef *)&itemParams->itemRef); 2775 } 2776 require_noerr(status = _ValidateDictionaryEntry(dict, kSecValuePersistentRef, (const void **)&itemParams->itemPersistentRef, CFDataGetTypeID(), NULL), error_exit); 2777 if (itemParams->itemRef || itemParams->itemPersistentRef) { 2778 // Caller is trying to add or find an item by reference. 2779 // The supported method for doing that is to provide a kSecUseItemList array 2780 // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al, 2781 // so add the item reference to those arrays here. 2782 if (itemParams->useItems) { 2783 CFArrayRef tmpItems = itemParams->useItems; 2784 itemParams->useItems = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems); 2785 CFRelease(tmpItems); 2786 } else { 2787 itemParams->useItems = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 2788 } 2789 if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemRef); 2790 if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemPersistentRef); 2791 2792 if (itemParams->itemList) { 2793 CFArrayRef tmpItems = itemParams->itemList; 2794 itemParams->itemList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems); 2795 CFRelease(tmpItems); 2796 } else { 2797 itemParams->itemList = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 2798 } 2799 if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemRef); 2800 if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemPersistentRef); 2801 } 2802 2803 // must have an explicit item class, unless one of the following is true: 2804 // - we have an item list to add or search (kSecUseItemList) 2805 // - we have an item reference or persistent reference for the thing we want to look up 2806 // Note that both of these cases will set itemParams->useItems. 2807 // If we have an item list to match (kSecMatchItemList), that still requires an item class, 2808 // so we can perform a search and see if the results match items in the list. 2809 // 2810 if (!CFDictionaryGetValueIfPresent(dict, kSecClass, (const void**) &value) && !itemParams->useItems) { 2811 require_action(false, error_exit, status = errSecItemClassMissing); 2812 } 2813 else if (value) { 2814 itemParams->itemClass = _ConvertItemClass(value, itemParams->keyClass, &itemParams->returnIdentity); 2815 if (itemParams->itemClass == kSecSymmetricKeyItemClass && !itemParams->keyClass) { 2816 itemParams->assumedKeyClass = kSecAttrKeyClassSymmetric; // no key class specified, so start with symmetric key class; will search the others later 2817 } 2818 require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing); 2819 } 2820 2821 itemParams->keyUsage = _CssmKeyUsageFromQuery(dict); 2822 itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value); 2823 itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL); 2824 2825 // other input attributes, used for SecItemAdd but not for finding items 2826 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit); 2827 if (itemParams->access == NULL) { 2828 // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>) 2829 require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit); 2830 } 2831 2832 // determine how to return the result 2833 itemParams->numResultTypes = 0; 2834 itemParams->returningRef = CFDictionaryGetValueIfPresent(dict, kSecReturnRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value); 2835 if (itemParams->returningRef) ++itemParams->numResultTypes; 2836 itemParams->returningPersistentRef = CFDictionaryGetValueIfPresent(dict, kSecReturnPersistentRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value); 2837 if (itemParams->returningPersistentRef) ++itemParams->numResultTypes; 2838 itemParams->returningAttributes = CFDictionaryGetValueIfPresent(dict, kSecReturnAttributes, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value); 2839 if (itemParams->returningAttributes) ++itemParams->numResultTypes; 2840 itemParams->returningData = CFDictionaryGetValueIfPresent(dict, kSecReturnData, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value); 2841 if (itemParams->returningData) ++itemParams->numResultTypes; 2842 2843 // default is kSecReturnRef if no result types were specified 2844 if (!itemParams->numResultTypes) { 2845 itemParams->returningRef = TRUE; 2846 itemParams->numResultTypes = 1; 2847 } 2848 2849 // determine if one, some or all matches should be returned (default is kSecMatchLimitOne) 2850 itemParams->maxMatches = 1; 2851 itemParams->returnAllMatches = FALSE; 2852 if (itemParams->matchLimit) { 2853 if (CFStringGetTypeID() == CFGetTypeID(itemParams->matchLimit)) { 2854 itemParams->returnAllMatches = CFEqual(kSecMatchLimitAll, itemParams->matchLimit); 2855 } 2856 else if (CFNumberGetTypeID() == CFGetTypeID(itemParams->matchLimit)) { 2857 CFNumberGetValue((CFNumberRef)itemParams->matchLimit, kCFNumberIntType, &itemParams->maxMatches); 2858 require_action(!(itemParams->maxMatches < 0), error_exit, status = errSecMatchLimitUnsupported); 2859 } 2860 } 2861 if (itemParams->returnAllMatches) { 2862 itemParams->maxMatches = INT32_MAX; 2863 // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each) 2864 if ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData) 2865 status = errSecReturnDataUnsupported; 2866 require_noerr(status, error_exit); 2867 } 2868 2869 // if we already have an item list (to add or find items in), we don't need an item class, attribute list or a search reference 2870 if (itemParams->useItems) { 2871 if (itemParams->itemClass == 0) { 2872 itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems); 2873 } 2874 status = errSecSuccess; 2875 goto error_exit; // all done here 2876 } 2877 2878 // build a SecKeychainAttributeList from the query dictionary for the specified item class 2879 require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit); 2880 2881 // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef) 2882 if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) { 2883 // searching for certificates by email address 2884 char *nameBuf = (char*)malloc(MAXPATHLEN); 2885 if (!nameBuf) { 2886 status = errSecAllocate; 2887 } 2888 else if (CFStringGetCString((CFStringRef)itemParams->emailAddrToMatch, nameBuf, (CFIndex)MAXPATHLEN-1, kCFStringEncodingUTF8)) { 2889 status = SecKeychainSearchCreateForCertificateByEmail(itemParams->searchList, (const char *)nameBuf, (SecKeychainSearchRef*)&itemParams->search); 2890 } 2891 else { 2892 status = errSecItemInvalidValue; 2893 } 2894 if (nameBuf) free(nameBuf); 2895 } 2896 else if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->issuerAndSNToMatch) { 2897 // searching for certificates by issuer and serial number 2898 status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList, 2899 (CFDataRef)itemParams->issuer, 2900 (CFDataRef)itemParams->serialNumber, 2901 (SecKeychainSearchRef*)&itemParams->search); 2902 } 2903 else if (itemParams->returnIdentity && itemParams->policy) { 2904 // searching for identities by policy 2905 status = SecIdentitySearchCreateWithPolicy(itemParams->policy, 2906 (CFStringRef)itemParams->service, 2907 itemParams->keyUsage, 2908 itemParams->searchList, 2909 itemParams->trustedOnly, 2910 (SecIdentitySearchRef*)&itemParams->search); 2911 } 2912 else if (itemParams->returnIdentity) { 2913 // searching for identities 2914 status = SecIdentitySearchCreate(itemParams->searchList, 2915 itemParams->keyUsage, 2916 (SecIdentitySearchRef*)&itemParams->search); 2917 } 2918 else { 2919 // normal keychain item search 2920 status = SecKeychainSearchCreateFromAttributes(itemParams->searchList, 2921 itemParams->itemClass, 2922 (itemParams->attrList->count == 0) ? NULL : itemParams->attrList, 2923 (SecKeychainSearchRef*)&itemParams->search); 2924 } 2925 2926error_exit: 2927 if (status) { 2928 _FreeSecItemParams(itemParams); 2929 itemParams = NULL; 2930 } 2931 if (error) { 2932 *error = status; 2933 } 2934 return itemParams; 2935} 2936 2937 2938static OSStatus 2939_ImportKey( 2940 SecKeyRef keyRef, 2941 SecKeychainRef keychainRef, 2942 SecAccessRef accessRef, 2943 SecKeychainAttributeList *attrList, 2944 SecKeychainItemRef *outItemRef) 2945{ 2946 BEGIN_SECAPI 2947 2948 // We must specify the access, since a free-floating key won't have one yet by default 2949 SecPointer<Access> access; 2950 if (accessRef) { 2951 access = Access::required(accessRef); 2952 } 2953 else { 2954 CFStringRef descriptor = NULL; 2955 if (attrList) { 2956 for (UInt32 index=0; index < attrList->count; index++) { 2957 SecKeychainAttribute attr = attrList->attr[index]; 2958 if (attr.tag == kSecKeyPrintName) { 2959 descriptor = CFStringCreateWithBytes(NULL, (const UInt8 *)attr.data, attr.length, kCFStringEncodingUTF8, FALSE); 2960 break; 2961 } 2962 } 2963 } 2964 if (descriptor == NULL) { 2965 descriptor = (CFStringRef) CFRetain(CFSTR("<unknown>")); 2966 } 2967 access = new Access(cfString(descriptor)); 2968 CFRelease(descriptor); 2969 } 2970 2971 KeyItem *key = KeyItem::required(keyRef); 2972 Item item = key->importTo(Keychain::optional(keychainRef), access, attrList); 2973 if (outItemRef) 2974 *outItemRef = item->handle(); 2975 2976 END_SECAPI 2977} 2978 2979static Boolean 2980_CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO *evidence) 2981{ 2982 /* Check for ignorable status codes in leaf certificate's evidence */ 2983 Boolean result = true; 2984 unsigned int i; 2985 for (i=0; i < evidence->NumStatusCodes; i++) { 2986 CSSM_RETURN scode = evidence->StatusCodes[i]; 2987 if (scode == CSSMERR_APPLETP_INVALID_CA) { 2988 // the TP has rejected this CA cert because it's in the leaf position 2989 result = true; 2990 } 2991 else if (ignorableRevocationStatusCode(scode)) { 2992 result = true; 2993 } 2994 else { 2995 result = false; 2996 break; 2997 } 2998 } 2999 return result; 3000} 3001 3002static OSStatus 3003_FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert) 3004{ 3005 CFDictionaryRef props = NULL; 3006 CFArrayRef keychains = NULL; 3007 CFArrayRef anchors = NULL; 3008 CFArrayRef certs = NULL; 3009 CFArrayRef chain = NULL; 3010 SecTrustRef trust = NULL; 3011 3012 SecTrustResultType trustResult; 3013 CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL; 3014 Boolean needChain = false; 3015 OSStatus status; 3016 if (!policy || !cert) return errSecParam; 3017 3018 certs = CFArrayCreate(NULL, (const void **)&cert, (CFIndex)1, &kCFTypeArrayCallBacks); 3019 status = SecTrustCreateWithCertificates(certs, policy, &trust); 3020 if(status) goto cleanup; 3021 3022 /* Set evaluation date, if specified (otherwise current date is implied) */ 3023 if (date && (CFGetTypeID(date) == CFDateGetTypeID())) { 3024 status = SecTrustSetVerifyDate(trust, date); 3025 if(status) goto cleanup; 3026 } 3027 3028 /* Check whether this is the X509 Basic policy, which means chain building */ 3029 props = SecPolicyCopyProperties(policy); 3030 if (props) { 3031 CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid); 3032 if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) { 3033 needChain = true; 3034 } 3035 } 3036 3037 if (!needChain) { 3038 /* To make the evaluation as lightweight as possible, specify an empty array 3039 * of keychains which will be searched for certificates. 3040 */ 3041 keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); 3042 status = SecTrustSetKeychains(trust, keychains); 3043 if(status) goto cleanup; 3044 3045 /* To make the evaluation as lightweight as possible, specify an empty array 3046 * of trusted anchors. 3047 */ 3048 anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); 3049 status = SecTrustSetAnchorCertificates(trust, anchors); 3050 if(status) goto cleanup; 3051 } 3052 3053 /* All parameters are locked and loaded, ready to evaluate! */ 3054 status = SecTrustEvaluate(trust, &trustResult); 3055 if(status) goto cleanup; 3056 3057 /* If we didn't provide trust anchors or a way to look for them, 3058 * the evaluation will fail with kSecTrustResultRecoverableTrustFailure. 3059 * However, we can tell whether the policy evaluation succeeded by 3060 * looking at the per-cert status codes in the returned evidence. 3061 */ 3062 status = SecTrustGetResult(trust, &trustResult, &chain, &evidence); 3063 if(status) goto cleanup; 3064 3065 if (!(trustResult == kSecTrustResultProceed || 3066 trustResult == kSecTrustResultUnspecified || 3067 trustResult == kSecTrustResultRecoverableTrustFailure)) { 3068 /* The evaluation failed in a non-recoverable way */ 3069 status = errSecCertificateCannotOperate; 3070 goto cleanup; 3071 } 3072 3073 /* If there are no per-cert policy status codes, 3074 * and the cert has not expired, consider it valid for the policy. 3075 */ 3076 if((evidence != NULL) && _CanIgnoreLeafStatusCodes(evidence) && 3077 ((evidence[0].StatusBits & CSSM_CERT_STATUS_EXPIRED) == 0) && 3078 ((evidence[0].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) == 0)) { 3079 status = errSecSuccess; 3080 } 3081 else { 3082 status = errSecCertificateCannotOperate; 3083 } 3084 3085cleanup: 3086 if(props) CFRelease(props); 3087 if(chain) CFRelease(chain); 3088 if(anchors) CFRelease(anchors); 3089 if(keychains) CFRelease(keychains); 3090 if(certs) CFRelease(certs); 3091 if(trust) CFRelease(trust); 3092 3093 return status; 3094} 3095 3096static OSStatus 3097_FilterWithDate(CFTypeRef validOnDate, SecCertificateRef cert) 3098{ 3099 if (!validOnDate || !cert) return errSecParam; 3100 3101 CFAbsoluteTime at, nb, na; 3102 if (CFGetTypeID(validOnDate) == CFDateGetTypeID()) 3103 at = CFDateGetAbsoluteTime((CFDateRef)validOnDate); 3104 else 3105 at = CFAbsoluteTimeGetCurrent(); 3106 3107 OSStatus status = errSecSuccess; 3108 nb = SecCertificateNotValidBefore(cert); 3109 na = SecCertificateNotValidAfter(cert); 3110 3111 if (nb == 0 || na == 0 || nb == na) 3112 status = errSecCertificateCannotOperate; 3113 else if (at < nb) 3114 status = errSecCertificateNotValidYet; 3115 else if (at > na) 3116 status = errSecCertificateExpired; 3117 3118 return status; 3119} 3120 3121static OSStatus 3122_FilterWithTrust(Boolean trustedOnly, SecCertificateRef cert) 3123{ 3124 if (!cert) return errSecParam; 3125 if (!trustedOnly) return errSecSuccess; 3126 3127 CFArrayRef certArray = CFArrayCreate(NULL, (const void**)&cert, 1, &kCFTypeArrayCallBacks); 3128 SecPolicyRef policy = SecPolicyCreateWithOID(kSecPolicyAppleX509Basic); 3129 OSStatus status = (policy == NULL) ? errSecPolicyNotFound : errSecSuccess; 3130 3131 if (!status) { 3132 SecTrustRef trust = NULL; 3133 status = SecTrustCreateWithCertificates(certArray, policy, &trust); 3134 if (!status) { 3135 SecTrustResultType trustResult; 3136 status = SecTrustEvaluate(trust, &trustResult); 3137 if (!status) { 3138 if (!(trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified)) { 3139 status = (trustResult == kSecTrustResultDeny) ? errSecTrustSettingDeny : errSecNotTrusted; 3140 } 3141 } 3142 CFRelease(trust); 3143 } 3144 CFRelease(policy); 3145 } 3146 if (certArray) { 3147 CFRelease(certArray); 3148 } 3149 3150 return status; 3151} 3152 3153static SecKeychainItemRef 3154CopyResolvedKeychainItem(CFTypeRef item) 3155{ 3156 SecKeychainItemRef kcItem = NULL; 3157 OSStatus status; 3158 if (item) { 3159 if (CFGetTypeID(item) == CFDataGetTypeID()) { 3160 // persistent reference, resolve first 3161 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &kcItem); 3162 } 3163 else { 3164 // normal reference 3165 kcItem = (SecKeychainItemRef) CFRetain(item); 3166 } 3167 if (kcItem) { 3168 // ask for the item's class: 3169 // will return an error if the item has been deleted 3170 SecItemClass itemClass; 3171 SecKeychainItemRef certRef = NULL; 3172 if (CFGetTypeID(kcItem) == SecIdentityGetTypeID()) { 3173 status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, (SecCertificateRef *)&certRef); 3174 } 3175 status = SecKeychainItemCopyAttributesAndData((certRef) ? certRef : kcItem, NULL, &itemClass, NULL, NULL, NULL); 3176 if (certRef) { 3177 CFRelease(certRef); 3178 } 3179 if (status) { 3180 CFRelease(kcItem); 3181 kcItem = NULL; 3182 } 3183 } 3184 } 3185 return kcItem; 3186} 3187 3188static OSStatus 3189UpdateKeychainSearchAndCopyNext(SecItemParams *params, CFTypeRef *item) 3190{ 3191 // This function refreshes the search parameters in the specific case where 3192 // the caller is searching for kSecClassKey items but did not provide the 3193 // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and 3194 // we must perform separate searches to obtain all results. 3195 3196 OSStatus status = errSecItemNotFound; 3197 if (!params || !params->assumedKeyClass || !params->query || !item) 3198 return status; 3199 3200 // Free the previous search reference and attribute list. 3201 if (params->search) 3202 CFRelease(params->search); 3203 params->search = NULL; 3204 _FreeAttrList(params->attrList); 3205 params->attrList = NULL; 3206 3207 // Make a copy of the query dictionary so we can set the key class parameter. 3208 CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query); 3209 CFRelease(params->query); 3210 params->query = dict; 3211 CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass); 3212 3213 // Determine the current item class for this search, and the next assumed key class. 3214 if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassSymmetric)) { 3215 params->itemClass = kSecSymmetricKeyItemClass; 3216 params->assumedKeyClass = kSecAttrKeyClassPublic; 3217 } else if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassPublic)) { 3218 params->itemClass = kSecPublicKeyItemClass; 3219 params->assumedKeyClass = kSecAttrKeyClassPrivate; 3220 } else { 3221 params->itemClass = kSecPrivateKeyItemClass; 3222 params->assumedKeyClass = NULL; 3223 } 3224 3225 // Rebuild the attribute list for the new key class. 3226 if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, ¶ms->attrList) == errSecSuccess) { 3227 // Create a new search reference for the new attribute list. 3228 if (SecKeychainSearchCreateFromAttributes(params->searchList, 3229 params->itemClass, 3230 (params->attrList->count == 0) ? NULL : params->attrList, 3231 (SecKeychainSearchRef*)¶ms->search) == errSecSuccess) { 3232 // Return the first matching item from the new search. 3233 // We won't come back here again until there are no more matching items for this search. 3234 status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item); 3235 } 3236 } 3237 return status; 3238} 3239 3240 3241static OSStatus 3242SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item) 3243{ 3244 // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef. 3245 // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter, 3246 // depending on the type of search reference. 3247 3248 OSStatus status; 3249 CFTypeRef search = (params) ? params->search : NULL; 3250 CFTypeID typeID = (search) ? CFGetTypeID(search) : 0; 3251 if (typeID == SecIdentitySearchGetTypeID()) { 3252 status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item); 3253 } 3254 else if (typeID == SecKeychainSearchGetTypeID()) { 3255 status = SecKeychainSearchCopyNext((SecKeychainSearchRef)search, (SecKeychainItemRef*)item); 3256 // Check if we need to refresh the search for the next key class 3257 while (status == errSecItemNotFound && params->assumedKeyClass != NULL) 3258 status = UpdateKeychainSearchAndCopyNext(params, item); 3259 } 3260 else if (typeID == 0 && (params->useItems || params->itemList)) { 3261 // No search available, but there is an item list available. 3262 // Return the next candidate item from the caller's item list 3263 CFArrayRef itemList = (params->useItems) ? params->useItems : params->itemList; 3264 CFIndex count = CFArrayGetCount(itemList); 3265 *item = (CFTypeRef) NULL; 3266 if (params->itemListIndex < count) { 3267 *item = (CFTypeRef)CFArrayGetValueAtIndex(itemList, params->itemListIndex++); 3268 if (*item) { 3269 // Potentially resolve persistent item references here, and 3270 // verify the item reference we're about to hand back is still 3271 // valid (it could have been deleted from the keychain while 3272 // our query was holding onto the itemList). 3273 *item = CopyResolvedKeychainItem(*item); 3274 if (*item && (CFGetTypeID(*item) == SecIdentityGetTypeID())) { 3275 // Persistent reference resolved to an identity, so return that type. 3276 params->returnIdentity = true; 3277 } 3278 } 3279 } 3280 status = (*item) ? errSecSuccess : errSecItemNotFound; 3281 } 3282 else { 3283 status = errSecItemNotFound; 3284 } 3285 return status; 3286} 3287 3288static OSStatus 3289FilterCandidateItem(CFTypeRef *item, SecItemParams *itemParams, SecIdentityRef *identity) 3290{ 3291 if (!item || *item == NULL || !itemParams) 3292 return errSecItemNotFound; 3293 3294 OSStatus status; 3295 CFStringRef commonName = NULL; 3296 SecIdentityRef foundIdentity = NULL; 3297 if (CFGetTypeID(*item) == SecIdentityGetTypeID()) { 3298 // we found a SecIdentityRef, rather than a SecKeychainItemRef; 3299 // replace the found "item" with its associated certificate (which is the 3300 // item we actually want for purposes of getting attributes, data, or a 3301 // persistent data reference), and return the identity separately. 3302 SecCertificateRef certificate; 3303 status = SecIdentityCopyCertificate((SecIdentityRef) *item, &certificate); 3304 if (itemParams->returnIdentity) { 3305 foundIdentity = (SecIdentityRef) *item; 3306 if (identity) { 3307 *identity = foundIdentity; 3308 } 3309 } 3310 else { 3311 CFRelease(*item); 3312 } 3313 *item = (CFTypeRef)certificate; 3314 } 3315 3316 CFDictionaryRef query = itemParams->query; 3317 3318 if (itemParams->itemClass == kSecCertificateItemClass) { 3319 // perform string comparisons first 3320 CFStringCompareFlags flags = _StringCompareFlagsFromQuery(query); 3321 CFStringRef nameContains, nameStarts, nameEnds, nameExact; 3322 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectContains, (const void **)&nameContains)) 3323 nameContains = NULL; 3324 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&nameStarts)) 3325 nameStarts = NULL; 3326 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&nameEnds)) 3327 nameEnds = NULL; 3328 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectWholeString, (const void **)&nameExact)) 3329 nameExact = NULL; 3330 if (nameContains || nameStarts || nameEnds || nameExact) { 3331 status = SecCertificateCopyCommonName((SecCertificateRef)*item, &commonName); 3332 if (status || !commonName) goto filterOut; 3333 } 3334 if (nameContains) { 3335 CFRange range = CFStringFind(commonName, nameContains, flags); 3336 if (range.length < 1) 3337 goto filterOut; 3338 // certificate item contains string; proceed to next check 3339 } 3340 if (nameStarts) { 3341 CFRange range = CFStringFind(commonName, nameStarts, flags); 3342 if (range.length < 1 || range.location > 1) 3343 goto filterOut; 3344 // certificate item starts with string; proceed to next check 3345 } 3346 if (nameEnds) { 3347 CFRange range = CFStringFind(commonName, nameEnds, flags); 3348 if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds))) 3349 goto filterOut; 3350 // certificate item ends with string; proceed to next check 3351 } 3352 if (nameExact) { 3353 CFRange range = CFStringFind(commonName, nameExact, flags); 3354 if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact))) 3355 goto filterOut; 3356 // certificate item exactly matches string; proceed to next check 3357 } 3358 if (itemParams->returnIdentity) { 3359 // if we already found and returned the identity, we can skip this 3360 if (!foundIdentity) { 3361 status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity); 3362 if (status) goto filterOut; 3363 } 3364 // certificate item is part of an identity; proceed to next check 3365 } 3366 if (itemParams->policy) { 3367 status = _FilterWithPolicy(itemParams->policy, (CFDateRef)itemParams->validOnDate, (SecCertificateRef) *item); 3368 if (status) goto filterOut; 3369 // certificate item is valid for specified policy (and optionally specified date) 3370 } 3371 if (itemParams->validOnDate) { 3372 status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item); 3373 if (status) goto filterOut; 3374 // certificate item is valid for specified date 3375 } 3376 if (itemParams->trustedOnly) { 3377 // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search, 3378 // their trust has already been validated and we can skip this part. 3379 if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) { 3380 status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item); 3381 if (status) goto filterOut; 3382 } 3383 // certificate item is trusted on this system 3384 } 3385 } 3386 if (itemParams->itemList) { 3387 Boolean foundMatch = FALSE; 3388 CFIndex idx, count = CFArrayGetCount(itemParams->itemList); 3389 for (idx=0; idx<count; idx++) { 3390 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->itemList, idx); 3391 SecKeychainItemRef realItem = NULL; 3392 SecCertificateRef aCert = NULL; 3393 if (anItem == NULL) { 3394 continue; 3395 } 3396 if (CFDataGetTypeID() == CFGetTypeID(anItem) && 3397 errSecSuccess == SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem)) { 3398 anItem = realItem; 3399 } 3400 if (SecIdentityGetTypeID() == CFGetTypeID(anItem) && 3401 errSecSuccess == SecIdentityCopyCertificate((SecIdentityRef)anItem, &aCert)) { 3402 anItem = aCert; 3403 } 3404 if (CFEqual(anItem, (CFTypeRef) *item)) { 3405 foundMatch = TRUE; 3406 } 3407 if (aCert) { 3408 CFRelease(aCert); 3409 } 3410 if (realItem) { 3411 CFRelease(realItem); 3412 } 3413 if (foundMatch) { 3414 break; 3415 } 3416 } 3417 if (!foundMatch) goto filterOut; 3418 // item was found on provided list 3419 } 3420 3421 if (foundIdentity && !identity) { 3422 CFRelease(foundIdentity); 3423 } 3424 if (commonName) { 3425 CFRelease(commonName); 3426 } 3427 3428 // if we get here, consider the item a match 3429 return errSecSuccess; 3430 3431filterOut: 3432 if (commonName) { 3433 CFRelease(commonName); 3434 } 3435 CFRelease(*item); 3436 *item = NULL; 3437 if (foundIdentity) { 3438 CFRelease(foundIdentity); 3439 if (identity) { 3440 *identity = NULL; 3441 } 3442 } 3443 return errSecItemNotFound; 3444} 3445 3446static OSStatus 3447AddItemResults(SecKeychainItemRef item, 3448 SecIdentityRef identity, 3449 SecItemParams *itemParams, 3450 CFAllocatorRef allocator, 3451 CFMutableArrayRef *items, 3452 CFTypeRef *result) 3453{ 3454 // Given a found item (which may also be an identity), this function adds 3455 // the requested result types (specified in itemParams) to the appropriate 3456 // container as follows: 3457 // 3458 // 1. If there is only one result type (numResultTypes == 1) and only one 3459 // match requested (maxMatches == 1), set *result directly. 3460 // 3461 // 2. If there are multiple result types (numResultTypes > 1), and only one 3462 // match requested (maxMatches == 1), add each result type to itemDict 3463 // and set itemDict as the value of *result. 3464 // 3465 // 3. If there is only one result type (numResultTypes == 1) and multiple 3466 // possible matches (maxMatches > 1), add the result type to *items 3467 // and set *items as the value of *result. 3468 // 3469 // 4. If there are multiple result types (numResultTypes > 1) and multiple 3470 // possible matches (maxMatches > 1), add each result type to itemDict, 3471 // add itemDict to *items, and set *items as the value of *result. 3472 // 3473 // Note that we allocate *items if needed. 3474 3475 if (!item || !itemParams || !result) 3476 return errSecParam; 3477 3478 if (itemParams->maxMatches > 1) { 3479 // if we can return more than one item, we must have an array 3480 if (!items) 3481 return errSecParam; 3482 else if (*items == NULL) 3483 *items = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks); 3484 } 3485 3486 OSStatus tmpStatus, status = errSecSuccess; 3487 CFMutableArrayRef itemArray = (items) ? *items : NULL; 3488 CFMutableDictionaryRef itemDict = NULL; 3489 if (itemParams->numResultTypes > 1) { 3490 // if we're returning more than one result type, each item we return must be a dictionary 3491 itemDict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 3492 } 3493 3494 if (itemParams->returningRef) { 3495 const void* itemRef = (identity) ? (const void*)identity : (const void*)item; 3496 if (itemDict) { 3497 CFDictionaryAddValue(itemDict, kSecValueRef, itemRef); 3498 } 3499 else if (itemArray) { 3500 CFArrayAppendValue(itemArray, itemRef); 3501 } 3502 else { 3503 *result = CFRetain((CFTypeRef)itemRef); 3504 } 3505 } 3506 3507 if (itemParams->returningPersistentRef) { 3508 CFDataRef persistentRef; 3509 SecKeychainItemRef tmpItem = item; 3510 if (itemParams->identityRef) { 3511 tmpItem = (SecKeychainItemRef)itemParams->identityRef; 3512 } 3513 tmpStatus = SecKeychainItemCreatePersistentReference(tmpItem, &persistentRef); 3514 if (tmpStatus == errSecSuccess) { 3515 if (itemDict) { 3516 CFDictionaryAddValue(itemDict, kSecValuePersistentRef, persistentRef); 3517 } 3518 else if (itemArray) { 3519 CFArrayAppendValue(itemArray, persistentRef); 3520 } 3521 else { 3522 *result = CFRetain(persistentRef); 3523 } 3524 CFRelease(persistentRef); 3525 } 3526 else if (status == errSecSuccess) { 3527 status = tmpStatus; 3528 } 3529 } 3530 3531 if (itemParams->returningData) { 3532 UInt32 length; 3533 void *data; 3534 tmpStatus = SecKeychainItemCopyContent(item, NULL, NULL, &length, &data); 3535 if (tmpStatus == errSecSuccess) { 3536 CFDataRef dataRef = CFDataCreate(allocator, (UInt8 *)data, length); 3537 if (itemDict) { 3538 CFDictionaryAddValue(itemDict, kSecValueData, dataRef); 3539 } 3540 else if (itemArray) { 3541 CFArrayAppendValue(itemArray, dataRef); 3542 } 3543 else { 3544 *result = CFRetain(dataRef); 3545 } 3546 CFRelease(dataRef); 3547 (void) SecKeychainItemFreeContent(NULL, data); 3548 } 3549 else if (status == errSecSuccess) { 3550 status = tmpStatus; 3551 } 3552 } 3553 3554 if (itemParams->returningAttributes) { 3555 CFDictionaryRef attrsDict = NULL; 3556 SecItemClass itemClass; 3557 // since we have an item, allow its actual class to override the query-specified item class 3558 tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL); 3559 if (tmpStatus) { 3560 itemClass = itemParams->itemClass; 3561 } 3562 tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict); 3563 if (attrsDict) { 3564 if (itemDict) { 3565 // add all keys and values from attrsDict to the item dictionary 3566 CFDictionaryApplyFunction(attrsDict, _AddDictValueToOtherDict, &itemDict); 3567 } 3568 else if (itemArray) { 3569 CFArrayAppendValue(itemArray, attrsDict); 3570 } 3571 else { 3572 *result = CFRetain(attrsDict); 3573 } 3574 CFRelease(attrsDict); 3575 } 3576 if (tmpStatus && (status == errSecSuccess)) { 3577 status = tmpStatus; 3578 } 3579 } 3580 3581 if (itemDict) { 3582 if (itemArray) { 3583 CFArrayAppendValue(itemArray, itemDict); 3584 CFRelease(itemDict); 3585 *result = itemArray; 3586 } 3587 else { 3588 *result = itemDict; 3589 } 3590 } 3591 else if (itemArray) { 3592 *result = itemArray; 3593 } 3594 3595 return status; 3596} 3597 3598CFDataRef _SecItemGetPersistentReference(CFTypeRef raw_item) 3599{ 3600 try { 3601 Item item = ItemImpl::required((SecKeychainItemRef)raw_item); 3602 return item->getPersistentRef(); 3603 } catch(...) { 3604 return NULL; 3605 } 3606} 3607 3608/******************************************************************************/ 3609#pragma mark SecItem API functions 3610/******************************************************************************/ 3611 3612// 3613// Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error. 3614// 3615static SInt32 readNumber(CFTypeRef obj) { 3616 CFTypeID tid = CFGetTypeID(obj); 3617 SInt32 v = 0; 3618 if (tid == CFNumberGetTypeID()) { 3619 CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt32Type, &v); 3620 return v; 3621 } else if (tid == CFBooleanGetTypeID()) { 3622 v = CFBooleanGetValue((CFBooleanRef)obj); 3623 return v; 3624 } else if (tid == CFStringGetTypeID()) { 3625 v = CFStringGetIntValue((CFStringRef)obj); 3626 CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v); 3627 /* If a string converted to an int isn't equal to the int printed as 3628 a string, return a CFStringRef instead. */ 3629 if (!CFEqual(t, obj)) { 3630 CFRelease(t); 3631 return 0; 3632 } 3633 CFRelease(t); 3634 return v; 3635 } else 3636 return NULL; 3637} 3638 3639// 3640// Function to ensure the syncable keychain is unlocked. 3641// Currently, this means unlocking the login keychain, 3642// which will also unlock the keybag as a side effect. 3643// 3644static OSStatus SecItemUnlockSynchronizableKeychain() 3645{ 3646 SecKeychainRef keychain = NULL; 3647 OSStatus status = SecKeychainCopyLogin(&keychain); 3648 if (!status) { 3649 status = SecKeychainUnlock(keychain, 0, NULL, false); 3650 } 3651 CFReleaseSafe(keychain); 3652 return status; 3653} 3654 3655// 3656// Function to check whether the kSecAttrSynchronizable flag is set in the query. 3657// 3658static Boolean SecItemSynchronizable(CFDictionaryRef query) 3659{ 3660 CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable); 3661 Boolean result = (value && readNumber(value)); 3662 3663 return result; 3664} 3665 3666// 3667// Function to check whether the kSecAttrSynchronizable flag is set in the query, 3668// and has the special value of kSecAttrSynchronizableAny. 3669// 3670static Boolean SecItemSynchronizableAny(CFDictionaryRef query) 3671{ 3672 CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable); 3673 if (value) { 3674 return (CFGetTypeID(value) == CFStringGetTypeID() && 3675 CFEqual(value, kSecAttrSynchronizableAny)); 3676 } 3677 return false; 3678} 3679 3680// 3681// Function to check whether the kSecAttrSynchronizable attribute is being updated. 3682// 3683static Boolean SecItemHasSynchronizableUpdate(Boolean synchronizable, CFDictionaryRef changes) 3684{ 3685 CFTypeRef newValue = CFDictionaryGetValue(changes, kSecAttrSynchronizable); 3686 if (!newValue) 3687 return false; 3688 3689 Boolean new_sync = readNumber(newValue); 3690 Boolean old_sync = synchronizable; 3691 3692 return (old_sync != new_sync); 3693} 3694 3695// 3696// Returns true if keychain syncing is globally enabled. 3697// 3698static Boolean SecItemSyncEnabled() 3699{ 3700 static dispatch_once_t onceToken; 3701 static Boolean syncEnabled = true; 3702 3703 //sudo defaults write /Library/Preferences/com.apple.security SecItemSynchronizable -bool YES 3704 dispatch_once(&onceToken, ^{ 3705 CFTypeRef sync = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 3706 3707 if (sync && CFGetTypeID(sync) == CFBooleanGetTypeID()) { 3708 syncEnabled = CFBooleanGetValue((CFBooleanRef)sync); 3709 CFRelease(sync); 3710 } 3711 }); 3712 3713 return syncEnabled; 3714} 3715 3716// 3717// Function to check whether a synchronizable persistent reference was provided. 3718// 3719static Boolean SecItemHasSynchronizablePersistentReference(CFDictionaryRef query) 3720{ 3721 CFTypeRef value = CFDictionaryGetValue(query, kSecValuePersistentRef); 3722 if (value) { 3723 /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */ 3724 const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4; 3725 return (CFGetTypeID(value) == CFDataGetTypeID() && 3726 CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength); 3727 } 3728 return false; 3729} 3730 3731// 3732// Function to apply changes to a mutable dictionary. 3733// (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction) 3734// 3735static void SecItemApplyChanges(const void *key, const void *value, void *context) 3736{ 3737 CFMutableDictionaryRef dict = (CFMutableDictionaryRef) context; 3738 if (!dict) return; 3739 3740 CFDictionarySetValue(dict, key, value); 3741} 3742 3743// 3744// Function to change matching items from non-syncable to syncable 3745// (if toSyncable is true), otherwise from syncable to non-syncable. 3746// This currently moves items between keychain containers. 3747// 3748static OSStatus SecItemChangeSynchronizability(CFDictionaryRef query, CFDictionaryRef changes, Boolean toSyncable) 3749{ 3750 // Note: the input query dictionary is a mutable copy of the query originally 3751 // provided by the caller as the first parameter to SecItemUpdate. It may not 3752 // specify returning attributes or data, but we will need both to make a copy. 3753 // 3754 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnRef); 3755 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnPersistentRef); 3756 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnData); 3757 CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnAttributes, kCFBooleanTrue); 3758 if (NULL == CFDictionaryGetValue(changes, kSecValueData)) 3759 CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnData, kCFBooleanTrue); 3760 3761 CFTypeRef result; 3762 OSStatus status; 3763 if (toSyncable) 3764 status = SecItemCopyMatching_osx(query, &result); 3765 else 3766 status = SecItemCopyMatching_ios(query, &result); 3767 3768 if (status) 3769 return status; 3770 if (!result) 3771 return errSecItemNotFound; 3772 3773 CFMutableArrayRef items; 3774 if (CFGetTypeID(result) != CFArrayGetTypeID()) { 3775 items = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 3776 CFArrayAppendValue(items, result); 3777 CFRelease(result); 3778 } 3779 else { 3780 items = (CFMutableArrayRef)result; 3781 } 3782 3783 CFIndex idx, count = (items) ? CFArrayGetCount(items) : 0; 3784 int priority = LOG_DEBUG; 3785 OSStatus err = 0; 3786 for (idx = 0; idx < count; idx++) { 3787 CFDictionaryRef dict = (CFDictionaryRef) CFArrayGetValueAtIndex(items, idx); 3788 CFMutableDictionaryRef item = (CFMutableDictionaryRef) 3789 SecItemCopyTranslatedAttributes(dict, 3790 CFDictionaryGetValue(query, kSecClass), 3791 (toSyncable) ? true : false /*iOSOut*/, 3792 true /*pruneMatch*/, 3793 true /*pruneSync*/, 3794 true /*pruneReturn*/, 3795 false /*pruneData*/, 3796 (toSyncable) ? true : false /*pruneAccess*/); 3797 // hold onto the query before applying changes, in case the item already exists. 3798 // note that we cannot include the creation or modification dates from our 3799 // found item in this query, as they may not match the item in the other keychain. 3800 CFMutableDictionaryRef itemQuery = CFDictionaryCreateMutableCopy(NULL, 0, item); 3801 CFDictionaryRemoveValue(itemQuery, kSecAttrCreationDate); 3802 CFDictionaryRemoveValue(itemQuery, kSecAttrModificationDate); 3803 // apply changes to the item dictionary that we will pass to SecItemAdd 3804 CFDictionaryApplyFunction(changes, SecItemApplyChanges, item); 3805 if (toSyncable) { 3806 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanTrue); 3807 status = SecItemAdd_ios(item, NULL); 3808 secitemlog(priority, "ChangeSync: SecItemAdd_ios=%d", status); 3809 if (errSecDuplicateItem == status) { 3810 // find and apply changes to the existing syncable item. 3811 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue); 3812 status = SecItemUpdate_ios(itemQuery, changes); 3813 secitemlog(priority, "ChangeSync: SecItemUpdate_ios=%d", status); 3814 } 3815 if (errSecSuccess == status) { 3816 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse); 3817 status = SecItemDelete_osx(itemQuery); 3818 secitemlog(priority, "ChangeSync: SecItemDelete_osx=%d", status); 3819 } 3820 } 3821 else { 3822 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse); 3823 status = SecItemAdd_osx(item, NULL); 3824 secitemlog(priority, "ChangeSync: SecItemAdd_osx=%d", status); 3825 if (errSecDuplicateItem == status) { 3826 // find and apply changes to the existing non-syncable item. 3827 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse); 3828 status = SecItemUpdate_osx(itemQuery, changes); 3829 secitemlog(priority, "ChangeSync: SecItemUpdate_osx=%d", status); 3830 } 3831 if (errSecSuccess == status) { 3832 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue); 3833 status = SecItemDelete_ios(itemQuery); 3834 secitemlog(priority, "ChangeSync: SecItemDelete_ios=%d", status); 3835 } 3836 } 3837 CFReleaseSafe(item); 3838 CFReleaseSafe(itemQuery); 3839 if (status) 3840 err = status; 3841 } 3842 CFReleaseSafe(items); 3843 3844 return err; 3845} 3846 3847 3848extern "C" { 3849 3850CFTypeRef 3851SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) { 3852 CFTypeRef ref = NULL; 3853 CFStringRef key_class_string = (CFStringRef)CFDictionaryGetValue(refAttributes, kSecClass); 3854 SecItemClass key_class; 3855 bool key_class_found = false; 3856 3857 if (CFEqual(key_class_string, kSecClassGenericPassword)) { 3858 key_class = kSecGenericPasswordItemClass; 3859 key_class_found = true; 3860 } 3861 if (CFEqual(key_class_string, kSecClassInternetPassword)) { 3862 key_class = kSecInternetPasswordItemClass; 3863 key_class_found = true; 3864 } 3865 3866 if (key_class_found) { 3867 // we carry v_Data around here so the *_ios calls can find it and locate 3868 // their own data. Putting things in the attribute list doesn't help as 3869 // the osx keychainitem and item calls bail when they don't see a keychain 3870 // object. If we need to make them work we either have to bridge them, or 3871 // find a way to craft a workable keychain object. #if'ed code left below 3872 // in case we need to go down that path. 3873 3874 struct SecKeychainAttributeList *attrs = (struct SecKeychainAttributeList *)malloc(sizeof(struct SecKeychainAttributeList) + sizeof(struct SecKeychainAttribute) * 0); 3875 attrs->attr = (struct SecKeychainAttribute *)(attrs + 1); 3876 attrs->count = 0; 3877 CFTypeRef v; 3878#if 0 3879 // The C++ string objects need to last at least as long as the attr struct. 3880 string account; 3881 3882 v = CFDictionaryGetValue(refAttributes, CFSTR("mdat")); 3883 if (v) { 3884 attrs->attr[attrs->count].tag = kSecModDateItemAttr; 3885 // XXX need to convert to YYYYMMDDhhmmSSZ 3886 attrs->attr[attrs->count].data = (void*)"19690223140232Z"; 3887 attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data)); 3888 attrs->count++; 3889 } 3890 v = CFDictionaryGetValue(refAttributes, CFSTR("cdat")); 3891 if (v) { 3892 attrs->attr[attrs->count].tag = kSecCreationDateItemAttr; 3893 // XXX need to convert to YYYYMMDDhhmmSSZ 3894 attrs->attr[attrs->count].data = (void*)"19690223140232Z"; 3895 attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data)); 3896 attrs->count++; 3897 } 3898 3899 v = CFDictionaryGetValue(refAttributes, CFSTR("acct")); 3900 if (v) { 3901 attrs->attr[attrs->count].tag = kSecAccountItemAttr; 3902 account = cfString((CFStringRef)v); 3903 attrs->attr[attrs->count].data = (void*)(account.c_str()); 3904 attrs->attr[attrs->count].length = account.length(); 3905 attrs->count++; 3906 } 3907 3908 // class isn't treated as an attribute by the creation API 3909 3910 v = CFDictionaryGetValue(refAttributes, CFSTR("svce")); 3911 if (v) { 3912 attrs->attr[attrs->count].tag = kSecServiceItemAttr; 3913 account = cfString((CFStringRef)v); 3914 attrs->attr[attrs->count].data = (void*)(account.c_str()); 3915 attrs->attr[attrs->count].length = account.length(); 3916 attrs->count++; 3917 } 3918 3919 v = CFDictionaryGetValue(refAttributes, CFSTR("acct")); 3920 if (v) { 3921 attrs->attr[attrs->count].tag = kSecLabelItemAttr; 3922 account = cfString((CFStringRef)v); 3923 attrs->attr[attrs->count].data = (void*)(account.c_str()); 3924 attrs->attr[attrs->count].length = account.length(); 3925 attrs->count++; 3926 } 3927#endif 3928 Item item = Item(key_class, attrs, 0, ""); 3929 ItemImpl *real_item = item.get(); 3930 v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef); 3931 if (v) { 3932 real_item->setPersistentRef((CFDataRef)v); 3933 } 3934 ref = real_item->handle(); 3935 } else { 3936 // keys, certs, identities are not currently sync'able. 3937 ref = NULL; 3938 } 3939 return ref; 3940} 3941 3942/* 3943 * SecItemValidateAppleApplicationGroupAccess determines if the caller 3944 * is a member of the specified application group, and is signed by Apple. 3945 */ 3946OSStatus 3947SecItemValidateAppleApplicationGroupAccess(CFStringRef group) 3948{ 3949 SecTrustedApplicationRef app = NULL; 3950 SecRequirementRef requirement = NULL; 3951 SecCodeRef code = NULL; 3952 OSStatus status = errSecParam; 3953 3954 if (group) { 3955 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(group), kCFStringEncodingUTF8) + 1; 3956 char* buffer = (char*) malloc(length); 3957 if (buffer) { 3958 if (CFStringGetCString(group, buffer, length, kCFStringEncodingUTF8)) { 3959 status = SecTrustedApplicationCreateApplicationGroup(buffer, NULL, &app); 3960 } 3961 free(buffer); 3962 } else { 3963 status = errSecMemoryError; 3964 } 3965 } 3966 if (!status) { 3967 status = SecTrustedApplicationCopyRequirement(app, &requirement); 3968 } 3969 if (!status) { 3970 status = SecCodeCopySelf(kSecCSDefaultFlags, &code); 3971 } 3972 if (!status) { 3973 status = SecCodeCheckValidity(code, kSecCSDefaultFlags, requirement); 3974 } 3975 3976 CFReleaseSafe(code); 3977 CFReleaseSafe(requirement); 3978 CFReleaseSafe(app); 3979 return status; 3980} 3981 3982/* 3983 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary 3984 * and attempts to return a sanitized copy for passing to the underlying 3985 * platform-specific implementation code. 3986 * 3987 * If iOSOut is true, one or more translations may apply: 3988 * - SecKeychain refs are removed, since there aren't multiple keychains 3989 * - SecPolicy refs are removed, since they can't be externalized 3990 * - SecAccess refs are removed, and potentially translated to entitlements 3991 * 3992 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids 3993 * parameter errors due to strict input checks in secd, which only permits 3994 * these constants for calls to SecItemCopyMatching. 3995 * 3996 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed. 3997 * This permits a query to be reused for non-synchronizable items, or to 3998 * resolve a search based on a persistent item reference for iOS. 3999 * 4000 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids 4001 * parameter errors due to strict input checks in secd, which do not permit 4002 * these constants for calls to SecItemUpdate. 4003 */ 4004CFDictionaryRef 4005SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, 4006 bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess) 4007{ 4008 CFMutableDictionaryRef result = CFDictionaryCreateMutableCopy(NULL, 0, inOSXDict); 4009 if (result == NULL) { 4010 return result; 4011 } 4012 4013 if (pruneSync) { 4014 CFDictionaryRemoveValue(result, kSecAttrSynchronizable); 4015 } 4016 4017 if (pruneMatch) { 4018 /* Match constants are only supported on iOS for SecItemCopyMatching, 4019 * and will generate an error if passed to other SecItem API functions; 4020 * on OS X, they're just ignored if not applicable for the context. 4021 */ 4022 CFDictionaryRemoveValue(result, kSecMatchPolicy); 4023 CFDictionaryRemoveValue(result, kSecMatchItemList); 4024 CFDictionaryRemoveValue(result, kSecMatchSearchList); 4025 CFDictionaryRemoveValue(result, kSecMatchIssuers); 4026 CFDictionaryRemoveValue(result, kSecMatchEmailAddressIfPresent); 4027 CFDictionaryRemoveValue(result, kSecMatchSubjectContains); 4028 CFDictionaryRemoveValue(result, kSecMatchCaseInsensitive); 4029 CFDictionaryRemoveValue(result, kSecMatchTrustedOnly); 4030 CFDictionaryRemoveValue(result, kSecMatchValidOnDate); 4031 CFDictionaryRemoveValue(result, kSecMatchLimit); 4032 CFDictionaryRemoveValue(result, kSecMatchLimitOne); 4033 CFDictionaryRemoveValue(result, kSecMatchLimitAll); 4034 } 4035 4036 if (pruneReturn) { 4037 /* Return constants are not supported on iOS for SecItemUpdate, 4038 * where they will generate an error; on OS X, they're just ignored 4039 * if not applicable for the context. 4040 */ 4041 CFDictionaryRemoveValue(result, kSecReturnData); 4042 CFDictionaryRemoveValue(result, kSecReturnAttributes); 4043 CFDictionaryRemoveValue(result, kSecReturnRef); 4044 CFDictionaryRemoveValue(result, kSecReturnPersistentRef); 4045 } 4046 4047 if (pruneData) { 4048 /* Searching on data is not supported. */ 4049 CFDictionaryRemoveValue(result, kSecValueData); 4050 } 4051 4052 if (pruneAccess) { 4053 /* Searching on access lists is not supported */ 4054 CFDictionaryRemoveValue(result, kSecAttrAccess); 4055 } 4056 4057 if (iOSOut) { 4058 /* Remove kSecMatchSearchList (value is array of SecKeychainRef); 4059 * cannot specify a keychain search list on iOS 4060 */ 4061 CFDictionaryRemoveValue(result, kSecMatchSearchList); 4062 4063 /* Remove kSecUseKeychain (value is a SecKeychainRef); 4064 * cannot specify a keychain on iOS 4065 */ 4066 CFDictionaryRemoveValue(result, kSecUseKeychain); 4067 4068 /* Remove kSecMatchPolicy (value is a SecPolicyRef); 4069 * TODO: need a way to externalize and restore a policy instance 4070 */ 4071 CFDictionaryRemoveValue(result, kSecMatchPolicy); 4072 4073 /* Potentially translate kSecAttrAccess (value is a SecAccessRef), 4074 * unless kSecAttrAccessGroup has already been specified. 4075 */ 4076 SecAccessRef access = (SecAccessRef) CFDictionaryGetValue(result, kSecAttrAccess); 4077 CFStringRef accessGroup = (CFStringRef) CFDictionaryGetValue(result, kSecAttrAccessGroup); 4078 if (access != NULL && accessGroup == NULL) { 4079 /* Translate "InternetAccounts" application group to an access group */ 4080 if (errSecSuccess == SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) { 4081 /* The caller is a valid member of the application group. */ 4082 CFStringRef groupName = CFSTR("appleaccount"); 4083 CFTypeRef value = CFDictionaryGetValue(result, kSecAttrAuthenticationType); 4084 if (value && CFEqual(value, kSecAttrAuthenticationTypeHTMLForm)) { 4085 groupName = CFSTR("com.apple.cfnetwork"); 4086 } 4087 CFDictionarySetValue(result, kSecAttrAccessGroup, groupName); 4088 } 4089 } 4090 CFDictionaryRemoveValue(result, kSecAttrAccess); 4091 4092 /* If item is specified by direct reference, and this is an iOS search, 4093 * replace it with a persistent reference. 4094 */ 4095 CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef); 4096 if (directRef) { 4097 CFDataRef persistentRef = _SecItemGetPersistentReference(directRef); 4098 if (persistentRef) { 4099 CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef); 4100 } 4101 CFDictionaryRemoveValue(result, kSecValueRef); 4102 } 4103 4104 /* If item is specified by persistent reference, and this is an iOS search, 4105 * remove the synchronizable attribute as it will be rejected by secd. 4106 */ 4107 CFTypeRef persistentRef = CFDictionaryGetValue(result, kSecValuePersistentRef); 4108 if (persistentRef) { 4109 CFDictionaryRemoveValue(result, kSecAttrSynchronizable); 4110 } 4111 4112 /* Remove kSecAttrModificationDate; this should never be used as criteria 4113 * for a search, or to add/modify an item. (If we are cloning an item 4114 * and want to keep its modification date, we don't call this function.) 4115 * It turns out that some clients are using the full attributes dictionary 4116 * returned by SecItemCopyMatching as a query to find the same item later, 4117 * which won't work once the item is updated. 4118 */ 4119 CFDictionaryRemoveValue(result, kSecAttrModificationDate); 4120 } 4121 else { 4122 /* iOS doesn't add the class attribute, so we must do it here. */ 4123 if (itemClass) 4124 CFDictionarySetValue(result, kSecClass, itemClass); 4125 4126 /* Remove attributes which are not part of the OS X database schema. */ 4127 CFDictionaryRemoveValue(result, kSecAttrAccessible); 4128 CFDictionaryRemoveValue(result, kSecAttrAccessGroup); 4129 CFDictionaryRemoveValue(result, kSecAttrSynchronizable); 4130 CFDictionaryRemoveValue(result, kSecAttrTombstone); 4131 } 4132 4133 return result; 4134} 4135 4136/* 4137 * SecItemCopyMergedResults takes two input objects, which may be containers, 4138 * and returns a retained object which merges the results. Merging depends on the 4139 * result type. If each result is valid and is not an array, then only one match was 4140 * requested; in that case, the syncable (ios) match is preferred. 4141 * 4142 * FIXME: There are some edge cases still to deal with; e.g. if the OSX search specified a 4143 * particular keychain to search, we do not want to merge in any IOS results. Also, may need 4144 * to filter out duplicates if two items differ only in the sync attribute. 4145 */ 4146CFTypeRef 4147SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios) 4148{ 4149 CFTypeID id_osx = (result_osx) ? CFGetTypeID(result_osx) : 0; 4150 CFTypeID id_ios = (result_ios) ? CFGetTypeID(result_ios) : 0; 4151 CFTypeID id_array = CFArrayGetTypeID(); 4152 if ((id_osx == id_array) && (id_ios == id_array)) { 4153 // Fold the arrays into one. 4154 CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 4155 CFArrayAppendArray(results, (CFArrayRef)result_ios, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_ios))); 4156 CFArrayAppendArray(results, (CFArrayRef)result_osx, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_osx))); 4157 return results; 4158 } 4159 // Result type is not an array, so only one match can be returned. 4160 return (id_ios) ? CFRetain(result_ios) : CFRetain(result_osx); 4161} 4162 4163} /* extern "C" */ 4164 4165 4166OSStatus 4167SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) 4168{ 4169 secitemlog(LOG_NOTICE, "SecItemCopyMatching"); 4170 if (!query) { 4171 return errSecParam; 4172 } 4173 secitemshow(query, "SecItemCopyMatching query:"); 4174 4175 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; 4176 CFTypeRef result_osx = NULL, result_ios = NULL; 4177 Boolean sync_enabled = SecItemSyncEnabled(); 4178 Boolean search_ios = SecItemSynchronizable(query); 4179 Boolean merge_search = SecItemSynchronizableAny(query); 4180 Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query); 4181 4182 if (sync_enabled && (merge_search || persistref_ios || search_ios)) { 4183 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, 4184 CFDictionaryGetValue(query, kSecClass), true, false, false, false, true, true); 4185 if (!attrs_ios) { 4186 status_ios = errSecParam; 4187 } 4188 else { 4189 SecItemUnlockSynchronizableKeychain(); 4190 status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios); 4191 CFRelease(attrs_ios); 4192 } 4193 secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios); 4194 if (!merge_search || persistref_ios) { 4195 AssignOrReleaseResult(result_ios, result); 4196 return status_ios; // no need to search non-syncable keychains 4197 } 4198 } 4199 4200 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, 4201 CFDictionaryGetValue(query, kSecClass), false, false, true, false, true, true); 4202 if (!attrs_osx) { 4203 status_osx = errSecParam; 4204 } 4205 else { 4206 status_osx = SecItemCopyMatching_osx(attrs_osx, &result_osx); 4207 CFRelease(attrs_osx); 4208 } 4209 secitemlog(LOG_NOTICE, "SecItemCopyMatching_osx result: %d", status_osx); 4210 4211 // If one of the searches failed to occur or produce results, we can eliminate it 4212 if (result_ios == NULL) { 4213 AssignOrReleaseResult(result_osx, result); 4214 return status_osx; // we can only have non-syncable results 4215 } 4216 if (result_osx == NULL) { 4217 AssignOrReleaseResult(result_ios, result); 4218 return status_ios; // we can only have syncable results 4219 } 4220 4221 // If we get here, need to merge results 4222 CFTypeRef result_merged = SecItemCopyMergedResults(query, result_osx, result_ios); 4223 CFReleaseSafe(result_osx); 4224 CFReleaseSafe(result_ios); 4225 AssignOrReleaseResult(result_merged, result); 4226 4227 if (status_osx == status_ios) { 4228 return status_osx; // both searches produced the same result 4229 } 4230 else if (!status_osx || !status_ios) { 4231 return errSecSuccess; // one of the searches succeeded 4232 } 4233 else if (status_osx == errSecItemNotFound) { 4234 return status_ios; // this failure was more interesting 4235 } 4236 return status_osx; 4237} 4238 4239OSStatus 4240SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) 4241{ 4242 secitemlog(LOG_NOTICE, "SecItemAdd"); 4243 if (!attributes) { 4244 return errSecParam; 4245 } 4246 else if (result) { 4247 *result = NULL; 4248 } 4249 secitemshow(attributes, "SecItemAdd attrs:"); 4250 4251 OSStatus status_osx, status_ios; 4252 CFTypeRef result_osx = NULL, result_ios = NULL; 4253 Boolean sync_enabled = SecItemSyncEnabled(); 4254 Boolean add_ios = SecItemSynchronizable(attributes); 4255 4256 if (sync_enabled && add_ios) { 4257 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(attributes, 4258 NULL, true, true, false, false, false, false); 4259 if (!attrs_ios) { 4260 status_ios = errSecParam; 4261 } 4262 else { 4263 SecItemUnlockSynchronizableKeychain(); 4264 status_ios = SecItemAdd_ios(attrs_ios, &result_ios); 4265 CFRelease(attrs_ios); 4266 } 4267 secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status_ios); 4268 if (result) 4269 *result = result_ios; 4270 else 4271 CFReleaseSafe(result_ios); 4272 return status_ios; 4273 } 4274 4275 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(attributes, 4276 NULL, false, false, true, false, false, false); 4277 if (!attrs_osx) { 4278 status_osx = errSecParam; 4279 } 4280 else { 4281 status_osx = SecItemAdd_osx(attrs_osx, &result_osx); 4282 CFRelease(attrs_osx); 4283 } 4284 secitemlog(LOG_NOTICE, "SecItemAdd_osx result: %d", status_osx); 4285 if (result) 4286 *result = result_osx; 4287 else 4288 CFReleaseSafe(result_osx); 4289 return status_osx; 4290} 4291 4292OSStatus 4293SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) 4294{ 4295 secitemlog(LOG_NOTICE, "SecItemUpdate"); 4296 if (!query || !attributesToUpdate) { 4297 return errSecParam; 4298 } 4299 secitemshow(query, "SecItemUpdate query:"); 4300 secitemshow(attributesToUpdate, "SecItemUpdate attrs:"); 4301 4302 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; 4303 Boolean sync_enabled = SecItemSyncEnabled(); 4304 Boolean search_ios = SecItemSynchronizable(query); 4305 Boolean merge_search = SecItemSynchronizableAny(query); 4306 Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query); 4307 4308 if (sync_enabled && (merge_search || persistref_ios || search_ios)) { 4309 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, 4310 CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true); 4311 if (!attrs_ios) { 4312 status_ios = errSecParam; 4313 } 4314 else { 4315 SecItemUnlockSynchronizableKeychain(); 4316 if (SecItemHasSynchronizableUpdate(true, attributesToUpdate)) 4317 status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false); 4318 else 4319 status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate); 4320 CFRelease(attrs_ios); 4321 } 4322 secitemlog(LOG_NOTICE, "SecItemUpdate_ios result: %d", status_ios); 4323 if (!merge_search || persistref_ios) 4324 return status_ios; 4325 } 4326 4327 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, 4328 CFDictionaryGetValue(query, kSecClass), false, false, true, true, true, true); 4329 if (!attrs_osx) { 4330 status_osx = errSecParam; 4331 } 4332 else { 4333 if (SecItemHasSynchronizableUpdate(false, attributesToUpdate)) 4334 status_osx = SecItemChangeSynchronizability(attrs_osx, attributesToUpdate, true); 4335 else 4336 status_osx = SecItemUpdate_osx(attrs_osx, attributesToUpdate); 4337 4338 CFRelease(attrs_osx); 4339 } 4340 secitemlog(LOG_NOTICE, "SecItemUpdate_osx result: %d", status_osx); 4341 if (merge_search) { 4342 // Harmonize the result of the update attempts. 4343 if (status_osx == status_ios) { 4344 // both updates produced the same result 4345 return status_ios; 4346 } 4347 else if (!status_osx || !status_ios) { 4348 // one of the updates succeeded, but the other failed 4349 if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound) 4350 return errSecSuccess; // item only found in one keychain 4351 else 4352 return (status_osx) ? status_osx : status_ios; // return the error 4353 } 4354 else if (status_osx == errSecItemNotFound) { 4355 // both updates failed, status_ios failure is more interesting 4356 // since the item was actually found 4357 return status_ios; 4358 } 4359 } 4360 return status_osx; 4361} 4362 4363OSStatus 4364SecItemDelete(CFDictionaryRef query) 4365{ 4366 secitemlog(LOG_NOTICE, "SecItemDelete"); 4367 if (!query) { 4368 return errSecParam; 4369 } 4370 secitemshow(query, "SecItemDelete query:"); 4371 4372 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; 4373 Boolean sync_enabled = SecItemSyncEnabled(); 4374 Boolean search_ios = SecItemSynchronizable(query); 4375 Boolean merge_search = SecItemSynchronizableAny(query); 4376 Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query); 4377 4378 if (sync_enabled && (merge_search || persistref_ios || search_ios)) { 4379 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, 4380 NULL, true, true, false, true, true, true); 4381 if (!attrs_ios) { 4382 status_ios = errSecParam; 4383 } 4384 else { 4385 SecItemUnlockSynchronizableKeychain(); 4386 status_ios = SecItemDelete_ios(attrs_ios); 4387 CFRelease(attrs_ios); 4388 } 4389 secitemlog(LOG_NOTICE, "SecItemDelete_ios result: %d", status_ios); 4390 if (!merge_search || persistref_ios) 4391 return status_ios; 4392 } 4393 4394 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, 4395 NULL, false, false, true, true, true, true); 4396 if (!attrs_osx) { 4397 status_osx = errSecParam; 4398 } 4399 else { 4400 status_osx = SecItemDelete_osx(attrs_osx); 4401 CFRelease(attrs_osx); 4402 } 4403 secitemlog(LOG_NOTICE, "SecItemDelete_osx result: %d", status_osx); 4404 4405 if (merge_search) { 4406 // Harmonize the result of the delete attempts. 4407 if (status_osx == status_ios) { 4408 // both deletes produced the same result 4409 return status_ios; 4410 } 4411 else if (!status_osx || !status_ios) { 4412 // one of the deletes succeeded, but the other failed 4413 if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound) 4414 return errSecSuccess; // item only found in one keychain 4415 else 4416 return (status_osx) ? status_osx : status_ios; // return the error 4417 } 4418 else if (status_osx == errSecItemNotFound) { 4419 // both deletes failed, status_ios failure is more interesting 4420 // since the item was actually found 4421 return status_ios; 4422 } 4423 } 4424 return status_osx; 4425} 4426 4427OSStatus 4428SecItemCopyMatching_osx( 4429 CFDictionaryRef query, 4430 CFTypeRef *result) 4431{ 4432 if (!query || !result) 4433 return errSecParam; 4434 else 4435 *result = NULL; 4436 4437 CFAllocatorRef allocator = CFGetAllocator(query); 4438 CFIndex matchCount = 0; 4439 CFMutableArrayRef itemArray = NULL; 4440 SecKeychainItemRef item = NULL; 4441 SecIdentityRef identity = NULL; 4442 OSStatus tmpStatus, status = errSecSuccess; 4443 4444 // validate input query parameters and create the search reference 4445 SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status); 4446 require_action(itemParams != NULL, error_exit, itemParams = NULL); 4447 4448 // find the next match until we hit maxMatches, or no more matches found 4449 while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) && 4450 SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == errSecSuccess) { 4451 4452 if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity)) 4453 continue; // move on to next item 4454 4455 ++matchCount; // we have a match 4456 4457 tmpStatus = AddItemResults(item, identity, itemParams, allocator, &itemArray, result); 4458 if (tmpStatus && (status == errSecSuccess)) 4459 status = tmpStatus; 4460 4461 if (item) { 4462 CFRelease(item); 4463 item = NULL; 4464 } 4465 if (identity) { 4466 CFRelease(identity); 4467 identity = NULL; 4468 } 4469 } 4470 4471 if (status == errSecSuccess) 4472 status = (matchCount > 0) ? errSecSuccess : errSecItemNotFound; 4473 4474error_exit: 4475 if (status != errSecSuccess && result != NULL && *result != NULL) { 4476 CFRelease(*result); 4477 *result = NULL; 4478 } 4479 _FreeSecItemParams(itemParams); 4480 4481 return status; 4482} 4483 4484OSStatus 4485SecItemCopyDisplayNames( 4486 CFArrayRef items, 4487 CFArrayRef *displayNames) 4488{ 4489 BEGIN_SECAPI 4490 Required(items); 4491 Required(displayNames); 4492 //%%%TBI 4493 return errSecUnimplemented; 4494 END_SECAPI 4495} 4496 4497OSStatus 4498SecItemAdd_osx( 4499 CFDictionaryRef attributes, 4500 CFTypeRef *result) 4501{ 4502 if (!attributes) 4503 return errSecParam; 4504 else if (result) 4505 *result = NULL; 4506 4507 CFAllocatorRef allocator = CFGetAllocator(attributes); 4508 CFMutableArrayRef itemArray = NULL; 4509 SecKeychainItemRef item = NULL; 4510 OSStatus tmpStatus, status = errSecSuccess; 4511 4512 // validate input attribute parameters 4513 SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status); 4514 require_action(itemParams != NULL, error_exit, itemParams = NULL); 4515 4516 // currently, we don't support adding SecIdentityRef items (an aggregate item class), 4517 // since the private key should already be in a keychain by definition. We could support 4518 // this as a copy operation for the private key if a different keychain is specified, 4519 // but in any case it should try to add the certificate. See <rdar://8317887>. 4520 require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue); 4521 4522 if (!itemParams->useItems) { 4523 // create a single keychain item specified by the input attributes 4524 status = SecKeychainItemCreateFromContent(itemParams->itemClass, 4525 itemParams->attrList, 4526 (itemParams->itemData) ? (UInt32)CFDataGetLength(itemParams->itemData) : 0, 4527 (itemParams->itemData) ? CFDataGetBytePtrVoid(itemParams->itemData) : NULL, 4528 itemParams->keychain, 4529 itemParams->access, 4530 &item); 4531 require_noerr(status, error_exit); 4532 4533 // return results (if requested) 4534 if (result) { 4535 itemParams->maxMatches = 1; // in case kSecMatchLimit was set to > 1 4536 tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result); 4537 if (tmpStatus && (status == errSecSuccess)) 4538 status = tmpStatus; 4539 } 4540 CFRelease(item); 4541 } 4542 else { 4543 // add multiple items which are specified in the itemParams->useItems array. 4544 // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain. 4545 // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain. 4546 // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain. 4547 // 4548 OSStatus aggregateStatus = errSecSuccess; 4549 CFIndex ix, count = CFArrayGetCount(itemParams->useItems); 4550 itemParams->maxMatches = (count > 1) ? (int)count : 2; // force results to always be returned as an array 4551 for (ix=0; ix < count; ix++) { 4552 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix); 4553 if (anItem) { 4554 if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) { 4555 // SecCertificateRef item 4556 tmpStatus = SecCertificateAddToKeychain((SecCertificateRef)anItem, itemParams->keychain); 4557 if (!tmpStatus && result) { 4558 tmpStatus = AddItemResults((SecKeychainItemRef)anItem, NULL, itemParams, allocator, &itemArray, result); 4559 } 4560 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem); 4561 } 4562 else if (SecKeyGetTypeID() == CFGetTypeID(anItem)) { 4563 // SecKeyRef item 4564 SecKeychainRef itemKeychain = NULL; 4565 tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain); 4566 if (tmpStatus == errSecSuccess) { 4567 // key was in a keychain, so we can attempt to copy it 4568 SecKeychainItemRef itemCopy = NULL; 4569 tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy); 4570 if (!tmpStatus && result) { 4571 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result); 4572 } 4573 if (itemCopy) { 4574 CFRelease(itemCopy); 4575 } 4576 } 4577 else { 4578 // key was not in any keychain, so must be imported 4579 SecKeychainItemRef keyItem = NULL; 4580 tmpStatus = _ImportKey((SecKeyRef)anItem, itemParams->keychain, itemParams->access, itemParams->attrList, &keyItem); 4581 if (!tmpStatus && result) { 4582 tmpStatus = AddItemResults(keyItem, NULL, itemParams, allocator, &itemArray, result); 4583 } 4584 if (keyItem) { 4585 CFRelease(keyItem); 4586 } 4587 } 4588 if (itemKeychain) { 4589 CFRelease(itemKeychain); 4590 } 4591 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem); 4592 } 4593 else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem)) { 4594 // SecKeychainItemRef item 4595 SecKeychainItemRef itemCopy = NULL; 4596 tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy); 4597 if (!tmpStatus && result) { 4598 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result); 4599 } 4600 if (itemCopy) { 4601 CFRelease(itemCopy); 4602 } 4603 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem); 4604 } 4605 else if (CFDataGetTypeID() == CFGetTypeID(anItem)) { 4606 // CFDataRef item (persistent reference) 4607 SecKeychainItemRef realItem = NULL; 4608 tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem); 4609 if (tmpStatus == errSecSuccess) { 4610 // persistent reference resolved to a keychain item, so we can attempt to copy it 4611 SecKeychainItemRef itemCopy = NULL; 4612 tmpStatus = SecKeychainItemCreateCopy(realItem, itemParams->keychain, itemParams->access, &itemCopy); 4613 if (!tmpStatus && result) { 4614 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result); 4615 } 4616 if (itemCopy) { 4617 CFRelease(itemCopy); 4618 } 4619 } 4620 if (realItem) { 4621 CFRelease(realItem); 4622 } 4623 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem); 4624 } 4625 } 4626 } // end of itemList array loop 4627 status = aggregateStatus; 4628 } // end processing multiple items 4629 4630error_exit: 4631 if (status != errSecSuccess && result != NULL && *result != NULL) { 4632 CFRelease(*result); 4633 *result = NULL; 4634 } 4635 _FreeSecItemParams(itemParams); 4636 4637 return status; 4638} 4639 4640OSStatus 4641SecItemUpdate_osx( 4642 CFDictionaryRef query, 4643 CFDictionaryRef attributesToUpdate) 4644{ 4645 if (!query || !attributesToUpdate) 4646 return errSecParam; 4647 4648 // run the provided query to get a list of items to update 4649 CFTypeRef results = NULL; 4650 OSStatus status = SecItemCopyMatching(query, &results); 4651 if (status != errSecSuccess) 4652 return status; // nothing was matched, or the query was bad 4653 4654 CFArrayRef items = NULL; 4655 if (CFArrayGetTypeID() == CFGetTypeID(results)) { 4656 items = (CFArrayRef) results; 4657 } 4658 else { 4659 items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks); 4660 CFRelease(results); 4661 } 4662 4663 OSStatus result = errSecSuccess; 4664 CFIndex ix, count = CFArrayGetCount(items); 4665 for (ix=0; ix < count; ix++) { 4666 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix); 4667 if (anItem) { 4668 status = _UpdateKeychainItem(anItem, attributesToUpdate); 4669 result = _UpdateAggregateStatus(status, result, errSecSuccess); 4670 } 4671 } 4672 4673 if (items) { 4674 CFRelease(items); 4675 } 4676 return result; 4677} 4678 4679OSStatus 4680SecItemDelete_osx( 4681 CFDictionaryRef query) 4682{ 4683 if (!query) 4684 return errSecParam; 4685 4686 // run the provided query to get a list of items to delete 4687 CFTypeRef results = NULL; 4688 OSStatus status = SecItemCopyMatching_osx(query, &results); 4689 if (status != errSecSuccess) 4690 return status; // nothing was matched, or the query was bad 4691 4692 CFArrayRef items = NULL; 4693 if (CFArrayGetTypeID() == CFGetTypeID(results)) { 4694 items = (CFArrayRef) results; 4695 } 4696 else { 4697 items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks); 4698 CFRelease(results); 4699 } 4700 4701 OSStatus result = errSecSuccess; 4702 CFIndex ix, count = CFArrayGetCount(items); 4703 for (ix=0; ix < count; ix++) { 4704 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix); 4705 if (anItem) { 4706 if (SecIdentityGetTypeID() == CFGetTypeID(anItem)) { 4707 status = _DeleteIdentity((SecIdentityRef)anItem); 4708 } 4709 else { 4710 status = _DeleteKeychainItem(anItem); 4711 } 4712 result = _UpdateAggregateStatus(status, result, errSecSuccess); 4713 } 4714 } 4715 4716 if (items) 4717 CFRelease(items); 4718 4719 return result; 4720} 4721