2  * Copyright (c) 2002-2015 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  24 #include <Security/SecIdentity.h> 
  25 #include <Security/SecIdentityPriv.h> 
  26 #include <Security/SecKeychainItemPriv.h> 
  27 #include <Security/SecItem.h> 
  28 #include <Security/SecIdentityPriv.h> 
  29 #include <Security/SecCertificatePriv.h> 
  31 #include "SecBridge.h" 
  32 #include <security_keychain/Certificate.h> 
  33 #include <security_keychain/Identity.h> 
  34 #include <security_keychain/KeyItem.h> 
  35 #include <security_keychain/KCCursor.h> 
  36 #include <security_cdsa_utilities/Schema.h> 
  37 #include <security_utilities/simpleprefs.h> 
  38 #include <sys/param.h> 
  41 /* private function declarations */ 
  43 SecIdentityFindPreferenceItemWithNameAndKeyUsage( 
  44         CFTypeRef keychainOrArray
, 
  47         SecKeychainItemRef 
*itemRef
); 
  49 OSStatus 
SecIdentityDeletePreferenceItemWithNameAndKeyUsage( 
  50         CFTypeRef keychainOrArray
, 
  55 CSSM_KEYUSE 
ConvertArrayToKeyUsage(CFArrayRef usage
) 
  58         CSSM_KEYUSE result 
= (CSSM_KEYUSE
) 0; 
  60         if ((NULL 
== usage
) || (0 == (count 
= CFArrayGetCount(usage
)))) 
  65         for (CFIndex iCnt 
= 0; iCnt 
< count
; iCnt
++) 
  67                 CFStringRef keyUsageStr 
= NULL
; 
  68                 keyUsageStr 
= (CFStringRef
)CFArrayGetValueAtIndex(usage
,iCnt
); 
  69                 if (NULL 
!= keyUsageStr
) 
  71                         if (kCFCompareEqualTo 
== CFStringCompare((CFStringRef
)kSecAttrCanEncrypt
, keyUsageStr
, 0)) 
  73                                 result 
|= CSSM_KEYUSE_ENCRYPT
; 
  75                         else if (kCFCompareEqualTo 
== CFStringCompare((CFStringRef
)kSecAttrCanDecrypt
, keyUsageStr
, 0)) 
  77                                 result 
|= CSSM_KEYUSE_DECRYPT
; 
  79                         else if (kCFCompareEqualTo 
== CFStringCompare((CFStringRef
)kSecAttrCanDerive
, keyUsageStr
, 0)) 
  81                                 result 
|= CSSM_KEYUSE_DERIVE
; 
  83                         else if (kCFCompareEqualTo 
== CFStringCompare((CFStringRef
)kSecAttrCanSign
, keyUsageStr
, 0)) 
  85                                 result 
|= CSSM_KEYUSE_SIGN
; 
  87                         else if (kCFCompareEqualTo 
== CFStringCompare((CFStringRef
)kSecAttrCanVerify
, keyUsageStr
, 0)) 
  89                                 result 
|= CSSM_KEYUSE_VERIFY
; 
  91                         else if (kCFCompareEqualTo 
== CFStringCompare((CFStringRef
)kSecAttrCanWrap
, keyUsageStr
, 0)) 
  93                                 result 
|= CSSM_KEYUSE_WRAP
; 
  95                         else if (kCFCompareEqualTo 
== CFStringCompare((CFStringRef
)kSecAttrCanUnwrap
, keyUsageStr
, 0)) 
  97                                 result 
|= CSSM_KEYUSE_UNWRAP
; 
 107 SecIdentityGetTypeID(void) 
 111         return gTypes().Identity
.typeID
; 
 113         END_SECAPI1(_kCFRuntimeNotATypeID
) 
 118 SecIdentityCopyCertificate( 
 119             SecIdentityRef identityRef
, 
 120             SecCertificateRef 
*certificateRef
) 
 124         SecPointer
<Certificate
> certificatePtr(Identity::required(identityRef
)->certificate()); 
 125         Required(certificateRef
) = certificatePtr
->handle(); 
 128         /* convert outgoing item to a unified SecCertificateRef */ 
 129         CssmData certData 
= certificatePtr
->data(); 
 130         CFDataRef data 
= NULL
; 
 131         if (certData
.Data 
&& certData
.Length
) { 
 132                 data 
= CFDataCreate(NULL
, certData
.Data
, certData
.Length
); 
 135                 *certificateRef 
= NULL
; 
 136                 syslog(LOG_ERR
, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)", 
 137                         (long)certData
.Length
, (uintptr_t)certData
.Data
); 
 138                 return errSecInternal
; 
 140         SecCertificateRef tmpRef 
= *certificateRef
; 
 141         *certificateRef 
= SecCertificateCreateWithKeychainItem(NULL
, data
, tmpRef
); 
 153 SecIdentityCopyPrivateKey( 
 154             SecIdentityRef identityRef
, 
 155             SecKeyRef 
*privateKeyRef
) 
 159         SecPointer
<KeyItem
> keyItemPtr(Identity::required(identityRef
)->privateKey()); 
 160         Required(privateKeyRef
) = keyItemPtr
->handle(); 
 166 SecIdentityCreateWithCertificate( 
 167         CFTypeRef keychainOrArray
, 
 168         SecCertificateRef certificate
, 
 169         SecIdentityRef 
*identityRef
) 
 171         // This macro converts a new-style SecCertificateRef to an old-style ItemImpl 
 174         SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
)); 
 175         StorageManager::KeychainList keychains
; 
 176         globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
); 
 177         SecPointer
<Identity
> identityPtr(new Identity(keychains
, certificatePtr
)); 
 178         Required(identityRef
) = identityPtr
->handle(); 
 185         CFAllocatorRef allocator
, 
 186         SecCertificateRef certificate
, 
 187         SecKeyRef privateKey
) 
 189         SecIdentityRef identityRef 
= NULL
; 
 190         OSStatus __secapiresult
; 
 191         SecCertificateRef __itemImplRef
=SecCertificateCreateItemImplInstance(certificate
); 
 193                 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
)); 
 194                 SecPointer
<KeyItem
> keyItemPtr(KeyItem::required(privateKey
)); 
 195                 SecPointer
<Identity
> identityPtr(new Identity(keyItemPtr
, certificatePtr
)); 
 196                 identityRef 
= identityPtr
->handle(); 
 198                 __secapiresult
=errSecSuccess
; 
 200         catch (const MacOSError 
&err
) { __secapiresult
=err
.osStatus(); } 
 201         catch (const CommonError 
&err
) { __secapiresult
=SecKeychainErrFromOSStatus(err
.osStatus()); } 
 202         catch (const std::bad_alloc 
&) { __secapiresult
=errSecAllocate
; } 
 203         catch (...) { __secapiresult
=errSecInternalComponent
; } 
 209         SecIdentityRef identity1
, 
 210         SecIdentityRef identity2
, 
 211         CFOptionFlags compareOptions
) 
 213         if (!identity1 
|| !identity2
) 
 215                 if (identity1 
== identity2
) 
 216                         return kCFCompareEqualTo
; 
 217                 else if (identity1 
< identity2
) 
 218                         return kCFCompareLessThan
; 
 220                         return kCFCompareGreaterThan
; 
 225         SecPointer
<Identity
> id1(Identity::required(identity1
)); 
 226         SecPointer
<Identity
> id2(Identity::required(identity2
)); 
 229                 return kCFCompareEqualTo
; 
 231                 return kCFCompareLessThan
; 
 233                 return kCFCompareGreaterThan
; 
 235         END_SECAPI1(kCFCompareGreaterThan
); 
 239 CFArrayRef 
_SecIdentityCopyPossiblePaths( 
 242     // utility function to build and return an array of possible paths for the given name. 
 243     // if name is not a URL, this returns a single-element array. 
 244     // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy. 
 246     CFMutableArrayRef names 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 250     CFIndex oldLength 
= CFStringGetLength(name
); 
 251     CFArrayAppendValue(names
, name
); 
 253     CFURLRef url 
= CFURLCreateWithString(NULL
, name
, NULL
); 
 255                 if (CFURLCanBeDecomposed(url
)) { 
 256                         // first, remove the query portion of this URL, if any 
 257                         CFStringRef qs 
= CFURLCopyQueryString(url
, NULL
); 
 259                                 CFMutableStringRef newName 
= CFStringCreateMutableCopy(NULL
, oldLength
, name
); 
 261                                         CFIndex qsLength 
= CFStringGetLength(qs
) + 1; // include the '?' 
 262                                         CFStringDelete(newName
, CFRangeMake(oldLength
-qsLength
, qsLength
)); 
 264                                         url 
= CFURLCreateWithString(NULL
, newName
, NULL
); 
 265                                         CFArraySetValueAtIndex(names
, 0, newName
); 
 270                         // now add an entry for each level of the path 
 272                                 CFURLRef parent 
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
); 
 274                                         CFStringRef parentURLString 
= CFURLGetString(parent
); 
 275                                         if (parentURLString
) { 
 276                                                 CFIndex newLength 
= CFStringGetLength(parentURLString
); 
 277                                                 // check that string length has decreased as expected; for file URLs, 
 278                                                 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../' 
 279                                                 if ((newLength 
>= oldLength
) || (!CFStringHasPrefix(name
, parentURLString
))) { 
 284                                                 oldLength 
= newLength
; 
 285                                                 CFArrayAppendValue(names
, parentURLString
); 
 296         // finally, add wildcard entries for each subdomain 
 297         url 
= CFURLCreateWithString(NULL
, name
, NULL
); 
 299                 if (CFURLCanBeDecomposed(url
)) { 
 300                         CFStringRef netLocString 
= CFURLCopyNetLocation(url
); 
 302                                 // first strip off port number, if present 
 303                                 CFStringRef tmpLocString 
= netLocString
; 
 304                                 CFArrayRef hostnameArray 
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(":")); 
 305                                 tmpLocString 
= (CFStringRef
)CFRetain((CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, 0)); 
 306                                 CFRelease(netLocString
); 
 307                                 CFRelease(hostnameArray
); 
 308                                 netLocString 
= tmpLocString
; 
 309                                 // split remaining string into domain components 
 310                                 hostnameArray 
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(".")); 
 311                                 CFIndex subdomainCount 
= CFArrayGetCount(hostnameArray
); 
 313                                 while (++i 
< subdomainCount
) { 
 315                                         CFMutableStringRef wildcardString 
= CFStringCreateMutable(NULL
, 0); 
 316                                         if (wildcardString
) { 
 317                                                 CFStringAppendCString(wildcardString
, "*", kCFStringEncodingUTF8
); 
 318                                                 while (j 
< subdomainCount
) { 
 319                                                         CFStringRef domainString 
= (CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, j
++); 
 320                                                         if (CFStringGetLength(domainString
) > 0) { 
 321                                                                 CFStringAppendCString(wildcardString
, ".", kCFStringEncodingUTF8
); 
 322                                                                 CFStringAppend(wildcardString
, domainString
); 
 325                                                 if (CFStringGetLength(wildcardString
) > 1) { 
 326                                                         CFArrayAppendValue(names
, wildcardString
); 
 328                                                 CFRelease(wildcardString
); 
 331                                 CFRelease(hostnameArray
); 
 332                                 CFRelease(netLocString
); 
 342 OSStatus 
_SecIdentityCopyPreferenceMatchingName( 
 344     CSSM_KEYUSE keyUsage
, 
 345     CFArrayRef validIssuers
, 
 346     SecIdentityRef 
*identity
) 
 348     // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here; 
 349     // caller must handle exceptions 
 351         StorageManager::KeychainList keychains
; 
 352         globals().storageManager
.getSearchList(keychains
); 
 353         KCCursor 
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
); 
 355         char idUTF8
[MAXPATHLEN
]; 
 357     if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
)) 
 358         idUTF8
[0] = (char)'\0'; 
 359     CssmData 
service(const_cast<char *>(idUTF8
), strlen(idUTF8
)); 
 360         FourCharCode itemType 
= 'iprf'; 
 361     cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
); 
 362         cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
); 
 364         cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
); 
 367         if (!cursor
->next(prefItem
)) 
 368                 return errSecItemNotFound
; 
 370         // get persistent certificate reference 
 371         SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL 
} }; 
 372         SecKeychainAttributeList itemAttrList 
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs 
}; 
 373         prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
); 
 375         // find certificate, given persistent reference data 
 376         CFDataRef pItemRef 
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8 
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
); 
 377         SecKeychainItemRef certItemRef 
= nil
; 
 378         OSStatus status 
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl 
 379         prefItem
->freeContent(&itemAttrList
, NULL
); 
 385     // filter on valid issuers, if provided 
 390         // create identity reference, given certificate 
 392     status 
= SecIdentityCreateWithCertificate(NULL
, (SecCertificateRef
)certItemRef
, identity
); 
 395         Item certItem 
= ItemImpl::required(SecKeychainItemRef(certItemRef
)); 
 396         SecPointer
<Certificate
> certificate(static_cast<Certificate 
*>(certItem
.get())); 
 397         SecPointer
<Identity
> identity_ptr(new Identity(keychains
, certificate
)); 
 399             CFRelease(certItemRef
); // retained by identity 
 401         Required(identity
) = identity_ptr
->handle(); 
 403     catch (const MacOSError 
&err
)   { status
=err
.osStatus(); } 
 404     catch (const CommonError 
&err
)  { status
=SecKeychainErrFromOSStatus(err
.osStatus()); } 
 405     catch (const std::bad_alloc 
&)  { status
=errSecAllocate
; } 
 406     catch (...)                     { status
=errSecInvalidItemRef
; } 
 412 SecIdentityRef 
SecIdentityCopyPreferred(CFStringRef name
, CFArrayRef keyUsage
, CFArrayRef validIssuers
) 
 414         // This function will look for a matching preference in the following order: 
 415         // - matches the name and the supplied key use 
 416         // - matches the name and the special 'ANY' key use 
 417         // - matches the name with no key usage constraint 
 419         SecIdentityRef identityRef 
= NULL
; 
 420         CSSM_KEYUSE keyUse 
= ConvertArrayToKeyUsage(keyUsage
); 
 421         OSStatus status 
= SecIdentityCopyPreference(name
, keyUse
, validIssuers
, &identityRef
); 
 422         if (status 
!= errSecSuccess 
&& keyUse 
!= CSSM_KEYUSE_ANY
) 
 423                 status 
= SecIdentityCopyPreference(name
, CSSM_KEYUSE_ANY
, validIssuers
, &identityRef
); 
 424         if (status 
!= errSecSuccess 
&& keyUse 
!= 0) 
 425                 status 
= SecIdentityCopyPreference(name
, 0, validIssuers
, &identityRef
); 
 430 OSStatus 
SecIdentityCopyPreference( 
 432     CSSM_KEYUSE keyUsage
, 
 433     CFArrayRef validIssuers
, 
 434     SecIdentityRef 
*identity
) 
 436     // The original implementation of SecIdentityCopyPreference matches the exact string only. 
 437     // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above), 
 438     // and this function is a wrapper which calls it, so that existing clients will get the 
 439     // extended behavior of server domain matching for items that specify URLs. 
 440     // (Note that behavior is unchanged if the specified name is not a URL.) 
 444     CFTypeRef val 
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"), 
 445                     CFSTR("com.apple.security"), 
 446                     kCFPreferencesCurrentUser
, 
 447                     kCFPreferencesAnyHost
); 
 448     Boolean logging 
= false; 
 449     if (val 
&& CFGetTypeID(val
) == CFBooleanGetTypeID()) { 
 450         logging 
= CFBooleanGetValue((CFBooleanRef
)val
); 
 454     OSStatus status 
= errSecItemNotFound
; 
 455     CFArrayRef names 
= _SecIdentityCopyPossiblePaths(name
); 
 460     CFIndex idx
, total 
= CFArrayGetCount(names
); 
 461     for (idx 
= 0; idx 
< total
; idx
++) { 
 462         CFStringRef aName 
= (CFStringRef
)CFArrayGetValueAtIndex(names
, idx
); 
 464             status 
= _SecIdentityCopyPreferenceMatchingName(aName
, keyUsage
, validIssuers
, identity
); 
 466         catch (...) { status 
= errSecItemNotFound
; } 
 469             // get identity label 
 470             CFStringRef labelString 
= NULL
; 
 471             if (!status 
&& identity 
&& *identity
) { 
 473                     SecPointer
<Certificate
> cert(Identity::required(*identity
)->certificate()); 
 474                     cert
->inferLabel(false, &labelString
); 
 476                 catch (...) { labelString 
= NULL
; }; 
 478             char *labelBuf 
= NULL
; 
 479             CFIndex labelBufSize 
= (labelString
) ? CFStringGetLength(labelString
) * 4 : 4; 
 480             labelBuf 
= (char *)malloc(labelBufSize
); 
 481             if (!labelString 
|| !CFStringGetCString(labelString
, labelBuf
, labelBufSize
, kCFStringEncodingUTF8
)) { 
 485                 CFRelease(labelString
); 
 489             char *serviceBuf 
= NULL
; 
 490             CFIndex serviceBufSize 
= CFStringGetLength(aName
) * 4; 
 491             serviceBuf 
= (char *)malloc(serviceBufSize
); 
 492             if (!CFStringGetCString(aName
, serviceBuf
, serviceBufSize
, kCFStringEncodingUTF8
)) { 
 496             syslog(LOG_NOTICE
, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf
, serviceBuf
); 
 497             if (!status 
&& name
) { 
 498                 char *nameBuf 
= NULL
; 
 499                 CFIndex nameBufSize 
= CFStringGetLength(name
) * 4; 
 500                 nameBuf 
= (char *)malloc(nameBufSize
); 
 501                 if (!CFStringGetCString(name
, nameBuf
, nameBufSize
, kCFStringEncodingUTF8
)) { 
 504                 syslog(LOG_NOTICE
, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf
, nameBuf
); 
 512         if (status 
== errSecSuccess
) { 
 513             break; // match found 
 523 OSStatus 
SecIdentitySetPreference( 
 524     SecIdentityRef identity
, 
 526     CSSM_KEYUSE keyUsage
) 
 532                 // treat NULL identity as a request to clear the preference 
 533                 // (note: if keyUsage is 0, this clears all key usage prefs for name) 
 534                 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL
, name
, keyUsage
); 
 539         SecPointer
<Certificate
> certificate(Identity::required(identity
)->certificate()); 
 541         // determine the account attribute 
 543         // This attribute must be synthesized from certificate label + pref item type + key usage, 
 544         // as only the account and service attributes can make a generic keychain item unique. 
 545         // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that 
 546         // we can save a certificate preference if an identity preference already exists for the 
 547         // given service name, and vice-versa. 
 548         // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. 
 550     CFStringRef labelStr 
= nil
; 
 551         certificate
->inferLabel(false, &labelStr
); 
 553         MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed" 
 555         CFIndex accountUTF8Len 
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1; 
 556         const char *templateStr 
= "%s [key usage 0x%X]"; 
 557         const int keyUsageMaxStrLen 
= 8; 
 558         accountUTF8Len 
+= strlen(templateStr
) + keyUsageMaxStrLen
; 
 559         char accountUTF8
[accountUTF8Len
]; 
 560     if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
)) 
 561                 accountUTF8
[0] = (char)'\0'; 
 563                 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
); 
 564         snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
); 
 565     CssmData 
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
)); 
 568         // service attribute (name provided by the caller) 
 569         CFIndex serviceUTF8Len 
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(name
), kCFStringEncodingUTF8
) + 1;; 
 570         char serviceUTF8
[serviceUTF8Len
]; 
 571     if (!CFStringGetCString(name
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
)) 
 572         serviceUTF8
[0] = (char)'\0'; 
 573     CssmData 
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
)); 
 575     // look for existing identity preference item, in case this is an update 
 576         StorageManager::KeychainList keychains
; 
 577         globals().storageManager
.getSearchList(keychains
); 
 578         KCCursor 
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
); 
 579     FourCharCode itemType 
= 'iprf'; 
 580     cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
); 
 581         cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
); 
 583         cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
); 
 586         Item 
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false); 
 587     bool add 
= (!cursor
->next(item
)); 
 588         // at this point, we either have a new item to add or an existing item to update 
 590     // set item attribute values 
 591     item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
); 
 592     item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), itemType
); 
 593     item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
); 
 594         item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
); 
 595     item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
); 
 597         // generic attribute (store persistent certificate reference) 
 598         CFDataRef pItemRef 
= nil
; 
 599     certificate
->copyPersistentReference(pItemRef
); 
 601                 MacOSError::throwMe(errSecInvalidItemRef
); 
 603         const UInt8 
*dataPtr 
= CFDataGetBytePtr(pItemRef
); 
 604         CFIndex dataLen 
= CFDataGetLength(pItemRef
); 
 605         CssmData 
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
); 
 606         item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
); 
 610         Keychain keychain 
= nil
; 
 612             keychain 
= globals().storageManager
.defaultKeychain(); 
 613             if (!keychain
->exists()) 
 614                 MacOSError::throwMe(errSecNoSuchKeychain
);      // Might be deleted or not available at this time. 
 617             keychain 
= globals().storageManager
.defaultKeychainUI(item
); 
 623                 catch (const MacOSError 
&err
) { 
 624                         if (err
.osStatus() != errSecDuplicateItem
) 
 625                                 throw; // if item already exists, fall through to update 
 634 SecIdentitySetPreferred(SecIdentityRef identity
, CFStringRef name
, CFArrayRef keyUsage
) 
 636         CSSM_KEYUSE keyUse 
= ConvertArrayToKeyUsage(keyUsage
); 
 637         return SecIdentitySetPreference(identity
, name
, keyUse
); 
 641 SecIdentityFindPreferenceItem( 
 642         CFTypeRef keychainOrArray
, 
 643         CFStringRef idString
, 
 644         SecKeychainItemRef 
*itemRef
) 
 648         StorageManager::KeychainList keychains
; 
 649         globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
); 
 650         KCCursor 
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
); 
 652         char idUTF8
[MAXPATHLEN
]; 
 653     idUTF8
[0] = (char)'\0'; 
 656                 if (!CFStringGetCString(idString
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
)) 
 657                         idUTF8
[0] = (char)'\0'; 
 659     size_t idUTF8Len 
= strlen(idUTF8
); 
 661         MacOSError::throwMe(errSecParam
); 
 663     CssmData 
service(const_cast<char *>(idUTF8
), idUTF8Len
); 
 664     cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
); 
 665         cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf'); 
 668         if (!cursor
->next(item
)) 
 669                 MacOSError::throwMe(errSecItemNotFound
); 
 672                 *itemRef
=item
->handle(); 
 678 SecIdentityFindPreferenceItemWithNameAndKeyUsage( 
 679         CFTypeRef keychainOrArray
, 
 682         SecKeychainItemRef 
*itemRef
) 
 686         StorageManager::KeychainList keychains
; 
 687         globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
); 
 688         KCCursor 
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
); 
 690         char idUTF8
[MAXPATHLEN
]; 
 691     idUTF8
[0] = (char)'\0'; 
 694                 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
)) 
 695                         idUTF8
[0] = (char)'\0'; 
 697     size_t idUTF8Len 
= strlen(idUTF8
); 
 699         MacOSError::throwMe(errSecParam
); 
 701     CssmData 
service(const_cast<char *>(idUTF8
), idUTF8Len
); 
 702     cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
); 
 703         cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf'); 
 705         cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
); 
 708         if (!cursor
->next(item
)) 
 709                 MacOSError::throwMe(errSecItemNotFound
); 
 712                 *itemRef
=item
->handle(); 
 717 OSStatus 
SecIdentityDeletePreferenceItemWithNameAndKeyUsage( 
 718         CFTypeRef keychainOrArray
, 
 722         // when a specific key usage is passed, we'll only match & delete that pref; 
 723         // when a key usage of 0 is passed, all matching prefs should be deleted. 
 724         // maxUsages represents the most matches there could theoretically be, so 
 725         // cut things off at that point if we're still finding items (if they can't 
 726         // be deleted for some reason, we'd never break out of the loop.) 
 729         SecKeychainItemRef item 
= NULL
; 
 730         int count 
= 0, maxUsages 
= 12; 
 731         while (++count 
<= maxUsages 
&& 
 732                         (status 
= SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray
, name
, keyUsage
, &item
)) == errSecSuccess
) { 
 733                 status 
= SecKeychainItemDelete(item
); 
 738         // it's not an error if the item isn't found 
 739         return (status 
== errSecItemNotFound
) ? errSecSuccess 
: status
; 
 744 OSStatus 
_SecIdentityAddPreferenceItemWithName( 
 745         SecKeychainRef keychainRef
, 
 746         SecIdentityRef identityRef
, 
 747         CFStringRef idString
, 
 748         SecKeychainItemRef 
*itemRef
) 
 750     // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here; 
 751     // caller must handle exceptions 
 753         if (!identityRef 
|| !idString
) 
 755         SecPointer
<Certificate
> cert(Identity::required(identityRef
)->certificate()); 
 756         Item 
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false); 
 759         // determine the account attribute 
 761         // This attribute must be synthesized from certificate label + pref item type + key usage, 
 762         // as only the account and service attributes can make a generic keychain item unique. 
 763         // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that 
 764         // we can save a certificate preference if an identity preference already exists for the 
 765         // given service name, and vice-versa. 
 766         // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. 
 768     CFStringRef labelStr 
= nil
; 
 769         cert
->inferLabel(false, &labelStr
); 
 771         return errSecDataTooLarge
; // data is "in a format which cannot be displayed" 
 773         CFIndex accountUTF8Len 
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1; 
 774         const char *templateStr 
= "%s [key usage 0x%X]"; 
 775         const int keyUsageMaxStrLen 
= 8; 
 776         accountUTF8Len 
+= strlen(templateStr
) + keyUsageMaxStrLen
; 
 777         char accountUTF8
[accountUTF8Len
]; 
 778     if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
)) 
 779                 accountUTF8
[0] = (char)'\0'; 
 781                 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
); 
 782         snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
); 
 783     CssmData 
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
)); 
 786         // service attribute (name provided by the caller) 
 787         CFIndex serviceUTF8Len 
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString
), kCFStringEncodingUTF8
) + 1;; 
 788         char serviceUTF8
[serviceUTF8Len
]; 
 789     if (!CFStringGetCString(idString
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
)) 
 790         serviceUTF8
[0] = (char)'\0'; 
 791     CssmData 
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
)); 
 793         // set item attribute values 
 794         item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
); 
 795         item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
); 
 796         item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf'); 
 797         item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
); 
 798         item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), keyUsage
); 
 800         // generic attribute (store persistent certificate reference) 
 801         CFDataRef pItemRef 
= nil
; 
 802         OSStatus status 
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)cert
->handle(), &pItemRef
); 
 804                 status 
= errSecInvalidItemRef
; 
 807         const UInt8 
*dataPtr 
= CFDataGetBytePtr(pItemRef
); 
 808         CFIndex dataLen 
= CFDataGetLength(pItemRef
); 
 809         CssmData 
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
); 
 810         item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
); 
 813         Keychain keychain 
= nil
; 
 815         keychain 
= Keychain::optional(keychainRef
); 
 816         if (!keychain
->exists()) 
 817             MacOSError::throwMe(errSecNoSuchKeychain
);  // Might be deleted or not available at this time. 
 820         keychain 
= globals().storageManager
.defaultKeychainUI(item
); 
 826         catch (const MacOSError 
&err
) { 
 827                 if (err
.osStatus() != errSecDuplicateItem
) 
 828                         throw; // if item already exists, fall through to update 
 834                 *itemRef 
= item
->handle(); 
 839 OSStatus 
SecIdentityAddPreferenceItem( 
 840         SecKeychainRef keychainRef
, 
 841         SecIdentityRef identityRef
, 
 842         CFStringRef idString
, 
 843         SecKeychainItemRef 
*itemRef
) 
 845     // The original implementation of SecIdentityAddPreferenceItem adds the exact string only. 
 846     // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above), 
 847     // and this function is a wrapper which calls it, so that existing clients will get the 
 848     // extended behavior of server domain matching for items that specify URLs. 
 849     // (Note that behavior is unchanged if the specified idString is not a URL.) 
 853     OSStatus status 
= errSecInternalComponent
; 
 854     CFArrayRef names 
= _SecIdentityCopyPossiblePaths(idString
); 
 859     CFIndex total 
= CFArrayGetCount(names
); 
 861         // add item for name (first element in array) 
 862         CFStringRef aName 
= (CFStringRef
)CFArrayGetValueAtIndex(names
, 0); 
 864             status 
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
); 
 866         catch (const MacOSError 
&err
)   { status
=err
.osStatus(); } 
 867         catch (const CommonError 
&err
)  { status
=SecKeychainErrFromOSStatus(err
.osStatus()); } 
 868         catch (const std::bad_alloc 
&)  { status
=errSecAllocate
; } 
 869         catch (...)                     { status
=errSecInternalComponent
; } 
 872                 Boolean setDomainDefaultIdentity 
= FALSE
; 
 873                 CFTypeRef val 
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"), 
 874                                                                                                                   CFSTR("com.apple.security.identities"), 
 875                                                                                                                   kCFPreferencesCurrentUser
, 
 876                                                                                                                   kCFPreferencesAnyHost
); 
 878                         if (CFGetTypeID(val
) == CFBooleanGetTypeID()) 
 879                                 setDomainDefaultIdentity 
= CFBooleanGetValue((CFBooleanRef
)val
) ? TRUE 
: FALSE
; 
 882                 if (setDomainDefaultIdentity
) { 
 883                         // add item for domain (second-to-last element in array, e.g. "*.apple.com") 
 884                         OSStatus tmpStatus 
= errSecSuccess
; 
 885                         CFStringRef aName 
= (CFStringRef
)CFArrayGetValueAtIndex(names
, total
-2); 
 887                                 tmpStatus 
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
); 
 889                         catch (const MacOSError 
&err
)   { tmpStatus
=err
.osStatus(); } 
 890                         catch (const CommonError 
&err
)  { tmpStatus
=SecKeychainErrFromOSStatus(err
.osStatus()); } 
 891                         catch (const std::bad_alloc 
&)  { tmpStatus
=errSecAllocate
; } 
 892                         catch (...)                     { tmpStatus
=errSecInternalComponent
; } 
 902 /* deprecated in 10.5 */ 
 903 OSStatus 
SecIdentityUpdatePreferenceItem( 
 904                         SecKeychainItemRef itemRef
, 
 905                         SecIdentityRef identityRef
) 
 909         if (!itemRef 
|| !identityRef
) 
 910                 MacOSError::throwMe(errSecParam
); 
 911         SecPointer
<Certificate
> certificate(Identity::required(identityRef
)->certificate()); 
 912         Item prefItem 
= ItemImpl::required(itemRef
); 
 914         // get the current key usage value for this item 
 917         SecKeychainAttribute attr 
= { kSecScriptCodeItemAttr
, sizeof(sint32
), &keyUsage 
}; 
 919                 prefItem
->getAttribute(attr
, &actLen
); 
 925         // set the account attribute 
 927         // This attribute must be synthesized from certificate label + pref item type + key usage, 
 928         // as only the account and service attributes can make a generic keychain item unique. 
 929         // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that 
 930         // we can save a certificate preference if an identity preference already exists for the 
 931         // given service name, and vice-versa. 
 932         // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. 
 934     CFStringRef labelStr 
= nil
; 
 935         certificate
->inferLabel(false, &labelStr
); 
 937         MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed" 
 939         CFIndex accountUTF8Len 
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1; 
 940         const char *templateStr 
= "%s [key usage 0x%X]"; 
 941         const int keyUsageMaxStrLen 
= 8; 
 942         accountUTF8Len 
+= strlen(templateStr
) + keyUsageMaxStrLen
; 
 943         char accountUTF8
[accountUTF8Len
]; 
 944     if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
)) 
 945                 accountUTF8
[0] = (char)'\0'; 
 947                 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
); 
 948         snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
); 
 949     CssmData 
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
)); 
 950         prefItem
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
); 
 953         // generic attribute (store persistent certificate reference) 
 954         CFDataRef pItemRef 
= nil
; 
 955         OSStatus status 
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certificate
->handle(), &pItemRef
); 
 957                 status 
= errSecInvalidItemRef
; 
 959                 MacOSError::throwMe(status
); 
 960         const UInt8 
*dataPtr 
= CFDataGetBytePtr(pItemRef
); 
 961         CFIndex dataLen 
= CFDataGetLength(pItemRef
); 
 962         CssmData 
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
); 
 963         prefItem
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
); 
 971 OSStatus 
SecIdentityCopyFromPreferenceItem( 
 972                         SecKeychainItemRef itemRef
, 
 973                         SecIdentityRef 
*identityRef
) 
 977         if (!itemRef 
|| !identityRef
) 
 978                 MacOSError::throwMe(errSecParam
); 
 979         Item prefItem 
= ItemImpl::required(itemRef
); 
 981         // get persistent certificate reference 
 982         SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL 
} }; 
 983         SecKeychainAttributeList itemAttrList 
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs 
}; 
 984         prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
); 
 986         // find certificate, given persistent reference data 
 987         CFDataRef pItemRef 
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8 
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
); 
 988         SecKeychainItemRef certItemRef 
= nil
; 
 989         OSStatus status 
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl 
 990         prefItem
->freeContent(&itemAttrList
, NULL
); 
 996         // create identity reference, given certificate 
 997         StorageManager::KeychainList keychains
; 
 998         globals().storageManager
.optionalSearchList((CFTypeRef
)NULL
, keychains
); 
 999         Item certItem 
= ItemImpl::required(SecKeychainItemRef(certItemRef
)); 
1000         SecPointer
<Certificate
> certificate(static_cast<Certificate 
*>(certItem
.get())); 
1001         SecPointer
<Identity
> identity(new Identity(keychains
, certificate
)); 
1003                 CFRelease(certItemRef
); 
1005         Required(identityRef
) = identity
->handle(); 
1011  * System Identity Support. 
1014 /* plist domain (in /Library/Preferences) */ 
1015 #define IDENTITY_DOMAIN         "com.apple.security.systemidentities" 
1018  * Our plist is a dictionary whose entries have the following format: 
1019  * key   = domain name as CFString 
1020  * value = public key hash as CFData 
1023 #define SYSTEM_KEYCHAIN_PATH    kSystemKeychainDir "/" kSystemKeychainName 
1026  * All accesses to system identities and its associated plist are 
1027  * protected by this lock. 
1029 ModuleNexus
<Mutex
> systemIdentityLock
; 
1031 OSStatus 
SecIdentityCopySystemIdentity( 
1033    SecIdentityRef 
*idRef
, 
1034    CFStringRef 
*actualDomain
) /* optional */ 
1038         StLock
<Mutex
> _(systemIdentityLock()); 
1039         auto_ptr
<Dictionary
> identDict
; 
1041         /* get top-level dictionary - if not present, we're done */ 
1042         Dictionary
* d 
= Dictionary::CreateDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
); 
1045                 return errSecNotAvailable
; 
1050         /* see if there's an entry for specified domain */ 
1051         CFDataRef entryValue 
= identDict
->getDataValue(domain
); 
1052         if(entryValue 
== NULL
) { 
1053                 /* try for default entry if we're not already looking for default */ 
1054                 if(!CFEqual(domain
, kSecIdentityDomainDefault
)) { 
1055                         entryValue 
= identDict
->getDataValue(kSecIdentityDomainDefault
); 
1057                 if(entryValue 
== NULL
) { 
1058                         /* no default identity */ 
1059                         MacOSError::throwMe(errSecItemNotFound
); 
1062                 /* remember that we're not fetching the requested domain */ 
1063                 domain 
= kSecIdentityDomainDefault
; 
1066         /* open system keychain - error here is fatal */ 
1067         Keychain systemKc 
= globals().storageManager
.make(SYSTEM_KEYCHAIN_PATH
, false); 
1068         CFRef
<SecKeychainRef
> systemKcRef(systemKc
->handle()); 
1069         StorageManager::KeychainList keychains
; 
1070         globals().storageManager
.optionalSearchList(systemKcRef
, keychains
); 
1072         /* search for specified cert */ 
1073         SecKeychainAttributeList        attrList
; 
1074         SecKeychainAttribute            attr
; 
1075         attr
.tag        
= kSecPublicKeyHashItemAttr
; 
1076         attr
.length     
= (UInt32
)CFDataGetLength(entryValue
); 
1077         attr
.data       
= (void *)CFDataGetBytePtr(entryValue
); 
1079         attrList
.attr   
= &attr
; 
1081         KCCursor 
cursor(keychains
, kSecCertificateItemClass
, &attrList
); 
1083         if(!cursor
->next(certItem
)) { 
1084                 MacOSError::throwMe(errSecItemNotFound
); 
1087         /* found the cert; try matching with key to cook up identity */ 
1088         SecPointer
<Certificate
> certificate(static_cast<Certificate 
*>(certItem
.get())); 
1089         SecPointer
<Identity
> identity(new Identity(keychains
, certificate
)); 
1091         Required(idRef
) = identity
->handle(); 
1093                 *actualDomain 
= domain
; 
1094                 CFRetain(*actualDomain
); 
1100 OSStatus 
SecIdentitySetSystemIdentity( 
1102    SecIdentityRef idRef
) 
1106         StLock
<Mutex
> _(systemIdentityLock()); 
1107         if(geteuid() != 0) { 
1108                 MacOSError::throwMe(errSecAuthFailed
); 
1111         auto_ptr
<MutableDictionary
> identDict
; 
1112         MutableDictionary 
*d 
= MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
); 
1120                         /* nothing there, nothing to set - done */ 
1121                         return errSecSuccess
; 
1123                 identDict
.reset(new MutableDictionary()); 
1127                 /* Just delete the possible entry for this domain */ 
1128                 identDict
->removeValue(domain
); 
1131                 /* obtain public key hash of identity's cert */ 
1132                 SecPointer
<Identity
> identity(Identity::required(idRef
)); 
1133                 SecPointer
<Certificate
> cert 
= identity
->certificate(); 
1134                 const CssmData 
&pubKeyHash 
= cert
->publicKeyHash(); 
1135                 CFRef
<CFDataRef
> pubKeyHashData(CFDataCreate(NULL
, pubKeyHash
.Data
, 
1136                         pubKeyHash
.Length
)); 
1138                 /* add/replace to dictionary */ 
1139                 identDict
->setValue(domain
, pubKeyHashData
); 
1143         if(!identDict
->writePlistToPrefs(IDENTITY_DOMAIN
, Dictionary::US_System
)) { 
1144                 MacOSError::throwMe(errSecIO
); 
1150 const CFStringRef kSecIdentityDomainDefault 
= CFSTR("com.apple.systemdefault"); 
1151 const CFStringRef kSecIdentityDomainKerberosKDC 
= CFSTR("com.apple.kerberos.kdc");