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