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>
42 /* private function declarations */
44 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
45 CFTypeRef keychainOrArray
,
48 SecKeychainItemRef
*itemRef
);
50 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
51 CFTypeRef keychainOrArray
,
56 CSSM_KEYUSE
ConvertArrayToKeyUsage(CFArrayRef usage
)
59 CSSM_KEYUSE result
= (CSSM_KEYUSE
) 0;
61 if ((NULL
== usage
) || (0 == (count
= CFArrayGetCount(usage
))))
66 for (CFIndex iCnt
= 0; iCnt
< count
; iCnt
++)
68 CFStringRef keyUsageStr
= NULL
;
69 keyUsageStr
= (CFStringRef
)CFArrayGetValueAtIndex(usage
,iCnt
);
70 if (NULL
!= keyUsageStr
)
72 if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanEncrypt
, keyUsageStr
, 0))
74 result
|= CSSM_KEYUSE_ENCRYPT
;
76 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDecrypt
, keyUsageStr
, 0))
78 result
|= CSSM_KEYUSE_DECRYPT
;
80 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDerive
, keyUsageStr
, 0))
82 result
|= CSSM_KEYUSE_DERIVE
;
84 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanSign
, keyUsageStr
, 0))
86 result
|= CSSM_KEYUSE_SIGN
;
88 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanVerify
, keyUsageStr
, 0))
90 result
|= CSSM_KEYUSE_VERIFY
;
92 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanWrap
, keyUsageStr
, 0))
94 result
|= CSSM_KEYUSE_WRAP
;
96 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanUnwrap
, keyUsageStr
, 0))
98 result
|= CSSM_KEYUSE_UNWRAP
;
108 SecIdentityGetTypeID(void)
112 return gTypes().Identity
.typeID
;
114 END_SECAPI1(_kCFRuntimeNotATypeID
)
119 SecIdentityCopyCertificate(
120 SecIdentityRef identityRef
,
121 SecCertificateRef
*certificateRef
)
125 if (!identityRef
|| !certificateRef
) {
128 CFTypeID itemType
= CFGetTypeID(identityRef
);
129 if (itemType
== SecIdentityGetTypeID()) {
130 SecPointer
<Certificate
> certificatePtr(Identity::required(identityRef
)->certificate());
131 Required(certificateRef
) = certificatePtr
->handle();
133 /* convert outgoing certificate item to a unified SecCertificateRef */
134 CssmData certData
= certificatePtr
->data();
135 CFDataRef data
= NULL
;
136 if (certData
.Data
&& certData
.Length
) {
137 data
= CFDataCreate(NULL
, certData
.Data
, certData
.Length
);
140 *certificateRef
= NULL
;
141 syslog(LOG_ERR
, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)",
142 (long)certData
.Length
, (uintptr_t)certData
.Data
);
143 return errSecInternal
;
145 SecCertificateRef tmpRef
= *certificateRef
;
146 *certificateRef
= SecCertificateCreateWithKeychainItem(NULL
, data
, tmpRef
);
154 else if (itemType
== SecCertificateGetTypeID()) {
156 // reconstituting a persistent identity reference could return the certificate
157 SecCertificateRef certificate
= (SecCertificateRef
)identityRef
;
159 /* convert outgoing certificate item to a unified SecCertificateRef, if needed */
160 if (SecCertificateIsItemImplInstance(certificate
)) {
161 *certificateRef
= SecCertificateCreateFromItemImplInstance(certificate
);
164 *certificateRef
= (SecCertificateRef
) CFRetain(certificate
);
166 return errSecSuccess
;
177 SecIdentityCopyPrivateKey(
178 SecIdentityRef identityRef
,
179 SecKeyRef
*privateKeyRef
)
183 Required(privateKeyRef
) = (SecKeyRef
)CFRetain(Identity::required(identityRef
)->privateKeyRef());
189 SecIdentityCreateWithCertificate(
190 CFTypeRef keychainOrArray
,
191 SecCertificateRef certificate
,
192 SecIdentityRef
*identityRef
)
194 // This macro converts a new-style SecCertificateRef to an old-style ItemImpl
197 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
198 StorageManager::KeychainList keychains
;
199 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
200 SecPointer
<Identity
> identityPtr(new Identity(keychains
, certificatePtr
));
201 Required(identityRef
) = identityPtr
->handle();
208 CFAllocatorRef allocator
,
209 SecCertificateRef certificate
,
210 SecKeyRef privateKey
)
212 SecIdentityRef identityRef
= NULL
;
213 OSStatus __secapiresult
;
214 SecCertificateRef __itemImplRef
= NULL
;
215 if (SecCertificateIsItemImplInstance(certificate
)) {
216 __itemImplRef
=(SecCertificateRef
)CFRetain(certificate
);
218 if (!__itemImplRef
&& certificate
) {
219 __itemImplRef
=(SecCertificateRef
)SecCertificateCopyKeychainItem(certificate
);
221 if (!__itemImplRef
&& certificate
) {
222 __itemImplRef
=SecCertificateCreateItemImplInstance(certificate
);
223 (void)SecCertificateSetKeychainItem(certificate
,__itemImplRef
);
226 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
227 SecPointer
<Identity
> identityPtr(new Identity(privateKey
, certificatePtr
));
228 identityRef
= identityPtr
->handle();
230 __secapiresult
=errSecSuccess
;
232 catch (const MacOSError
&err
) { __secapiresult
=err
.osStatus(); }
233 catch (const CommonError
&err
) { __secapiresult
=SecKeychainErrFromOSStatus(err
.osStatus()); }
234 catch (const std::bad_alloc
&) { __secapiresult
=errSecAllocate
; }
235 catch (...) { __secapiresult
=errSecInternalComponent
; }
236 if (__itemImplRef
) { CFRelease(__itemImplRef
); }
242 SecIdentityRef identity1
,
243 SecIdentityRef identity2
,
244 CFOptionFlags compareOptions
)
246 if (!identity1
|| !identity2
)
248 if (identity1
== identity2
)
249 return kCFCompareEqualTo
;
250 else if (identity1
< identity2
)
251 return kCFCompareLessThan
;
253 return kCFCompareGreaterThan
;
257 SecPointer
<Identity
> id1(Identity::required(identity1
));
258 SecPointer
<Identity
> id2(Identity::required(identity2
));
261 return kCFCompareEqualTo
;
263 return kCFCompareLessThan
;
265 return kCFCompareGreaterThan
;
269 return kCFCompareGreaterThan
;
273 CFArrayRef
_SecIdentityCopyPossiblePaths(
276 // utility function to build and return an array of possible paths for the given name.
277 // if name is not a URL, this returns a single-element array.
278 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
280 CFMutableArrayRef names
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
284 CFIndex oldLength
= CFStringGetLength(name
);
285 CFArrayAppendValue(names
, name
);
287 CFURLRef url
= CFURLCreateWithString(NULL
, name
, NULL
);
289 if (CFURLCanBeDecomposed(url
)) {
290 // first, remove the query portion of this URL, if any
291 CFStringRef qs
= CFURLCopyQueryString(url
, NULL
);
293 CFMutableStringRef newName
= CFStringCreateMutableCopy(NULL
, oldLength
, name
);
295 CFIndex qsLength
= CFStringGetLength(qs
) + 1; // include the '?'
296 CFStringDelete(newName
, CFRangeMake(oldLength
-qsLength
, qsLength
));
298 url
= CFURLCreateWithString(NULL
, newName
, NULL
);
299 CFArraySetValueAtIndex(names
, 0, newName
);
304 // now add an entry for each level of the path
306 CFURLRef parent
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
);
308 CFStringRef parentURLString
= CFURLGetString(parent
);
309 if (parentURLString
) {
310 CFIndex newLength
= CFStringGetLength(parentURLString
);
311 // check that string length has decreased as expected; for file URLs,
312 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
313 if ((newLength
>= oldLength
) || (!CFStringHasPrefix(name
, parentURLString
))) {
318 oldLength
= newLength
;
319 CFArrayAppendValue(names
, parentURLString
);
330 // finally, add wildcard entries for each subdomain
331 url
= CFURLCreateWithString(NULL
, name
, NULL
);
333 if (CFURLCanBeDecomposed(url
)) {
334 CFStringRef netLocString
= CFURLCopyNetLocation(url
);
336 // first strip off port number, if present
337 CFStringRef tmpLocString
= netLocString
;
338 CFArrayRef hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(":"));
339 tmpLocString
= (CFStringRef
)CFRetain((CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, 0));
340 CFRelease(netLocString
);
341 CFRelease(hostnameArray
);
342 netLocString
= tmpLocString
;
343 // split remaining string into domain components
344 hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR("."));
345 CFIndex subdomainCount
= CFArrayGetCount(hostnameArray
);
347 while (++i
< subdomainCount
) {
349 CFMutableStringRef wildcardString
= CFStringCreateMutable(NULL
, 0);
350 if (wildcardString
) {
351 CFStringAppendCString(wildcardString
, "*", kCFStringEncodingUTF8
);
352 while (j
< subdomainCount
) {
353 CFStringRef domainString
= (CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, j
++);
354 if (CFStringGetLength(domainString
) > 0) {
355 CFStringAppendCString(wildcardString
, ".", kCFStringEncodingUTF8
);
356 CFStringAppend(wildcardString
, domainString
);
359 if (CFStringGetLength(wildcardString
) > 1) {
360 CFArrayAppendValue(names
, wildcardString
);
362 CFRelease(wildcardString
);
365 CFRelease(hostnameArray
);
366 CFRelease(netLocString
);
376 OSStatus
_SecIdentityCopyPreferenceMatchingName(
378 CSSM_KEYUSE keyUsage
,
379 CFArrayRef validIssuers
,
380 SecIdentityRef
*identity
)
382 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
383 // caller must handle exceptions
385 StorageManager::KeychainList keychains
;
386 globals().storageManager
.getSearchList(keychains
);
387 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
389 char idUTF8
[MAXPATHLEN
];
391 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
392 idUTF8
[0] = (char)'\0';
393 CssmData
service(const_cast<char *>(idUTF8
), strlen(idUTF8
));
394 FourCharCode itemType
= 'iprf';
395 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
396 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
398 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
401 if (!cursor
->next(prefItem
))
402 return errSecItemNotFound
;
404 // get persistent certificate reference
405 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
406 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
407 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
409 // find certificate, given persistent reference data
410 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
411 SecKeychainItemRef certItemRef
= nil
;
412 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
413 prefItem
->freeContent(&itemAttrList
, NULL
);
419 // filter on valid issuers, if provided
424 // create identity reference, given certificate
425 status
= SecIdentityCreateWithCertificate(NULL
, (SecCertificateRef
)certItemRef
, identity
);
427 CFRelease(certItemRef
);
433 SecIdentityRef
SecIdentityCopyPreferred(CFStringRef name
, CFArrayRef keyUsage
, CFArrayRef validIssuers
)
435 // This function will look for a matching preference in the following order:
436 // - matches the name and the supplied key use
437 // - matches the name and the special 'ANY' key use
438 // - matches the name with no key usage constraint
440 SecIdentityRef identityRef
= NULL
;
441 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
442 OSStatus status
= SecIdentityCopyPreference(name
, keyUse
, validIssuers
, &identityRef
);
443 if (status
!= errSecSuccess
&& keyUse
!= CSSM_KEYUSE_ANY
)
444 status
= SecIdentityCopyPreference(name
, CSSM_KEYUSE_ANY
, validIssuers
, &identityRef
);
445 if (status
!= errSecSuccess
&& keyUse
!= 0)
446 status
= SecIdentityCopyPreference(name
, 0, validIssuers
, &identityRef
);
451 OSStatus
SecIdentityCopyPreference(
453 CSSM_KEYUSE keyUsage
,
454 CFArrayRef validIssuers
,
455 SecIdentityRef
*identity
)
457 // The original implementation of SecIdentityCopyPreference matches the exact string only.
458 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
459 // and this function is a wrapper which calls it, so that existing clients will get the
460 // extended behavior of server domain matching for items that specify URLs.
461 // (Note that behavior is unchanged if the specified name is not a URL.)
465 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
466 CFSTR("com.apple.security"),
467 kCFPreferencesCurrentUser
,
468 kCFPreferencesAnyHost
);
469 Boolean logging
= false;
470 if (val
&& CFGetTypeID(val
) == CFBooleanGetTypeID()) {
471 logging
= CFBooleanGetValue((CFBooleanRef
)val
);
475 OSStatus status
= errSecItemNotFound
;
476 CFArrayRef names
= _SecIdentityCopyPossiblePaths(name
);
481 CFIndex idx
, total
= CFArrayGetCount(names
);
482 for (idx
= 0; idx
< total
; idx
++) {
483 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, idx
);
485 status
= _SecIdentityCopyPreferenceMatchingName(aName
, keyUsage
, validIssuers
, identity
);
487 catch (...) { status
= errSecItemNotFound
; }
490 // get identity label
491 CFStringRef labelString
= NULL
;
492 if (!status
&& identity
&& *identity
) {
494 SecPointer
<Certificate
> cert(Identity::required(*identity
)->certificate());
495 cert
->inferLabel(false, &labelString
);
497 catch (...) { labelString
= NULL
; };
499 char *labelBuf
= NULL
;
500 CFIndex labelBufSize
= (labelString
) ? CFStringGetLength(labelString
) * 4 : 4;
501 labelBuf
= (char *)malloc(labelBufSize
);
502 if (!labelString
|| !CFStringGetCString(labelString
, labelBuf
, labelBufSize
, kCFStringEncodingUTF8
)) {
506 CFRelease(labelString
);
510 char *serviceBuf
= NULL
;
511 CFIndex serviceBufSize
= CFStringGetLength(aName
) * 4;
512 serviceBuf
= (char *)malloc(serviceBufSize
);
513 if (!CFStringGetCString(aName
, serviceBuf
, serviceBufSize
, kCFStringEncodingUTF8
)) {
517 syslog(LOG_NOTICE
, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf
, serviceBuf
);
518 if (!status
&& name
) {
519 char *nameBuf
= NULL
;
520 CFIndex nameBufSize
= CFStringGetLength(name
) * 4;
521 nameBuf
= (char *)malloc(nameBufSize
);
522 if (!CFStringGetCString(name
, nameBuf
, nameBufSize
, kCFStringEncodingUTF8
)) {
525 syslog(LOG_NOTICE
, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf
, nameBuf
);
533 if (status
== errSecSuccess
) {
534 break; // match found
544 OSStatus
SecIdentitySetPreference(
545 SecIdentityRef identity
,
547 CSSM_KEYUSE keyUsage
)
553 // treat NULL identity as a request to clear the preference
554 // (note: if keyUsage is 0, this clears all key usage prefs for name)
555 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL
, name
, keyUsage
);
560 SecPointer
<Certificate
> certificate(Identity::required(identity
)->certificate());
562 // determine the account attribute
564 // This attribute must be synthesized from certificate label + pref item type + key usage,
565 // as only the account and service attributes can make a generic keychain item unique.
566 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
567 // we can save a certificate preference if an identity preference already exists for the
568 // given service name, and vice-versa.
569 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
571 CFStringRef labelStr
= nil
;
572 certificate
->inferLabel(false, &labelStr
);
574 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
576 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
577 const char *templateStr
= "%s [key usage 0x%X]";
578 const int keyUsageMaxStrLen
= 8;
579 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
580 char accountUTF8
[accountUTF8Len
];
581 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
582 accountUTF8
[0] = (char)'\0';
584 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
585 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
586 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
589 // service attribute (name provided by the caller)
590 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(name
), kCFStringEncodingUTF8
) + 1;;
591 char serviceUTF8
[serviceUTF8Len
];
592 if (!CFStringGetCString(name
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
593 serviceUTF8
[0] = (char)'\0';
594 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
596 // look for existing identity preference item, in case this is an update
597 StorageManager::KeychainList keychains
;
598 globals().storageManager
.getSearchList(keychains
);
599 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
600 FourCharCode itemType
= 'iprf';
601 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
602 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
604 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
607 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
608 bool add
= (!cursor
->next(item
));
609 // at this point, we either have a new item to add or an existing item to update
611 // set item attribute values
612 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
613 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
614 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
615 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
616 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
618 // generic attribute (store persistent certificate reference)
619 CFDataRef pItemRef
= nil
;
620 certificate
->copyPersistentReference(pItemRef
);
622 MacOSError::throwMe(errSecInvalidItemRef
);
624 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
625 CFIndex dataLen
= CFDataGetLength(pItemRef
);
626 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
627 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
631 Keychain keychain
= nil
;
633 keychain
= globals().storageManager
.defaultKeychain();
634 if (!keychain
->exists())
635 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
638 keychain
= globals().storageManager
.defaultKeychainUI(item
);
644 catch (const MacOSError
&err
) {
645 if (err
.osStatus() != errSecDuplicateItem
)
646 throw; // if item already exists, fall through to update
655 SecIdentitySetPreferred(SecIdentityRef identity
, CFStringRef name
, CFArrayRef keyUsage
)
657 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
658 return SecIdentitySetPreference(identity
, name
, keyUse
);
662 SecIdentityFindPreferenceItem(
663 CFTypeRef keychainOrArray
,
664 CFStringRef idString
,
665 SecKeychainItemRef
*itemRef
)
669 StorageManager::KeychainList keychains
;
670 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
671 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
673 char idUTF8
[MAXPATHLEN
];
674 idUTF8
[0] = (char)'\0';
677 if (!CFStringGetCString(idString
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
678 idUTF8
[0] = (char)'\0';
680 size_t idUTF8Len
= strlen(idUTF8
);
682 MacOSError::throwMe(errSecParam
);
684 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
685 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
686 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
689 if (!cursor
->next(item
))
690 MacOSError::throwMe(errSecItemNotFound
);
693 *itemRef
=item
->handle();
699 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
700 CFTypeRef keychainOrArray
,
703 SecKeychainItemRef
*itemRef
)
707 StorageManager::KeychainList keychains
;
708 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
709 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
711 char idUTF8
[MAXPATHLEN
];
712 idUTF8
[0] = (char)'\0';
715 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
716 idUTF8
[0] = (char)'\0';
718 size_t idUTF8Len
= strlen(idUTF8
);
720 MacOSError::throwMe(errSecParam
);
722 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
723 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
724 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
726 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
729 if (!cursor
->next(item
))
730 MacOSError::throwMe(errSecItemNotFound
);
733 *itemRef
=item
->handle();
738 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
739 CFTypeRef keychainOrArray
,
743 // when a specific key usage is passed, we'll only match & delete that pref;
744 // when a key usage of 0 is passed, all matching prefs should be deleted.
745 // maxUsages represents the most matches there could theoretically be, so
746 // cut things off at that point if we're still finding items (if they can't
747 // be deleted for some reason, we'd never break out of the loop.)
749 OSStatus status
= errSecInternalError
;
750 SecKeychainItemRef item
= NULL
;
751 int count
= 0, maxUsages
= 12;
752 while (++count
<= maxUsages
&&
753 (status
= SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray
, name
, keyUsage
, &item
)) == errSecSuccess
) {
754 status
= SecKeychainItemDelete(item
);
759 // it's not an error if the item isn't found
760 return (status
== errSecItemNotFound
) ? errSecSuccess
: status
;
765 OSStatus
_SecIdentityAddPreferenceItemWithName(
766 SecKeychainRef keychainRef
,
767 SecIdentityRef identityRef
,
768 CFStringRef idString
,
769 SecKeychainItemRef
*itemRef
)
771 // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
772 // caller must handle exceptions
774 if (!identityRef
|| !idString
)
776 SecPointer
<Certificate
> cert(Identity::required(identityRef
)->certificate());
777 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
780 // determine the account attribute
782 // This attribute must be synthesized from certificate label + pref item type + key usage,
783 // as only the account and service attributes can make a generic keychain item unique.
784 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
785 // we can save a certificate preference if an identity preference already exists for the
786 // given service name, and vice-versa.
787 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
789 CFStringRef labelStr
= nil
;
790 cert
->inferLabel(false, &labelStr
);
792 return errSecDataTooLarge
; // data is "in a format which cannot be displayed"
794 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
795 const char *templateStr
= "%s [key usage 0x%X]";
796 const int keyUsageMaxStrLen
= 8;
797 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
798 char accountUTF8
[accountUTF8Len
];
799 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
800 accountUTF8
[0] = (char)'\0';
802 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
803 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
804 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
807 // service attribute (name provided by the caller)
808 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString
), kCFStringEncodingUTF8
) + 1;;
809 char serviceUTF8
[serviceUTF8Len
];
810 if (!CFStringGetCString(idString
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
811 serviceUTF8
[0] = (char)'\0';
812 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
814 // set item attribute values
815 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
816 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
817 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
818 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
819 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), keyUsage
);
821 // generic attribute (store persistent certificate reference)
822 CFDataRef pItemRef
= nil
;
823 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)cert
->handle(), &pItemRef
);
825 status
= errSecInvalidItemRef
;
828 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
829 CFIndex dataLen
= CFDataGetLength(pItemRef
);
830 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
831 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
834 Keychain keychain
= nil
;
836 keychain
= Keychain::optional(keychainRef
);
837 if (!keychain
->exists())
838 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
841 keychain
= globals().storageManager
.defaultKeychainUI(item
);
847 catch (const MacOSError
&err
) {
848 if (err
.osStatus() != errSecDuplicateItem
)
849 throw; // if item already exists, fall through to update
855 *itemRef
= item
->handle();
860 OSStatus
SecIdentityAddPreferenceItem(
861 SecKeychainRef keychainRef
,
862 SecIdentityRef identityRef
,
863 CFStringRef idString
,
864 SecKeychainItemRef
*itemRef
)
866 // The original implementation of SecIdentityAddPreferenceItem adds the exact string only.
867 // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above),
868 // and this function is a wrapper which calls it, so that existing clients will get the
869 // extended behavior of server domain matching for items that specify URLs.
870 // (Note that behavior is unchanged if the specified idString is not a URL.)
874 OSStatus status
= errSecInternalComponent
;
875 CFArrayRef names
= _SecIdentityCopyPossiblePaths(idString
);
880 CFIndex total
= CFArrayGetCount(names
);
882 // add item for name (first element in array)
883 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, 0);
885 status
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
887 catch (const MacOSError
&err
) { status
=err
.osStatus(); }
888 catch (const CommonError
&err
) { status
=SecKeychainErrFromOSStatus(err
.osStatus()); }
889 catch (const std::bad_alloc
&) { status
=errSecAllocate
; }
890 catch (...) { status
=errSecInternalComponent
; }
893 Boolean setDomainDefaultIdentity
= FALSE
;
894 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"),
895 CFSTR("com.apple.security.identities"),
896 kCFPreferencesCurrentUser
,
897 kCFPreferencesAnyHost
);
899 if (CFGetTypeID(val
) == CFBooleanGetTypeID())
900 setDomainDefaultIdentity
= CFBooleanGetValue((CFBooleanRef
)val
) ? TRUE
: FALSE
;
903 if (setDomainDefaultIdentity
) {
904 // add item for domain (second-to-last element in array, e.g. "*.apple.com")
905 OSStatus tmpStatus
= errSecSuccess
;
906 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, total
-2);
908 tmpStatus
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
910 catch (const MacOSError
&err
) { tmpStatus
=err
.osStatus(); }
911 catch (const CommonError
&err
) { tmpStatus
=SecKeychainErrFromOSStatus(err
.osStatus()); }
912 catch (const std::bad_alloc
&) { tmpStatus
=errSecAllocate
; }
913 catch (...) { tmpStatus
=errSecInternalComponent
; }
923 /* deprecated in 10.5 */
924 OSStatus
SecIdentityUpdatePreferenceItem(
925 SecKeychainItemRef itemRef
,
926 SecIdentityRef identityRef
)
930 if (!itemRef
|| !identityRef
)
931 MacOSError::throwMe(errSecParam
);
932 SecPointer
<Certificate
> certificate(Identity::required(identityRef
)->certificate());
933 Item prefItem
= ItemImpl::required(itemRef
);
935 // get the current key usage value for this item
938 SecKeychainAttribute attr
= { kSecScriptCodeItemAttr
, sizeof(sint32
), &keyUsage
};
940 prefItem
->getAttribute(attr
, &actLen
);
946 // set the account attribute
948 // This attribute must be synthesized from certificate label + pref item type + key usage,
949 // as only the account and service attributes can make a generic keychain item unique.
950 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
951 // we can save a certificate preference if an identity preference already exists for the
952 // given service name, and vice-versa.
953 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
955 CFStringRef labelStr
= nil
;
956 certificate
->inferLabel(false, &labelStr
);
958 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
960 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
961 const char *templateStr
= "%s [key usage 0x%X]";
962 const int keyUsageMaxStrLen
= 8;
963 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
964 char accountUTF8
[accountUTF8Len
];
965 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
966 accountUTF8
[0] = (char)'\0';
968 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
969 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
970 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
971 prefItem
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
974 // generic attribute (store persistent certificate reference)
975 CFDataRef pItemRef
= nil
;
976 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certificate
->handle(), &pItemRef
);
978 status
= errSecInvalidItemRef
;
980 MacOSError::throwMe(status
);
981 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
982 CFIndex dataLen
= CFDataGetLength(pItemRef
);
983 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
984 prefItem
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
992 OSStatus
SecIdentityCopyFromPreferenceItem(
993 SecKeychainItemRef itemRef
,
994 SecIdentityRef
*identityRef
)
998 if (!itemRef
|| !identityRef
)
999 MacOSError::throwMe(errSecParam
);
1000 Item prefItem
= ItemImpl::required(itemRef
);
1002 // get persistent certificate reference
1003 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
1004 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
1005 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
1007 // find certificate, given persistent reference data
1008 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
1009 SecKeychainItemRef certItemRef
= nil
;
1010 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
1011 prefItem
->freeContent(&itemAttrList
, NULL
);
1013 CFRelease(pItemRef
);
1017 // create identity reference, given certificate
1018 StorageManager::KeychainList keychains
;
1019 globals().storageManager
.optionalSearchList((CFTypeRef
)NULL
, keychains
);
1020 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
1021 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1022 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1024 CFRelease(certItemRef
);
1026 Required(identityRef
) = identity
->handle();
1032 * System Identity Support.
1035 /* plist domain (in /Library/Preferences) */
1036 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
1039 * Our plist is a dictionary whose entries have the following format:
1040 * key = domain name as CFString
1041 * value = public key hash as CFData
1044 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
1047 * All accesses to system identities and its associated plist are
1048 * protected by this lock.
1050 ModuleNexus
<Mutex
> systemIdentityLock
;
1052 OSStatus
SecIdentityCopySystemIdentity(
1054 SecIdentityRef
*idRef
,
1055 CFStringRef
*actualDomain
) /* optional */
1059 StLock
<Mutex
> _(systemIdentityLock());
1060 auto_ptr
<Dictionary
> identDict
;
1062 /* get top-level dictionary - if not present, we're done */
1063 Dictionary
* d
= Dictionary::CreateDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1066 return errSecNotAvailable
;
1071 /* see if there's an entry for specified domain */
1072 CFDataRef entryValue
= identDict
->getDataValue(domain
);
1073 if(entryValue
== NULL
) {
1074 /* try for default entry if we're not already looking for default */
1075 if(!CFEqual(domain
, kSecIdentityDomainDefault
)) {
1076 entryValue
= identDict
->getDataValue(kSecIdentityDomainDefault
);
1078 if(entryValue
== NULL
) {
1079 /* no default identity */
1080 MacOSError::throwMe(errSecItemNotFound
);
1083 /* remember that we're not fetching the requested domain */
1084 domain
= kSecIdentityDomainDefault
;
1087 /* open system keychain - error here is fatal */
1088 Keychain systemKc
= globals().storageManager
.make(SYSTEM_KEYCHAIN_PATH
, false);
1089 CFRef
<SecKeychainRef
> systemKcRef(systemKc
->handle());
1090 StorageManager::KeychainList keychains
;
1091 globals().storageManager
.optionalSearchList(systemKcRef
, keychains
);
1093 /* search for specified cert */
1094 SecKeychainAttributeList attrList
;
1095 SecKeychainAttribute attr
;
1096 attr
.tag
= kSecPublicKeyHashItemAttr
;
1097 attr
.length
= (UInt32
)CFDataGetLength(entryValue
);
1098 attr
.data
= (void *)CFDataGetBytePtr(entryValue
);
1100 attrList
.attr
= &attr
;
1102 KCCursor
cursor(keychains
, kSecCertificateItemClass
, &attrList
);
1104 if(!cursor
->next(certItem
)) {
1105 MacOSError::throwMe(errSecItemNotFound
);
1108 /* found the cert; try matching with key to cook up identity */
1109 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1110 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1112 Required(idRef
) = identity
->handle();
1114 *actualDomain
= domain
;
1115 CFRetain(*actualDomain
);
1121 OSStatus
SecIdentitySetSystemIdentity(
1123 SecIdentityRef idRef
)
1127 StLock
<Mutex
> _(systemIdentityLock());
1128 if(geteuid() != 0) {
1129 MacOSError::throwMe(errSecAuthFailed
);
1132 auto_ptr
<MutableDictionary
> identDict
;
1133 MutableDictionary
*d
= MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1141 /* nothing there, nothing to set - done */
1142 return errSecSuccess
;
1144 identDict
.reset(new MutableDictionary());
1148 /* Just delete the possible entry for this domain */
1149 identDict
->removeValue(domain
);
1152 /* obtain public key hash of identity's cert */
1153 SecPointer
<Identity
> identity(Identity::required(idRef
));
1154 SecPointer
<Certificate
> cert
= identity
->certificate();
1155 const CssmData
&pubKeyHash
= cert
->publicKeyHash();
1156 CFRef
<CFDataRef
> pubKeyHashData(CFDataCreate(NULL
, pubKeyHash
.Data
,
1157 pubKeyHash
.Length
));
1159 /* add/replace to dictionary */
1160 identDict
->setValue(domain
, pubKeyHashData
);
1164 if(!identDict
->writePlistToPrefs(IDENTITY_DOMAIN
, Dictionary::US_System
)) {
1165 MacOSError::throwMe(errSecIO
);
1171 const CFStringRef kSecIdentityDomainDefault
= CFSTR("com.apple.systemdefault");
1172 const CFStringRef kSecIdentityDomainKerberosKDC
= CFSTR("com.apple.kerberos.kdc");