2 * Copyright (c) 2002-2010 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>
30 #include "SecBridge.h"
31 #include <security_keychain/Certificate.h>
32 #include <security_keychain/Identity.h>
33 #include <security_keychain/KeyItem.h>
34 #include <security_keychain/KCCursor.h>
35 #include <security_cdsa_utilities/Schema.h>
36 #include <security_utilities/simpleprefs.h>
37 #include <sys/param.h>
41 /* private function declarations */
43 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
44 CFTypeRef keychainOrArray
,
47 SecKeychainItemRef
*itemRef
);
49 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
50 CFTypeRef keychainOrArray
,
55 CSSM_KEYUSE
ConvertArrayToKeyUsage(CFArrayRef usage
)
58 CSSM_KEYUSE result
= (CSSM_KEYUSE
) 0;
60 if ((NULL
== usage
) || (0 == (count
= CFArrayGetCount(usage
))))
65 for (CFIndex iCnt
= 0; iCnt
< count
; iCnt
++)
67 CFStringRef keyUsageStr
= NULL
;
68 keyUsageStr
= (CFStringRef
)CFArrayGetValueAtIndex(usage
,iCnt
);
69 if (NULL
!= keyUsageStr
)
71 if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanEncrypt
, keyUsageStr
, 0))
73 result
|= CSSM_KEYUSE_ENCRYPT
;
75 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDecrypt
, keyUsageStr
, 0))
77 result
|= CSSM_KEYUSE_DECRYPT
;
79 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanDerive
, keyUsageStr
, 0))
81 result
|= CSSM_KEYUSE_DERIVE
;
83 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanSign
, keyUsageStr
, 0))
85 result
|= CSSM_KEYUSE_SIGN
;
87 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanVerify
, keyUsageStr
, 0))
89 result
|= CSSM_KEYUSE_VERIFY
;
91 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanWrap
, keyUsageStr
, 0))
93 result
|= CSSM_KEYUSE_WRAP
;
95 else if (kCFCompareEqualTo
== CFStringCompare((CFStringRef
)kSecAttrCanUnwrap
, keyUsageStr
, 0))
97 result
|= CSSM_KEYUSE_UNWRAP
;
107 SecIdentityGetTypeID(void)
111 return gTypes().Identity
.typeID
;
113 END_SECAPI1(_kCFRuntimeNotATypeID
)
118 SecIdentityCopyCertificate(
119 SecIdentityRef identityRef
,
120 SecCertificateRef
*certificateRef
)
124 SecPointer
<Certificate
> certificatePtr(Identity::required(identityRef
)->certificate());
125 Required(certificateRef
) = certificatePtr
->handle();
132 SecIdentityCopyPrivateKey(
133 SecIdentityRef identityRef
,
134 SecKeyRef
*privateKeyRef
)
138 SecPointer
<KeyItem
> keyItemPtr(Identity::required(identityRef
)->privateKey());
139 Required(privateKeyRef
) = keyItemPtr
->handle();
145 SecIdentityCreateWithCertificate(
146 CFTypeRef keychainOrArray
,
147 SecCertificateRef certificateRef
,
148 SecIdentityRef
*identityRef
)
152 SecPointer
<Certificate
> certificatePtr(Certificate::required(certificateRef
));
153 StorageManager::KeychainList keychains
;
154 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
155 SecPointer
<Identity
> identityPtr(new Identity(keychains
, certificatePtr
));
156 Required(identityRef
) = identityPtr
->handle();
163 CFAllocatorRef allocator
,
164 SecCertificateRef certificate
,
165 SecKeyRef privateKey
)
167 SecIdentityRef identityRef
= NULL
;
168 OSStatus __secapiresult
;
170 SecPointer
<Certificate
> certificatePtr(Certificate::required(certificate
));
171 SecPointer
<KeyItem
> keyItemPtr(KeyItem::required(privateKey
));
172 SecPointer
<Identity
> identityPtr(new Identity(keyItemPtr
, certificatePtr
));
173 identityRef
= identityPtr
->handle();
175 __secapiresult
=noErr
;
177 catch (const MacOSError
&err
) { __secapiresult
=err
.osStatus(); }
178 catch (const CommonError
&err
) { __secapiresult
=SecKeychainErrFromOSStatus(err
.osStatus()); }
179 catch (const std::bad_alloc
&) { __secapiresult
=memFullErr
; }
180 catch (...) { __secapiresult
=internalComponentErr
; }
186 SecIdentityRef identity1
,
187 SecIdentityRef identity2
,
188 CFOptionFlags compareOptions
)
190 if (!identity1
|| !identity2
)
192 if (identity1
== identity2
)
193 return kCFCompareEqualTo
;
194 else if (identity1
< identity2
)
195 return kCFCompareLessThan
;
197 return kCFCompareGreaterThan
;
202 SecPointer
<Identity
> id1(Identity::required(identity1
));
203 SecPointer
<Identity
> id2(Identity::required(identity2
));
206 return kCFCompareEqualTo
;
208 return kCFCompareLessThan
;
210 return kCFCompareGreaterThan
;
212 END_SECAPI1(kCFCompareGreaterThan
);
215 CFArrayRef
_SecIdentityCopyPossiblePaths(
218 // utility function to build and return an array of possible paths for the given name.
219 // if name is not a URL, this returns a single-element array.
220 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
222 CFMutableArrayRef names
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
226 CFIndex oldLength
= CFStringGetLength(name
);
227 CFArrayAppendValue(names
, name
);
229 CFURLRef url
= CFURLCreateWithString(NULL
, name
, NULL
);
230 if (url
&& CFURLCanBeDecomposed(url
)) {
231 // first, remove the query portion of this URL, if any
232 CFStringRef qs
= CFURLCopyQueryString(url
, NULL
);
234 CFMutableStringRef newName
= CFStringCreateMutableCopy(NULL
, oldLength
, name
);
236 CFIndex qsLength
= CFStringGetLength(qs
) + 1; // include the '?'
237 CFStringDelete(newName
, CFRangeMake(oldLength
-qsLength
, qsLength
));
239 url
= CFURLCreateWithString(NULL
, newName
, NULL
);
240 CFArraySetValueAtIndex(names
, 0, newName
);
245 // now add an entry for each level of the path
247 CFURLRef parent
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
);
249 CFStringRef parentURLString
= CFURLGetString(parent
);
250 if (parentURLString
) {
251 CFIndex newLength
= CFStringGetLength(parentURLString
);
252 // check that string length has decreased as expected; for file URLs,
253 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
254 if ((newLength
>= oldLength
) || (!CFStringHasPrefix(name
, parentURLString
))) {
259 oldLength
= newLength
;
260 CFArrayAppendValue(names
, parentURLString
);
266 // finally, add wildcard entries for each subdomain
267 url
= CFURLCreateWithString(NULL
, name
, NULL
);
268 if (url
&& CFURLCanBeDecomposed(url
)) {
269 CFStringRef netLocString
= CFURLCopyNetLocation(url
);
271 // first strip off port number, if present
272 CFStringRef tmpLocString
= netLocString
;
273 CFArrayRef hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(":"));
274 tmpLocString
= (CFStringRef
)CFRetain((CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, 0));
275 CFRelease(netLocString
);
276 CFRelease(hostnameArray
);
277 netLocString
= tmpLocString
;
278 // split remaining string into domain components
279 hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR("."));
280 CFIndex subdomainCount
= CFArrayGetCount(hostnameArray
);
282 while (++i
< subdomainCount
) {
284 CFMutableStringRef wildcardString
= CFStringCreateMutable(NULL
, 0);
285 if (wildcardString
) {
286 CFStringAppendCString(wildcardString
, "*", kCFStringEncodingUTF8
);
287 while (j
< subdomainCount
) {
288 CFStringRef domainString
= (CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, j
++);
289 if (CFStringGetLength(domainString
) > 0) {
290 CFStringAppendCString(wildcardString
, ".", kCFStringEncodingUTF8
);
291 CFStringAppend(wildcardString
, domainString
);
294 if (CFStringGetLength(wildcardString
) > 1) {
295 CFArrayAppendValue(names
, wildcardString
);
297 CFRelease(wildcardString
);
300 CFRelease(hostnameArray
);
301 CFRelease(netLocString
);
310 OSStatus
_SecIdentityCopyPreferenceMatchingName(
312 CSSM_KEYUSE keyUsage
,
313 CFArrayRef validIssuers
,
314 SecIdentityRef
*identity
)
316 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
317 // caller must handle exceptions
319 StorageManager::KeychainList keychains
;
320 globals().storageManager
.getSearchList(keychains
);
321 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
323 char idUTF8
[MAXPATHLEN
];
325 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
326 idUTF8
[0] = (char)'\0';
327 CssmData
service(const_cast<char *>(idUTF8
), strlen(idUTF8
));
328 FourCharCode itemType
= 'iprf';
329 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
330 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
332 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
335 if (!cursor
->next(prefItem
))
336 return errSecItemNotFound
;
338 // get persistent certificate reference
339 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
340 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
341 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
343 // find certificate, given persistent reference data
344 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
345 SecKeychainItemRef certItemRef
= nil
;
346 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
347 prefItem
->freeContent(&itemAttrList
, NULL
);
353 // filter on valid issuers, if provided
358 // create identity reference, given certificate
359 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
360 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
361 SecPointer
<Identity
> identity_ptr(new Identity(keychains
, certificate
));
363 CFRelease(certItemRef
);
365 Required(identity
) = identity_ptr
->handle();
370 SecIdentityRef
SecIdentityCopyPreferred(CFStringRef name
, CFArrayRef keyUsage
, CFArrayRef validIssuers
)
372 // This function will look for a matching preference in the following order:
373 // - matches the name and the supplied key use
374 // - matches the name and the special 'ANY' key use
375 // - matches the name with no key usage constraint
377 SecIdentityRef identityRef
= NULL
;
378 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
379 OSStatus status
= SecIdentityCopyPreference(name
, keyUse
, validIssuers
, &identityRef
);
380 if (status
!= noErr
&& keyUse
!= CSSM_KEYUSE_ANY
)
381 status
= SecIdentityCopyPreference(name
, CSSM_KEYUSE_ANY
, validIssuers
, &identityRef
);
382 if (status
!= noErr
&& keyUse
!= 0)
383 status
= SecIdentityCopyPreference(name
, 0, validIssuers
, &identityRef
);
388 OSStatus
SecIdentityCopyPreference(
390 CSSM_KEYUSE keyUsage
,
391 CFArrayRef validIssuers
,
392 SecIdentityRef
*identity
)
394 // The original implementation of SecIdentityCopyPreference matches the exact string only.
395 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
396 // and this function is a wrapper which calls it, so that existing clients will get the
397 // extended behavior of server domain matching for items that specify URLs.
398 // (Note that behavior is unchanged if the specified name is not a URL.)
402 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
403 CFSTR("com.apple.security"),
404 kCFPreferencesCurrentUser
,
405 kCFPreferencesAnyHost
);
406 Boolean logging
= false;
407 if (val
&& CFGetTypeID(val
) == CFBooleanGetTypeID()) {
408 logging
= CFBooleanGetValue((CFBooleanRef
)val
);
412 OSStatus status
= errSecItemNotFound
;
413 CFArrayRef names
= _SecIdentityCopyPossiblePaths(name
);
418 CFIndex idx
, total
= CFArrayGetCount(names
);
419 for (idx
= 0; idx
< total
; idx
++) {
420 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, idx
);
422 status
= _SecIdentityCopyPreferenceMatchingName(aName
, keyUsage
, validIssuers
, identity
);
424 catch (...) { status
= errSecItemNotFound
; }
427 // get identity label
428 CFStringRef labelString
= NULL
;
429 if (!status
&& identity
&& *identity
) {
431 SecPointer
<Certificate
> cert(Identity::required(*identity
)->certificate());
432 cert
->inferLabel(false, &labelString
);
434 catch (...) { labelString
= NULL
; };
436 char *labelBuf
= NULL
;
437 CFIndex labelBufSize
= (labelString
) ? CFStringGetLength(labelString
) * 4 : 4;
438 labelBuf
= (char *)malloc(labelBufSize
);
439 if (!labelString
|| !CFStringGetCString(labelString
, labelBuf
, labelBufSize
, kCFStringEncodingUTF8
)) {
443 CFRelease(labelString
);
447 char *serviceBuf
= NULL
;
448 CFIndex serviceBufSize
= CFStringGetLength(aName
) * 4;
449 serviceBuf
= (char *)malloc(serviceBufSize
);
450 if (!CFStringGetCString(aName
, serviceBuf
, serviceBufSize
, kCFStringEncodingUTF8
)) {
454 syslog(LOG_NOTICE
, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf
, serviceBuf
);
455 if (!status
&& name
) {
456 char *nameBuf
= NULL
;
457 CFIndex nameBufSize
= CFStringGetLength(name
) * 4;
458 nameBuf
= (char *)malloc(nameBufSize
);
459 if (!CFStringGetCString(name
, nameBuf
, nameBufSize
, kCFStringEncodingUTF8
)) {
462 syslog(LOG_NOTICE
, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf
, nameBuf
);
470 if (status
== noErr
) {
471 break; // match found
481 OSStatus
SecIdentitySetPreference(
482 SecIdentityRef identity
,
484 CSSM_KEYUSE keyUsage
)
490 // treat NULL identity as a request to clear the preference
491 // (note: if keyUsage is 0, this clears all key usage prefs for name)
492 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL
, name
, keyUsage
);
497 SecPointer
<Certificate
> certificate(Identity::required(identity
)->certificate());
499 // determine the account attribute
501 // This attribute must be synthesized from certificate label + pref item type + key usage,
502 // as only the account and service attributes can make a generic keychain item unique.
503 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
504 // we can save a certificate preference if an identity preference already exists for the
505 // given service name, and vice-versa.
506 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
508 CFStringRef labelStr
= nil
;
509 certificate
->inferLabel(false, &labelStr
);
511 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
513 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
514 const char *templateStr
= "%s [key usage 0x%X]";
515 const int keyUsageMaxStrLen
= 8;
516 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
517 char accountUTF8
[accountUTF8Len
];
518 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
519 accountUTF8
[0] = (char)'\0';
521 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
522 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
523 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
526 // service attribute (name provided by the caller)
527 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(name
), kCFStringEncodingUTF8
) + 1;;
528 char serviceUTF8
[serviceUTF8Len
];
529 if (!CFStringGetCString(name
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
530 serviceUTF8
[0] = (char)'\0';
531 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
533 // look for existing identity preference item, in case this is an update
534 StorageManager::KeychainList keychains
;
535 globals().storageManager
.getSearchList(keychains
);
536 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
537 FourCharCode itemType
= 'iprf';
538 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
539 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
541 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
544 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
545 bool add
= (!cursor
->next(item
));
546 // at this point, we either have a new item to add or an existing item to update
548 // set item attribute values
549 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
550 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
551 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
552 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
553 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
555 // generic attribute (store persistent certificate reference)
556 CFDataRef pItemRef
= nil
;
557 certificate
->copyPersistentReference(pItemRef
);
559 MacOSError::throwMe(errSecInvalidItemRef
);
561 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
562 CFIndex dataLen
= CFDataGetLength(pItemRef
);
563 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
564 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
568 Keychain keychain
= nil
;
570 keychain
= globals().storageManager
.defaultKeychain();
571 if (!keychain
->exists())
572 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
575 keychain
= globals().storageManager
.defaultKeychainUI(item
);
581 catch (const MacOSError
&err
) {
582 if (err
.osStatus() != errSecDuplicateItem
)
583 throw; // if item already exists, fall through to update
592 SecIdentitySetPreferred(SecIdentityRef identity
, CFStringRef name
, CFArrayRef keyUsage
)
594 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
595 return SecIdentitySetPreference(identity
, name
, keyUse
);
599 SecIdentityFindPreferenceItem(
600 CFTypeRef keychainOrArray
,
601 CFStringRef idString
,
602 SecKeychainItemRef
*itemRef
)
606 StorageManager::KeychainList keychains
;
607 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
608 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
610 char idUTF8
[MAXPATHLEN
];
611 idUTF8
[0] = (char)'\0';
614 if (!CFStringGetCString(idString
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
615 idUTF8
[0] = (char)'\0';
617 size_t idUTF8Len
= strlen(idUTF8
);
619 MacOSError::throwMe(paramErr
);
621 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
622 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
623 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
626 if (!cursor
->next(item
))
627 MacOSError::throwMe(errSecItemNotFound
);
630 *itemRef
=item
->handle();
636 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
637 CFTypeRef keychainOrArray
,
640 SecKeychainItemRef
*itemRef
)
644 StorageManager::KeychainList keychains
;
645 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
646 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
648 char idUTF8
[MAXPATHLEN
];
649 idUTF8
[0] = (char)'\0';
652 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
653 idUTF8
[0] = (char)'\0';
655 size_t idUTF8Len
= strlen(idUTF8
);
657 MacOSError::throwMe(paramErr
);
659 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
660 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
661 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
663 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
666 if (!cursor
->next(item
))
667 MacOSError::throwMe(errSecItemNotFound
);
670 *itemRef
=item
->handle();
675 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
676 CFTypeRef keychainOrArray
,
680 // when a specific key usage is passed, we'll only match & delete that pref;
681 // when a key usage of 0 is passed, all matching prefs should be deleted.
682 // maxUsages represents the most matches there could theoretically be, so
683 // cut things off at that point if we're still finding items (if they can't
684 // be deleted for some reason, we'd never break out of the loop.)
687 SecKeychainItemRef item
= NULL
;
688 int count
= 0, maxUsages
= 12;
689 while (++count
<= maxUsages
&&
690 (status
= SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray
, name
, keyUsage
, &item
)) == noErr
) {
691 status
= SecKeychainItemDelete(item
);
696 // it's not an error if the item isn't found
697 return (status
== errSecItemNotFound
) ? noErr
: status
;
701 OSStatus
_SecIdentityAddPreferenceItemWithName(
702 SecKeychainRef keychainRef
,
703 SecIdentityRef identityRef
,
704 CFStringRef idString
,
705 SecKeychainItemRef
*itemRef
)
707 // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
708 // caller must handle exceptions
710 if (!identityRef
|| !idString
)
712 SecPointer
<Certificate
> cert(Identity::required(identityRef
)->certificate());
713 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
716 // determine the account attribute
718 // This attribute must be synthesized from certificate label + pref item type + key usage,
719 // as only the account and service attributes can make a generic keychain item unique.
720 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
721 // we can save a certificate preference if an identity preference already exists for the
722 // given service name, and vice-versa.
723 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
725 CFStringRef labelStr
= nil
;
726 cert
->inferLabel(false, &labelStr
);
728 return errSecDataTooLarge
; // data is "in a format which cannot be displayed"
730 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
731 const char *templateStr
= "%s [key usage 0x%X]";
732 const int keyUsageMaxStrLen
= 8;
733 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
734 char accountUTF8
[accountUTF8Len
];
735 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
736 accountUTF8
[0] = (char)'\0';
738 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
739 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
740 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
743 // service attribute (name provided by the caller)
744 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString
), kCFStringEncodingUTF8
) + 1;;
745 char serviceUTF8
[serviceUTF8Len
];
746 if (!CFStringGetCString(idString
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
747 serviceUTF8
[0] = (char)'\0';
748 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
750 // set item attribute values
751 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
752 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
753 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
754 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
755 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), keyUsage
);
757 // generic attribute (store persistent certificate reference)
758 CFDataRef pItemRef
= nil
;
759 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)cert
->handle(), &pItemRef
);
761 status
= errSecInvalidItemRef
;
764 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
765 CFIndex dataLen
= CFDataGetLength(pItemRef
);
766 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
767 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
770 Keychain keychain
= nil
;
772 keychain
= Keychain::optional(keychainRef
);
773 if (!keychain
->exists())
774 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
777 keychain
= globals().storageManager
.defaultKeychainUI(item
);
783 catch (const MacOSError
&err
) {
784 if (err
.osStatus() != errSecDuplicateItem
)
785 throw; // if item already exists, fall through to update
791 *itemRef
= item
->handle();
796 OSStatus
SecIdentityAddPreferenceItem(
797 SecKeychainRef keychainRef
,
798 SecIdentityRef identityRef
,
799 CFStringRef idString
,
800 SecKeychainItemRef
*itemRef
)
802 // The original implementation of SecIdentityAddPreferenceItem adds the exact string only.
803 // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above),
804 // and this function is a wrapper which calls it, so that existing clients will get the
805 // extended behavior of server domain matching for items that specify URLs.
806 // (Note that behavior is unchanged if the specified idString is not a URL.)
810 OSStatus status
= internalComponentErr
;
811 CFArrayRef names
= _SecIdentityCopyPossiblePaths(idString
);
816 CFIndex total
= CFArrayGetCount(names
);
818 // add item for name (first element in array)
819 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, 0);
821 status
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
823 catch (const MacOSError
&err
) { status
=err
.osStatus(); }
824 catch (const CommonError
&err
) { status
=SecKeychainErrFromOSStatus(err
.osStatus()); }
825 catch (const std::bad_alloc
&) { status
=memFullErr
; }
826 catch (...) { status
=internalComponentErr
; }
829 Boolean setDomainDefaultIdentity
= FALSE
;
830 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"),
831 CFSTR("com.apple.security.identities"),
832 kCFPreferencesCurrentUser
,
833 kCFPreferencesAnyHost
);
835 if (CFGetTypeID(val
) == CFBooleanGetTypeID())
836 setDomainDefaultIdentity
= CFBooleanGetValue((CFBooleanRef
)val
) ? TRUE
: FALSE
;
839 if (setDomainDefaultIdentity
) {
840 // add item for domain (second-to-last element in array, e.g. "*.apple.com")
841 OSStatus tmpStatus
= noErr
;
842 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, total
-2);
844 tmpStatus
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
846 catch (const MacOSError
&err
) { tmpStatus
=err
.osStatus(); }
847 catch (const CommonError
&err
) { tmpStatus
=SecKeychainErrFromOSStatus(err
.osStatus()); }
848 catch (const std::bad_alloc
&) { tmpStatus
=memFullErr
; }
849 catch (...) { tmpStatus
=internalComponentErr
; }
859 /* deprecated in 10.5 */
860 OSStatus
SecIdentityUpdatePreferenceItem(
861 SecKeychainItemRef itemRef
,
862 SecIdentityRef identityRef
)
866 if (!itemRef
|| !identityRef
)
867 MacOSError::throwMe(paramErr
);
868 SecPointer
<Certificate
> certificate(Identity::required(identityRef
)->certificate());
869 Item prefItem
= ItemImpl::required(itemRef
);
871 // get the current key usage value for this item
874 SecKeychainAttribute attr
= { kSecScriptCodeItemAttr
, sizeof(sint32
), &keyUsage
};
876 prefItem
->getAttribute(attr
, &actLen
);
882 // set the account attribute
884 // This attribute must be synthesized from certificate label + pref item type + key usage,
885 // as only the account and service attributes can make a generic keychain item unique.
886 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
887 // we can save a certificate preference if an identity preference already exists for the
888 // given service name, and vice-versa.
889 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
891 CFStringRef labelStr
= nil
;
892 certificate
->inferLabel(false, &labelStr
);
894 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
896 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
897 const char *templateStr
= "%s [key usage 0x%X]";
898 const int keyUsageMaxStrLen
= 8;
899 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
900 char accountUTF8
[accountUTF8Len
];
901 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
902 accountUTF8
[0] = (char)'\0';
904 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
905 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
906 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
907 prefItem
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
910 // generic attribute (store persistent certificate reference)
911 CFDataRef pItemRef
= nil
;
912 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certificate
->handle(), &pItemRef
);
914 status
= errSecInvalidItemRef
;
916 MacOSError::throwMe(status
);
917 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
918 CFIndex dataLen
= CFDataGetLength(pItemRef
);
919 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
920 prefItem
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
928 OSStatus
SecIdentityCopyFromPreferenceItem(
929 SecKeychainItemRef itemRef
,
930 SecIdentityRef
*identityRef
)
934 if (!itemRef
|| !identityRef
)
935 MacOSError::throwMe(paramErr
);
936 Item prefItem
= ItemImpl::required(itemRef
);
938 // get persistent certificate reference
939 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
940 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
941 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
943 // find certificate, given persistent reference data
944 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
945 SecKeychainItemRef certItemRef
= nil
;
946 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
947 prefItem
->freeContent(&itemAttrList
, NULL
);
953 // create identity reference, given certificate
954 StorageManager::KeychainList keychains
;
955 globals().storageManager
.optionalSearchList((CFTypeRef
)NULL
, keychains
);
956 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
957 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
958 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
960 CFRelease(certItemRef
);
962 Required(identityRef
) = identity
->handle();
968 * System Identity Support.
971 /* plist domain (in /Library/Preferences) */
972 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
975 * Our plist is a dictionary whose entries have the following format:
976 * key = domain name as CFString
977 * value = public key hash as CFData
980 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
983 * All accesses to system identities and its associated plist are
984 * protected by this lock.
986 ModuleNexus
<Mutex
> systemIdentityLock
;
988 OSStatus
SecIdentityCopySystemIdentity(
990 SecIdentityRef
*idRef
,
991 CFStringRef
*actualDomain
) /* optional */
995 StLock
<Mutex
> _(systemIdentityLock());
996 auto_ptr
<Dictionary
> identDict
;
998 /* get top-level dictionary - if not present, we're done */
999 Dictionary
* d
= Dictionary::CreateDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1002 return errSecNotAvailable
;
1007 /* see if there's an entry for specified domain */
1008 CFDataRef entryValue
= identDict
->getDataValue(domain
);
1009 if(entryValue
== NULL
) {
1010 /* try for default entry if we're not already looking for default */
1011 if(!CFEqual(domain
, kSecIdentityDomainDefault
)) {
1012 entryValue
= identDict
->getDataValue(kSecIdentityDomainDefault
);
1014 if(entryValue
== NULL
) {
1015 /* no default identity */
1016 MacOSError::throwMe(errSecItemNotFound
);
1019 /* remember that we're not fetching the requested domain */
1020 domain
= kSecIdentityDomainDefault
;
1023 /* open system keychain - error here is fatal */
1024 Keychain systemKc
= globals().storageManager
.make(SYSTEM_KEYCHAIN_PATH
, false);
1025 CFRef
<SecKeychainRef
> systemKcRef(systemKc
->handle());
1026 StorageManager::KeychainList keychains
;
1027 globals().storageManager
.optionalSearchList(systemKcRef
, keychains
);
1029 /* search for specified cert */
1030 SecKeychainAttributeList attrList
;
1031 SecKeychainAttribute attr
;
1032 attr
.tag
= kSecPublicKeyHashItemAttr
;
1033 attr
.length
= CFDataGetLength(entryValue
);
1034 attr
.data
= (void *)CFDataGetBytePtr(entryValue
);
1036 attrList
.attr
= &attr
;
1038 KCCursor
cursor(keychains
, kSecCertificateItemClass
, &attrList
);
1040 if(!cursor
->next(certItem
)) {
1041 MacOSError::throwMe(errSecItemNotFound
);
1044 /* found the cert; try matching with key to cook up identity */
1045 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1046 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1048 Required(idRef
) = identity
->handle();
1050 *actualDomain
= domain
;
1051 CFRetain(*actualDomain
);
1057 OSStatus
SecIdentitySetSystemIdentity(
1059 SecIdentityRef idRef
)
1063 StLock
<Mutex
> _(systemIdentityLock());
1064 if(geteuid() != 0) {
1065 MacOSError::throwMe(errSecAuthFailed
);
1068 auto_ptr
<MutableDictionary
> identDict
;
1069 MutableDictionary
*d
= MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1077 /* nothing there, nothing to set - done */
1080 identDict
.reset(new MutableDictionary());
1084 /* Just delete the possible entry for this domain */
1085 identDict
->removeValue(domain
);
1088 /* obtain public key hash of identity's cert */
1089 SecPointer
<Identity
> identity(Identity::required(idRef
));
1090 SecPointer
<Certificate
> cert
= identity
->certificate();
1091 const CssmData
&pubKeyHash
= cert
->publicKeyHash();
1092 CFRef
<CFDataRef
> pubKeyHashData(CFDataCreate(NULL
, pubKeyHash
.Data
,
1093 pubKeyHash
.Length
));
1095 /* add/replace to dictionary */
1096 identDict
->setValue(domain
, pubKeyHashData
);
1100 if(!identDict
->writePlistToPrefs(IDENTITY_DOMAIN
, Dictionary::US_System
)) {
1101 MacOSError::throwMe(ioErr
);
1107 const CFStringRef kSecIdentityDomainDefault
= CFSTR("com.apple.systemdefault");
1108 const CFStringRef kSecIdentityDomainKerberosKDC
= CFSTR("com.apple.kerberos.kdc");