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