]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecIdentity.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecIdentity.cpp
1 /*
2 * Copyright (c) 2002-2015 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <Security/SecIdentity.h>
25 #include <Security/SecIdentityPriv.h>
26 #include <Security/SecKeychainItemPriv.h>
27 #include <Security/SecItem.h>
28 #include <Security/SecIdentityPriv.h>
29 #include <Security/SecCertificatePriv.h>
30
31 #include "SecBridge.h"
32 #include <security_keychain/Certificate.h>
33 #include <security_keychain/Identity.h>
34 #include <security_keychain/KeyItem.h>
35 #include <security_keychain/KCCursor.h>
36 #include <security_cdsa_utilities/Schema.h>
37 #include <security_utilities/simpleprefs.h>
38 #include <utilities/SecCFRelease.h>
39 #include <sys/param.h>
40 #include <syslog.h>
41 #include <os/activity.h>
42 #include "LegacyAPICounts.h"
43
44 /* private function declarations */
45 OSStatus
46 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
47 CFTypeRef keychainOrArray,
48 CFStringRef name,
49 int32_t keyUsage,
50 SecKeychainItemRef *itemRef);
51
52 OSStatus SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
53 CFTypeRef keychainOrArray,
54 CFStringRef name,
55 int32_t keyUsage);
56
57
58 CSSM_KEYUSE ConvertArrayToKeyUsage(CFArrayRef usage)
59 {
60 CFIndex count = 0;
61 CSSM_KEYUSE result = (CSSM_KEYUSE) 0;
62
63 if ((NULL == usage) || (0 == (count = CFArrayGetCount(usage))))
64 {
65 return result;
66 }
67
68 for (CFIndex iCnt = 0; iCnt < count; iCnt++)
69 {
70 CFStringRef keyUsageStr = NULL;
71 keyUsageStr = (CFStringRef)CFArrayGetValueAtIndex(usage,iCnt);
72 if (NULL != keyUsageStr)
73 {
74 if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanEncrypt, keyUsageStr, 0))
75 {
76 result |= CSSM_KEYUSE_ENCRYPT;
77 }
78 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanDecrypt, keyUsageStr, 0))
79 {
80 result |= CSSM_KEYUSE_DECRYPT;
81 }
82 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanDerive, keyUsageStr, 0))
83 {
84 result |= CSSM_KEYUSE_DERIVE;
85 }
86 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanSign, keyUsageStr, 0))
87 {
88 result |= CSSM_KEYUSE_SIGN;
89 }
90 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanVerify, keyUsageStr, 0))
91 {
92 result |= CSSM_KEYUSE_VERIFY;
93 }
94 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanWrap, keyUsageStr, 0))
95 {
96 result |= CSSM_KEYUSE_WRAP;
97 }
98 else if (kCFCompareEqualTo == CFStringCompare((CFStringRef)kSecAttrCanUnwrap, keyUsageStr, 0))
99 {
100 result |= CSSM_KEYUSE_UNWRAP;
101 }
102 }
103 }
104
105 return result;
106 }
107
108
109 CFTypeID
110 SecIdentityGetTypeID(void)
111 {
112 BEGIN_SECAPI
113
114 return gTypes().Identity.typeID;
115
116 END_SECAPI1(_kCFRuntimeNotATypeID)
117 }
118
119
120 OSStatus
121 SecIdentityCopyCertificate(
122 SecIdentityRef identityRef,
123 SecCertificateRef *certificateRef)
124 {
125 BEGIN_SECAPI
126 os_activity_t activity = os_activity_create("SecIdentityCopyCertificate", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
127 os_activity_scope(activity);
128 os_release(activity);
129
130 if (!identityRef || !certificateRef) {
131 return errSecParam;
132 }
133 CFTypeID itemType = CFGetTypeID(identityRef);
134 if (itemType == SecIdentityGetTypeID()) {
135 SecPointer<Certificate> certificatePtr(Identity::required(identityRef)->certificate());
136 Required(certificateRef) = certificatePtr->handle();
137
138 /* convert outgoing certificate item to a unified SecCertificateRef */
139 CssmData certData = certificatePtr->data();
140 CFDataRef data = NULL;
141 if (certData.Data && certData.Length) {
142 data = CFDataCreate(NULL, certData.Data, certData.Length);
143 }
144 if (!data) {
145 *certificateRef = NULL;
146 syslog(LOG_ERR, "ERROR: SecIdentityCopyCertificate failed to retrieve certificate data (length=%ld, data=0x%lX)",
147 (long)certData.Length, (uintptr_t)certData.Data);
148 return errSecInternal;
149 }
150 SecCertificateRef tmpRef = *certificateRef;
151 *certificateRef = SecCertificateCreateWithKeychainItem(NULL, data, tmpRef);
152 if (data) {
153 CFRelease(data);
154 }
155 if (tmpRef) {
156 CFRelease(tmpRef);
157 }
158 }
159 else if (itemType == SecCertificateGetTypeID()) {
160 // rdar://24483382
161 // reconstituting a persistent identity reference could return the certificate
162 SecCertificateRef certificate = (SecCertificateRef)identityRef;
163
164 /* convert outgoing certificate item to a unified SecCertificateRef, if needed */
165 if (SecCertificateIsItemImplInstance(certificate)) {
166 *certificateRef = SecCertificateCreateFromItemImplInstance(certificate);
167 }
168 else {
169 *certificateRef = (SecCertificateRef) CFRetain(certificate);
170 }
171 return errSecSuccess;
172 }
173 else {
174 return errSecParam;
175 }
176
177 END_SECAPI
178 }
179
180
181 OSStatus
182 SecIdentityCopyPrivateKey(
183 SecIdentityRef identityRef,
184 SecKeyRef *privateKeyRef)
185 {
186 BEGIN_SECAPI
187 os_activity_t activity = os_activity_create("SecIdentityCopyPrivateKey", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
188 os_activity_scope(activity);
189 os_release(activity);
190
191 Required(privateKeyRef) = (SecKeyRef)CFRetain(Identity::required(identityRef)->privateKeyRef());
192
193 END_SECAPI
194 }
195
196 OSStatus
197 SecIdentityCreateWithCertificate(
198 CFTypeRef keychainOrArray,
199 SecCertificateRef certificate,
200 SecIdentityRef *identityRef)
201 {
202 // This macro converts a new-style SecCertificateRef to an old-style ItemImpl
203 BEGIN_SECCERTAPI
204
205 SecPointer<Certificate> certificatePtr(Certificate::required(__itemImplRef));
206 StorageManager::KeychainList keychains;
207 globals().storageManager.optionalSearchList(keychainOrArray, keychains);
208 SecPointer<Identity> identityPtr(new Identity(keychains, certificatePtr));
209 Required(identityRef) = identityPtr->handle();
210
211 END_SECCERTAPI
212 }
213
214 SecIdentityRef
215 SecIdentityCreate(
216 CFAllocatorRef allocator,
217 SecCertificateRef certificate,
218 SecKeyRef privateKey)
219 {
220 COUNTLEGACYAPI
221 SecIdentityRef identityRef = NULL;
222 OSStatus __secapiresult;
223 SecCertificateRef __itemImplRef = NULL;
224 if (SecCertificateIsItemImplInstance(certificate)) {
225 __itemImplRef=(SecCertificateRef)CFRetain(certificate);
226 }
227 if (!__itemImplRef && certificate) {
228 __itemImplRef=(SecCertificateRef)SecCertificateCopyKeychainItem(certificate);
229 }
230 if (!__itemImplRef && certificate) {
231 __itemImplRef=SecCertificateCreateItemImplInstance(certificate);
232 (void)SecCertificateSetKeychainItem(certificate,__itemImplRef);
233 }
234 try {
235 SecPointer<Certificate> certificatePtr(Certificate::required(__itemImplRef));
236 SecPointer<Identity> identityPtr(new Identity(privateKey, certificatePtr));
237 identityRef = identityPtr->handle();
238
239 __secapiresult=errSecSuccess;
240 }
241 catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
242 catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
243 catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
244 catch (...) { __secapiresult=errSecInternalComponent; }
245 if (__itemImplRef) { CFRelease(__itemImplRef); }
246 return identityRef;
247 }
248
249 static
250 CFArrayRef _SecIdentityCopyPossiblePaths(
251 CFStringRef name)
252 {
253 // utility function to build and return an array of possible paths for the given name.
254 // if name is not a URL, this returns a single-element array.
255 // if name is a URL, the array may contain 1..N elements, one for each level of the path hierarchy.
256
257 CFMutableArrayRef names = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
258 if (!name) {
259 return names;
260 }
261 CFIndex oldLength = CFStringGetLength(name);
262 CFArrayAppendValue(names, name);
263
264 CFURLRef url = CFURLCreateWithString(NULL, name, NULL);
265 if (url) {
266 if (CFURLCanBeDecomposed(url)) {
267 // first, remove the query portion of this URL, if any
268 CFStringRef qs = CFURLCopyQueryString(url, NULL);
269 if (qs) {
270 CFMutableStringRef newName = CFStringCreateMutableCopy(NULL, oldLength, name);
271 if (newName) {
272 CFIndex qsLength = CFStringGetLength(qs) + 1; // include the '?'
273 CFStringDelete(newName, CFRangeMake(oldLength-qsLength, qsLength));
274 CFRelease(url);
275 url = CFURLCreateWithString(NULL, newName, NULL);
276 CFArraySetValueAtIndex(names, 0, newName);
277 CFRelease(newName);
278 }
279 CFRelease(qs);
280 }
281 // now add an entry for each level of the path
282 while (url) {
283 CFURLRef parent = CFURLCreateCopyDeletingLastPathComponent(NULL, url);
284 if (parent) {
285 CFStringRef parentURLString = CFURLGetString(parent);
286 if (parentURLString) {
287 CFIndex newLength = CFStringGetLength(parentURLString);
288 // check that string length has decreased as expected; for file URLs,
289 // CFURLCreateCopyDeletingLastPathComponent can insert './' or '../'
290 if ((newLength >= oldLength) || (!CFStringHasPrefix(name, parentURLString))) {
291 CFRelease(parent);
292 CFRelease(url);
293 break;
294 }
295 oldLength = newLength;
296 CFArrayAppendValue(names, parentURLString);
297 }
298 }
299 CFRelease(url);
300 url = parent;
301 }
302 }
303 else {
304 CFRelease(url);
305 }
306 }
307 // finally, add wildcard entries for each subdomain
308 url = CFURLCreateWithString(NULL, name, NULL);
309 if (url) {
310 if (CFURLCanBeDecomposed(url)) {
311 CFStringRef netLocString = CFURLCopyNetLocation(url);
312 if (netLocString) {
313 // first strip off port number, if present
314 CFStringRef tmpLocString = netLocString;
315 CFArrayRef hostnameArray = CFStringCreateArrayBySeparatingStrings(NULL, netLocString, CFSTR(":"));
316 tmpLocString = (CFStringRef)CFRetain((CFStringRef)CFArrayGetValueAtIndex(hostnameArray, 0));
317 CFRelease(netLocString);
318 CFRelease(hostnameArray);
319 netLocString = tmpLocString;
320 // split remaining string into domain components
321 hostnameArray = CFStringCreateArrayBySeparatingStrings(NULL, netLocString, CFSTR("."));
322 CFIndex subdomainCount = CFArrayGetCount(hostnameArray);
323 CFIndex i = 0;
324 while (++i < subdomainCount) {
325 CFIndex j = i;
326 CFMutableStringRef wildcardString = CFStringCreateMutable(NULL, 0);
327 if (wildcardString) {
328 CFStringAppendCString(wildcardString, "*", kCFStringEncodingUTF8);
329 while (j < subdomainCount) {
330 CFStringRef domainString = (CFStringRef)CFArrayGetValueAtIndex(hostnameArray, j++);
331 if (CFStringGetLength(domainString) > 0) {
332 CFStringAppendCString(wildcardString, ".", kCFStringEncodingUTF8);
333 CFStringAppend(wildcardString, domainString);
334 }
335 }
336 if (CFStringGetLength(wildcardString) > 1) {
337 CFArrayAppendValue(names, wildcardString);
338 }
339 CFRelease(wildcardString);
340 }
341 }
342 CFRelease(hostnameArray);
343 CFRelease(netLocString);
344 }
345 }
346 CFRelease(url);
347 }
348
349 return names;
350 }
351
352 static
353 OSStatus _SecIdentityCopyPreferenceMatchingName(
354 CFStringRef name,
355 CSSM_KEYUSE keyUsage,
356 CFArrayRef validIssuers,
357 SecIdentityRef *identity)
358 {
359 // this is NOT exported, and called only from SecIdentityCopyPreference (below), so no BEGIN/END macros here;
360 // caller must handle exceptions
361
362 StorageManager::KeychainList keychains;
363 globals().storageManager.getSearchList(keychains);
364 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
365
366 char idUTF8[MAXPATHLEN];
367 Required(name);
368 if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
369 idUTF8[0] = (char)'\0';
370 CssmData service(const_cast<char *>(idUTF8), strlen(idUTF8));
371 FourCharCode itemType = 'iprf';
372 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
373 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType);
374 if (keyUsage) {
375 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
376 }
377
378 Item prefItem;
379 if (!cursor->next(prefItem))
380 return errSecItemNotFound;
381
382 // get persistent certificate reference
383 SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
384 SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
385 prefItem->getContent(NULL, &itemAttrList, NULL, NULL);
386
387 // find certificate, given persistent reference data
388 CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
389 SecKeychainItemRef certItemRef = nil;
390 OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef); //%%% need to make this a method of ItemImpl
391 prefItem->freeContent(&itemAttrList, NULL);
392 if (pItemRef)
393 CFRelease(pItemRef);
394 if (status)
395 return status;
396
397 // filter on valid issuers, if provided
398 if (validIssuers) {
399 //%%%TBI
400 }
401
402 // create identity reference, given certificate
403 status = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)certItemRef, identity);
404 if (certItemRef) {
405 CFRelease(certItemRef);
406 }
407
408 return status;
409 }
410
411 SecIdentityRef SecIdentityCopyPreferred(CFStringRef name, CFArrayRef keyUsage, CFArrayRef validIssuers)
412 {
413 // This function will look for a matching preference in the following order:
414 // - matches the name and the supplied key use
415 // - matches the name and the special 'ANY' key use
416 // - matches the name with no key usage constraint
417
418 SecIdentityRef identityRef = NULL;
419 CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage);
420 OSStatus status = SecIdentityCopyPreference(name, keyUse, validIssuers, &identityRef);
421 if (status != errSecSuccess && keyUse != CSSM_KEYUSE_ANY)
422 status = SecIdentityCopyPreference(name, CSSM_KEYUSE_ANY, validIssuers, &identityRef);
423 if (status != errSecSuccess && keyUse != 0)
424 status = SecIdentityCopyPreference(name, 0, validIssuers, &identityRef);
425
426 return identityRef;
427 }
428
429 OSStatus SecIdentityCopyPreference(
430 CFStringRef name,
431 CSSM_KEYUSE keyUsage,
432 CFArrayRef validIssuers,
433 SecIdentityRef *identity)
434 {
435 // The original implementation of SecIdentityCopyPreference matches the exact string only.
436 // That implementation has been moved to _SecIdentityCopyPreferenceMatchingName (above),
437 // and this function is a wrapper which calls it, so that existing clients will get the
438 // extended behavior of server domain matching for items that specify URLs.
439 // (Note that behavior is unchanged if the specified name is not a URL.)
440
441 BEGIN_SECAPI
442 os_activity_t activity = os_activity_create("SecIdentityCopyPreference", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
443 os_activity_scope(activity);
444 os_release(activity);
445
446 CFTypeRef val = (CFTypeRef)CFPreferencesCopyValue(CFSTR("LogIdentityPreferenceLookup"),
447 CFSTR("com.apple.security"),
448 kCFPreferencesCurrentUser,
449 kCFPreferencesAnyHost);
450 Boolean logging = false;
451 if (val) {
452 if (CFGetTypeID(val) == CFBooleanGetTypeID()) {
453 logging = CFBooleanGetValue((CFBooleanRef)val);
454 }
455 }
456 CFReleaseNull(val);
457
458 OSStatus status = errSecItemNotFound;
459 CFArrayRef names = _SecIdentityCopyPossiblePaths(name);
460 if (!names) {
461 return status;
462 }
463
464 CFIndex idx, total = CFArrayGetCount(names);
465 for (idx = 0; idx < total; idx++) {
466 CFStringRef aName = (CFStringRef)CFArrayGetValueAtIndex(names, idx);
467 try {
468 status = _SecIdentityCopyPreferenceMatchingName(aName, keyUsage, validIssuers, identity);
469 }
470 catch (...) { status = errSecItemNotFound; }
471
472 if (logging) {
473 // get identity label
474 CFStringRef labelString = NULL;
475 if (!status && identity && *identity) {
476 try {
477 SecPointer<Certificate> cert(Identity::required(*identity)->certificate());
478 cert->inferLabel(false, &labelString);
479 }
480 catch (...) { labelString = NULL; };
481 }
482 char *labelBuf = NULL;
483 CFIndex labelBufSize = (labelString) ? CFStringGetLength(labelString) * 4 : 4;
484 labelBuf = (char *)malloc(labelBufSize);
485 if (!labelString || !CFStringGetCString(labelString, labelBuf, labelBufSize, kCFStringEncodingUTF8)) {
486 labelBuf[0] = 0;
487 }
488 if (labelString) {
489 CFRelease(labelString);
490 }
491
492 // get service name
493 char *serviceBuf = NULL;
494 CFIndex serviceBufSize = CFStringGetLength(aName) * 4;
495 serviceBuf = (char *)malloc(serviceBufSize);
496 if (!CFStringGetCString(aName, serviceBuf, serviceBufSize, kCFStringEncodingUTF8)) {
497 serviceBuf[0] = 0;
498 }
499
500 syslog(LOG_NOTICE, "preferred identity: \"%s\" found for \"%s\"\n", labelBuf, serviceBuf);
501 if (!status && name) {
502 char *nameBuf = NULL;
503 CFIndex nameBufSize = CFStringGetLength(name) * 4;
504 nameBuf = (char *)malloc(nameBufSize);
505 if (!CFStringGetCString(name, nameBuf, nameBufSize, kCFStringEncodingUTF8)) {
506 nameBuf[0] = 0;
507 }
508 syslog(LOG_NOTICE, "lookup complete; will use: \"%s\" for \"%s\"\n", labelBuf, nameBuf);
509 free(nameBuf);
510 }
511
512 free(labelBuf);
513 free(serviceBuf);
514 }
515
516 if (status == errSecSuccess) {
517 break; // match found
518 }
519 }
520
521 CFRelease(names);
522 return status;
523
524 END_SECAPI
525 }
526
527 OSStatus SecIdentitySetPreference(
528 SecIdentityRef identity,
529 CFStringRef name,
530 CSSM_KEYUSE keyUsage)
531 {
532 if (!name) {
533 return errSecParam;
534 }
535 if (!identity) {
536 // treat NULL identity as a request to clear the preference
537 // (note: if keyUsage is 0, this clears all key usage prefs for name)
538 return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL, name, keyUsage);
539 }
540
541 BEGIN_SECAPI
542 os_activity_t activity = os_activity_create("SecIdentitySetPreference", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
543 os_activity_scope(activity);
544 os_release(activity);
545
546 CFRef<SecCertificateRef> certRef;
547 OSStatus status = SecIdentityCopyCertificate(identity, certRef.take());
548 if(status != errSecSuccess) {
549 MacOSError::throwMe(status);
550 }
551
552 // determine the account attribute
553 //
554 // This attribute must be synthesized from certificate label + pref item type + key usage,
555 // as only the account and service attributes can make a generic keychain item unique.
556 // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
557 // we can save a certificate preference if an identity preference already exists for the
558 // given service name, and vice-versa.
559 // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
560 //
561 CFStringRef labelStr = nil;
562 SecCertificateInferLabel(certRef.get(), &labelStr);
563 if (!labelStr) {
564 MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed"
565 }
566 CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
567 const char *templateStr = "%s [key usage 0x%X]";
568 const int keyUsageMaxStrLen = 8;
569 accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
570 char *accountUTF8 = (char *)malloc(accountUTF8Len);
571 if (!accountUTF8) {
572 MacOSError::throwMe(errSecMemoryError);
573 }
574 if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
575 accountUTF8[0] = (char)'\0';
576 if (keyUsage)
577 snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
578 snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
579 CssmDataContainer account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
580 free(accountUTF8);
581 CFRelease(labelStr);
582
583 // service attribute (name provided by the caller)
584 CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name), kCFStringEncodingUTF8) + 1;;
585 char *serviceUTF8 = (char *)malloc(serviceUTF8Len);
586 if (!serviceUTF8) {
587 MacOSError::throwMe(errSecMemoryError);
588 }
589 if (!CFStringGetCString(name, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8))
590 serviceUTF8[0] = (char)'\0';
591 CssmDataContainer service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8));
592 free(serviceUTF8);
593
594 // look for existing identity preference item, in case this is an update
595 StorageManager::KeychainList keychains;
596 globals().storageManager.getSearchList(keychains);
597 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
598 FourCharCode itemType = 'iprf';
599 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
600 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType);
601 if (keyUsage) {
602 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
603 }
604
605 Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
606 bool add = (!cursor->next(item));
607 // at this point, we either have a new item to add or an existing item to update
608
609 // set item attribute values
610 item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
611 item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), itemType);
612 item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
613 item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
614 item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
615
616 // generic attribute (store persistent certificate reference)
617 CFDataRef pItemRef = nil;
618 SecKeychainItemCreatePersistentReference((SecKeychainItemRef)certRef.get(), &pItemRef);
619 if (!pItemRef) {
620 MacOSError::throwMe(errSecInvalidItemRef);
621 }
622 const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
623 CFIndex dataLen = CFDataGetLength(pItemRef);
624 CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
625 item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
626 CFRelease(pItemRef);
627
628 if (add) {
629 Keychain keychain = nil;
630 try {
631 keychain = globals().storageManager.defaultKeychain();
632 if (!keychain->exists())
633 MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time.
634 }
635 catch(...) {
636 keychain = globals().storageManager.defaultKeychainUI(item);
637 }
638
639 try {
640 keychain->add(item);
641 }
642 catch (const MacOSError &err) {
643 if (err.osStatus() != errSecDuplicateItem)
644 throw; // if item already exists, fall through to update
645 }
646 }
647 item->update();
648
649 END_SECAPI
650 }
651
652 OSStatus
653 SecIdentitySetPreferred(SecIdentityRef identity, CFStringRef name, CFArrayRef keyUsage)
654 {
655 COUNTLEGACYAPI
656 CSSM_KEYUSE keyUse = ConvertArrayToKeyUsage(keyUsage);
657 return SecIdentitySetPreference(identity, name, keyUse);
658 }
659
660 OSStatus
661 SecIdentityFindPreferenceItemWithNameAndKeyUsage(
662 CFTypeRef keychainOrArray,
663 CFStringRef name,
664 int32_t keyUsage,
665 SecKeychainItemRef *itemRef)
666 {
667 BEGIN_SECAPI
668 os_activity_t activity = os_activity_create("SecIdentityFindPreferenceItemWithNameAndKeyUsage", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
669 os_activity_scope(activity);
670 os_release(activity);
671
672 StorageManager::KeychainList keychains;
673 globals().storageManager.optionalSearchList(keychainOrArray, keychains);
674 KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
675
676 char idUTF8[MAXPATHLEN];
677 idUTF8[0] = (char)'\0';
678 if (name)
679 {
680 if (!CFStringGetCString(name, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
681 idUTF8[0] = (char)'\0';
682 }
683 size_t idUTF8Len = strlen(idUTF8);
684 if (!idUTF8Len)
685 MacOSError::throwMe(errSecParam);
686
687 CssmData service(const_cast<char *>(idUTF8), idUTF8Len);
688 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
689 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
690 if (keyUsage) {
691 cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
692 }
693
694 Item item;
695 if (!cursor->next(item))
696 MacOSError::throwMe(errSecItemNotFound);
697
698 if (itemRef)
699 *itemRef=item->handle();
700
701 END_SECAPI
702 }
703
704 OSStatus SecIdentityDeletePreferenceItemWithNameAndKeyUsage(
705 CFTypeRef keychainOrArray,
706 CFStringRef name,
707 int32_t keyUsage)
708 {
709 COUNTLEGACYAPI
710 // when a specific key usage is passed, we'll only match & delete that pref;
711 // when a key usage of 0 is passed, all matching prefs should be deleted.
712 // maxUsages represents the most matches there could theoretically be, so
713 // cut things off at that point if we're still finding items (if they can't
714 // be deleted for some reason, we'd never break out of the loop.)
715
716 OSStatus status = errSecInternalError;
717 SecKeychainItemRef item = NULL;
718 int count = 0, maxUsages = 12;
719 while (++count <= maxUsages &&
720 (status = SecIdentityFindPreferenceItemWithNameAndKeyUsage(keychainOrArray, name, keyUsage, &item)) == errSecSuccess) {
721 status = SecKeychainItemDelete(item);
722 CFRelease(item);
723 item = NULL;
724 }
725
726 // it's not an error if the item isn't found
727 return (status == errSecItemNotFound) ? errSecSuccess : status;
728 }
729
730 /*
731 * System Identity Support.
732 */
733
734 /* plist domain (in /Library/Preferences) */
735 #define IDENTITY_DOMAIN "com.apple.security.systemidentities"
736
737 /*
738 * Our plist is a dictionary whose entries have the following format:
739 * key = domain name as CFString
740 * value = public key hash as CFData
741 */
742
743 #define SYSTEM_KEYCHAIN_PATH kSystemKeychainDir "/" kSystemKeychainName
744
745 /*
746 * All accesses to system identities and its associated plist are
747 * protected by this lock.
748 */
749 ModuleNexus<Mutex> systemIdentityLock;
750
751 OSStatus SecIdentityCopySystemIdentity(
752 CFStringRef domain,
753 SecIdentityRef *idRef,
754 CFStringRef *actualDomain) /* optional */
755 {
756 BEGIN_SECAPI
757 os_activity_t activity = os_activity_create("SecIdentityCopySystemIdentity", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
758 os_activity_scope(activity);
759 os_release(activity);
760
761 StLock<Mutex> _(systemIdentityLock());
762 unique_ptr<Dictionary> identDict;
763
764 /* get top-level dictionary - if not present, we're done */
765 Dictionary* d = Dictionary::CreateDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
766 if (d == NULL)
767 {
768 return errSecNotAvailable;
769 }
770
771 identDict.reset(d);
772
773 /* see if there's an entry for specified domain */
774 CFDataRef entryValue = identDict->getDataValue(domain);
775 if(entryValue == NULL) {
776 /* try for default entry if we're not already looking for default */
777 if(!CFEqual(domain, kSecIdentityDomainDefault)) {
778 entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
779 }
780 if(entryValue == NULL) {
781 /* no default identity */
782 MacOSError::throwMe(errSecItemNotFound);
783 }
784
785 /* remember that we're not fetching the requested domain */
786 domain = kSecIdentityDomainDefault;
787 }
788
789 /* open system keychain - error here is fatal */
790 Keychain systemKc = globals().storageManager.make(SYSTEM_KEYCHAIN_PATH, false);
791 CFRef<SecKeychainRef> systemKcRef(systemKc->handle());
792 StorageManager::KeychainList keychains;
793 globals().storageManager.optionalSearchList(systemKcRef, keychains);
794
795 /* search for specified cert */
796 SecKeychainAttributeList attrList;
797 SecKeychainAttribute attr;
798 attr.tag = kSecPublicKeyHashItemAttr;
799 attr.length = (UInt32)CFDataGetLength(entryValue);
800 attr.data = (void *)CFDataGetBytePtr(entryValue);
801 attrList.count = 1;
802 attrList.attr = &attr;
803
804 KCCursor cursor(keychains, kSecCertificateItemClass, &attrList);
805 Item certItem;
806 if(!cursor->next(certItem)) {
807 MacOSError::throwMe(errSecItemNotFound);
808 }
809
810 /* found the cert; try matching with key to cook up identity */
811 SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
812 SecPointer<Identity> identity(new Identity(keychains, certificate));
813
814 Required(idRef) = identity->handle();
815 if(actualDomain) {
816 *actualDomain = domain;
817 CFRetain(*actualDomain);
818 }
819
820 END_SECAPI
821 }
822
823 OSStatus SecIdentitySetSystemIdentity(
824 CFStringRef domain,
825 SecIdentityRef idRef)
826 {
827 BEGIN_SECAPI
828 os_activity_t activity = os_activity_create("SecIdentitySetSystemIdentity", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
829 os_activity_scope(activity);
830 os_release(activity);
831
832 StLock<Mutex> _(systemIdentityLock());
833 if(geteuid() != 0) {
834 MacOSError::throwMe(errSecAuthFailed);
835 }
836
837 unique_ptr<MutableDictionary> identDict;
838 MutableDictionary *d = MutableDictionary::CreateMutableDictionary(IDENTITY_DOMAIN, Dictionary::US_System);
839 if (d)
840 {
841 identDict.reset(d);
842 }
843 else
844 {
845 if(idRef == NULL) {
846 /* nothing there, nothing to set - done */
847 return errSecSuccess;
848 }
849 identDict.reset(new MutableDictionary());
850 }
851
852 if(idRef == NULL) {
853 /* Just delete the possible entry for this domain */
854 identDict->removeValue(domain);
855 }
856 else {
857 /* obtain public key hash of identity's cert */
858 SecPointer<Identity> identity(Identity::required(idRef));
859 SecPointer<Certificate> cert = identity->certificate();
860 const CssmData &pubKeyHash = cert->publicKeyHash();
861 CFRef<CFDataRef> pubKeyHashData(CFDataCreate(NULL, pubKeyHash.Data,
862 pubKeyHash.Length));
863
864 /* add/replace to dictionary */
865 identDict->setValue(domain, pubKeyHashData);
866 }
867
868 /* flush to disk */
869 if(!identDict->writePlistToPrefs(IDENTITY_DOMAIN, Dictionary::US_System)) {
870 MacOSError::throwMe(errSecIO);
871 }
872
873 END_SECAPI
874 }
875
876 const CFStringRef kSecIdentityDomainDefault = CFSTR("com.apple.systemdefault");
877 const CFStringRef kSecIdentityDomainKerberosKDC = CFSTR("com.apple.kerberos.kdc");
878