]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecIdentity.cpp
Security-57337.50.23.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecIdentity.cpp
1 /*
2 * Copyright (c) 2002-2015 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
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>
30
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>
39 #include <syslog.h>
40
41 /* private function declarations */
42 OSStatus
43 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
44 CFTypeRef keychainOrArray,
45 CFStringRef name,
46 int32_t keyUsage,
47 SecKeychainItemRef *itemRef);
48
49 OSStatus SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
50 CFTypeRef keychainOrArray,
51 CFStringRef name,
52 int32_t keyUsage);
53
54
55 CSSM_KEYUSE ConvertArrayToKeyUsage(CFArrayRef usage)
56 {
57 CFIndex count = 0;
58 CSSM_KEYUSE result = (CSSM_KEYUSE) 0;
59
60 if ((NULL == usage) || (0 == (count = CFArrayGetCount(usage))))
61 {
62 return result;
63 }
64
65 for (CFIndex iCnt = 0; iCnt < count; iCnt++)
66 {
67 CFStringRef keyUsageStr = NULL;
68 keyUsageStr = (CFStringRef)CFArrayGetValueAtIndex(usage,iCnt);
69 if (NULL != keyUsageStr)
70 {
71 if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanEncrypt, keyUsageStr, 0))
72 {
73 result |= CSSM_KEYUSE_ENCRYPT;
74 }
75 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanDecrypt, keyUsageStr, 0))
76 {
77 result |= CSSM_KEYUSE_DECRYPT;
78 }
79 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanDerive, keyUsageStr, 0))
80 {
81 result |= CSSM_KEYUSE_DERIVE;
82 }
83 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanSign, keyUsageStr, 0))
84 {
85 result |= CSSM_KEYUSE_SIGN;
86 }
87 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanVerify, keyUsageStr, 0))
88 {
89 result |= CSSM_KEYUSE_VERIFY;
90 }
91 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanWrap, keyUsageStr, 0))
92 {
93 result |= CSSM_KEYUSE_WRAP;
94 }
95 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanUnwrap, keyUsageStr, 0))
96 {
97 result |= CSSM_KEYUSE_UNWRAP;
98 }
99 }
100 }
101
102 return result;
103 }
104
105
106 CFTypeID
107 SecIdentityGetTypeID(void)
108 {
109 BEGIN_SECAPI
110
111 return gTypes().Identity.typeID;
112
113 END_SECAPI1(_kCFRuntimeNotATypeID)
114 }
115
116
117 OSStatus
118 SecIdentityCopyCertificate(
119 SecIdentityRef identityRef,
120 SecCertificateRef *certificateRef)
121 {
122 BEGIN_SECAPI
123
124 SecPointer<Certificate> certificatePtr(Identity::required(identityRef)->certificate());
125 Required(certificateRef) = certificatePtr->handle();
126
127 #if SECTRUST_OSX
128 /* convert outgoing item to a unified SecCertificateRef */
129 CssmData certData = certificatePtr->data();
130 CFDataRef data = NULL;
131 if (certData.Data && certData.Length) {
132 data = CFDataCreate(NULL, certData.Data, certData.Length);
133 }
134 if (!data) {
135 *certificateRef = NULL;
136 syslog(LOG_ERR, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)",
137 (long)certData.Length, (uintptr_t)certData.Data);
138 return errSecInternal;
139 }
140 SecCertificateRef tmpRef = *certificateRef;
141 *certificateRef = SecCertificateCreateWithKeychainItem(NULL, data, tmpRef);
142 if (data)
143 CFRelease(data);
144 if (tmpRef)
145 CFRelease(tmpRef);
146 #endif
147
148 END_SECAPI
149 }
150
151
152 OSStatus
153 SecIdentityCopyPrivateKey(
154 SecIdentityRef identityRef,
155 SecKeyRef *privateKeyRef)
156 {
157 BEGIN_SECAPI
158
159 SecPointer<KeyItem> keyItemPtr(Identity::required(identityRef)->privateKey());
160 Required(privateKeyRef) = keyItemPtr->handle();
161
162 END_SECAPI
163 }
164
165 OSStatus
166 SecIdentityCreateWithCertificate(
167 CFTypeRef keychainOrArray,
168 SecCertificateRef certificate,
169 SecIdentityRef *identityRef)
170 {
171 // This macro converts a new-style SecCertificateRef to an old-style ItemImpl
172 BEGIN_SECCERTAPI
173
174 SecPointer<Certificate> certificatePtr(Certificate::required(__itemImplRef));
175 StorageManager::KeychainList keychains;
176 globals().storageManager.optionalSearchList(keychainOrArray, keychains);
177 SecPointer<Identity> identityPtr(new Identity(keychains, certificatePtr));
178 Required(identityRef) = identityPtr->handle();
179
180 END_SECCERTAPI
181 }
182
183 SecIdentityRef
184 SecIdentityCreate(
185 CFAllocatorRef allocator,
186 SecCertificateRef certificate,
187 SecKeyRef privateKey)
188 {
189 SecIdentityRef identityRef = NULL;
190 OSStatus __secapiresult;
191 SecCertificateRef __itemImplRef=SecCertificateCreateItemImplInstance(certificate);
192 try {
193 SecPointer<Certificate> certificatePtr(Certificate::required(__itemImplRef));
194 SecPointer<KeyItem> keyItemPtr(KeyItem::required(privateKey));
195 SecPointer<Identity> identityPtr(new Identity(keyItemPtr, certificatePtr));
196 identityRef = identityPtr->handle();
197
198 __secapiresult=errSecSuccess;
199 }
200 catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
201 catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
202 catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
203 catch (...) { __secapiresult=errSecInternalComponent; }
204 return identityRef;
205 }
206
207 CFComparisonResult
208 SecIdentityCompare(
209 SecIdentityRef identity1,
210 SecIdentityRef identity2,
211 CFOptionFlags compareOptions)
212 {
213 if (!identity1 || !identity2)
214 {
215 if (identity1 == identity2)
216 return kCFCompareEqualTo;
217 else if (identity1 < identity2)
218 return kCFCompareLessThan;
219 else
220 return kCFCompareGreaterThan;
221 }
222
223 BEGIN_SECAPI
224
225 SecPointer<Identity> id1(Identity::required(identity1));
226 SecPointer<Identity> id2(Identity::required(identity2));
227
228 if (id1 == id2)
229 return kCFCompareEqualTo;
230 else if (id1 < id2)
231 return kCFCompareLessThan;
232 else
233 return kCFCompareGreaterThan;
234
235 END_SECAPI1(kCFCompareGreaterThan);
236 }
237
238 static
239 CFArrayRef _SecIdentityCopyPossiblePaths(
240 CFStringRef name)
241 {
242 // utility function to build and return an array of possible paths for the given name.
243 // if name is not a URL, this returns a single-element array.
244 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
245
246 CFMutableArrayRef names = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
247 if (!name) {
248 return names;
249 }
250 CFIndex oldLength = CFStringGetLength(name);
251 CFArrayAppendValue(names, name);
252
253 CFURLRef url = CFURLCreateWithString(NULL, name, NULL);
254 if (url) {
255 if (CFURLCanBeDecomposed(url)) {
256 // first, remove the query portion of this URL, if any
257 CFStringRef qs = CFURLCopyQueryString(url, NULL);
258 if (qs) {
259 CFMutableStringRef newName = CFStringCreateMutableCopy(NULL, oldLength, name);
260 if (newName) {
261 CFIndex qsLength = CFStringGetLength(qs) + 1; // include the '?'
262 CFStringDelete(newName, CFRangeMake(oldLength-qsLength, qsLength));
263 CFRelease(url);
264 url = CFURLCreateWithString(NULL, newName, NULL);
265 CFArraySetValueAtIndex(names, 0, newName);
266 CFRelease(newName);
267 }
268 CFRelease(qs);
269 }
270 // now add an entry for each level of the path
271 while (url) {
272 CFURLRef parent = CFURLCreateCopyDeletingLastPathComponent(NULL, url);
273 if (parent) {
274 CFStringRef parentURLString = CFURLGetString(parent);
275 if (parentURLString) {
276 CFIndex newLength = CFStringGetLength(parentURLString);
277 // check that string length has decreased as expected; for file URLs,
278 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
279 if ((newLength >= oldLength) || (!CFStringHasPrefix(name, parentURLString))) {
280 CFRelease(parent);
281 CFRelease(url);
282 break;
283 }
284 oldLength = newLength;
285 CFArrayAppendValue(names, parentURLString);
286 }
287 }
288 CFRelease(url);
289 url = parent;
290 }
291 }
292 else {
293 CFRelease(url);
294 }
295 }
296 // finally, add wildcard entries for each subdomain
297 url = CFURLCreateWithString(NULL, name, NULL);
298 if (url) {
299 if (CFURLCanBeDecomposed(url)) {
300 CFStringRef netLocString = CFURLCopyNetLocation(url);
301 if (netLocString) {
302 // first strip off port number, if present
303 CFStringRef tmpLocString = netLocString;
304 CFArrayRef hostnameArray = CFStringCreateArrayBySeparatingStrings(NULL, netLocString, CFSTR(":"));
305 tmpLocString = (CFStringRef)CFRetain((CFStringRef)CFArrayGetValueAtIndex(hostnameArray, 0));
306 CFRelease(netLocString);
307 CFRelease(hostnameArray);
308 netLocString = tmpLocString;
309 // split remaining string into domain components
310 hostnameArray = CFStringCreateArrayBySeparatingStrings(NULL, netLocString, CFSTR("."));
311 CFIndex subdomainCount = CFArrayGetCount(hostnameArray);
312 CFIndex i = 0;
313 while (++i < subdomainCount) {
314 CFIndex j = i;
315 CFMutableStringRef wildcardString = CFStringCreateMutable(NULL, 0);
316 if (wildcardString) {
317 CFStringAppendCString(wildcardString, "*", kCFStringEncodingUTF8);
318 while (j < subdomainCount) {
319 CFStringRef domainString = (CFStringRef)CFArrayGetValueAtIndex(hostnameArray, j++);
320 if (CFStringGetLength(domainString) > 0) {
321 CFStringAppendCString(wildcardString, ".", kCFStringEncodingUTF8);
322 CFStringAppend(wildcardString, domainString);
323 }
324 }
325 if (CFStringGetLength(wildcardString) > 1) {
326 CFArrayAppendValue(names, wildcardString);
327 }
328 CFRelease(wildcardString);
329 }
330 }
331 CFRelease(hostnameArray);
332 CFRelease(netLocString);
333 }
334 }
335 CFRelease(url);
336 }
337
338 return names;
339 }
340
341 static
342 OSStatus _SecIdentityCopyPreferenceMatchingName(
343 CFStringRef name,
344 CSSM_KEYUSE keyUsage,
345 CFArrayRef validIssuers,
346 SecIdentityRef *identity)
347 {
348 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
349 // caller must handle exceptions
350
351 StorageManager::KeychainList keychains;
352 globals().storageManager.getSearchList(keychains);
353 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
354
355 char idUTF8[MAXPATHLEN];
356 Required(name);
357 if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
358 idUTF8[0] = (char)'\0';
359 CssmData service(const_cast<char *>(idUTF8), strlen(idUTF8));
360 FourCharCode itemType = 'iprf';
361 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
362 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType);
363 if (keyUsage)
364 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
365
366 Item prefItem;
367 if (!cursor->next(prefItem))
368 return errSecItemNotFound;
369
370 // get persistent certificate reference
371 SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
372 SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
373 prefItem->getContent(NULL, &itemAttrList, NULL, NULL);
374
375 // find certificate, given persistent reference data
376 CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
377 SecKeychainItemRef certItemRef = nil;
378 OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); //%%% need to make this a method of ItemImpl
379 prefItem->freeContent(&itemAttrList, NULL);
380 if (pItemRef)
381 CFRelease(pItemRef);
382 if (status)
383 return status;
384
385 // filter on valid issuers, if provided
386 if (validIssuers) {
387 //%%%TBI
388 }
389
390 // create identity reference, given certificate
391 #if SECTRUST_OSX
392 status = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)certItemRef, identity);
393 #else
394 try {
395 Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
396 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
397 SecPointer<Identity> identity_ptr(new Identity(keychains, certificate));
398 if (certItemRef) {
399 CFRelease(certItemRef); // retained by identity
400 }
401 Required(identity) = identity_ptr->handle();
402 }
403 catch (const MacOSError &err) { status=err.osStatus(); }
404 catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); }
405 catch (const std::bad_alloc &) { status=errSecAllocate; }
406 catch (...) { status=errSecInvalidItemRef; }
407 #endif
408
409 return status;
410 }
411
412 SecIdentityRef SecIdentityCopyPreferred(CFStringRef name, CFArrayRef keyUsage, CFArrayRef validIssuers)
413 {
414 // This function will look for a matching preference in the following order:
415 // - matches the name and the supplied key use
416 // - matches the name and the special 'ANY' key use
417 // - matches the name with no key usage constraint
418
419 SecIdentityRef identityRef = NULL;
420 CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage);
421 OSStatus status = SecIdentityCopyPreference(name, keyUse, validIssuers, &identityRef);
422 if (status != errSecSuccess && keyUse != CSSM_KEYUSE_ANY)
423 status = SecIdentityCopyPreference(name, CSSM_KEYUSE_ANY, validIssuers, &identityRef);
424 if (status != errSecSuccess && keyUse != 0)
425 status = SecIdentityCopyPreference(name, 0, validIssuers, &identityRef);
426
427 return identityRef;
428 }
429
430 OSStatus SecIdentityCopyPreference(
431 CFStringRef name,
432 CSSM_KEYUSE keyUsage,
433 CFArrayRef validIssuers,
434 SecIdentityRef *identity)
435 {
436 // The original implementation of SecIdentityCopyPreference matches the exact string only.
437 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
438 // and this function is a wrapper which calls it, so that existing clients will get the
439 // extended behavior of server domain matching for items that specify URLs.
440 // (Note that behavior is unchanged if the specified name is not a URL.)
441
442 BEGIN_SECAPI
443
444 CFTypeRef val = (CFTypeRef)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
445 CFSTR("com.apple.security"),
446 kCFPreferencesCurrentUser,
447 kCFPreferencesAnyHost);
448 Boolean logging = false;
449 if (val && CFGetTypeID(val) == CFBooleanGetTypeID()) {
450 logging = CFBooleanGetValue((CFBooleanRef)val);
451 CFRelease(val);
452 }
453
454 OSStatus status = errSecItemNotFound;
455 CFArrayRef names = _SecIdentityCopyPossiblePaths(name);
456 if (!names) {
457 return status;
458 }
459
460 CFIndex idx, total = CFArrayGetCount(names);
461 for (idx = 0; idx < total; idx++) {
462 CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, idx);
463 try {
464 status = _SecIdentityCopyPreferenceMatchingName(aName, keyUsage, validIssuers, identity);
465 }
466 catch (...) { status = errSecItemNotFound; }
467
468 if (logging) {
469 // get identity label
470 CFStringRef labelString = NULL;
471 if (!status && identity && *identity) {
472 try {
473 SecPointer<Certificate> cert(Identity::required(*identity)->certificate());
474 cert->inferLabel(false, &labelString);
475 }
476 catch (...) { labelString = NULL; };
477 }
478 char *labelBuf = NULL;
479 CFIndex labelBufSize = (labelString) ? CFStringGetLength(labelString) * 4 : 4;
480 labelBuf = (char *)malloc(labelBufSize);
481 if (!labelString || !CFStringGetCString(labelString, labelBuf, labelBufSize, kCFStringEncodingUTF8)) {
482 labelBuf[0] = 0;
483 }
484 if (labelString) {
485 CFRelease(labelString);
486 }
487
488 // get service name
489 char *serviceBuf = NULL;
490 CFIndex serviceBufSize = CFStringGetLength(aName) * 4;
491 serviceBuf = (char *)malloc(serviceBufSize);
492 if (!CFStringGetCString(aName, serviceBuf, serviceBufSize, kCFStringEncodingUTF8)) {
493 serviceBuf[0] = 0;
494 }
495
496 syslog(LOG_NOTICE, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf, serviceBuf);
497 if (!status && name) {
498 char *nameBuf = NULL;
499 CFIndex nameBufSize = CFStringGetLength(name) * 4;
500 nameBuf = (char *)malloc(nameBufSize);
501 if (!CFStringGetCString(name, nameBuf, nameBufSize, kCFStringEncodingUTF8)) {
502 nameBuf[0] = 0;
503 }
504 syslog(LOG_NOTICE, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf, nameBuf);
505 free(nameBuf);
506 }
507
508 free(labelBuf);
509 free(serviceBuf);
510 }
511
512 if (status == errSecSuccess) {
513 break; // match found
514 }
515 }
516
517 CFRelease(names);
518 return status;
519
520 END_SECAPI
521 }
522
523 OSStatus SecIdentitySetPreference(
524 SecIdentityRef identity,
525 CFStringRef name,
526 CSSM_KEYUSE keyUsage)
527 {
528 if (!name) {
529 return errSecParam;
530 }
531 if (!identity) {
532 // treat NULL identity as a request to clear the preference
533 // (note: if keyUsage is 0, this clears all key usage prefs for name)
534 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL, name, keyUsage);
535 }
536
537 BEGIN_SECAPI
538
539 SecPointer<Certificate> certificate(Identity::required(identity)->certificate());
540
541 // determine the account attribute
542 //
543 // This attribute must be synthesized from certificate label + pref item type + key usage,
544 // as only the account and service attributes can make a generic keychain item unique.
545 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
546 // we can save a certificate preference if an identity preference already exists for the
547 // given service name, and vice-versa.
548 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
549 //
550 CFStringRef labelStr = nil;
551 certificate->inferLabel(false, &labelStr);
552 if (!labelStr) {
553 MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed"
554 }
555 CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
556 const char *templateStr = "%s [key usage 0x%X]";
557 const int keyUsageMaxStrLen = 8;
558 accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
559 char accountUTF8[accountUTF8Len];
560 if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
561 accountUTF8[0] = (char)'\0';
562 if (keyUsage)
563 snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
564 snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
565 CssmData account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
566 CFRelease(labelStr);
567
568 // service attribute (name provided by the caller)
569 CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name), kCFStringEncodingUTF8) + 1;;
570 char serviceUTF8[serviceUTF8Len];
571 if (!CFStringGetCString(name, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8))
572 serviceUTF8[0] = (char)'\0';
573 CssmData service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8));
574
575 // look for existing identity preference item, in case this is an update
576 StorageManager::KeychainList keychains;
577 globals().storageManager.getSearchList(keychains);
578 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
579 FourCharCode itemType = 'iprf';
580 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
581 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType);
582 if (keyUsage) {
583 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
584 }
585
586 Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
587 bool add = (!cursor->next(item));
588 // at this point, we either have a new item to add or an existing item to update
589
590 // set item attribute values
591 item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
592 item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), itemType);
593 item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
594 item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
595 item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
596
597 // generic attribute (store persistent certificate reference)
598 CFDataRef pItemRef = nil;
599 certificate->copyPersistentReference(pItemRef);
600 if (!pItemRef) {
601 MacOSError::throwMe(errSecInvalidItemRef);
602 }
603 const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
604 CFIndex dataLen = CFDataGetLength(pItemRef);
605 CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
606 item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
607 CFRelease(pItemRef);
608
609 if (add) {
610 Keychain keychain = nil;
611 try {
612 keychain = globals().storageManager.defaultKeychain();
613 if (!keychain->exists())
614 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time.
615 }
616 catch(...) {
617 keychain = globals().storageManager.defaultKeychainUI(item);
618 }
619
620 try {
621 keychain->add(item);
622 }
623 catch (const MacOSError &err) {
624 if (err.osStatus() != errSecDuplicateItem)
625 throw; // if item already exists, fall through to update
626 }
627 }
628 item->update();
629
630 END_SECAPI
631 }
632
633 OSStatus
634 SecIdentitySetPreferred(SecIdentityRef identity, CFStringRef name, CFArrayRef keyUsage)
635 {
636 CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage);
637 return SecIdentitySetPreference(identity, name, keyUse);
638 }
639
640 OSStatus
641 SecIdentityFindPreferenceItem(
642 CFTypeRef keychainOrArray,
643 CFStringRef idString,
644 SecKeychainItemRef *itemRef)
645 {
646 BEGIN_SECAPI
647
648 StorageManager::KeychainList keychains;
649 globals().storageManager.optionalSearchList(keychainOrArray, keychains);
650 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
651
652 char idUTF8[MAXPATHLEN];
653 idUTF8[0] = (char)'\0';
654 if (idString)
655 {
656 if (!CFStringGetCString(idString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
657 idUTF8[0] = (char)'\0';
658 }
659 size_t idUTF8Len = strlen(idUTF8);
660 if (!idUTF8Len)
661 MacOSError::throwMe(errSecParam);
662
663 CssmData service(const_cast<char *>(idUTF8), idUTF8Len);
664 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
665 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
666
667 Item item;
668 if (!cursor->next(item))
669 MacOSError::throwMe(errSecItemNotFound);
670
671 if (itemRef)
672 *itemRef=item->handle();
673
674 END_SECAPI
675 }
676
677 OSStatus
678 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
679 CFTypeRef keychainOrArray,
680 CFStringRef name,
681 int32_t keyUsage,
682 SecKeychainItemRef *itemRef)
683 {
684 BEGIN_SECAPI
685
686 StorageManager::KeychainList keychains;
687 globals().storageManager.optionalSearchList(keychainOrArray, keychains);
688 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
689
690 char idUTF8[MAXPATHLEN];
691 idUTF8[0] = (char)'\0';
692 if (name)
693 {
694 if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
695 idUTF8[0] = (char)'\0';
696 }
697 size_t idUTF8Len = strlen(idUTF8);
698 if (!idUTF8Len)
699 MacOSError::throwMe(errSecParam);
700
701 CssmData service(const_cast<char *>(idUTF8), idUTF8Len);
702 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
703 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
704 if (keyUsage)
705 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
706
707 Item item;
708 if (!cursor->next(item))
709 MacOSError::throwMe(errSecItemNotFound);
710
711 if (itemRef)
712 *itemRef=item->handle();
713
714 END_SECAPI
715 }
716
717 OSStatus SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
718 CFTypeRef keychainOrArray,
719 CFStringRef name,
720 int32_t keyUsage)
721 {
722 // when a specific key usage is passed, we'll only match & delete that pref;
723 // when a key usage of 0 is passed, all matching prefs should be deleted.
724 // maxUsages represents the most matches there could theoretically be, so
725 // cut things off at that point if we're still finding items (if they can't
726 // be deleted for some reason, we'd never break out of the loop.)
727
728 OSStatus status;
729 SecKeychainItemRef item = NULL;
730 int count = 0, maxUsages = 12;
731 while (++count <= maxUsages &&
732 (status = SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray, name, keyUsage, &item)) == errSecSuccess) {
733 status = SecKeychainItemDelete(item);
734 CFRelease(item);
735 item = NULL;
736 }
737
738 // it's not an error if the item isn't found
739 return (status == errSecItemNotFound) ? errSecSuccess : status;
740 }
741
742
743 static
744 OSStatus _SecIdentityAddPreferenceItemWithName(
745 SecKeychainRef keychainRef,
746 SecIdentityRef identityRef,
747 CFStringRef idString,
748 SecKeychainItemRef *itemRef)
749 {
750 // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
751 // caller must handle exceptions
752
753 if (!identityRef || !idString)
754 return errSecParam;
755 SecPointer<Certificate> cert(Identity::required(identityRef)->certificate());
756 Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
757 sint32 keyUsage = 0;
758
759 // determine the account attribute
760 //
761 // This attribute must be synthesized from certificate label + pref item type + key usage,
762 // as only the account and service attributes can make a generic keychain item unique.
763 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
764 // we can save a certificate preference if an identity preference already exists for the
765 // given service name, and vice-versa.
766 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
767 //
768 CFStringRef labelStr = nil;
769 cert->inferLabel(false, &labelStr);
770 if (!labelStr) {
771 return errSecDataTooLarge; // data is "in a format which cannot be displayed"
772 }
773 CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
774 const char *templateStr = "%s [key usage 0x%X]";
775 const int keyUsageMaxStrLen = 8;
776 accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
777 char accountUTF8[accountUTF8Len];
778 if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
779 accountUTF8[0] = (char)'\0';
780 if (keyUsage)
781 snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
782 snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
783 CssmData account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
784 CFRelease(labelStr);
785
786 // service attribute (name provided by the caller)
787 CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString), kCFStringEncodingUTF8) + 1;;
788 char serviceUTF8[serviceUTF8Len];
789 if (!CFStringGetCString(idString, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8))
790 serviceUTF8[0] = (char)'\0';
791 CssmData service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8));
792
793 // set item attribute values
794 item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
795 item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
796 item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
797 item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
798 item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), keyUsage);
799
800 // generic attribute (store persistent certificate reference)
801 CFDataRef pItemRef = nil;
802 OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert->handle(), &pItemRef);
803 if (!pItemRef)
804 status = errSecInvalidItemRef;
805 if (status)
806 return status;
807 const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
808 CFIndex dataLen = CFDataGetLength(pItemRef);
809 CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
810 item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
811 CFRelease(pItemRef);
812
813 Keychain keychain = nil;
814 try {
815 keychain = Keychain::optional(keychainRef);
816 if (!keychain->exists())
817 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time.
818 }
819 catch(...) {
820 keychain = globals().storageManager.defaultKeychainUI(item);
821 }
822
823 try {
824 keychain->add(item);
825 }
826 catch (const MacOSError &err) {
827 if (err.osStatus() != errSecDuplicateItem)
828 throw; // if item already exists, fall through to update
829 }
830
831 item->update();
832
833 if (itemRef)
834 *itemRef = item->handle();
835
836 return status;
837 }
838
839 OSStatus SecIdentityAddPreferenceItem(
840 SecKeychainRef keychainRef,
841 SecIdentityRef identityRef,
842 CFStringRef idString,
843 SecKeychainItemRef *itemRef)
844 {
845 // The original implementation of SecIdentityAddPreferenceItem adds the exact string only.
846 // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above),
847 // and this function is a wrapper which calls it, so that existing clients will get the
848 // extended behavior of server domain matching for items that specify URLs.
849 // (Note that behavior is unchanged if the specified idString is not a URL.)
850
851 BEGIN_SECAPI
852
853 OSStatus status = errSecInternalComponent;
854 CFArrayRef names = _SecIdentityCopyPossiblePaths(idString);
855 if (!names) {
856 return status;
857 }
858
859 CFIndex total = CFArrayGetCount(names);
860 if (total > 0) {
861 // add item for name (first element in array)
862 CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
863 try {
864 status = _SecIdentityAddPreferenceItemWithName(keychainRef, identityRef, aName, itemRef);
865 }
866 catch (const MacOSError &err) { status=err.osStatus(); }
867 catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); }
868 catch (const std::bad_alloc &) { status=errSecAllocate; }
869 catch (...) { status=errSecInternalComponent; }
870 }
871 if (total > 2) {
872 Boolean setDomainDefaultIdentity = FALSE;
873 CFTypeRef val = (CFTypeRef)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"),
874 CFSTR("com.apple.security.identities"),
875 kCFPreferencesCurrentUser,
876 kCFPreferencesAnyHost);
877 if (val) {
878 if (CFGetTypeID(val) == CFBooleanGetTypeID())
879 setDomainDefaultIdentity = CFBooleanGetValue((CFBooleanRef)val) ? TRUE : FALSE;
880 CFRelease(val);
881 }
882 if (setDomainDefaultIdentity) {
883 // add item for domain (second-to-last element in array, e.g. "*.apple.com")
884 OSStatus tmpStatus = errSecSuccess;
885 CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, total-2);
886 try {
887 tmpStatus = _SecIdentityAddPreferenceItemWithName(keychainRef, identityRef, aName, itemRef);
888 }
889 catch (const MacOSError &err) { tmpStatus=err.osStatus(); }
890 catch (const CommonError &err) { tmpStatus=SecKeychainErrFromOSStatus(err.osStatus()); }
891 catch (const std::bad_alloc &) { tmpStatus=errSecAllocate; }
892 catch (...) { tmpStatus=errSecInternalComponent; }
893 }
894 }
895
896 CFRelease(names);
897 return status;
898
899 END_SECAPI
900 }
901
902 /* deprecated in 10.5 */
903 OSStatus SecIdentityUpdatePreferenceItem(
904 SecKeychainItemRef itemRef,
905 SecIdentityRef identityRef)
906 {
907 BEGIN_SECAPI
908
909 if (!itemRef || !identityRef)
910 MacOSError::throwMe(errSecParam);
911 SecPointer<Certificate> certificate(Identity::required(identityRef)->certificate());
912 Item prefItem = ItemImpl::required(itemRef);
913
914 // get the current key usage value for this item
915 sint32 keyUsage = 0;
916 UInt32 actLen = 0;
917 SecKeychainAttribute attr = { kSecScriptCodeItemAttr, sizeof(sint32), &keyUsage };
918 try {
919 prefItem->getAttribute(attr, &actLen);
920 }
921 catch(...) {
922 keyUsage = 0;
923 };
924
925 // set the account attribute
926 //
927 // This attribute must be synthesized from certificate label + pref item type + key usage,
928 // as only the account and service attributes can make a generic keychain item unique.
929 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
930 // we can save a certificate preference if an identity preference already exists for the
931 // given service name, and vice-versa.
932 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
933 //
934 CFStringRef labelStr = nil;
935 certificate->inferLabel(false, &labelStr);
936 if (!labelStr) {
937 MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed"
938 }
939 CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
940 const char *templateStr = "%s [key usage 0x%X]";
941 const int keyUsageMaxStrLen = 8;
942 accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
943 char accountUTF8[accountUTF8Len];
944 if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
945 accountUTF8[0] = (char)'\0';
946 if (keyUsage)
947 snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
948 snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
949 CssmData account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
950 prefItem->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
951 CFRelease(labelStr);
952
953 // generic attribute (store persistent certificate reference)
954 CFDataRef pItemRef = nil;
955 OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)certificate->handle(), &pItemRef);
956 if (!pItemRef)
957 status = errSecInvalidItemRef;
958 if (status)
959 MacOSError::throwMe(status);
960 const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
961 CFIndex dataLen = CFDataGetLength(pItemRef);
962 CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
963 prefItem->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
964 CFRelease(pItemRef);
965
966 prefItem->update();
967
968 END_SECAPI
969 }
970
971 OSStatus SecIdentityCopyFromPreferenceItem(
972 SecKeychainItemRef itemRef,
973 SecIdentityRef *identityRef)
974 {
975 BEGIN_SECAPI
976
977 if (!itemRef || !identityRef)
978 MacOSError::throwMe(errSecParam);
979 Item prefItem = ItemImpl::required(itemRef);
980
981 // get persistent certificate reference
982 SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
983 SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
984 prefItem->getContent(NULL, &itemAttrList, NULL, NULL);
985
986 // find certificate, given persistent reference data
987 CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
988 SecKeychainItemRef certItemRef = nil;
989 OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); //%%% need to make this a method of ItemImpl
990 prefItem->freeContent(&itemAttrList, NULL);
991 if (pItemRef)
992 CFRelease(pItemRef);
993 if (status)
994 return status;
995
996 // create identity reference, given certificate
997 StorageManager::KeychainList keychains;
998 globals().storageManager.optionalSearchList((CFTypeRef)NULL, keychains);
999 Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
1000 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
1001 SecPointer<Identity> identity(new Identity(keychains, certificate));
1002 if (certItemRef)
1003 CFRelease(certItemRef);
1004
1005 Required(identityRef) = identity->handle();
1006
1007 END_SECAPI
1008 }
1009
1010 /*
1011 * System Identity Support.
1012 */
1013
1014 /* plist domain (in /Library/Preferences) */
1015 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
1016
1017 /*
1018 * Our plist is a dictionary whose entries have the following format:
1019 * key = domain name as CFString
1020 * value = public key hash as CFData
1021 */
1022
1023 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
1024
1025 /*
1026 * All accesses to system identities and its associated plist are
1027 * protected by this lock.
1028 */
1029 ModuleNexus<Mutex> systemIdentityLock;
1030
1031 OSStatus SecIdentityCopySystemIdentity(
1032 CFStringRef domain,
1033 SecIdentityRef *idRef,
1034 CFStringRef *actualDomain) /* optional */
1035 {
1036 BEGIN_SECAPI
1037
1038 StLock<Mutex> _(systemIdentityLock());
1039 auto_ptr<Dictionary> identDict;
1040
1041 /* get top-level dictionary - if not present, we're done */
1042 Dictionary* d = Dictionary::CreateDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
1043 if (d == NULL)
1044 {
1045 return errSecNotAvailable;
1046 }
1047
1048 identDict.reset(d);
1049
1050 /* see if there's an entry for specified domain */
1051 CFDataRef entryValue = identDict->getDataValue(domain);
1052 if(entryValue == NULL) {
1053 /* try for default entry if we're not already looking for default */
1054 if(!CFEqual(domain, kSecIdentityDomainDefault)) {
1055 entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
1056 }
1057 if(entryValue == NULL) {
1058 /* no default identity */
1059 MacOSError::throwMe(errSecItemNotFound);
1060 }
1061
1062 /* remember that we're not fetching the requested domain */
1063 domain = kSecIdentityDomainDefault;
1064 }
1065
1066 /* open system keychain - error here is fatal */
1067 Keychain systemKc = globals().storageManager.make(SYSTEM_KEYCHAIN_PATH, false);
1068 CFRef<SecKeychainRef> systemKcRef(systemKc->handle());
1069 StorageManager::KeychainList keychains;
1070 globals().storageManager.optionalSearchList(systemKcRef, keychains);
1071
1072 /* search for specified cert */
1073 SecKeychainAttributeList attrList;
1074 SecKeychainAttribute attr;
1075 attr.tag = kSecPublicKeyHashItemAttr;
1076 attr.length = (UInt32)CFDataGetLength(entryValue);
1077 attr.data = (void *)CFDataGetBytePtr(entryValue);
1078 attrList.count = 1;
1079 attrList.attr = &attr;
1080
1081 KCCursor cursor(keychains, kSecCertificateItemClass, &attrList);
1082 Item certItem;
1083 if(!cursor->next(certItem)) {
1084 MacOSError::throwMe(errSecItemNotFound);
1085 }
1086
1087 /* found the cert; try matching with key to cook up identity */
1088 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
1089 SecPointer<Identity> identity(new Identity(keychains, certificate));
1090
1091 Required(idRef) = identity->handle();
1092 if(actualDomain) {
1093 *actualDomain = domain;
1094 CFRetain(*actualDomain);
1095 }
1096
1097 END_SECAPI
1098 }
1099
1100 OSStatus SecIdentitySetSystemIdentity(
1101 CFStringRef domain,
1102 SecIdentityRef idRef)
1103 {
1104 BEGIN_SECAPI
1105
1106 StLock<Mutex> _(systemIdentityLock());
1107 if(geteuid() != 0) {
1108 MacOSError::throwMe(errSecAuthFailed);
1109 }
1110
1111 auto_ptr<MutableDictionary> identDict;
1112 MutableDictionary *d = MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
1113 if (d)
1114 {
1115 identDict.reset(d);
1116 }
1117 else
1118 {
1119 if(idRef == NULL) {
1120 /* nothing there, nothing to set - done */
1121 return errSecSuccess;
1122 }
1123 identDict.reset(new MutableDictionary());
1124 }
1125
1126 if(idRef == NULL) {
1127 /* Just delete the possible entry for this domain */
1128 identDict->removeValue(domain);
1129 }
1130 else {
1131 /* obtain public key hash of identity's cert */
1132 SecPointer<Identity> identity(Identity::required(idRef));
1133 SecPointer<Certificate> cert = identity->certificate();
1134 const CssmData &pubKeyHash = cert->publicKeyHash();
1135 CFRef<CFDataRef> pubKeyHashData(CFDataCreate(NULL, pubKeyHash.Data,
1136 pubKeyHash.Length));
1137
1138 /* add/replace to dictionary */
1139 identDict->setValue(domain, pubKeyHashData);
1140 }
1141
1142 /* flush to disk */
1143 if(!identDict->writePlistToPrefs(IDENTITY_DOMAIN, Dictionary::US_System)) {
1144 MacOSError::throwMe(errSecIO);
1145 }
1146
1147 END_SECAPI
1148 }
1149
1150 const CFStringRef kSecIdentityDomainDefault = CFSTR("com.apple.systemdefault");
1151 const CFStringRef kSecIdentityDomainKerberosKDC = CFSTR("com.apple.kerberos.kdc");
1152