2 * Copyright (c) 2002-2014 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
=errSecSuccess
;
177 catch (const MacOSError
&err
) { __secapiresult
=err
.osStatus(); }
178 catch (const CommonError
&err
) { __secapiresult
=SecKeychainErrFromOSStatus(err
.osStatus()); }
179 catch (const std::bad_alloc
&) { __secapiresult
=errSecAllocate
; }
180 catch (...) { __secapiresult
=errSecInternalComponent
; }
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
);
216 CFArrayRef
_SecIdentityCopyPossiblePaths(
219 // utility function to build and return an array of possible paths for the given name.
220 // if name is not a URL, this returns a single-element array.
221 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
223 CFMutableArrayRef names
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
227 CFIndex oldLength
= CFStringGetLength(name
);
228 CFArrayAppendValue(names
, name
);
230 CFURLRef url
= CFURLCreateWithString(NULL
, name
, NULL
);
232 if (CFURLCanBeDecomposed(url
)) {
233 // first, remove the query portion of this URL, if any
234 CFStringRef qs
= CFURLCopyQueryString(url
, NULL
);
236 CFMutableStringRef newName
= CFStringCreateMutableCopy(NULL
, oldLength
, name
);
238 CFIndex qsLength
= CFStringGetLength(qs
) + 1; // include the '?'
239 CFStringDelete(newName
, CFRangeMake(oldLength
-qsLength
, qsLength
));
241 url
= CFURLCreateWithString(NULL
, newName
, NULL
);
242 CFArraySetValueAtIndex(names
, 0, newName
);
247 // now add an entry for each level of the path
249 CFURLRef parent
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
);
251 CFStringRef parentURLString
= CFURLGetString(parent
);
252 if (parentURLString
) {
253 CFIndex newLength
= CFStringGetLength(parentURLString
);
254 // check that string length has decreased as expected; for file URLs,
255 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
256 if ((newLength
>= oldLength
) || (!CFStringHasPrefix(name
, parentURLString
))) {
261 oldLength
= newLength
;
262 CFArrayAppendValue(names
, parentURLString
);
273 // finally, add wildcard entries for each subdomain
274 url
= CFURLCreateWithString(NULL
, name
, NULL
);
276 if (CFURLCanBeDecomposed(url
)) {
277 CFStringRef netLocString
= CFURLCopyNetLocation(url
);
279 // first strip off port number, if present
280 CFStringRef tmpLocString
= netLocString
;
281 CFArrayRef hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR(":"));
282 tmpLocString
= (CFStringRef
)CFRetain((CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, 0));
283 CFRelease(netLocString
);
284 CFRelease(hostnameArray
);
285 netLocString
= tmpLocString
;
286 // split remaining string into domain components
287 hostnameArray
= CFStringCreateArrayBySeparatingStrings(NULL
, netLocString
, CFSTR("."));
288 CFIndex subdomainCount
= CFArrayGetCount(hostnameArray
);
290 while (++i
< subdomainCount
) {
292 CFMutableStringRef wildcardString
= CFStringCreateMutable(NULL
, 0);
293 if (wildcardString
) {
294 CFStringAppendCString(wildcardString
, "*", kCFStringEncodingUTF8
);
295 while (j
< subdomainCount
) {
296 CFStringRef domainString
= (CFStringRef
)CFArrayGetValueAtIndex(hostnameArray
, j
++);
297 if (CFStringGetLength(domainString
) > 0) {
298 CFStringAppendCString(wildcardString
, ".", kCFStringEncodingUTF8
);
299 CFStringAppend(wildcardString
, domainString
);
302 if (CFStringGetLength(wildcardString
) > 1) {
303 CFArrayAppendValue(names
, wildcardString
);
305 CFRelease(wildcardString
);
308 CFRelease(hostnameArray
);
309 CFRelease(netLocString
);
319 OSStatus
_SecIdentityCopyPreferenceMatchingName(
321 CSSM_KEYUSE keyUsage
,
322 CFArrayRef validIssuers
,
323 SecIdentityRef
*identity
)
325 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
326 // caller must handle exceptions
328 StorageManager::KeychainList keychains
;
329 globals().storageManager
.getSearchList(keychains
);
330 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
332 char idUTF8
[MAXPATHLEN
];
334 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
335 idUTF8
[0] = (char)'\0';
336 CssmData
service(const_cast<char *>(idUTF8
), strlen(idUTF8
));
337 FourCharCode itemType
= 'iprf';
338 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
339 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
341 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
344 if (!cursor
->next(prefItem
))
345 return errSecItemNotFound
;
347 // get persistent certificate reference
348 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
349 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
350 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
352 // find certificate, given persistent reference data
353 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
354 SecKeychainItemRef certItemRef
= nil
;
355 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
356 prefItem
->freeContent(&itemAttrList
, NULL
);
362 // filter on valid issuers, if provided
367 // create identity reference, given certificate
368 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
369 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
370 SecPointer
<Identity
> identity_ptr(new Identity(keychains
, certificate
));
372 CFRelease(certItemRef
);
374 Required(identity
) = identity_ptr
->handle();
379 SecIdentityRef
SecIdentityCopyPreferred(CFStringRef name
, CFArrayRef keyUsage
, CFArrayRef validIssuers
)
381 // This function will look for a matching preference in the following order:
382 // - matches the name and the supplied key use
383 // - matches the name and the special 'ANY' key use
384 // - matches the name with no key usage constraint
386 SecIdentityRef identityRef
= NULL
;
387 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
388 OSStatus status
= SecIdentityCopyPreference(name
, keyUse
, validIssuers
, &identityRef
);
389 if (status
!= errSecSuccess
&& keyUse
!= CSSM_KEYUSE_ANY
)
390 status
= SecIdentityCopyPreference(name
, CSSM_KEYUSE_ANY
, validIssuers
, &identityRef
);
391 if (status
!= errSecSuccess
&& keyUse
!= 0)
392 status
= SecIdentityCopyPreference(name
, 0, validIssuers
, &identityRef
);
397 OSStatus
SecIdentityCopyPreference(
399 CSSM_KEYUSE keyUsage
,
400 CFArrayRef validIssuers
,
401 SecIdentityRef
*identity
)
403 // The original implementation of SecIdentityCopyPreference matches the exact string only.
404 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
405 // and this function is a wrapper which calls it, so that existing clients will get the
406 // extended behavior of server domain matching for items that specify URLs.
407 // (Note that behavior is unchanged if the specified name is not a URL.)
411 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
412 CFSTR("com.apple.security"),
413 kCFPreferencesCurrentUser
,
414 kCFPreferencesAnyHost
);
415 Boolean logging
= false;
416 if (val
&& CFGetTypeID(val
) == CFBooleanGetTypeID()) {
417 logging
= CFBooleanGetValue((CFBooleanRef
)val
);
421 OSStatus status
= errSecItemNotFound
;
422 CFArrayRef names
= _SecIdentityCopyPossiblePaths(name
);
427 CFIndex idx
, total
= CFArrayGetCount(names
);
428 for (idx
= 0; idx
< total
; idx
++) {
429 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, idx
);
431 status
= _SecIdentityCopyPreferenceMatchingName(aName
, keyUsage
, validIssuers
, identity
);
433 catch (...) { status
= errSecItemNotFound
; }
436 // get identity label
437 CFStringRef labelString
= NULL
;
438 if (!status
&& identity
&& *identity
) {
440 SecPointer
<Certificate
> cert(Identity::required(*identity
)->certificate());
441 cert
->inferLabel(false, &labelString
);
443 catch (...) { labelString
= NULL
; };
445 char *labelBuf
= NULL
;
446 CFIndex labelBufSize
= (labelString
) ? CFStringGetLength(labelString
) * 4 : 4;
447 labelBuf
= (char *)malloc(labelBufSize
);
448 if (!labelString
|| !CFStringGetCString(labelString
, labelBuf
, labelBufSize
, kCFStringEncodingUTF8
)) {
452 CFRelease(labelString
);
456 char *serviceBuf
= NULL
;
457 CFIndex serviceBufSize
= CFStringGetLength(aName
) * 4;
458 serviceBuf
= (char *)malloc(serviceBufSize
);
459 if (!CFStringGetCString(aName
, serviceBuf
, serviceBufSize
, kCFStringEncodingUTF8
)) {
463 syslog(LOG_NOTICE
, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf
, serviceBuf
);
464 if (!status
&& name
) {
465 char *nameBuf
= NULL
;
466 CFIndex nameBufSize
= CFStringGetLength(name
) * 4;
467 nameBuf
= (char *)malloc(nameBufSize
);
468 if (!CFStringGetCString(name
, nameBuf
, nameBufSize
, kCFStringEncodingUTF8
)) {
471 syslog(LOG_NOTICE
, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf
, nameBuf
);
479 if (status
== errSecSuccess
) {
480 break; // match found
490 OSStatus
SecIdentitySetPreference(
491 SecIdentityRef identity
,
493 CSSM_KEYUSE keyUsage
)
499 // treat NULL identity as a request to clear the preference
500 // (note: if keyUsage is 0, this clears all key usage prefs for name)
501 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL
, name
, keyUsage
);
506 SecPointer
<Certificate
> certificate(Identity::required(identity
)->certificate());
508 // determine the account attribute
510 // This attribute must be synthesized from certificate label + pref item type + key usage,
511 // as only the account and service attributes can make a generic keychain item unique.
512 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
513 // we can save a certificate preference if an identity preference already exists for the
514 // given service name, and vice-versa.
515 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
517 CFStringRef labelStr
= nil
;
518 certificate
->inferLabel(false, &labelStr
);
520 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
522 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
523 const char *templateStr
= "%s [key usage 0x%X]";
524 const int keyUsageMaxStrLen
= 8;
525 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
526 char accountUTF8
[accountUTF8Len
];
527 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
528 accountUTF8
[0] = (char)'\0';
530 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
531 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
532 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
535 // service attribute (name provided by the caller)
536 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(name
), kCFStringEncodingUTF8
) + 1;;
537 char serviceUTF8
[serviceUTF8Len
];
538 if (!CFStringGetCString(name
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
539 serviceUTF8
[0] = (char)'\0';
540 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
542 // look for existing identity preference item, in case this is an update
543 StorageManager::KeychainList keychains
;
544 globals().storageManager
.getSearchList(keychains
);
545 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
546 FourCharCode itemType
= 'iprf';
547 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
548 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
550 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
553 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
554 bool add
= (!cursor
->next(item
));
555 // at this point, we either have a new item to add or an existing item to update
557 // set item attribute values
558 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
559 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), itemType
);
560 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
561 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
562 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
564 // generic attribute (store persistent certificate reference)
565 CFDataRef pItemRef
= nil
;
566 certificate
->copyPersistentReference(pItemRef
);
568 MacOSError::throwMe(errSecInvalidItemRef
);
570 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
571 CFIndex dataLen
= CFDataGetLength(pItemRef
);
572 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
573 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
577 Keychain keychain
= nil
;
579 keychain
= globals().storageManager
.defaultKeychain();
580 if (!keychain
->exists())
581 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
584 keychain
= globals().storageManager
.defaultKeychainUI(item
);
590 catch (const MacOSError
&err
) {
591 if (err
.osStatus() != errSecDuplicateItem
)
592 throw; // if item already exists, fall through to update
601 SecIdentitySetPreferred(SecIdentityRef identity
, CFStringRef name
, CFArrayRef keyUsage
)
603 CSSM_KEYUSE keyUse
= ConvertArrayToKeyUsage(keyUsage
);
604 return SecIdentitySetPreference(identity
, name
, keyUse
);
608 SecIdentityFindPreferenceItem(
609 CFTypeRef keychainOrArray
,
610 CFStringRef idString
,
611 SecKeychainItemRef
*itemRef
)
615 StorageManager::KeychainList keychains
;
616 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
617 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
619 char idUTF8
[MAXPATHLEN
];
620 idUTF8
[0] = (char)'\0';
623 if (!CFStringGetCString(idString
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
624 idUTF8
[0] = (char)'\0';
626 size_t idUTF8Len
= strlen(idUTF8
);
628 MacOSError::throwMe(errSecParam
);
630 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
631 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
632 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
635 if (!cursor
->next(item
))
636 MacOSError::throwMe(errSecItemNotFound
);
639 *itemRef
=item
->handle();
645 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
646 CFTypeRef keychainOrArray
,
649 SecKeychainItemRef
*itemRef
)
653 StorageManager::KeychainList keychains
;
654 globals().storageManager
.optionalSearchList(keychainOrArray
, keychains
);
655 KCCursor
cursor(keychains
, kSecGenericPasswordItemClass
, NULL
);
657 char idUTF8
[MAXPATHLEN
];
658 idUTF8
[0] = (char)'\0';
661 if (!CFStringGetCString(name
, idUTF8
, sizeof(idUTF8
)-1, kCFStringEncodingUTF8
))
662 idUTF8
[0] = (char)'\0';
664 size_t idUTF8Len
= strlen(idUTF8
);
666 MacOSError::throwMe(errSecParam
);
668 CssmData
service(const_cast<char *>(idUTF8
), idUTF8Len
);
669 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecServiceItemAttr
), service
);
670 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
672 cursor
->add(CSSM_DB_EQUAL
, Schema::attributeInfo(kSecScriptCodeItemAttr
), (sint32
)keyUsage
);
675 if (!cursor
->next(item
))
676 MacOSError::throwMe(errSecItemNotFound
);
679 *itemRef
=item
->handle();
684 OSStatus
SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
685 CFTypeRef keychainOrArray
,
689 // when a specific key usage is passed, we'll only match & delete that pref;
690 // when a key usage of 0 is passed, all matching prefs should be deleted.
691 // maxUsages represents the most matches there could theoretically be, so
692 // cut things off at that point if we're still finding items (if they can't
693 // be deleted for some reason, we'd never break out of the loop.)
696 SecKeychainItemRef item
= NULL
;
697 int count
= 0, maxUsages
= 12;
698 while (++count
<= maxUsages
&&
699 (status
= SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray
, name
, keyUsage
, &item
)) == errSecSuccess
) {
700 status
= SecKeychainItemDelete(item
);
705 // it's not an error if the item isn't found
706 return (status
== errSecItemNotFound
) ? errSecSuccess
: status
;
711 OSStatus
_SecIdentityAddPreferenceItemWithName(
712 SecKeychainRef keychainRef
,
713 SecIdentityRef identityRef
,
714 CFStringRef idString
,
715 SecKeychainItemRef
*itemRef
)
717 // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
718 // caller must handle exceptions
720 if (!identityRef
|| !idString
)
722 SecPointer
<Certificate
> cert(Identity::required(identityRef
)->certificate());
723 Item
item(kSecGenericPasswordItemClass
, 'aapl', 0, NULL
, false);
726 // determine the account attribute
728 // This attribute must be synthesized from certificate label + pref item type + key usage,
729 // as only the account and service attributes can make a generic keychain item unique.
730 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
731 // we can save a certificate preference if an identity preference already exists for the
732 // given service name, and vice-versa.
733 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
735 CFStringRef labelStr
= nil
;
736 cert
->inferLabel(false, &labelStr
);
738 return errSecDataTooLarge
; // data is "in a format which cannot be displayed"
740 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
741 const char *templateStr
= "%s [key usage 0x%X]";
742 const int keyUsageMaxStrLen
= 8;
743 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
744 char accountUTF8
[accountUTF8Len
];
745 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
746 accountUTF8
[0] = (char)'\0';
748 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
749 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
750 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
753 // service attribute (name provided by the caller)
754 CFIndex serviceUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString
), kCFStringEncodingUTF8
) + 1;;
755 char serviceUTF8
[serviceUTF8Len
];
756 if (!CFStringGetCString(idString
, serviceUTF8
, serviceUTF8Len
-1, kCFStringEncodingUTF8
))
757 serviceUTF8
[0] = (char)'\0';
758 CssmData
service(const_cast<char *>(serviceUTF8
), strlen(serviceUTF8
));
760 // set item attribute values
761 item
->setAttribute(Schema::attributeInfo(kSecServiceItemAttr
), service
);
762 item
->setAttribute(Schema::attributeInfo(kSecLabelItemAttr
), service
);
763 item
->setAttribute(Schema::attributeInfo(kSecTypeItemAttr
), (FourCharCode
)'iprf');
764 item
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
765 item
->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr
), keyUsage
);
767 // generic attribute (store persistent certificate reference)
768 CFDataRef pItemRef
= nil
;
769 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)cert
->handle(), &pItemRef
);
771 status
= errSecInvalidItemRef
;
774 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
775 CFIndex dataLen
= CFDataGetLength(pItemRef
);
776 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
777 item
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
780 Keychain keychain
= nil
;
782 keychain
= Keychain::optional(keychainRef
);
783 if (!keychain
->exists())
784 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
787 keychain
= globals().storageManager
.defaultKeychainUI(item
);
793 catch (const MacOSError
&err
) {
794 if (err
.osStatus() != errSecDuplicateItem
)
795 throw; // if item already exists, fall through to update
801 *itemRef
= item
->handle();
806 OSStatus
SecIdentityAddPreferenceItem(
807 SecKeychainRef keychainRef
,
808 SecIdentityRef identityRef
,
809 CFStringRef idString
,
810 SecKeychainItemRef
*itemRef
)
812 // The original implementation of SecIdentityAddPreferenceItem adds the exact string only.
813 // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above),
814 // and this function is a wrapper which calls it, so that existing clients will get the
815 // extended behavior of server domain matching for items that specify URLs.
816 // (Note that behavior is unchanged if the specified idString is not a URL.)
820 OSStatus status
= errSecInternalComponent
;
821 CFArrayRef names
= _SecIdentityCopyPossiblePaths(idString
);
826 CFIndex total
= CFArrayGetCount(names
);
828 // add item for name (first element in array)
829 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, 0);
831 status
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
833 catch (const MacOSError
&err
) { status
=err
.osStatus(); }
834 catch (const CommonError
&err
) { status
=SecKeychainErrFromOSStatus(err
.osStatus()); }
835 catch (const std::bad_alloc
&) { status
=errSecAllocate
; }
836 catch (...) { status
=errSecInternalComponent
; }
839 Boolean setDomainDefaultIdentity
= FALSE
;
840 CFTypeRef val
= (CFTypeRef
)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"),
841 CFSTR("com.apple.security.identities"),
842 kCFPreferencesCurrentUser
,
843 kCFPreferencesAnyHost
);
845 if (CFGetTypeID(val
) == CFBooleanGetTypeID())
846 setDomainDefaultIdentity
= CFBooleanGetValue((CFBooleanRef
)val
) ? TRUE
: FALSE
;
849 if (setDomainDefaultIdentity
) {
850 // add item for domain (second-to-last element in array, e.g. "*.apple.com")
851 OSStatus tmpStatus
= errSecSuccess
;
852 CFStringRef aName
= (CFStringRef
)CFArrayGetValueAtIndex(names
, total
-2);
854 tmpStatus
= _SecIdentityAddPreferenceItemWithName(keychainRef
, identityRef
, aName
, itemRef
);
856 catch (const MacOSError
&err
) { tmpStatus
=err
.osStatus(); }
857 catch (const CommonError
&err
) { tmpStatus
=SecKeychainErrFromOSStatus(err
.osStatus()); }
858 catch (const std::bad_alloc
&) { tmpStatus
=errSecAllocate
; }
859 catch (...) { tmpStatus
=errSecInternalComponent
; }
869 /* deprecated in 10.5 */
870 OSStatus
SecIdentityUpdatePreferenceItem(
871 SecKeychainItemRef itemRef
,
872 SecIdentityRef identityRef
)
876 if (!itemRef
|| !identityRef
)
877 MacOSError::throwMe(errSecParam
);
878 SecPointer
<Certificate
> certificate(Identity::required(identityRef
)->certificate());
879 Item prefItem
= ItemImpl::required(itemRef
);
881 // get the current key usage value for this item
884 SecKeychainAttribute attr
= { kSecScriptCodeItemAttr
, sizeof(sint32
), &keyUsage
};
886 prefItem
->getAttribute(attr
, &actLen
);
892 // set the account attribute
894 // This attribute must be synthesized from certificate label + pref item type + key usage,
895 // as only the account and service attributes can make a generic keychain item unique.
896 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
897 // we can save a certificate preference if an identity preference already exists for the
898 // given service name, and vice-versa.
899 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
901 CFStringRef labelStr
= nil
;
902 certificate
->inferLabel(false, &labelStr
);
904 MacOSError::throwMe(errSecDataTooLarge
); // data is "in a format which cannot be displayed"
906 CFIndex accountUTF8Len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr
), kCFStringEncodingUTF8
) + 1;
907 const char *templateStr
= "%s [key usage 0x%X]";
908 const int keyUsageMaxStrLen
= 8;
909 accountUTF8Len
+= strlen(templateStr
) + keyUsageMaxStrLen
;
910 char accountUTF8
[accountUTF8Len
];
911 if (!CFStringGetCString(labelStr
, accountUTF8
, accountUTF8Len
-1, kCFStringEncodingUTF8
))
912 accountUTF8
[0] = (char)'\0';
914 snprintf(accountUTF8
, accountUTF8Len
-1, templateStr
, accountUTF8
, keyUsage
);
915 snprintf(accountUTF8
, accountUTF8Len
-1, "%s ", accountUTF8
);
916 CssmData
account(const_cast<char *>(accountUTF8
), strlen(accountUTF8
));
917 prefItem
->setAttribute(Schema::attributeInfo(kSecAccountItemAttr
), account
);
920 // generic attribute (store persistent certificate reference)
921 CFDataRef pItemRef
= nil
;
922 OSStatus status
= SecKeychainItemCreatePersistentReference((SecKeychainItemRef
)certificate
->handle(), &pItemRef
);
924 status
= errSecInvalidItemRef
;
926 MacOSError::throwMe(status
);
927 const UInt8
*dataPtr
= CFDataGetBytePtr(pItemRef
);
928 CFIndex dataLen
= CFDataGetLength(pItemRef
);
929 CssmData
pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr
)), dataLen
);
930 prefItem
->setAttribute(Schema::attributeInfo(kSecGenericItemAttr
), pref
);
938 OSStatus
SecIdentityCopyFromPreferenceItem(
939 SecKeychainItemRef itemRef
,
940 SecIdentityRef
*identityRef
)
944 if (!itemRef
|| !identityRef
)
945 MacOSError::throwMe(errSecParam
);
946 Item prefItem
= ItemImpl::required(itemRef
);
948 // get persistent certificate reference
949 SecKeychainAttribute itemAttrs
[] = { { kSecGenericItemAttr
, 0, NULL
} };
950 SecKeychainAttributeList itemAttrList
= { sizeof(itemAttrs
) / sizeof(itemAttrs
[0]), itemAttrs
};
951 prefItem
->getContent(NULL
, &itemAttrList
, NULL
, NULL
);
953 // find certificate, given persistent reference data
954 CFDataRef pItemRef
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)itemAttrs
[0].data
, itemAttrs
[0].length
, kCFAllocatorNull
);
955 SecKeychainItemRef certItemRef
= nil
;
956 OSStatus status
= SecKeychainItemCopyFromPersistentReference(pItemRef
, &certItemRef
); //%%% need to make this a method of ItemImpl
957 prefItem
->freeContent(&itemAttrList
, NULL
);
963 // create identity reference, given certificate
964 StorageManager::KeychainList keychains
;
965 globals().storageManager
.optionalSearchList((CFTypeRef
)NULL
, keychains
);
966 Item certItem
= ItemImpl::required(SecKeychainItemRef(certItemRef
));
967 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
968 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
970 CFRelease(certItemRef
);
972 Required(identityRef
) = identity
->handle();
978 * System Identity Support.
981 /* plist domain (in /Library/Preferences) */
982 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
985 * Our plist is a dictionary whose entries have the following format:
986 * key = domain name as CFString
987 * value = public key hash as CFData
990 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
993 * All accesses to system identities and its associated plist are
994 * protected by this lock.
996 ModuleNexus
<Mutex
> systemIdentityLock
;
998 OSStatus
SecIdentityCopySystemIdentity(
1000 SecIdentityRef
*idRef
,
1001 CFStringRef
*actualDomain
) /* optional */
1005 StLock
<Mutex
> _(systemIdentityLock());
1006 auto_ptr
<Dictionary
> identDict
;
1008 /* get top-level dictionary - if not present, we're done */
1009 Dictionary
* d
= Dictionary::CreateDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1012 return errSecNotAvailable
;
1017 /* see if there's an entry for specified domain */
1018 CFDataRef entryValue
= identDict
->getDataValue(domain
);
1019 if(entryValue
== NULL
) {
1020 /* try for default entry if we're not already looking for default */
1021 if(!CFEqual(domain
, kSecIdentityDomainDefault
)) {
1022 entryValue
= identDict
->getDataValue(kSecIdentityDomainDefault
);
1024 if(entryValue
== NULL
) {
1025 /* no default identity */
1026 MacOSError::throwMe(errSecItemNotFound
);
1029 /* remember that we're not fetching the requested domain */
1030 domain
= kSecIdentityDomainDefault
;
1033 /* open system keychain - error here is fatal */
1034 Keychain systemKc
= globals().storageManager
.make(SYSTEM_KEYCHAIN_PATH
, false);
1035 CFRef
<SecKeychainRef
> systemKcRef(systemKc
->handle());
1036 StorageManager::KeychainList keychains
;
1037 globals().storageManager
.optionalSearchList(systemKcRef
, keychains
);
1039 /* search for specified cert */
1040 SecKeychainAttributeList attrList
;
1041 SecKeychainAttribute attr
;
1042 attr
.tag
= kSecPublicKeyHashItemAttr
;
1043 attr
.length
= (UInt32
)CFDataGetLength(entryValue
);
1044 attr
.data
= (void *)CFDataGetBytePtr(entryValue
);
1046 attrList
.attr
= &attr
;
1048 KCCursor
cursor(keychains
, kSecCertificateItemClass
, &attrList
);
1050 if(!cursor
->next(certItem
)) {
1051 MacOSError::throwMe(errSecItemNotFound
);
1054 /* found the cert; try matching with key to cook up identity */
1055 SecPointer
<Certificate
> certificate(static_cast<Certificate
*>(certItem
.get()));
1056 SecPointer
<Identity
> identity(new Identity(keychains
, certificate
));
1058 Required(idRef
) = identity
->handle();
1060 *actualDomain
= domain
;
1061 CFRetain(*actualDomain
);
1067 OSStatus
SecIdentitySetSystemIdentity(
1069 SecIdentityRef idRef
)
1073 StLock
<Mutex
> _(systemIdentityLock());
1074 if(geteuid() != 0) {
1075 MacOSError::throwMe(errSecAuthFailed
);
1078 auto_ptr
<MutableDictionary
> identDict
;
1079 MutableDictionary
*d
= MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN
, Dictionary::US_System
);
1087 /* nothing there, nothing to set - done */
1088 return errSecSuccess
;
1090 identDict
.reset(new MutableDictionary());
1094 /* Just delete the possible entry for this domain */
1095 identDict
->removeValue(domain
);
1098 /* obtain public key hash of identity's cert */
1099 SecPointer
<Identity
> identity(Identity::required(idRef
));
1100 SecPointer
<Certificate
> cert
= identity
->certificate();
1101 const CssmData
&pubKeyHash
= cert
->publicKeyHash();
1102 CFRef
<CFDataRef
> pubKeyHashData(CFDataCreate(NULL
, pubKeyHash
.Data
,
1103 pubKeyHash
.Length
));
1105 /* add/replace to dictionary */
1106 identDict
->setValue(domain
, pubKeyHashData
);
1110 if(!identDict
->writePlistToPrefs(IDENTITY_DOMAIN
, Dictionary::US_System
)) {
1111 MacOSError::throwMe(errSecIO
);
1117 const CFStringRef kSecIdentityDomainDefault
= CFSTR("com.apple.systemdefault");
1118 const CFStringRef kSecIdentityDomainKerberosKDC
= CFSTR("com.apple.kerberos.kdc");