2 * Copyright (c) 2002-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <Security/SecIdentity.h>
25 #include <Security/SecIdentityPriv.h>
26 #include <Security/SecKeychainItemPriv.h>
27 #include <Security/SecItem.h>
28 #include <Security/SecIdentityPriv.h>
29 #include <Security/SecCertificatePriv.h>
31 #include "SecBridge.h"
32 #include <security_keychain/Certificate.h>
33 #include <security_keychain/Identity.h>
34 #include <security_keychain/KeyItem.h>
35 #include <security_keychain/KCCursor.h>
36 #include <security_cdsa_utilities/Schema.h>
37 #include <security_utilities/simpleprefs.h>
38 #include <sys/param.h>
41 /* private function declarations */
43 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
44 CFTypeRef keychainOrArray
,
47 SecKeychainItemRef
*itemRef
);
49 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
50 CFTypeRef keychainOrArray
,
55 CSSM_KEYUSE
ConvertArrayToKeyUsage(CFArrayRef usage
)
58 CSSM_KEYUSE result
= (CSSM_KEYUSE
) 0;
60 if ((NULL
== usage
) || (0 == (count
= CFArrayGetCount(usage
))))
65 for (CFIndex iCnt
= 0; iCnt
< count
; iCnt
++)
67 CFStringRef keyUsageStr
= NULL
;
68 keyUsageStr
= (CFStringRef
)CFArrayGetValueAtIndex(usage
,iCnt
);
69 if (NULL
!= keyUsageStr
)
71 if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanEncrypt
, keyUsageStr
, 0))
73 result
|= CSSM_KEYUSE_ENCRYPT
;
75 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDecrypt
, keyUsageStr
, 0))
77 result
|= CSSM_KEYUSE_DECRYPT
;
79 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDerive
, keyUsageStr
, 0))
81 result
|= CSSM_KEYUSE_DERIVE
;
83 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanSign
, keyUsageStr
, 0))
85 result
|= CSSM_KEYUSE_SIGN
;
87 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanVerify
, keyUsageStr
, 0))
89 result
|= CSSM_KEYUSE_VERIFY
;
91 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanWrap
, keyUsageStr
, 0))
93 result
|= CSSM_KEYUSE_WRAP
;
95 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanUnwrap
, keyUsageStr
, 0))
97 result
|= CSSM_KEYUSE_UNWRAP
;
107 SecIdentityGetTypeID(void)
111 return gTypes().Identity
.typeID
;
113 END_SECAPI1(_kCFRuntimeNotATypeID
)
118 SecIdentityCopyCertificate(
119 SecIdentityRef identityRef
,
120 SecCertificateRef
*certificateRef
)
124 if (!identityRef
|| !certificateRef
) {
127 CFTypeID itemType
= CFGetTypeID(identityRef
);
128 if (itemType
== SecIdentityGetTypeID()) {
129 SecPointer
<Certificate
> certificatePtr(Identity::required(identityRef
)->certificate());
130 Required(certificateRef
) = certificatePtr
->handle();
132 /* convert outgoing certificate item to a unified SecCertificateRef */
133 CssmData certData
= certificatePtr
->data();
134 CFDataRef data
= NULL
;
135 if (certData
.Data
&& certData
.Length
) {
136 data
= CFDataCreate(NULL
, certData
.Data
, certData
.Length
);
139 *certificateRef
= NULL
;
140 syslog(LOG_ERR
, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)",
141 (long)certData
.Length
, (uintptr_t)certData
.Data
);
142 return errSecInternal
;
144 SecCertificateRef tmpRef
= *certificateRef
;
145 *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 SecPointer
<Certificate
> certificatePtr(Certificate::required(certificate
));
160 Required(certificateRef
) = certificatePtr
->handle();
162 /* convert outgoing certificate item to a unified SecCertificateRef, if needed */
163 if (SecCertificateIsItemImplInstance(certificate
)) {
164 *certificateRef
= SecCertificateCreateFromItemImplInstance(certificate
);
167 *certificateRef
= (SecCertificateRef
) CFRetain(certificate
);
170 return errSecSuccess
;
181 SecIdentityCopyPrivateKey(
182 SecIdentityRef identityRef
,
183 SecKeyRef
*privateKeyRef
)
187 Required(privateKeyRef
) = (SecKeyRef
)CFRetain(Identity::required(identityRef
)->privateKeyRef());
193 SecIdentityCreateWithCertificate(
194 CFTypeRef keychainOrArray
,
195 SecCertificateRef certificate
,
196 SecIdentityRef
*identityRef
)
198 // This macro converts a new-style SecCertificateRef to an old-style ItemImpl
201 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
202 StorageManager::KeychainList keychains
;
203 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
204 SecPointer
<Identity
> identityPtr(new Identity(keychains
, certificatePtr
));
205 Required(identityRef
) = identityPtr
->handle();
212 CFAllocatorRef allocator
,
213 SecCertificateRef certificate
,
214 SecKeyRef privateKey
)
216 SecIdentityRef identityRef
= NULL
;
217 OSStatus __secapiresult
;
218 SecCertificateRef __itemImplRef
= NULL
;
219 if (SecCertificateIsItemImplInstance(certificate
)) {
220 __itemImplRef
=(SecCertificateRef
)CFRetain(certificate
);
222 if (!__itemImplRef
&& certificate
) {
223 __itemImplRef
=(SecCertificateRef
)SecCertificateCopyKeychainItem(certificate
);
225 if (!__itemImplRef
&& certificate
) {
226 __itemImplRef
=SecCertificateCreateItemImplInstance(certificate
);
227 (void)SecCertificateSetKeychainItem(certificate
,__itemImplRef
);
230 SecPointer
<Certificate
> certificatePtr(Certificate::required(__itemImplRef
));
231 SecPointer
<Identity
> identityPtr(new Identity(privateKey
, certificatePtr
));
232 identityRef
= identityPtr
->handle();
234 __secapiresult
=errSecSuccess
;
236 catch (const MacOSError
&err
) { __secapiresult
=err
.osStatus(); }
237 catch (const CommonError
&err
) { __secapiresult
=SecKeychainErrFromOSStatus(err
.osStatus()); }
238 catch (const std::bad_alloc
&) { __secapiresult
=errSecAllocate
; }
239 catch (...) { __secapiresult
=errSecInternalComponent
; }
240 if (__itemImplRef
) { CFRelease(__itemImplRef
); }
246 SecIdentityRef identity1
,
247 SecIdentityRef identity2
,
248 CFOptionFlags compareOptions
)
250 if (!identity1
|| !identity2
)
252 if (identity1
== identity2
)
253 return kCFCompareEqualTo
;
254 else if (identity1
< identity2
)
255 return kCFCompareLessThan
;
257 return kCFCompareGreaterThan
;
262 SecPointer
<Identity
> id1(Identity::required(identity1
));
263 SecPointer
<Identity
> id2(Identity::required(identity2
));
266 return kCFCompareEqualTo
;
268 return kCFCompareLessThan
;
270 return kCFCompareGreaterThan
;
272 END_SECAPI1(kCFCompareGreaterThan
);
276 CFArrayRef
_SecIdentityCopyPossiblePaths(
279 // utility function to build and return an array of possible paths for the given name.
280 // if name is not a URL, this returns a single-element array.
281 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
283 CFMutableArrayRef names
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
287 CFIndex oldLength
= CFStringGetLength(name
);
288 CFArrayAppendValue(names
, name
);
290 CFURLRef url
= CFURLCreateWithString(NULL
, name
, NULL
);
292 if (CFURLCanBeDecomposed(url
)) {
293 // first, remove the query portion of this URL, if any
294 CFStringRef qs
= CFURLCopyQueryString(url
, NULL
);
296 CFMutableStringRef newName
= CFStringCreateMutableCopy(NULL
, oldLength
, name
);
298 CFIndex qsLength
= CFStringGetLength(qs
) + 1; // include the '?'
299 CFStringDelete(newName
, CFRangeMake(oldLength
-qsLength
, qsLength
));
301 url
= CFURLCreateWithString(NULL
, newName
, NULL
);
302 CFArraySetValueAtIndex(names
, 0, newName
);
307 // now add an entry for each level of the path
309 CFURLRef parent
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
);
311 CFStringRef parentURLString
= CFURLGetString(parent
);
312 if (parentURLString
) {
313 CFIndex newLength
= CFStringGetLength(parentURLString
);
314 // check that string length has decreased as expected; for file URLs,
315 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
316 if ((newLength
>= oldLength
) || (!CFStringHasPrefix(name
, parentURLString
))) {
321 oldLength
= newLength
;
322 CFArrayAppendValue(names
, parentURLString
);
333 // finally, add wildcard entries for each subdomain
334 url
= CFURLCreateWithString(NULL
, name
, NULL
);
336 if (CFURLCanBeDecomposed(url
)) {
337 CFStringRef netLocString
= CFURLCopyNetLocation(url
);
339 // first strip off port number, if present
340 CFStringRef tmpLocString
= netLocString
;
341 CFArrayRef hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(":"));
342 tmpLocString
= (CFStringRef
)CFRetain((CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, 0));
343 CFRelease(netLocString
);
344 CFRelease(hostnameArray
);
345 netLocString
= tmpLocString
;
346 // split remaining string into domain components
347 hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR("."));
348 CFIndex subdomainCount
= CFArrayGetCount(hostnameArray
);
350 while (++i
< subdomainCount
) {
352 CFMutableStringRef wildcardString
= CFStringCreateMutable(NULL
, 0);
353 if (wildcardString
) {
354 CFStringAppendCString(wildcardString
, "*", kCFStringEncodingUTF8
);
355 while (j
< subdomainCount
) {
356 CFStringRef domainString
= (CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, j
++);
357 if (CFStringGetLength(domainString
) > 0) {
358 CFStringAppendCString(wildcardString
, ".", kCFStringEncodingUTF8
);
359 CFStringAppend(wildcardString
, domainString
);
362 if (CFStringGetLength(wildcardString
) > 1) {
363 CFArrayAppendValue(names
, wildcardString
);
365 CFRelease(wildcardString
);
368 CFRelease(hostnameArray
);
369 CFRelease(netLocString
);
379 OSStatus
_SecIdentityCopyPreferenceMatchingName(
381 CSSM_KEYUSE keyUsage
,
382 CFArrayRef validIssuers
,
383 SecIdentityRef
*identity
)
385 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
386 // caller must handle exceptions
388 StorageManager::KeychainList keychains
;
389 globals().storageManager
.getSearchList(keychains
);
390 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
392 char idUTF8
[MAXPATHLEN
];
394 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
395 idUTF8
[0] = (char)'\0';
396 CssmData
service(const_cast<char *>(idUTF8
), strlen(idUTF8
));
397 FourCharCode itemType
= 'iprf';
398 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
399 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
401 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
404 if (!cursor
->next(prefItem
))
405 return errSecItemNotFound
;
407 // get persistent certificate reference
408 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
409 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
410 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
412 // find certificate, given persistent reference data
413 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
414 SecKeychainItemRef certItemRef
= nil
;
415 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
416 prefItem
->freeContent(&itemAttrList
, NULL
);
422 // filter on valid issuers, if provided
427 // create identity reference, given certificate
429 status
= SecIdentityCreateWithCertificate(NULL
, (SecCertificateRef
)certItemRef
, identity
);
432 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
433 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
434 SecPointer
<Identity
> identity_ptr(new Identity(keychains
, certificate
));
436 CFRelease(certItemRef
); // retained by identity
438 Required(identity
) = identity_ptr
->handle();
440 catch (const MacOSError
&err
) { status
=err
.osStatus(); }
441 catch (const CommonError
&err
) { status
=SecKeychainErrFromOSStatus(err
.osStatus()); }
442 catch (const std::bad_alloc
&) { status
=errSecAllocate
; }
443 catch (...) { status
=errSecInvalidItemRef
; }
449 SecIdentityRef
SecIdentityCopyPreferred(CFStringRef name
, CFArrayRef keyUsage
, CFArrayRef validIssuers
)
451 // This function will look for a matching preference in the following order:
452 // - matches the name and the supplied key use
453 // - matches the name and the special 'ANY' key use
454 // - matches the name with no key usage constraint
456 SecIdentityRef identityRef
= NULL
;
457 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
458 OSStatus status
= SecIdentityCopyPreference(name
, keyUse
, validIssuers
, &identityRef
);
459 if (status
!= errSecSuccess
&& keyUse
!= CSSM_KEYUSE_ANY
)
460 status
= SecIdentityCopyPreference(name
, CSSM_KEYUSE_ANY
, validIssuers
, &identityRef
);
461 if (status
!= errSecSuccess
&& keyUse
!= 0)
462 status
= SecIdentityCopyPreference(name
, 0, validIssuers
, &identityRef
);
467 OSStatus
SecIdentityCopyPreference(
469 CSSM_KEYUSE keyUsage
,
470 CFArrayRef validIssuers
,
471 SecIdentityRef
*identity
)
473 // The original implementation of SecIdentityCopyPreference matches the exact string only.
474 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
475 // and this function is a wrapper which calls it, so that existing clients will get the
476 // extended behavior of server domain matching for items that specify URLs.
477 // (Note that behavior is unchanged if the specified name is not a URL.)
481 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
482 CFSTR("com.apple.security"),
483 kCFPreferencesCurrentUser
,
484 kCFPreferencesAnyHost
);
485 Boolean logging
= false;
486 if (val
&& CFGetTypeID(val
) == CFBooleanGetTypeID()) {
487 logging
= CFBooleanGetValue((CFBooleanRef
)val
);
491 OSStatus status
= errSecItemNotFound
;
492 CFArrayRef names
= _SecIdentityCopyPossiblePaths(name
);
497 CFIndex idx
, total
= CFArrayGetCount(names
);
498 for (idx
= 0; idx
< total
; idx
++) {
499 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, idx
);
501 status
= _SecIdentityCopyPreferenceMatchingName(aName
, keyUsage
, validIssuers
, identity
);
503 catch (...) { status
= errSecItemNotFound
; }
506 // get identity label
507 CFStringRef labelString
= NULL
;
508 if (!status
&& identity
&& *identity
) {
510 SecPointer
<Certificate
> cert(Identity::required(*identity
)->certificate());
511 cert
->inferLabel(false, &labelString
);
513 catch (...) { labelString
= NULL
; };
515 char *labelBuf
= NULL
;
516 CFIndex labelBufSize
= (labelString
) ? CFStringGetLength(labelString
) * 4 : 4;
517 labelBuf
= (char *)malloc(labelBufSize
);
518 if (!labelString
|| !CFStringGetCString(labelString
, labelBuf
, labelBufSize
, kCFStringEncodingUTF8
)) {
522 CFRelease(labelString
);
526 char *serviceBuf
= NULL
;
527 CFIndex serviceBufSize
= CFStringGetLength(aName
) * 4;
528 serviceBuf
= (char *)malloc(serviceBufSize
);
529 if (!CFStringGetCString(aName
, serviceBuf
, serviceBufSize
, kCFStringEncodingUTF8
)) {
533 syslog(LOG_NOTICE
, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf
, serviceBuf
);
534 if (!status
&& name
) {
535 char *nameBuf
= NULL
;
536 CFIndex nameBufSize
= CFStringGetLength(name
) * 4;
537 nameBuf
= (char *)malloc(nameBufSize
);
538 if (!CFStringGetCString(name
, nameBuf
, nameBufSize
, kCFStringEncodingUTF8
)) {
541 syslog(LOG_NOTICE
, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf
, nameBuf
);
549 if (status
== errSecSuccess
) {
550 break; // match found
560 OSStatus
SecIdentitySetPreference(
561 SecIdentityRef identity
,
563 CSSM_KEYUSE keyUsage
)
569 // treat NULL identity as a request to clear the preference
570 // (note: if keyUsage is 0, this clears all key usage prefs for name)
571 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL
, name
, keyUsage
);
576 SecPointer
<Certificate
> certificate(Identity::required(identity
)->certificate());
578 // determine the account attribute
580 // This attribute must be synthesized from certificate label + pref item type + key usage,
581 // as only the account and service attributes can make a generic keychain item unique.
582 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
583 // we can save a certificate preference if an identity preference already exists for the
584 // given service name, and vice-versa.
585 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
587 CFStringRef labelStr
= nil
;
588 certificate
->inferLabel(false, &labelStr
);
590 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
592 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
593 const char *templateStr
= "%s [key usage 0x%X]";
594 const int keyUsageMaxStrLen
= 8;
595 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
596 char accountUTF8
[accountUTF8Len
];
597 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
598 accountUTF8
[0] = (char)'\0';
600 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
601 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
602 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
605 // service attribute (name provided by the caller)
606 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(name
), kCFStringEncodingUTF8
) + 1;;
607 char serviceUTF8
[serviceUTF8Len
];
608 if (!CFStringGetCString(name
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
609 serviceUTF8
[0] = (char)'\0';
610 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
612 // look for existing identity preference item, in case this is an update
613 StorageManager::KeychainList keychains
;
614 globals().storageManager
.getSearchList(keychains
);
615 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
616 FourCharCode itemType
= 'iprf';
617 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
618 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
620 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
623 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
624 bool add
= (!cursor
->next(item
));
625 // at this point, we either have a new item to add or an existing item to update
627 // set item attribute values
628 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
629 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
630 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
631 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
632 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
634 // generic attribute (store persistent certificate reference)
635 CFDataRef pItemRef
= nil
;
636 certificate
->copyPersistentReference(pItemRef
);
638 MacOSError::throwMe(errSecInvalidItemRef
);
640 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
641 CFIndex dataLen
= CFDataGetLength(pItemRef
);
642 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
643 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
647 Keychain keychain
= nil
;
649 keychain
= globals().storageManager
.defaultKeychain();
650 if (!keychain
->exists())
651 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
654 keychain
= globals().storageManager
.defaultKeychainUI(item
);
660 catch (const MacOSError
&err
) {
661 if (err
.osStatus() != errSecDuplicateItem
)
662 throw; // if item already exists, fall through to update
671 SecIdentitySetPreferred(SecIdentityRef identity
, CFStringRef name
, CFArrayRef keyUsage
)
673 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
674 return SecIdentitySetPreference(identity
, name
, keyUse
);
678 SecIdentityFindPreferenceItem(
679 CFTypeRef keychainOrArray
,
680 CFStringRef idString
,
681 SecKeychainItemRef
*itemRef
)
685 StorageManager::KeychainList keychains
;
686 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
687 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
689 char idUTF8
[MAXPATHLEN
];
690 idUTF8
[0] = (char)'\0';
693 if (!CFStringGetCString(idString
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
694 idUTF8
[0] = (char)'\0';
696 size_t idUTF8Len
= strlen(idUTF8
);
698 MacOSError::throwMe(errSecParam
);
700 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
701 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
702 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
705 if (!cursor
->next(item
))
706 MacOSError::throwMe(errSecItemNotFound
);
709 *itemRef
=item
->handle();
715 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
716 CFTypeRef keychainOrArray
,
719 SecKeychainItemRef
*itemRef
)
723 StorageManager::KeychainList keychains
;
724 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
725 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
727 char idUTF8
[MAXPATHLEN
];
728 idUTF8
[0] = (char)'\0';
731 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
732 idUTF8
[0] = (char)'\0';
734 size_t idUTF8Len
= strlen(idUTF8
);
736 MacOSError::throwMe(errSecParam
);
738 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
739 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
740 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
742 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
745 if (!cursor
->next(item
))
746 MacOSError::throwMe(errSecItemNotFound
);
749 *itemRef
=item
->handle();
754 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
755 CFTypeRef keychainOrArray
,
759 // when a specific key usage is passed, we'll only match & delete that pref;
760 // when a key usage of 0 is passed, all matching prefs should be deleted.
761 // maxUsages represents the most matches there could theoretically be, so
762 // cut things off at that point if we're still finding items (if they can't
763 // be deleted for some reason, we'd never break out of the loop.)
766 SecKeychainItemRef item
= NULL
;
767 int count
= 0, maxUsages
= 12;
768 while (++count
<= maxUsages
&&
769 (status
= SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray
, name
, keyUsage
, &item
)) == errSecSuccess
) {
770 status
= SecKeychainItemDelete(item
);
775 // it's not an error if the item isn't found
776 return (status
== errSecItemNotFound
) ? errSecSuccess
: status
;
781 OSStatus
_SecIdentityAddPreferenceItemWithName(
782 SecKeychainRef keychainRef
,
783 SecIdentityRef identityRef
,
784 CFStringRef idString
,
785 SecKeychainItemRef
*itemRef
)
787 // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
788 // caller must handle exceptions
790 if (!identityRef
|| !idString
)
792 SecPointer
<Certificate
> cert(Identity::required(identityRef
)->certificate());
793 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
796 // determine the account attribute
798 // This attribute must be synthesized from certificate label + pref item type + key usage,
799 // as only the account and service attributes can make a generic keychain item unique.
800 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
801 // we can save a certificate preference if an identity preference already exists for the
802 // given service name, and vice-versa.
803 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
805 CFStringRef labelStr
= nil
;
806 cert
->inferLabel(false, &labelStr
);
808 return errSecDataTooLarge
; // data is "in a format which cannot be displayed"
810 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
811 const char *templateStr
= "%s [key usage 0x%X]";
812 const int keyUsageMaxStrLen
= 8;
813 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
814 char accountUTF8
[accountUTF8Len
];
815 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
816 accountUTF8
[0] = (char)'\0';
818 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
819 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
820 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
823 // service attribute (name provided by the caller)
824 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString
), kCFStringEncodingUTF8
) + 1;;
825 char serviceUTF8
[serviceUTF8Len
];
826 if (!CFStringGetCString(idString
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
827 serviceUTF8
[0] = (char)'\0';
828 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
830 // set item attribute values
831 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
832 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
833 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
834 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
835 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), keyUsage
);
837 // generic attribute (store persistent certificate reference)
838 CFDataRef pItemRef
= nil
;
839 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)cert
->handle(), &pItemRef
);
841 status
= errSecInvalidItemRef
;
844 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
845 CFIndex dataLen
= CFDataGetLength(pItemRef
);
846 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
847 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
850 Keychain keychain
= nil
;
852 keychain
= Keychain::optional(keychainRef
);
853 if (!keychain
->exists())
854 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
857 keychain
= globals().storageManager
.defaultKeychainUI(item
);
863 catch (const MacOSError
&err
) {
864 if (err
.osStatus() != errSecDuplicateItem
)
865 throw; // if item already exists, fall through to update
871 *itemRef
= item
->handle();
876 OSStatus
SecIdentityAddPreferenceItem(
877 SecKeychainRef keychainRef
,
878 SecIdentityRef identityRef
,
879 CFStringRef idString
,
880 SecKeychainItemRef
*itemRef
)
882 // The original implementation of SecIdentityAddPreferenceItem adds the exact string only.
883 // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above),
884 // and this function is a wrapper which calls it, so that existing clients will get the
885 // extended behavior of server domain matching for items that specify URLs.
886 // (Note that behavior is unchanged if the specified idString is not a URL.)
890 OSStatus status
= errSecInternalComponent
;
891 CFArrayRef names
= _SecIdentityCopyPossiblePaths(idString
);
896 CFIndex total
= CFArrayGetCount(names
);
898 // add item for name (first element in array)
899 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, 0);
901 status
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
903 catch (const MacOSError
&err
) { status
=err
.osStatus(); }
904 catch (const CommonError
&err
) { status
=SecKeychainErrFromOSStatus(err
.osStatus()); }
905 catch (const std::bad_alloc
&) { status
=errSecAllocate
; }
906 catch (...) { status
=errSecInternalComponent
; }
909 Boolean setDomainDefaultIdentity
= FALSE
;
910 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"),
911 CFSTR("com.apple.security.identities"),
912 kCFPreferencesCurrentUser
,
913 kCFPreferencesAnyHost
);
915 if (CFGetTypeID(val
) == CFBooleanGetTypeID())
916 setDomainDefaultIdentity
= CFBooleanGetValue((CFBooleanRef
)val
) ? TRUE
: FALSE
;
919 if (setDomainDefaultIdentity
) {
920 // add item for domain (second-to-last element in array, e.g. "*.apple.com")
921 OSStatus tmpStatus
= errSecSuccess
;
922 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, total
-2);
924 tmpStatus
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
926 catch (const MacOSError
&err
) { tmpStatus
=err
.osStatus(); }
927 catch (const CommonError
&err
) { tmpStatus
=SecKeychainErrFromOSStatus(err
.osStatus()); }
928 catch (const std::bad_alloc
&) { tmpStatus
=errSecAllocate
; }
929 catch (...) { tmpStatus
=errSecInternalComponent
; }
939 /* deprecated in 10.5 */
940 OSStatus
SecIdentityUpdatePreferenceItem(
941 SecKeychainItemRef itemRef
,
942 SecIdentityRef identityRef
)
946 if (!itemRef
|| !identityRef
)
947 MacOSError::throwMe(errSecParam
);
948 SecPointer
<Certificate
> certificate(Identity::required(identityRef
)->certificate());
949 Item prefItem
= ItemImpl::required(itemRef
);
951 // get the current key usage value for this item
954 SecKeychainAttribute attr
= { kSecScriptCodeItemAttr
, sizeof(sint32
), &keyUsage
};
956 prefItem
->getAttribute(attr
, &actLen
);
962 // set the account attribute
964 // This attribute must be synthesized from certificate label + pref item type + key usage,
965 // as only the account and service attributes can make a generic keychain item unique.
966 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
967 // we can save a certificate preference if an identity preference already exists for the
968 // given service name, and vice-versa.
969 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
971 CFStringRef labelStr
= nil
;
972 certificate
->inferLabel(false, &labelStr
);
974 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
976 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
977 const char *templateStr
= "%s [key usage 0x%X]";
978 const int keyUsageMaxStrLen
= 8;
979 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
980 char accountUTF8
[accountUTF8Len
];
981 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
982 accountUTF8
[0] = (char)'\0';
984 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
985 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
986 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
987 prefItem
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
990 // generic attribute (store persistent certificate reference)
991 CFDataRef pItemRef
= nil
;
992 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certificate
->handle(), &pItemRef
);
994 status
= errSecInvalidItemRef
;
996 MacOSError::throwMe(status
);
997 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
998 CFIndex dataLen
= CFDataGetLength(pItemRef
);
999 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
1000 prefItem
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
1001 CFRelease(pItemRef
);
1008 OSStatus
SecIdentityCopyFromPreferenceItem(
1009 SecKeychainItemRef itemRef
,
1010 SecIdentityRef
*identityRef
)
1014 if (!itemRef
|| !identityRef
)
1015 MacOSError::throwMe(errSecParam
);
1016 Item prefItem
= ItemImpl::required(itemRef
);
1018 // get persistent certificate reference
1019 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
1020 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
1021 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
1023 // find certificate, given persistent reference data
1024 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
1025 SecKeychainItemRef certItemRef
= nil
;
1026 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
1027 prefItem
->freeContent(&itemAttrList
, NULL
);
1029 CFRelease(pItemRef
);
1033 // create identity reference, given certificate
1034 StorageManager::KeychainList keychains
;
1035 globals().storageManager
.optionalSearchList((CFTypeRef
)NULL
, keychains
);
1036 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
1037 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1038 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1040 CFRelease(certItemRef
);
1042 Required(identityRef
) = identity
->handle();
1048 * System Identity Support.
1051 /* plist domain (in /Library/Preferences) */
1052 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
1055 * Our plist is a dictionary whose entries have the following format:
1056 * key = domain name as CFString
1057 * value = public key hash as CFData
1060 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
1063 * All accesses to system identities and its associated plist are
1064 * protected by this lock.
1066 ModuleNexus
<Mutex
> systemIdentityLock
;
1068 OSStatus
SecIdentityCopySystemIdentity(
1070 SecIdentityRef
*idRef
,
1071 CFStringRef
*actualDomain
) /* optional */
1075 StLock
<Mutex
> _(systemIdentityLock());
1076 auto_ptr
<Dictionary
> identDict
;
1078 /* get top-level dictionary - if not present, we're done */
1079 Dictionary
* d
= Dictionary::CreateDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1082 return errSecNotAvailable
;
1087 /* see if there's an entry for specified domain */
1088 CFDataRef entryValue
= identDict
->getDataValue(domain
);
1089 if(entryValue
== NULL
) {
1090 /* try for default entry if we're not already looking for default */
1091 if(!CFEqual(domain
, kSecIdentityDomainDefault
)) {
1092 entryValue
= identDict
->getDataValue(kSecIdentityDomainDefault
);
1094 if(entryValue
== NULL
) {
1095 /* no default identity */
1096 MacOSError::throwMe(errSecItemNotFound
);
1099 /* remember that we're not fetching the requested domain */
1100 domain
= kSecIdentityDomainDefault
;
1103 /* open system keychain - error here is fatal */
1104 Keychain systemKc
= globals().storageManager
.make(SYSTEM_KEYCHAIN_PATH
, false);
1105 CFRef
<SecKeychainRef
> systemKcRef(systemKc
->handle());
1106 StorageManager::KeychainList keychains
;
1107 globals().storageManager
.optionalSearchList(systemKcRef
, keychains
);
1109 /* search for specified cert */
1110 SecKeychainAttributeList attrList
;
1111 SecKeychainAttribute attr
;
1112 attr
.tag
= kSecPublicKeyHashItemAttr
;
1113 attr
.length
= (UInt32
)CFDataGetLength(entryValue
);
1114 attr
.data
= (void *)CFDataGetBytePtr(entryValue
);
1116 attrList
.attr
= &attr
;
1118 KCCursor
cursor(keychains
, kSecCertificateItemClass
, &attrList
);
1120 if(!cursor
->next(certItem
)) {
1121 MacOSError::throwMe(errSecItemNotFound
);
1124 /* found the cert; try matching with key to cook up identity */
1125 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1126 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1128 Required(idRef
) = identity
->handle();
1130 *actualDomain
= domain
;
1131 CFRetain(*actualDomain
);
1137 OSStatus
SecIdentitySetSystemIdentity(
1139 SecIdentityRef idRef
)
1143 StLock
<Mutex
> _(systemIdentityLock());
1144 if(geteuid() != 0) {
1145 MacOSError::throwMe(errSecAuthFailed
);
1148 auto_ptr
<MutableDictionary
> identDict
;
1149 MutableDictionary
*d
= MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1157 /* nothing there, nothing to set - done */
1158 return errSecSuccess
;
1160 identDict
.reset(new MutableDictionary());
1164 /* Just delete the possible entry for this domain */
1165 identDict
->removeValue(domain
);
1168 /* obtain public key hash of identity's cert */
1169 SecPointer
<Identity
> identity(Identity::required(idRef
));
1170 SecPointer
<Certificate
> cert
= identity
->certificate();
1171 const CssmData
&pubKeyHash
= cert
->publicKeyHash();
1172 CFRef
<CFDataRef
> pubKeyHashData(CFDataCreate(NULL
, pubKeyHash
.Data
,
1173 pubKeyHash
.Length
));
1175 /* add/replace to dictionary */
1176 identDict
->setValue(domain
, pubKeyHashData
);
1180 if(!identDict
->writePlistToPrefs(IDENTITY_DOMAIN
, Dictionary::US_System
)) {
1181 MacOSError::throwMe(errSecIO
);
1187 const CFStringRef kSecIdentityDomainDefault
= CFSTR("com.apple.systemdefault");
1188 const CFStringRef kSecIdentityDomainKerberosKDC
= CFSTR("com.apple.kerberos.kdc");