]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecIdentity.cpp
Security-59306.101.1.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 = (char *)malloc(accountUTF8Len);
600 if (!accountUTF8) {
601 MacOSError::throwMe(errSecMemoryError);
602 }
603 if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
604 accountUTF8[0] = (char)'\0';
605 if (keyUsage)
606 snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
607 snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
608 CssmDataContainer account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
609 free(accountUTF8);
610 CFRelease(labelStr);
611
612 // service attribute (name provided by the caller)
613 CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name), kCFStringEncodingUTF8) + 1;;
614 char *serviceUTF8 = (char *)malloc(serviceUTF8Len);
615 if (!serviceUTF8) {
616 MacOSError::throwMe(errSecMemoryError);
617 }
618 if (!CFStringGetCString(name, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8))
619 serviceUTF8[0] = (char)'\0';
620 CssmDataContainer service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8));
621 free(serviceUTF8);
622
623 // look for existing identity preference item, in case this is an update
624 StorageManager::KeychainList keychains;
625 globals().storageManager.getSearchList(keychains);
626 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
627 FourCharCode itemType = 'iprf';
628 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
629 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType);
630 if (keyUsage) {
631 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
632 }
633
634 Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
635 bool add = (!cursor->next(item));
636 // at this point, we either have a new item to add or an existing item to update
637
638 // set item attribute values
639 item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
640 item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), itemType);
641 item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
642 item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
643 item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
644
645 // generic attribute (store persistent certificate reference)
646 CFDataRef pItemRef = nil;
647 SecKeychainItemCreatePersistentReference((SecKeychainItemRef)certRef.get(), &pItemRef);
648 if (!pItemRef) {
649 MacOSError::throwMe(errSecInvalidItemRef);
650 }
651 const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
652 CFIndex dataLen = CFDataGetLength(pItemRef);
653 CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
654 item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
655 CFRelease(pItemRef);
656
657 if (add) {
658 Keychain keychain = nil;
659 try {
660 keychain = globals().storageManager.defaultKeychain();
661 if (!keychain->exists())
662 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time.
663 }
664 catch(...) {
665 keychain = globals().storageManager.defaultKeychainUI(item);
666 }
667
668 try {
669 keychain->add(item);
670 }
671 catch (const MacOSError &err) {
672 if (err.osStatus() != errSecDuplicateItem)
673 throw; // if item already exists, fall through to update
674 }
675 }
676 item->update();
677
678 END_SECAPI
679 }
680
681 OSStatus
682 SecIdentitySetPreferred(SecIdentityRef identity, CFStringRef name, CFArrayRef keyUsage)
683 {
684 CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage);
685 return SecIdentitySetPreference(identity, name, keyUse);
686 }
687
688 OSStatus
689 SecIdentityFindPreferenceItem(
690 CFTypeRef keychainOrArray,
691 CFStringRef idString,
692 SecKeychainItemRef *itemRef)
693 {
694 BEGIN_SECAPI
695 os_activity_t activity = os_activity_create("SecIdentityFindPreferenceItem", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
696 os_activity_scope(activity);
697 os_release(activity);
698
699 StorageManager::KeychainList keychains;
700 globals().storageManager.optionalSearchList(keychainOrArray, keychains);
701 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
702
703 char idUTF8[MAXPATHLEN];
704 idUTF8[0] = (char)'\0';
705 if (idString)
706 {
707 if (!CFStringGetCString(idString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
708 idUTF8[0] = (char)'\0';
709 }
710 size_t idUTF8Len = strlen(idUTF8);
711 if (!idUTF8Len)
712 MacOSError::throwMe(errSecParam);
713
714 CssmData service(const_cast<char *>(idUTF8), idUTF8Len);
715 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
716 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
717
718 Item item;
719 if (!cursor->next(item))
720 MacOSError::throwMe(errSecItemNotFound);
721
722 if (itemRef)
723 *itemRef=item->handle();
724
725 END_SECAPI
726 }
727
728 OSStatus
729 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
730 CFTypeRef keychainOrArray,
731 CFStringRef name,
732 int32_t keyUsage,
733 SecKeychainItemRef *itemRef)
734 {
735 BEGIN_SECAPI
736 os_activity_t activity = os_activity_create("SecIdentityFindPreferenceItemWithNameAndKeyUsage", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
737 os_activity_scope(activity);
738 os_release(activity);
739
740 StorageManager::KeychainList keychains;
741 globals().storageManager.optionalSearchList(keychainOrArray, keychains);
742 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
743
744 char idUTF8[MAXPATHLEN];
745 idUTF8[0] = (char)'\0';
746 if (name)
747 {
748 if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
749 idUTF8[0] = (char)'\0';
750 }
751 size_t idUTF8Len = strlen(idUTF8);
752 if (!idUTF8Len)
753 MacOSError::throwMe(errSecParam);
754
755 CssmData service(const_cast<char *>(idUTF8), idUTF8Len);
756 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
757 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
758 if (keyUsage)
759 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
760
761 Item item;
762 if (!cursor->next(item))
763 MacOSError::throwMe(errSecItemNotFound);
764
765 if (itemRef)
766 *itemRef=item->handle();
767
768 END_SECAPI
769 }
770
771 OSStatus SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
772 CFTypeRef keychainOrArray,
773 CFStringRef name,
774 int32_t keyUsage)
775 {
776 // when a specific key usage is passed, we'll only match & delete that pref;
777 // when a key usage of 0 is passed, all matching prefs should be deleted.
778 // maxUsages represents the most matches there could theoretically be, so
779 // cut things off at that point if we're still finding items (if they can't
780 // be deleted for some reason, we'd never break out of the loop.)
781
782 OSStatus status = errSecInternalError;
783 SecKeychainItemRef item = NULL;
784 int count = 0, maxUsages = 12;
785 while (++count <= maxUsages &&
786 (status = SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray, name, keyUsage, &item)) == errSecSuccess) {
787 status = SecKeychainItemDelete(item);
788 CFRelease(item);
789 item = NULL;
790 }
791
792 // it's not an error if the item isn't found
793 return (status == errSecItemNotFound) ? errSecSuccess : status;
794 }
795
796
797 static
798 OSStatus _SecIdentityAddPreferenceItemWithName(
799 SecKeychainRef keychainRef,
800 SecIdentityRef identityRef,
801 CFStringRef idString,
802 SecKeychainItemRef *itemRef)
803 {
804 // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
805 // caller must handle exceptions
806
807 if (!identityRef || !idString)
808 return errSecParam;
809 SecPointer<Certificate> cert(Identity::required(identityRef)->certificate());
810 Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
811 sint32 keyUsage = 0;
812
813 // determine the account attribute
814 //
815 // This attribute must be synthesized from certificate label + pref item type + key usage,
816 // as only the account and service attributes can make a generic keychain item unique.
817 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
818 // we can save a certificate preference if an identity preference already exists for the
819 // given service name, and vice-versa.
820 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
821 //
822 CFStringRef labelStr = nil;
823 cert->inferLabel(false, &labelStr);
824 if (!labelStr) {
825 return errSecDataTooLarge; // data is "in a format which cannot be displayed"
826 }
827 CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
828 const char *templateStr = "%s [key usage 0x%X]";
829 const int keyUsageMaxStrLen = 8;
830 accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
831 char *accountUTF8 = (char *)malloc(accountUTF8Len);
832 if (!accountUTF8) {
833 MacOSError::throwMe(errSecMemoryError);
834 }
835 if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
836 accountUTF8[0] = (char)'\0';
837 if (keyUsage)
838 snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
839 snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
840 CssmDataContainer account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
841 free(accountUTF8);
842 CFRelease(labelStr);
843
844 // service attribute (name provided by the caller)
845 CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString), kCFStringEncodingUTF8) + 1;;
846 char *serviceUTF8 = (char *)malloc(serviceUTF8Len);
847 if (!serviceUTF8) {
848 MacOSError::throwMe(errSecMemoryError);
849 }
850 if (!CFStringGetCString(idString, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8))
851 serviceUTF8[0] = (char)'\0';
852 CssmDataContainer service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8));
853 free(serviceUTF8);
854
855 // set item attribute values
856 item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
857 item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
858 item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
859 item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
860 item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), keyUsage);
861
862 // generic attribute (store persistent certificate reference)
863 CFDataRef pItemRef = nil;
864 OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert->handle(), &pItemRef);
865 if (!pItemRef)
866 status = errSecInvalidItemRef;
867 if (status)
868 return status;
869 const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
870 CFIndex dataLen = CFDataGetLength(pItemRef);
871 CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
872 item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
873 CFRelease(pItemRef);
874
875 Keychain keychain = nil;
876 try {
877 keychain = Keychain::optional(keychainRef);
878 if (!keychain->exists())
879 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time.
880 }
881 catch(...) {
882 keychain = globals().storageManager.defaultKeychainUI(item);
883 }
884
885 try {
886 keychain->add(item);
887 }
888 catch (const MacOSError &err) {
889 if (err.osStatus() != errSecDuplicateItem)
890 throw; // if item already exists, fall through to update
891 }
892
893 item->update();
894
895 if (itemRef)
896 *itemRef = item->handle();
897
898 return status;
899 }
900
901 OSStatus SecIdentityAddPreferenceItem(
902 SecKeychainRef keychainRef,
903 SecIdentityRef identityRef,
904 CFStringRef idString,
905 SecKeychainItemRef *itemRef)
906 {
907 // The original implementation of SecIdentityAddPreferenceItem adds the exact string only.
908 // That implementation has been moved to _SecIdentityAddPreferenceItemWithName (above),
909 // and this function is a wrapper which calls it, so that existing clients will get the
910 // extended behavior of server domain matching for items that specify URLs.
911 // (Note that behavior is unchanged if the specified idString is not a URL.)
912
913 BEGIN_SECAPI
914 os_activity_t activity = os_activity_create("SecIdentityAddPreferenceItem", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
915 os_activity_scope(activity);
916 os_release(activity);
917
918 OSStatus status = errSecInternalComponent;
919 CFArrayRef names = _SecIdentityCopyPossiblePaths(idString);
920 if (!names) {
921 return status;
922 }
923
924 CFIndex total = CFArrayGetCount(names);
925 if (total > 0) {
926 // add item for name (first element in array)
927 CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
928 try {
929 status = _SecIdentityAddPreferenceItemWithName(keychainRef, identityRef, aName, itemRef);
930 }
931 catch (const MacOSError &err) { status=err.osStatus(); }
932 catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); }
933 catch (const std::bad_alloc &) { status=errSecAllocate; }
934 catch (...) { status=errSecInternalComponent; }
935 }
936 if (total > 2) {
937 Boolean setDomainDefaultIdentity = FALSE;
938 CFTypeRef val = (CFTypeRef)CFPreferencesCopyValue(CFSTR("SetDomainDefaultIdentity"),
939 CFSTR("com.apple.security.identities"),
940 kCFPreferencesCurrentUser,
941 kCFPreferencesAnyHost);
942 if (val) {
943 if (CFGetTypeID(val) == CFBooleanGetTypeID())
944 setDomainDefaultIdentity = CFBooleanGetValue((CFBooleanRef)val) ? TRUE : FALSE;
945 CFRelease(val);
946 }
947 if (setDomainDefaultIdentity) {
948 // add item for domain (second-to-last element in array, e.g. "*.apple.com")
949 OSStatus tmpStatus = errSecSuccess;
950 CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, total-2);
951 try {
952 tmpStatus = _SecIdentityAddPreferenceItemWithName(keychainRef, identityRef, aName, itemRef);
953 }
954 catch (const MacOSError &err) { tmpStatus=err.osStatus(); }
955 catch (const CommonError &err) { tmpStatus=SecKeychainErrFromOSStatus(err.osStatus()); }
956 catch (const std::bad_alloc &) { tmpStatus=errSecAllocate; }
957 catch (...) { tmpStatus=errSecInternalComponent; }
958 }
959 }
960
961 CFRelease(names);
962 return status;
963
964 END_SECAPI
965 }
966
967 /* deprecated in 10.5 */
968 OSStatus SecIdentityUpdatePreferenceItem(
969 SecKeychainItemRef itemRef,
970 SecIdentityRef identityRef)
971 {
972 BEGIN_SECAPI
973 os_activity_t activity = os_activity_create("SecIdentityUpdatePreferenceItem", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
974 os_activity_scope(activity);
975 os_release(activity);
976
977 if (!itemRef || !identityRef)
978 MacOSError::throwMe(errSecParam);
979 SecPointer<Certificate> certificate(Identity::required(identityRef)->certificate());
980 Item prefItem = ItemImpl::required(itemRef);
981
982 // get the current key usage value for this item
983 sint32 keyUsage = 0;
984 UInt32 actLen = 0;
985 SecKeychainAttribute attr = { kSecScriptCodeItemAttr, sizeof(sint32), &keyUsage };
986 try {
987 prefItem->getAttribute(attr, &actLen);
988 }
989 catch(...) {
990 keyUsage = 0;
991 };
992
993 // set the account attribute
994 //
995 // This attribute must be synthesized from certificate label + pref item type + key usage,
996 // as only the account and service attributes can make a generic keychain item unique.
997 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
998 // we can save a certificate preference if an identity preference already exists for the
999 // given service name, and vice-versa.
1000 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
1001 //
1002 CFStringRef labelStr = nil;
1003 certificate->inferLabel(false, &labelStr);
1004 if (!labelStr) {
1005 MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed"
1006 }
1007 CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
1008 const char *templateStr = "%s [key usage 0x%X]";
1009 const int keyUsageMaxStrLen = 8;
1010 accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
1011 char *accountUTF8 = (char *)malloc(accountUTF8Len);
1012 if (!accountUTF8) {
1013 MacOSError::throwMe(errSecMemoryError);
1014 }
1015 if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
1016 accountUTF8[0] = (char)'\0';
1017 if (keyUsage)
1018 snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
1019 snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
1020 CssmDataContainer account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
1021 prefItem->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
1022 free(accountUTF8);
1023 CFRelease(labelStr);
1024
1025 // generic attribute (store persistent certificate reference)
1026 CFDataRef pItemRef = nil;
1027 OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)certificate->handle(), &pItemRef);
1028 if (!pItemRef)
1029 status = errSecInvalidItemRef;
1030 if (status)
1031 MacOSError::throwMe(status);
1032 const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
1033 CFIndex dataLen = CFDataGetLength(pItemRef);
1034 CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
1035 prefItem->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
1036 CFRelease(pItemRef);
1037
1038 prefItem->update();
1039
1040 END_SECAPI
1041 }
1042
1043 OSStatus SecIdentityCopyFromPreferenceItem(
1044 SecKeychainItemRef itemRef,
1045 SecIdentityRef *identityRef)
1046 {
1047 BEGIN_SECAPI
1048 os_activity_t activity = os_activity_create("SecIdentityCopyFromPreferenceItem", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
1049 os_activity_scope(activity);
1050 os_release(activity);
1051
1052 if (!itemRef || !identityRef)
1053 MacOSError::throwMe(errSecParam);
1054 Item prefItem = ItemImpl::required(itemRef);
1055
1056 // get persistent certificate reference
1057 SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
1058 SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
1059 prefItem->getContent(NULL, &itemAttrList, NULL, NULL);
1060
1061 // find certificate, given persistent reference data
1062 CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
1063 SecKeychainItemRef certItemRef = nil;
1064 OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); //%%% need to make this a method of ItemImpl
1065 prefItem->freeContent(&itemAttrList, NULL);
1066 if (pItemRef)
1067 CFRelease(pItemRef);
1068 if (status)
1069 return status;
1070
1071 // create identity reference, given certificate
1072 StorageManager::KeychainList keychains;
1073 globals().storageManager.optionalSearchList((CFTypeRef)NULL, keychains);
1074 Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
1075 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
1076 SecPointer<Identity> identity(new Identity(keychains, certificate));
1077 if (certItemRef)
1078 CFRelease(certItemRef);
1079
1080 Required(identityRef) = identity->handle();
1081
1082 END_SECAPI
1083 }
1084
1085 /*
1086 * System Identity Support.
1087 */
1088
1089 /* plist domain (in /Library/Preferences) */
1090 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
1091
1092 /*
1093 * Our plist is a dictionary whose entries have the following format:
1094 * key = domain name as CFString
1095 * value = public key hash as CFData
1096 */
1097
1098 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
1099
1100 /*
1101 * All accesses to system identities and its associated plist are
1102 * protected by this lock.
1103 */
1104 ModuleNexus<Mutex> systemIdentityLock;
1105
1106 OSStatus SecIdentityCopySystemIdentity(
1107 CFStringRef domain,
1108 SecIdentityRef *idRef,
1109 CFStringRef *actualDomain) /* optional */
1110 {
1111 BEGIN_SECAPI
1112 os_activity_t activity = os_activity_create("SecIdentityCopySystemIdentity", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
1113 os_activity_scope(activity);
1114 os_release(activity);
1115
1116 StLock<Mutex> _(systemIdentityLock());
1117 auto_ptr<Dictionary> identDict;
1118
1119 /* get top-level dictionary - if not present, we're done */
1120 Dictionary* d = Dictionary::CreateDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
1121 if (d == NULL)
1122 {
1123 return errSecNotAvailable;
1124 }
1125
1126 identDict.reset(d);
1127
1128 /* see if there's an entry for specified domain */
1129 CFDataRef entryValue = identDict->getDataValue(domain);
1130 if(entryValue == NULL) {
1131 /* try for default entry if we're not already looking for default */
1132 if(!CFEqual(domain, kSecIdentityDomainDefault)) {
1133 entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
1134 }
1135 if(entryValue == NULL) {
1136 /* no default identity */
1137 MacOSError::throwMe(errSecItemNotFound);
1138 }
1139
1140 /* remember that we're not fetching the requested domain */
1141 domain = kSecIdentityDomainDefault;
1142 }
1143
1144 /* open system keychain - error here is fatal */
1145 Keychain systemKc = globals().storageManager.make(SYSTEM_KEYCHAIN_PATH, false);
1146 CFRef<SecKeychainRef> systemKcRef(systemKc->handle());
1147 StorageManager::KeychainList keychains;
1148 globals().storageManager.optionalSearchList(systemKcRef, keychains);
1149
1150 /* search for specified cert */
1151 SecKeychainAttributeList attrList;
1152 SecKeychainAttribute attr;
1153 attr.tag = kSecPublicKeyHashItemAttr;
1154 attr.length = (UInt32)CFDataGetLength(entryValue);
1155 attr.data = (void *)CFDataGetBytePtr(entryValue);
1156 attrList.count = 1;
1157 attrList.attr = &attr;
1158
1159 KCCursor cursor(keychains, kSecCertificateItemClass, &attrList);
1160 Item certItem;
1161 if(!cursor->next(certItem)) {
1162 MacOSError::throwMe(errSecItemNotFound);
1163 }
1164
1165 /* found the cert; try matching with key to cook up identity */
1166 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
1167 SecPointer<Identity> identity(new Identity(keychains, certificate));
1168
1169 Required(idRef) = identity->handle();
1170 if(actualDomain) {
1171 *actualDomain = domain;
1172 CFRetain(*actualDomain);
1173 }
1174
1175 END_SECAPI
1176 }
1177
1178 OSStatus SecIdentitySetSystemIdentity(
1179 CFStringRef domain,
1180 SecIdentityRef idRef)
1181 {
1182 BEGIN_SECAPI
1183 os_activity_t activity = os_activity_create("SecIdentitySetSystemIdentity", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
1184 os_activity_scope(activity);
1185 os_release(activity);
1186
1187 StLock<Mutex> _(systemIdentityLock());
1188 if(geteuid() != 0) {
1189 MacOSError::throwMe(errSecAuthFailed);
1190 }
1191
1192 auto_ptr<MutableDictionary> identDict;
1193 MutableDictionary *d = MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
1194 if (d)
1195 {
1196 identDict.reset(d);
1197 }
1198 else
1199 {
1200 if(idRef == NULL) {
1201 /* nothing there, nothing to set - done */
1202 return errSecSuccess;
1203 }
1204 identDict.reset(new MutableDictionary());
1205 }
1206
1207 if(idRef == NULL) {
1208 /* Just delete the possible entry for this domain */
1209 identDict->removeValue(domain);
1210 }
1211 else {
1212 /* obtain public key hash of identity's cert */
1213 SecPointer<Identity> identity(Identity::required(idRef));
1214 SecPointer<Certificate> cert = identity->certificate();
1215 const CssmData &pubKeyHash = cert->publicKeyHash();
1216 CFRef<CFDataRef> pubKeyHashData(CFDataCreate(NULL, pubKeyHash.Data,
1217 pubKeyHash.Length));
1218
1219 /* add/replace to dictionary */
1220 identDict->setValue(domain, pubKeyHashData);
1221 }
1222
1223 /* flush to disk */
1224 if(!identDict->writePlistToPrefs(IDENTITY_DOMAIN, Dictionary::US_System)) {
1225 MacOSError::throwMe(errSecIO);
1226 }
1227
1228 END_SECAPI
1229 }
1230
1231 const CFStringRef kSecIdentityDomainDefault = CFSTR("com.apple.systemdefault");
1232 const CFStringRef kSecIdentityDomainKerberosKDC = CFSTR("com.apple.kerberos.kdc");
1233