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>
43 /* private function declarations */
45 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
46 CFTypeRef keychainOrArray
,
49 SecKeychainItemRef
*itemRef
);
51 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
52 CFTypeRef keychainOrArray
,
57 CSSM_KEYUSE
ConvertArrayToKeyUsage(CFArrayRef usage
)
60 CSSM_KEYUSE result
= (CSSM_KEYUSE
) 0;
62 if ((NULL
== usage
) || (0 == (count
= CFArrayGetCount(usage
))))
67 for (CFIndex iCnt
= 0; iCnt
< count
; iCnt
++)
69 CFStringRef keyUsageStr
= NULL
;
70 keyUsageStr
= (CFStringRef
)CFArrayGetValueAtIndex(usage
,iCnt
);
71 if (NULL
!= keyUsageStr
)
73 if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanEncrypt
, keyUsageStr
, 0))
75 result
|= CSSM_KEYUSE_ENCRYPT
;
77 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDecrypt
, keyUsageStr
, 0))
79 result
|= CSSM_KEYUSE_DECRYPT
;
81 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDerive
, keyUsageStr
, 0))
83 result
|= CSSM_KEYUSE_DERIVE
;
85 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanSign
, keyUsageStr
, 0))
87 result
|= CSSM_KEYUSE_SIGN
;
89 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanVerify
, keyUsageStr
, 0))
91 result
|= CSSM_KEYUSE_VERIFY
;
93 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanWrap
, keyUsageStr
, 0))
95 result
|= CSSM_KEYUSE_WRAP
;
97 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanUnwrap
, keyUsageStr
, 0))
99 result
|= CSSM_KEYUSE_UNWRAP
;
109 SecIdentityGetTypeID(void)
113 return gTypes().Identity
.typeID
;
115 END_SECAPI1(_kCFRuntimeNotATypeID
)
120 SecIdentityCopyCertificate(
121 SecIdentityRef identityRef
,
122 SecCertificateRef
*certificateRef
)
125 os_activity_t activity
= os_activity_create("SecIdentityCopyCertificate", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
126 os_activity_scope(activity
);
127 os_release(activity
);
129 if (!identityRef
|| !certificateRef
) {
132 CFTypeID itemType
= CFGetTypeID(identityRef
);
133 if (itemType
== SecIdentityGetTypeID()) {
134 SecPointer
<Certificate
> certificatePtr(Identity::required(identityRef
)->certificate());
135 Required(certificateRef
) = certificatePtr
->handle();
137 /* convert outgoing certificate item to a unified SecCertificateRef */
138 CssmData certData
= certificatePtr
->data();
139 CFDataRef data
= NULL
;
140 if (certData
.Data
&& certData
.Length
) {
141 data
= CFDataCreate(NULL
, certData
.Data
, certData
.Length
);
144 *certificateRef
= NULL
;
145 syslog(LOG_ERR
, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)",
146 (long)certData
.Length
, (uintptr_t)certData
.Data
);
147 return errSecInternal
;
149 SecCertificateRef tmpRef
= *certificateRef
;
150 *certificateRef
= SecCertificateCreateWithKeychainItem(NULL
, data
, tmpRef
);
158 else if (itemType
== SecCertificateGetTypeID()) {
160 // reconstituting a persistent identity reference could return the certificate
161 SecCertificateRef certificate
= (SecCertificateRef
)identityRef
;
163 /* convert outgoing certificate item to a unified SecCertificateRef, if needed */
164 if (SecCertificateIsItemImplInstance(certificate
)) {
165 *certificateRef
= SecCertificateCreateFromItemImplInstance(certificate
);
168 *certificateRef
= (SecCertificateRef
) CFRetain(certificate
);
170 return errSecSuccess
;
181 SecIdentityCopyPrivateKey(
182 SecIdentityRef identityRef
,
183 SecKeyRef
*privateKeyRef
)
186 os_activity_t activity
= os_activity_create("SecIdentityCopyPrivateKey", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
187 os_activity_scope(activity
);
188 os_release(activity
);
190 Required(privateKeyRef
) = (SecKeyRef
)CFRetain(Identity::required(identityRef
)->privateKeyRef());
196 SecIdentityCreateWithCertificate(
197 CFTypeRef keychainOrArray
,
198 SecCertificateRef certificate
,
199 SecIdentityRef
*identityRef
)
201 // This macro converts a new-style SecCertificateRef to an old-style ItemImpl
204 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
205 StorageManager::KeychainList keychains
;
206 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
207 SecPointer
<Identity
> identityPtr(new Identity(keychains
, certificatePtr
));
208 Required(identityRef
) = identityPtr
->handle();
215 CFAllocatorRef allocator
,
216 SecCertificateRef certificate
,
217 SecKeyRef privateKey
)
219 SecIdentityRef identityRef
= NULL
;
220 OSStatus __secapiresult
;
221 SecCertificateRef __itemImplRef
= NULL
;
222 if (SecCertificateIsItemImplInstance(certificate
)) {
223 __itemImplRef
=(SecCertificateRef
)CFRetain(certificate
);
225 if (!__itemImplRef
&& certificate
) {
226 __itemImplRef
=(SecCertificateRef
)SecCertificateCopyKeychainItem(certificate
);
228 if (!__itemImplRef
&& certificate
) {
229 __itemImplRef
=SecCertificateCreateItemImplInstance(certificate
);
230 (void)SecCertificateSetKeychainItem(certificate
,__itemImplRef
);
233 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
234 SecPointer
<Identity
> identityPtr(new Identity(privateKey
, certificatePtr
));
235 identityRef
= identityPtr
->handle();
237 __secapiresult
=errSecSuccess
;
239 catch (const MacOSError
&err
) { __secapiresult
=err
.osStatus(); }
240 catch (const CommonError
&err
) { __secapiresult
=SecKeychainErrFromOSStatus(err
.osStatus()); }
241 catch (const std::bad_alloc
&) { __secapiresult
=errSecAllocate
; }
242 catch (...) { __secapiresult
=errSecInternalComponent
; }
243 if (__itemImplRef
) { CFRelease(__itemImplRef
); }
249 SecIdentityRef identity1
,
250 SecIdentityRef identity2
,
251 CFOptionFlags compareOptions
)
253 if (!identity1
|| !identity2
)
255 if (identity1
== identity2
)
256 return kCFCompareEqualTo
;
257 else if (identity1
< identity2
)
258 return kCFCompareLessThan
;
260 return kCFCompareGreaterThan
;
264 SecPointer
<Identity
> id1(Identity::required(identity1
));
265 SecPointer
<Identity
> id2(Identity::required(identity2
));
268 return kCFCompareEqualTo
;
270 return kCFCompareLessThan
;
272 return kCFCompareGreaterThan
;
276 return kCFCompareGreaterThan
;
280 CFArrayRef
_SecIdentityCopyPossiblePaths(
283 // utility function to build and return an array of possible paths for the given name.
284 // if name is not a URL, this returns a single-element array.
285 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
287 CFMutableArrayRef names
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
291 CFIndex oldLength
= CFStringGetLength(name
);
292 CFArrayAppendValue(names
, name
);
294 CFURLRef url
= CFURLCreateWithString(NULL
, name
, NULL
);
296 if (CFURLCanBeDecomposed(url
)) {
297 // first, remove the query portion of this URL, if any
298 CFStringRef qs
= CFURLCopyQueryString(url
, NULL
);
300 CFMutableStringRef newName
= CFStringCreateMutableCopy(NULL
, oldLength
, name
);
302 CFIndex qsLength
= CFStringGetLength(qs
) + 1; // include the '?'
303 CFStringDelete(newName
, CFRangeMake(oldLength
-qsLength
, qsLength
));
305 url
= CFURLCreateWithString(NULL
, newName
, NULL
);
306 CFArraySetValueAtIndex(names
, 0, newName
);
311 // now add an entry for each level of the path
313 CFURLRef parent
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
);
315 CFStringRef parentURLString
= CFURLGetString(parent
);
316 if (parentURLString
) {
317 CFIndex newLength
= CFStringGetLength(parentURLString
);
318 // check that string length has decreased as expected; for file URLs,
319 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
320 if ((newLength
>= oldLength
) || (!CFStringHasPrefix(name
, parentURLString
))) {
325 oldLength
= newLength
;
326 CFArrayAppendValue(names
, parentURLString
);
337 // finally, add wildcard entries for each subdomain
338 url
= CFURLCreateWithString(NULL
, name
, NULL
);
340 if (CFURLCanBeDecomposed(url
)) {
341 CFStringRef netLocString
= CFURLCopyNetLocation(url
);
343 // first strip off port number, if present
344 CFStringRef tmpLocString
= netLocString
;
345 CFArrayRef hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(":"));
346 tmpLocString
= (CFStringRef
)CFRetain((CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, 0));
347 CFRelease(netLocString
);
348 CFRelease(hostnameArray
);
349 netLocString
= tmpLocString
;
350 // split remaining string into domain components
351 hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR("."));
352 CFIndex subdomainCount
= CFArrayGetCount(hostnameArray
);
354 while (++i
< subdomainCount
) {
356 CFMutableStringRef wildcardString
= CFStringCreateMutable(NULL
, 0);
357 if (wildcardString
) {
358 CFStringAppendCString(wildcardString
, "*", kCFStringEncodingUTF8
);
359 while (j
< subdomainCount
) {
360 CFStringRef domainString
= (CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, j
++);
361 if (CFStringGetLength(domainString
) > 0) {
362 CFStringAppendCString(wildcardString
, ".", kCFStringEncodingUTF8
);
363 CFStringAppend(wildcardString
, domainString
);
366 if (CFStringGetLength(wildcardString
) > 1) {
367 CFArrayAppendValue(names
, wildcardString
);
369 CFRelease(wildcardString
);
372 CFRelease(hostnameArray
);
373 CFRelease(netLocString
);
383 OSStatus
_SecIdentityCopyPreferenceMatchingName(
385 CSSM_KEYUSE keyUsage
,
386 CFArrayRef validIssuers
,
387 SecIdentityRef
*identity
)
389 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
390 // caller must handle exceptions
392 StorageManager::KeychainList keychains
;
393 globals().storageManager
.getSearchList(keychains
);
394 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
396 char idUTF8
[MAXPATHLEN
];
398 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
399 idUTF8
[0] = (char)'\0';
400 CssmData
service(const_cast<char *>(idUTF8
), strlen(idUTF8
));
401 FourCharCode itemType
= 'iprf';
402 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
403 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
405 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
408 if (!cursor
->next(prefItem
))
409 return errSecItemNotFound
;
411 // get persistent certificate reference
412 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
413 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
414 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
416 // find certificate, given persistent reference data
417 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
418 SecKeychainItemRef certItemRef
= nil
;
419 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
420 prefItem
->freeContent(&itemAttrList
, NULL
);
426 // filter on valid issuers, if provided
431 // create identity reference, given certificate
432 status
= SecIdentityCreateWithCertificate(NULL
, (SecCertificateRef
)certItemRef
, identity
);
434 CFRelease(certItemRef
);
440 SecIdentityRef
SecIdentityCopyPreferred(CFStringRef name
, CFArrayRef keyUsage
, CFArrayRef validIssuers
)
442 // This function will look for a matching preference in the following order:
443 // - matches the name and the supplied key use
444 // - matches the name and the special 'ANY' key use
445 // - matches the name with no key usage constraint
447 SecIdentityRef identityRef
= NULL
;
448 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
449 OSStatus status
= SecIdentityCopyPreference(name
, keyUse
, validIssuers
, &identityRef
);
450 if (status
!= errSecSuccess
&& keyUse
!= CSSM_KEYUSE_ANY
)
451 status
= SecIdentityCopyPreference(name
, CSSM_KEYUSE_ANY
, validIssuers
, &identityRef
);
452 if (status
!= errSecSuccess
&& keyUse
!= 0)
453 status
= SecIdentityCopyPreference(name
, 0, validIssuers
, &identityRef
);
458 OSStatus
SecIdentityCopyPreference(
460 CSSM_KEYUSE keyUsage
,
461 CFArrayRef validIssuers
,
462 SecIdentityRef
*identity
)
464 // The original implementation of SecIdentityCopyPreference matches the exact string only.
465 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
466 // and this function is a wrapper which calls it, so that existing clients will get the
467 // extended behavior of server domain matching for items that specify URLs.
468 // (Note that behavior is unchanged if the specified name is not a URL.)
471 os_activity_t activity
= os_activity_create("SecIdentityCopyPreference", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
472 os_activity_scope(activity
);
473 os_release(activity
);
475 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
476 CFSTR("com.apple.security"),
477 kCFPreferencesCurrentUser
,
478 kCFPreferencesAnyHost
);
479 Boolean logging
= false;
481 if (CFGetTypeID(val
) == CFBooleanGetTypeID()) {
482 logging
= CFBooleanGetValue((CFBooleanRef
)val
);
487 OSStatus status
= errSecItemNotFound
;
488 CFArrayRef names
= _SecIdentityCopyPossiblePaths(name
);
493 CFIndex idx
, total
= CFArrayGetCount(names
);
494 for (idx
= 0; idx
< total
; idx
++) {
495 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, idx
);
497 status
= _SecIdentityCopyPreferenceMatchingName(aName
, keyUsage
, validIssuers
, identity
);
499 catch (...) { status
= errSecItemNotFound
; }
502 // get identity label
503 CFStringRef labelString
= NULL
;
504 if (!status
&& identity
&& *identity
) {
506 SecPointer
<Certificate
> cert(Identity::required(*identity
)->certificate());
507 cert
->inferLabel(false, &labelString
);
509 catch (...) { labelString
= NULL
; };
511 char *labelBuf
= NULL
;
512 CFIndex labelBufSize
= (labelString
) ? CFStringGetLength(labelString
) * 4 : 4;
513 labelBuf
= (char *)malloc(labelBufSize
);
514 if (!labelString
|| !CFStringGetCString(labelString
, labelBuf
, labelBufSize
, kCFStringEncodingUTF8
)) {
518 CFRelease(labelString
);
522 char *serviceBuf
= NULL
;
523 CFIndex serviceBufSize
= CFStringGetLength(aName
) * 4;
524 serviceBuf
= (char *)malloc(serviceBufSize
);
525 if (!CFStringGetCString(aName
, serviceBuf
, serviceBufSize
, kCFStringEncodingUTF8
)) {
529 syslog(LOG_NOTICE
, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf
, serviceBuf
);
530 if (!status
&& name
) {
531 char *nameBuf
= NULL
;
532 CFIndex nameBufSize
= CFStringGetLength(name
) * 4;
533 nameBuf
= (char *)malloc(nameBufSize
);
534 if (!CFStringGetCString(name
, nameBuf
, nameBufSize
, kCFStringEncodingUTF8
)) {
537 syslog(LOG_NOTICE
, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf
, nameBuf
);
545 if (status
== errSecSuccess
) {
546 break; // match found
556 OSStatus
SecIdentitySetPreference(
557 SecIdentityRef identity
,
559 CSSM_KEYUSE keyUsage
)
565 // treat NULL identity as a request to clear the preference
566 // (note: if keyUsage is 0, this clears all key usage prefs for name)
567 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL
, name
, keyUsage
);
571 os_activity_t activity
= os_activity_create("SecIdentitySetPreference", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
572 os_activity_scope(activity
);
573 os_release(activity
);
575 CFRef
<SecCertificateRef
> certRef
;
576 OSStatus status
= SecIdentityCopyCertificate(identity
, certRef
.take());
577 if(status
!= errSecSuccess
) {
578 MacOSError::throwMe(status
);
581 // determine the account attribute
583 // This attribute must be synthesized from certificate label + pref item type + key usage,
584 // as only the account and service attributes can make a generic keychain item unique.
585 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
586 // we can save a certificate preference if an identity preference already exists for the
587 // given service name, and vice-versa.
588 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
590 CFStringRef labelStr
= nil
;
591 SecCertificateInferLabel(certRef
.get(), &labelStr
);
593 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
595 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
596 const char *templateStr
= "%s [key usage 0x%X]";
597 const int keyUsageMaxStrLen
= 8;
598 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
599 char *accountUTF8
= (char *)malloc(accountUTF8Len
);
601 MacOSError::throwMe(errSecMemoryError
);
603 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
604 accountUTF8
[0] = (char)'\0';
606 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
607 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
608 CssmDataContainer
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
612 // service attribute (name provided by the caller)
613 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(name
), kCFStringEncodingUTF8
) + 1;;
614 char *serviceUTF8
= (char *)malloc(serviceUTF8Len
);
616 MacOSError::throwMe(errSecMemoryError
);
618 if (!CFStringGetCString(name
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
619 serviceUTF8
[0] = (char)'\0';
620 CssmDataContainer
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
623 // look for existing identity preference item, in case this is an update
624 StorageManager::KeychainList keychains
;
625 globals().storageManager
.getSearchList(keychains
);
626 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
627 FourCharCode itemType
= 'iprf';
628 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
629 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
631 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
634 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
635 bool add
= (!cursor
->next(item
));
636 // at this point, we either have a new item to add or an existing item to update
638 // set item attribute values
639 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
640 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
641 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
642 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
643 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
645 // generic attribute (store persistent certificate reference)
646 CFDataRef pItemRef
= nil
;
647 SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certRef
.get(), &pItemRef
);
649 MacOSError::throwMe(errSecInvalidItemRef
);
651 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
652 CFIndex dataLen
= CFDataGetLength(pItemRef
);
653 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
654 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
658 Keychain keychain
= nil
;
660 keychain
= globals().storageManager
.defaultKeychain();
661 if (!keychain
->exists())
662 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
665 keychain
= globals().storageManager
.defaultKeychainUI(item
);
671 catch (const MacOSError
&err
) {
672 if (err
.osStatus() != errSecDuplicateItem
)
673 throw; // if item already exists, fall through to update
682 SecIdentitySetPreferred(SecIdentityRef identity
, CFStringRef name
, CFArrayRef keyUsage
)
684 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
685 return SecIdentitySetPreference(identity
, name
, keyUse
);
689 SecIdentityFindPreferenceItem(
690 CFTypeRef keychainOrArray
,
691 CFStringRef idString
,
692 SecKeychainItemRef
*itemRef
)
695 os_activity_t activity
= os_activity_create("SecIdentityFindPreferenceItem", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
696 os_activity_scope(activity
);
697 os_release(activity
);
699 StorageManager::KeychainList keychains
;
700 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
701 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
703 char idUTF8
[MAXPATHLEN
];
704 idUTF8
[0] = (char)'\0';
707 if (!CFStringGetCString(idString
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
708 idUTF8
[0] = (char)'\0';
710 size_t idUTF8Len
= strlen(idUTF8
);
712 MacOSError::throwMe(errSecParam
);
714 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
715 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
716 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
719 if (!cursor
->next(item
))
720 MacOSError::throwMe(errSecItemNotFound
);
723 *itemRef
=item
->handle();
729 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
730 CFTypeRef keychainOrArray
,
733 SecKeychainItemRef
*itemRef
)
736 os_activity_t activity
= os_activity_create("SecIdentityFindPreferenceItemWithNameAndKeyUsage", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
737 os_activity_scope(activity
);
738 os_release(activity
);
740 StorageManager::KeychainList keychains
;
741 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
742 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
744 char idUTF8
[MAXPATHLEN
];
745 idUTF8
[0] = (char)'\0';
748 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
749 idUTF8
[0] = (char)'\0';
751 size_t idUTF8Len
= strlen(idUTF8
);
753 MacOSError::throwMe(errSecParam
);
755 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
756 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
757 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
759 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
762 if (!cursor
->next(item
))
763 MacOSError::throwMe(errSecItemNotFound
);
766 *itemRef
=item
->handle();
771 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
772 CFTypeRef keychainOrArray
,
776 // when a specific key usage is passed, we'll only match & delete that pref;
777 // when a key usage of 0 is passed, all matching prefs should be deleted.
778 // maxUsages represents the most matches there could theoretically be, so
779 // cut things off at that point if we're still finding items (if they can't
780 // be deleted for some reason, we'd never break out of the loop.)
782 OSStatus status
= errSecInternalError
;
783 SecKeychainItemRef item
= NULL
;
784 int count
= 0, maxUsages
= 12;
785 while (++count
<= maxUsages
&&
786 (status
= SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray
, name
, keyUsage
, &item
)) == errSecSuccess
) {
787 status
= SecKeychainItemDelete(item
);
792 // it's not an error if the item isn't found
793 return (status
== errSecItemNotFound
) ? errSecSuccess
: status
;
798 OSStatus
_SecIdentityAddPreferenceItemWithName(
799 SecKeychainRef keychainRef
,
800 SecIdentityRef identityRef
,
801 CFStringRef idString
,
802 SecKeychainItemRef
*itemRef
)
804 // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
805 // caller must handle exceptions
807 if (!identityRef
|| !idString
)
809 SecPointer
<Certificate
> cert(Identity::required(identityRef
)->certificate());
810 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
813 // determine the account attribute
815 // This attribute must be synthesized from certificate label + pref item type + key usage,
816 // as only the account and service attributes can make a generic keychain item unique.
817 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
818 // we can save a certificate preference if an identity preference already exists for the
819 // given service name, and vice-versa.
820 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
822 CFStringRef labelStr
= nil
;
823 cert
->inferLabel(false, &labelStr
);
825 return errSecDataTooLarge
; // data is "in a format which cannot be displayed"
827 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
828 const char *templateStr
= "%s [key usage 0x%X]";
829 const int keyUsageMaxStrLen
= 8;
830 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
831 char *accountUTF8
= (char *)malloc(accountUTF8Len
);
833 MacOSError::throwMe(errSecMemoryError
);
835 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
836 accountUTF8
[0] = (char)'\0';
838 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
839 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
840 CssmDataContainer
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
844 // service attribute (name provided by the caller)
845 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString
), kCFStringEncodingUTF8
) + 1;;
846 char *serviceUTF8
= (char *)malloc(serviceUTF8Len
);
848 MacOSError::throwMe(errSecMemoryError
);
850 if (!CFStringGetCString(idString
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
851 serviceUTF8
[0] = (char)'\0';
852 CssmDataContainer
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
855 // set item attribute values
856 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
857 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
858 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
859 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
860 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), keyUsage
);
862 // generic attribute (store persistent certificate reference)
863 CFDataRef pItemRef
= nil
;
864 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)cert
->handle(), &pItemRef
);
866 status
= errSecInvalidItemRef
;
869 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
870 CFIndex dataLen
= CFDataGetLength(pItemRef
);
871 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
872 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
875 Keychain keychain
= nil
;
877 keychain
= Keychain::optional(keychainRef
);
878 if (!keychain
->exists())
879 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
882 keychain
= globals().storageManager
.defaultKeychainUI(item
);
888 catch (const MacOSError
&err
) {
889 if (err
.osStatus() != errSecDuplicateItem
)
890 throw; // if item already exists, fall through to update
896 *itemRef
= item
->handle();
901 OSStatus
SecIdentityAddPreferenceItem(
902 SecKeychainRef keychainRef
,
903 SecIdentityRef identityRef
,
904 CFStringRef idString
,
905 SecKeychainItemRef
*itemRef
)
907 // The original implementation of SecIdentityAddPreferenceItem adds the exact string only.
908 // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above),
909 // and this function is a wrapper which calls it, so that existing clients will get the
910 // extended behavior of server domain matching for items that specify URLs.
911 // (Note that behavior is unchanged if the specified idString is not a URL.)
914 os_activity_t activity
= os_activity_create("SecIdentityAddPreferenceItem", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
915 os_activity_scope(activity
);
916 os_release(activity
);
918 OSStatus status
= errSecInternalComponent
;
919 CFArrayRef names
= _SecIdentityCopyPossiblePaths(idString
);
924 CFIndex total
= CFArrayGetCount(names
);
926 // add item for name (first element in array)
927 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, 0);
929 status
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
931 catch (const MacOSError
&err
) { status
=err
.osStatus(); }
932 catch (const CommonError
&err
) { status
=SecKeychainErrFromOSStatus(err
.osStatus()); }
933 catch (const std::bad_alloc
&) { status
=errSecAllocate
; }
934 catch (...) { status
=errSecInternalComponent
; }
937 Boolean setDomainDefaultIdentity
= FALSE
;
938 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"),
939 CFSTR("com.apple.security.identities"),
940 kCFPreferencesCurrentUser
,
941 kCFPreferencesAnyHost
);
943 if (CFGetTypeID(val
) == CFBooleanGetTypeID())
944 setDomainDefaultIdentity
= CFBooleanGetValue((CFBooleanRef
)val
) ? TRUE
: FALSE
;
947 if (setDomainDefaultIdentity
) {
948 // add item for domain (second-to-last element in array, e.g. "*.apple.com")
949 OSStatus tmpStatus
= errSecSuccess
;
950 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, total
-2);
952 tmpStatus
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
954 catch (const MacOSError
&err
) { tmpStatus
=err
.osStatus(); }
955 catch (const CommonError
&err
) { tmpStatus
=SecKeychainErrFromOSStatus(err
.osStatus()); }
956 catch (const std::bad_alloc
&) { tmpStatus
=errSecAllocate
; }
957 catch (...) { tmpStatus
=errSecInternalComponent
; }
967 /* deprecated in 10.5 */
968 OSStatus
SecIdentityUpdatePreferenceItem(
969 SecKeychainItemRef itemRef
,
970 SecIdentityRef identityRef
)
973 os_activity_t activity
= os_activity_create("SecIdentityUpdatePreferenceItem", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
974 os_activity_scope(activity
);
975 os_release(activity
);
977 if (!itemRef
|| !identityRef
)
978 MacOSError::throwMe(errSecParam
);
979 SecPointer
<Certificate
> certificate(Identity::required(identityRef
)->certificate());
980 Item prefItem
= ItemImpl::required(itemRef
);
982 // get the current key usage value for this item
985 SecKeychainAttribute attr
= { kSecScriptCodeItemAttr
, sizeof(sint32
), &keyUsage
};
987 prefItem
->getAttribute(attr
, &actLen
);
993 // set the account attribute
995 // This attribute must be synthesized from certificate label + pref item type + key usage,
996 // as only the account and service attributes can make a generic keychain item unique.
997 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
998 // we can save a certificate preference if an identity preference already exists for the
999 // given service name, and vice-versa.
1000 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
1002 CFStringRef labelStr
= nil
;
1003 certificate
->inferLabel(false, &labelStr
);
1005 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
1007 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
1008 const char *templateStr
= "%s [key usage 0x%X]";
1009 const int keyUsageMaxStrLen
= 8;
1010 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
1011 char *accountUTF8
= (char *)malloc(accountUTF8Len
);
1013 MacOSError::throwMe(errSecMemoryError
);
1015 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
1016 accountUTF8
[0] = (char)'\0';
1018 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
1019 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
1020 CssmDataContainer
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
1021 prefItem
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
1023 CFRelease(labelStr
);
1025 // generic attribute (store persistent certificate reference)
1026 CFDataRef pItemRef
= nil
;
1027 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certificate
->handle(), &pItemRef
);
1029 status
= errSecInvalidItemRef
;
1031 MacOSError::throwMe(status
);
1032 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
1033 CFIndex dataLen
= CFDataGetLength(pItemRef
);
1034 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
1035 prefItem
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
1036 CFRelease(pItemRef
);
1043 OSStatus
SecIdentityCopyFromPreferenceItem(
1044 SecKeychainItemRef itemRef
,
1045 SecIdentityRef
*identityRef
)
1048 os_activity_t activity
= os_activity_create("SecIdentityCopyFromPreferenceItem", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
1049 os_activity_scope(activity
);
1050 os_release(activity
);
1052 if (!itemRef
|| !identityRef
)
1053 MacOSError::throwMe(errSecParam
);
1054 Item prefItem
= ItemImpl::required(itemRef
);
1056 // get persistent certificate reference
1057 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
1058 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
1059 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
1061 // find certificate, given persistent reference data
1062 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
1063 SecKeychainItemRef certItemRef
= nil
;
1064 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
1065 prefItem
->freeContent(&itemAttrList
, NULL
);
1067 CFRelease(pItemRef
);
1071 // create identity reference, given certificate
1072 StorageManager::KeychainList keychains
;
1073 globals().storageManager
.optionalSearchList((CFTypeRef
)NULL
, keychains
);
1074 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
1075 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1076 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1078 CFRelease(certItemRef
);
1080 Required(identityRef
) = identity
->handle();
1086 * System Identity Support.
1089 /* plist domain (in /Library/Preferences) */
1090 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
1093 * Our plist is a dictionary whose entries have the following format:
1094 * key = domain name as CFString
1095 * value = public key hash as CFData
1098 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
1101 * All accesses to system identities and its associated plist are
1102 * protected by this lock.
1104 ModuleNexus
<Mutex
> systemIdentityLock
;
1106 OSStatus
SecIdentityCopySystemIdentity(
1108 SecIdentityRef
*idRef
,
1109 CFStringRef
*actualDomain
) /* optional */
1112 os_activity_t activity
= os_activity_create("SecIdentityCopySystemIdentity", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
1113 os_activity_scope(activity
);
1114 os_release(activity
);
1116 StLock
<Mutex
> _(systemIdentityLock());
1117 auto_ptr
<Dictionary
> identDict
;
1119 /* get top-level dictionary - if not present, we're done */
1120 Dictionary
* d
= Dictionary::CreateDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1123 return errSecNotAvailable
;
1128 /* see if there's an entry for specified domain */
1129 CFDataRef entryValue
= identDict
->getDataValue(domain
);
1130 if(entryValue
== NULL
) {
1131 /* try for default entry if we're not already looking for default */
1132 if(!CFEqual(domain
, kSecIdentityDomainDefault
)) {
1133 entryValue
= identDict
->getDataValue(kSecIdentityDomainDefault
);
1135 if(entryValue
== NULL
) {
1136 /* no default identity */
1137 MacOSError::throwMe(errSecItemNotFound
);
1140 /* remember that we're not fetching the requested domain */
1141 domain
= kSecIdentityDomainDefault
;
1144 /* open system keychain - error here is fatal */
1145 Keychain systemKc
= globals().storageManager
.make(SYSTEM_KEYCHAIN_PATH
, false);
1146 CFRef
<SecKeychainRef
> systemKcRef(systemKc
->handle());
1147 StorageManager::KeychainList keychains
;
1148 globals().storageManager
.optionalSearchList(systemKcRef
, keychains
);
1150 /* search for specified cert */
1151 SecKeychainAttributeList attrList
;
1152 SecKeychainAttribute attr
;
1153 attr
.tag
= kSecPublicKeyHashItemAttr
;
1154 attr
.length
= (UInt32
)CFDataGetLength(entryValue
);
1155 attr
.data
= (void *)CFDataGetBytePtr(entryValue
);
1157 attrList
.attr
= &attr
;
1159 KCCursor
cursor(keychains
, kSecCertificateItemClass
, &attrList
);
1161 if(!cursor
->next(certItem
)) {
1162 MacOSError::throwMe(errSecItemNotFound
);
1165 /* found the cert; try matching with key to cook up identity */
1166 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1167 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1169 Required(idRef
) = identity
->handle();
1171 *actualDomain
= domain
;
1172 CFRetain(*actualDomain
);
1178 OSStatus
SecIdentitySetSystemIdentity(
1180 SecIdentityRef idRef
)
1183 os_activity_t activity
= os_activity_create("SecIdentitySetSystemIdentity", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_IF_NONE_PRESENT
);
1184 os_activity_scope(activity
);
1185 os_release(activity
);
1187 StLock
<Mutex
> _(systemIdentityLock());
1188 if(geteuid() != 0) {
1189 MacOSError::throwMe(errSecAuthFailed
);
1192 auto_ptr
<MutableDictionary
> identDict
;
1193 MutableDictionary
*d
= MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1201 /* nothing there, nothing to set - done */
1202 return errSecSuccess
;
1204 identDict
.reset(new MutableDictionary());
1208 /* Just delete the possible entry for this domain */
1209 identDict
->removeValue(domain
);
1212 /* obtain public key hash of identity's cert */
1213 SecPointer
<Identity
> identity(Identity::required(idRef
));
1214 SecPointer
<Certificate
> cert
= identity
->certificate();
1215 const CssmData
&pubKeyHash
= cert
->publicKeyHash();
1216 CFRef
<CFDataRef
> pubKeyHashData(CFDataCreate(NULL
, pubKeyHash
.Data
,
1217 pubKeyHash
.Length
));
1219 /* add/replace to dictionary */
1220 identDict
->setValue(domain
, pubKeyHashData
);
1224 if(!identDict
->writePlistToPrefs(IDENTITY_DOMAIN
, Dictionary::US_System
)) {
1225 MacOSError::throwMe(errSecIO
);
1231 const CFStringRef kSecIdentityDomainDefault
= CFSTR("com.apple.systemdefault");
1232 const CFStringRef kSecIdentityDomainKerberosKDC
= CFSTR("com.apple.kerberos.kdc");