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 <utilities/SecCFRelease.h>
39 #include <sys/param.h>
41 #include <os/activity.h>
42 #include "LegacyAPICounts.h"
44 /* private function declarations */
46 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
47 CFTypeRef keychainOrArray
,
50 SecKeychainItemRef
*itemRef
);
52 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
53 CFTypeRef keychainOrArray
,
58 CSSM_KEYUSE
ConvertArrayToKeyUsage(CFArrayRef usage
)
61 CSSM_KEYUSE result
= (CSSM_KEYUSE
) 0;
63 if ((NULL
== usage
) || (0 == (count
= CFArrayGetCount(usage
))))
68 for (CFIndex iCnt
= 0; iCnt
< count
; iCnt
++)
70 CFStringRef keyUsageStr
= NULL
;
71 keyUsageStr
= (CFStringRef
)CFArrayGetValueAtIndex(usage
,iCnt
);
72 if (NULL
!= keyUsageStr
)
74 if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanEncrypt
, keyUsageStr
, 0))
76 result
|= CSSM_KEYUSE_ENCRYPT
;
78 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDecrypt
, keyUsageStr
, 0))
80 result
|= CSSM_KEYUSE_DECRYPT
;
82 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDerive
, keyUsageStr
, 0))
84 result
|= CSSM_KEYUSE_DERIVE
;
86 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanSign
, keyUsageStr
, 0))
88 result
|= CSSM_KEYUSE_SIGN
;
90 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanVerify
, keyUsageStr
, 0))
92 result
|= CSSM_KEYUSE_VERIFY
;
94 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanWrap
, keyUsageStr
, 0))
96 result
|= CSSM_KEYUSE_WRAP
;
98 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanUnwrap
, keyUsageStr
, 0))
100 result
|= CSSM_KEYUSE_UNWRAP
;
110 SecIdentityGetTypeID(void)
114 return gTypes().Identity
.typeID
;
116 END_SECAPI1(_kCFRuntimeNotATypeID
)
121 SecIdentityCopyCertificate(
122 SecIdentityRef identityRef
,
123 SecCertificateRef
*certificateRef
)
126 os_activity_t activity
= os_activity_create("SecIdentityCopyCertificate", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
127 os_activity_scope(activity
);
128 os_release(activity
);
130 if (!identityRef
|| !certificateRef
) {
133 CFTypeID itemType
= CFGetTypeID(identityRef
);
134 if (itemType
== SecIdentityGetTypeID()) {
135 SecPointer
<Certificate
> certificatePtr(Identity::required(identityRef
)->certificate());
136 Required(certificateRef
) = certificatePtr
->handle();
138 /* convert outgoing certificate item to a unified SecCertificateRef */
139 CssmData certData
= certificatePtr
->data();
140 CFDataRef data
= NULL
;
141 if (certData
.Data
&& certData
.Length
) {
142 data
= CFDataCreate(NULL
, certData
.Data
, certData
.Length
);
145 *certificateRef
= NULL
;
146 syslog(LOG_ERR
, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)",
147 (long)certData
.Length
, (uintptr_t)certData
.Data
);
148 return errSecInternal
;
150 SecCertificateRef tmpRef
= *certificateRef
;
151 *certificateRef
= SecCertificateCreateWithKeychainItem(NULL
, data
, tmpRef
);
159 else if (itemType
== SecCertificateGetTypeID()) {
161 // reconstituting a persistent identity reference could return the certificate
162 SecCertificateRef certificate
= (SecCertificateRef
)identityRef
;
164 /* convert outgoing certificate item to a unified SecCertificateRef, if needed */
165 if (SecCertificateIsItemImplInstance(certificate
)) {
166 *certificateRef
= SecCertificateCreateFromItemImplInstance(certificate
);
169 *certificateRef
= (SecCertificateRef
) CFRetain(certificate
);
171 return errSecSuccess
;
182 SecIdentityCopyPrivateKey(
183 SecIdentityRef identityRef
,
184 SecKeyRef
*privateKeyRef
)
187 os_activity_t activity
= os_activity_create("SecIdentityCopyPrivateKey", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
188 os_activity_scope(activity
);
189 os_release(activity
);
191 Required(privateKeyRef
) = (SecKeyRef
)CFRetain(Identity::required(identityRef
)->privateKeyRef());
197 SecIdentityCreateWithCertificate(
198 CFTypeRef keychainOrArray
,
199 SecCertificateRef certificate
,
200 SecIdentityRef
*identityRef
)
202 // This macro converts a new-style SecCertificateRef to an old-style ItemImpl
205 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
206 StorageManager::KeychainList keychains
;
207 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
208 SecPointer
<Identity
> identityPtr(new Identity(keychains
, certificatePtr
));
209 Required(identityRef
) = identityPtr
->handle();
216 CFAllocatorRef allocator
,
217 SecCertificateRef certificate
,
218 SecKeyRef privateKey
)
221 SecIdentityRef identityRef
= NULL
;
222 OSStatus __secapiresult
;
223 SecCertificateRef __itemImplRef
= NULL
;
224 if (SecCertificateIsItemImplInstance(certificate
)) {
225 __itemImplRef
=(SecCertificateRef
)CFRetain(certificate
);
227 if (!__itemImplRef
&& certificate
) {
228 __itemImplRef
=(SecCertificateRef
)SecCertificateCopyKeychainItem(certificate
);
230 if (!__itemImplRef
&& certificate
) {
231 __itemImplRef
=SecCertificateCreateItemImplInstance(certificate
);
232 (void)SecCertificateSetKeychainItem(certificate
,__itemImplRef
);
235 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
236 SecPointer
<Identity
> identityPtr(new Identity(privateKey
, certificatePtr
));
237 identityRef
= identityPtr
->handle();
239 __secapiresult
=errSecSuccess
;
241 catch (const MacOSError
&err
) { __secapiresult
=err
.osStatus(); }
242 catch (const CommonError
&err
) { __secapiresult
=SecKeychainErrFromOSStatus(err
.osStatus()); }
243 catch (const std::bad_alloc
&) { __secapiresult
=errSecAllocate
; }
244 catch (...) { __secapiresult
=errSecInternalComponent
; }
245 if (__itemImplRef
) { CFRelease(__itemImplRef
); }
250 CFArrayRef
_SecIdentityCopyPossiblePaths(
253 // utility function to build and return an array of possible paths for the given name.
254 // if name is not a URL, this returns a single-element array.
255 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
257 CFMutableArrayRef names
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
261 CFIndex oldLength
= CFStringGetLength(name
);
262 CFArrayAppendValue(names
, name
);
264 CFURLRef url
= CFURLCreateWithString(NULL
, name
, NULL
);
266 if (CFURLCanBeDecomposed(url
)) {
267 // first, remove the query portion of this URL, if any
268 CFStringRef qs
= CFURLCopyQueryString(url
, NULL
);
270 CFMutableStringRef newName
= CFStringCreateMutableCopy(NULL
, oldLength
, name
);
272 CFIndex qsLength
= CFStringGetLength(qs
) + 1; // include the '?'
273 CFStringDelete(newName
, CFRangeMake(oldLength
-qsLength
, qsLength
));
275 url
= CFURLCreateWithString(NULL
, newName
, NULL
);
276 CFArraySetValueAtIndex(names
, 0, newName
);
281 // now add an entry for each level of the path
283 CFURLRef parent
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
);
285 CFStringRef parentURLString
= CFURLGetString(parent
);
286 if (parentURLString
) {
287 CFIndex newLength
= CFStringGetLength(parentURLString
);
288 // check that string length has decreased as expected; for file URLs,
289 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
290 if ((newLength
>= oldLength
) || (!CFStringHasPrefix(name
, parentURLString
))) {
295 oldLength
= newLength
;
296 CFArrayAppendValue(names
, parentURLString
);
307 // finally, add wildcard entries for each subdomain
308 url
= CFURLCreateWithString(NULL
, name
, NULL
);
310 if (CFURLCanBeDecomposed(url
)) {
311 CFStringRef netLocString
= CFURLCopyNetLocation(url
);
313 // first strip off port number, if present
314 CFStringRef tmpLocString
= netLocString
;
315 CFArrayRef hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(":"));
316 tmpLocString
= (CFStringRef
)CFRetain((CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, 0));
317 CFRelease(netLocString
);
318 CFRelease(hostnameArray
);
319 netLocString
= tmpLocString
;
320 // split remaining string into domain components
321 hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR("."));
322 CFIndex subdomainCount
= CFArrayGetCount(hostnameArray
);
324 while (++i
< subdomainCount
) {
326 CFMutableStringRef wildcardString
= CFStringCreateMutable(NULL
, 0);
327 if (wildcardString
) {
328 CFStringAppendCString(wildcardString
, "*", kCFStringEncodingUTF8
);
329 while (j
< subdomainCount
) {
330 CFStringRef domainString
= (CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, j
++);
331 if (CFStringGetLength(domainString
) > 0) {
332 CFStringAppendCString(wildcardString
, ".", kCFStringEncodingUTF8
);
333 CFStringAppend(wildcardString
, domainString
);
336 if (CFStringGetLength(wildcardString
) > 1) {
337 CFArrayAppendValue(names
, wildcardString
);
339 CFRelease(wildcardString
);
342 CFRelease(hostnameArray
);
343 CFRelease(netLocString
);
353 OSStatus
_SecIdentityCopyPreferenceMatchingName(
355 CSSM_KEYUSE keyUsage
,
356 CFArrayRef validIssuers
,
357 SecIdentityRef
*identity
)
359 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
360 // caller must handle exceptions
362 StorageManager::KeychainList keychains
;
363 globals().storageManager
.getSearchList(keychains
);
364 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
366 char idUTF8
[MAXPATHLEN
];
368 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
369 idUTF8
[0] = (char)'\0';
370 CssmData
service(const_cast<char *>(idUTF8
), strlen(idUTF8
));
371 FourCharCode itemType
= 'iprf';
372 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
373 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
375 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
379 if (!cursor
->next(prefItem
))
380 return errSecItemNotFound
;
382 // get persistent certificate reference
383 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
384 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
385 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
387 // find certificate, given persistent reference data
388 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
389 SecKeychainItemRef certItemRef
= nil
;
390 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
391 prefItem
->freeContent(&itemAttrList
, NULL
);
397 // filter on valid issuers, if provided
402 // create identity reference, given certificate
403 status
= SecIdentityCreateWithCertificate(NULL
, (SecCertificateRef
)certItemRef
, identity
);
405 CFRelease(certItemRef
);
411 SecIdentityRef
SecIdentityCopyPreferred(CFStringRef name
, CFArrayRef keyUsage
, CFArrayRef validIssuers
)
413 // This function will look for a matching preference in the following order:
414 // - matches the name and the supplied key use
415 // - matches the name and the special 'ANY' key use
416 // - matches the name with no key usage constraint
418 SecIdentityRef identityRef
= NULL
;
419 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
420 OSStatus status
= SecIdentityCopyPreference(name
, keyUse
, validIssuers
, &identityRef
);
421 if (status
!= errSecSuccess
&& keyUse
!= CSSM_KEYUSE_ANY
)
422 status
= SecIdentityCopyPreference(name
, CSSM_KEYUSE_ANY
, validIssuers
, &identityRef
);
423 if (status
!= errSecSuccess
&& keyUse
!= 0)
424 status
= SecIdentityCopyPreference(name
, 0, validIssuers
, &identityRef
);
429 OSStatus
SecIdentityCopyPreference(
431 CSSM_KEYUSE keyUsage
,
432 CFArrayRef validIssuers
,
433 SecIdentityRef
*identity
)
435 // The original implementation of SecIdentityCopyPreference matches the exact string only.
436 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
437 // and this function is a wrapper which calls it, so that existing clients will get the
438 // extended behavior of server domain matching for items that specify URLs.
439 // (Note that behavior is unchanged if the specified name is not a URL.)
442 os_activity_t activity
= os_activity_create("SecIdentityCopyPreference", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
443 os_activity_scope(activity
);
444 os_release(activity
);
446 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
447 CFSTR("com.apple.security"),
448 kCFPreferencesCurrentUser
,
449 kCFPreferencesAnyHost
);
450 Boolean logging
= false;
452 if (CFGetTypeID(val
) == CFBooleanGetTypeID()) {
453 logging
= CFBooleanGetValue((CFBooleanRef
)val
);
458 OSStatus status
= errSecItemNotFound
;
459 CFArrayRef names
= _SecIdentityCopyPossiblePaths(name
);
464 CFIndex idx
, total
= CFArrayGetCount(names
);
465 for (idx
= 0; idx
< total
; idx
++) {
466 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, idx
);
468 status
= _SecIdentityCopyPreferenceMatchingName(aName
, keyUsage
, validIssuers
, identity
);
470 catch (...) { status
= errSecItemNotFound
; }
473 // get identity label
474 CFStringRef labelString
= NULL
;
475 if (!status
&& identity
&& *identity
) {
477 SecPointer
<Certificate
> cert(Identity::required(*identity
)->certificate());
478 cert
->inferLabel(false, &labelString
);
480 catch (...) { labelString
= NULL
; };
482 char *labelBuf
= NULL
;
483 CFIndex labelBufSize
= (labelString
) ? CFStringGetLength(labelString
) * 4 : 4;
484 labelBuf
= (char *)malloc(labelBufSize
);
485 if (!labelString
|| !CFStringGetCString(labelString
, labelBuf
, labelBufSize
, kCFStringEncodingUTF8
)) {
489 CFRelease(labelString
);
493 char *serviceBuf
= NULL
;
494 CFIndex serviceBufSize
= CFStringGetLength(aName
) * 4;
495 serviceBuf
= (char *)malloc(serviceBufSize
);
496 if (!CFStringGetCString(aName
, serviceBuf
, serviceBufSize
, kCFStringEncodingUTF8
)) {
500 syslog(LOG_NOTICE
, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf
, serviceBuf
);
501 if (!status
&& name
) {
502 char *nameBuf
= NULL
;
503 CFIndex nameBufSize
= CFStringGetLength(name
) * 4;
504 nameBuf
= (char *)malloc(nameBufSize
);
505 if (!CFStringGetCString(name
, nameBuf
, nameBufSize
, kCFStringEncodingUTF8
)) {
508 syslog(LOG_NOTICE
, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf
, nameBuf
);
516 if (status
== errSecSuccess
) {
517 break; // match found
527 OSStatus
SecIdentitySetPreference(
528 SecIdentityRef identity
,
530 CSSM_KEYUSE keyUsage
)
536 // treat NULL identity as a request to clear the preference
537 // (note: if keyUsage is 0, this clears all key usage prefs for name)
538 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL
, name
, keyUsage
);
542 os_activity_t activity
= os_activity_create("SecIdentitySetPreference", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
543 os_activity_scope(activity
);
544 os_release(activity
);
546 CFRef
<SecCertificateRef
> certRef
;
547 OSStatus status
= SecIdentityCopyCertificate(identity
, certRef
.take());
548 if(status
!= errSecSuccess
) {
549 MacOSError::throwMe(status
);
552 // determine the account attribute
554 // This attribute must be synthesized from certificate label + pref item type + key usage,
555 // as only the account and service attributes can make a generic keychain item unique.
556 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
557 // we can save a certificate preference if an identity preference already exists for the
558 // given service name, and vice-versa.
559 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
561 CFStringRef labelStr
= nil
;
562 SecCertificateInferLabel(certRef
.get(), &labelStr
);
564 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
566 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
567 const char *templateStr
= "%s [key usage 0x%X]";
568 const int keyUsageMaxStrLen
= 8;
569 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
570 char *accountUTF8
= (char *)malloc(accountUTF8Len
);
572 MacOSError::throwMe(errSecMemoryError
);
574 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
575 accountUTF8
[0] = (char)'\0';
577 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
578 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
579 CssmDataContainer
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
583 // service attribute (name provided by the caller)
584 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(name
), kCFStringEncodingUTF8
) + 1;;
585 char *serviceUTF8
= (char *)malloc(serviceUTF8Len
);
587 MacOSError::throwMe(errSecMemoryError
);
589 if (!CFStringGetCString(name
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
590 serviceUTF8
[0] = (char)'\0';
591 CssmDataContainer
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
594 // look for existing identity preference item, in case this is an update
595 StorageManager::KeychainList keychains
;
596 globals().storageManager
.getSearchList(keychains
);
597 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
598 FourCharCode itemType
= 'iprf';
599 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
600 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
602 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
605 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
606 bool add
= (!cursor
->next(item
));
607 // at this point, we either have a new item to add or an existing item to update
609 // set item attribute values
610 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
611 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
612 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
613 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
614 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
616 // generic attribute (store persistent certificate reference)
617 CFDataRef pItemRef
= nil
;
618 SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certRef
.get(), &pItemRef
);
620 MacOSError::throwMe(errSecInvalidItemRef
);
622 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
623 CFIndex dataLen
= CFDataGetLength(pItemRef
);
624 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
625 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
629 Keychain keychain
= nil
;
631 keychain
= globals().storageManager
.defaultKeychain();
632 if (!keychain
->exists())
633 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
636 keychain
= globals().storageManager
.defaultKeychainUI(item
);
642 catch (const MacOSError
&err
) {
643 if (err
.osStatus() != errSecDuplicateItem
)
644 throw; // if item already exists, fall through to update
653 SecIdentitySetPreferred(SecIdentityRef identity
, CFStringRef name
, CFArrayRef keyUsage
)
656 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
657 return SecIdentitySetPreference(identity
, name
, keyUse
);
661 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
662 CFTypeRef keychainOrArray
,
665 SecKeychainItemRef
*itemRef
)
668 os_activity_t activity
= os_activity_create("SecIdentityFindPreferenceItemWithNameAndKeyUsage", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
669 os_activity_scope(activity
);
670 os_release(activity
);
672 StorageManager::KeychainList keychains
;
673 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
674 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
676 char idUTF8
[MAXPATHLEN
];
677 idUTF8
[0] = (char)'\0';
680 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
681 idUTF8
[0] = (char)'\0';
683 size_t idUTF8Len
= strlen(idUTF8
);
685 MacOSError::throwMe(errSecParam
);
687 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
688 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
689 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
691 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
695 if (!cursor
->next(item
))
696 MacOSError::throwMe(errSecItemNotFound
);
699 *itemRef
=item
->handle();
704 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
705 CFTypeRef keychainOrArray
,
710 // when a specific key usage is passed, we'll only match & delete that pref;
711 // when a key usage of 0 is passed, all matching prefs should be deleted.
712 // maxUsages represents the most matches there could theoretically be, so
713 // cut things off at that point if we're still finding items (if they can't
714 // be deleted for some reason, we'd never break out of the loop.)
716 OSStatus status
= errSecInternalError
;
717 SecKeychainItemRef item
= NULL
;
718 int count
= 0, maxUsages
= 12;
719 while (++count
<= maxUsages
&&
720 (status
= SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray
, name
, keyUsage
, &item
)) == errSecSuccess
) {
721 status
= SecKeychainItemDelete(item
);
726 // it's not an error if the item isn't found
727 return (status
== errSecItemNotFound
) ? errSecSuccess
: status
;
731 * System Identity Support.
734 /* plist domain (in /Library/Preferences) */
735 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
738 * Our plist is a dictionary whose entries have the following format:
739 * key = domain name as CFString
740 * value = public key hash as CFData
743 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
746 * All accesses to system identities and its associated plist are
747 * protected by this lock.
749 ModuleNexus
<Mutex
> systemIdentityLock
;
751 OSStatus
SecIdentityCopySystemIdentity(
753 SecIdentityRef
*idRef
,
754 CFStringRef
*actualDomain
) /* optional */
757 os_activity_t activity
= os_activity_create("SecIdentityCopySystemIdentity", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
758 os_activity_scope(activity
);
759 os_release(activity
);
761 StLock
<Mutex
> _(systemIdentityLock());
762 unique_ptr
<Dictionary
> identDict
;
764 /* get top-level dictionary - if not present, we're done */
765 Dictionary
* d
= Dictionary::CreateDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
768 return errSecNotAvailable
;
773 /* see if there's an entry for specified domain */
774 CFDataRef entryValue
= identDict
->getDataValue(domain
);
775 if(entryValue
== NULL
) {
776 /* try for default entry if we're not already looking for default */
777 if(!CFEqual(domain
, kSecIdentityDomainDefault
)) {
778 entryValue
= identDict
->getDataValue(kSecIdentityDomainDefault
);
780 if(entryValue
== NULL
) {
781 /* no default identity */
782 MacOSError::throwMe(errSecItemNotFound
);
785 /* remember that we're not fetching the requested domain */
786 domain
= kSecIdentityDomainDefault
;
789 /* open system keychain - error here is fatal */
790 Keychain systemKc
= globals().storageManager
.make(SYSTEM_KEYCHAIN_PATH
, false);
791 CFRef
<SecKeychainRef
> systemKcRef(systemKc
->handle());
792 StorageManager::KeychainList keychains
;
793 globals().storageManager
.optionalSearchList(systemKcRef
, keychains
);
795 /* search for specified cert */
796 SecKeychainAttributeList attrList
;
797 SecKeychainAttribute attr
;
798 attr
.tag
= kSecPublicKeyHashItemAttr
;
799 attr
.length
= (UInt32
)CFDataGetLength(entryValue
);
800 attr
.data
= (void *)CFDataGetBytePtr(entryValue
);
802 attrList
.attr
= &attr
;
804 KCCursor
cursor(keychains
, kSecCertificateItemClass
, &attrList
);
806 if(!cursor
->next(certItem
)) {
807 MacOSError::throwMe(errSecItemNotFound
);
810 /* found the cert; try matching with key to cook up identity */
811 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
812 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
814 Required(idRef
) = identity
->handle();
816 *actualDomain
= domain
;
817 CFRetain(*actualDomain
);
823 OSStatus
SecIdentitySetSystemIdentity(
825 SecIdentityRef idRef
)
828 os_activity_t activity
= os_activity_create("SecIdentitySetSystemIdentity", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
829 os_activity_scope(activity
);
830 os_release(activity
);
832 StLock
<Mutex
> _(systemIdentityLock());
834 MacOSError::throwMe(errSecAuthFailed
);
837 unique_ptr
<MutableDictionary
> identDict
;
838 MutableDictionary
*d
= MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
846 /* nothing there, nothing to set - done */
847 return errSecSuccess
;
849 identDict
.reset(new MutableDictionary());
853 /* Just delete the possible entry for this domain */
854 identDict
->removeValue(domain
);
857 /* obtain public key hash of identity's cert */
858 SecPointer
<Identity
> identity(Identity::required(idRef
));
859 SecPointer
<Certificate
> cert
= identity
->certificate();
860 const CssmData
&pubKeyHash
= cert
->publicKeyHash();
861 CFRef
<CFDataRef
> pubKeyHashData(CFDataCreate(NULL
, pubKeyHash
.Data
,
864 /* add/replace to dictionary */
865 identDict
->setValue(domain
, pubKeyHashData
);
869 if(!identDict
->writePlistToPrefs(IDENTITY_DOMAIN
, Dictionary::US_System
)) {
870 MacOSError::throwMe(errSecIO
);
876 const CFStringRef kSecIdentityDomainDefault
= CFSTR("com.apple.systemdefault");
877 const CFStringRef kSecIdentityDomainKerberosKDC
= CFSTR("com.apple.kerberos.kdc");