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