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