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