/* * Copyright (c) 2006-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include "symbol_scope.h" #include "EAPSecurity.h" #include "EAPKeychainUtil.h" #include "EAPKeychainUtilInternal.h" #include "myCFUtil.h" #if TARGET_OS_EMBEDDED OSStatus EAPSecKeychainPasswordItemRemove(SecKeychainRef keychain, CFStringRef unique_id_str) { const void * keys[] = { kSecClass, kSecAttrService }; CFDictionaryRef query; OSStatus status; const void * values[] = { kSecClassGenericPassword, unique_id_str }; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemDelete(query); CFRelease(query); if (status != noErr) { fprintf(stderr, "SecItemDelete failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); } return (status); } OSStatus EAPSecKeychainPasswordItemCopy(SecKeychainRef keychain, CFStringRef unique_id_str, CFDataRef * ret_password) { const void * keys[] = { kSecClass, kSecAttrService, kSecReturnData }; CFDictionaryRef query; CFTypeRef results; OSStatus status; const void * values[] = { kSecClassGenericPassword, unique_id_str, kCFBooleanTrue }; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemCopyMatching(query, &results); CFRelease(query); if (status == noErr) { *ret_password = results; } else { *ret_password = NULL; } return (status); } OSStatus EAPSecKeychainPasswordItemCreateWithAccess(SecKeychainRef keychain, SecAccessRef access, CFStringRef unique_id_str, CFDataRef label, CFDataRef description, CFDataRef user, CFDataRef password) { CFMutableDictionaryRef attrs; OSStatus status; attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(attrs, kSecClass, kSecClassGenericPassword); CFDictionarySetValue(attrs, kSecAttrService, unique_id_str); if (label != NULL) { CFDictionarySetValue(attrs, kSecAttrLabel, label); } if (description != NULL) { CFDictionarySetValue(attrs, kSecAttrDescription, description); } if (user != NULL) { CFDictionarySetValue(attrs, kSecAttrAccount, user); } CFDictionarySetValue(attrs, kSecValueData, password); status = SecItemAdd(attrs, NULL); CFRelease(attrs); return (status); } OSStatus EAPSecKeychainPasswordItemSet(SecKeychainRef keychain, CFStringRef unique_id_str, CFDataRef password) { const void * keys[] = { kSecClass, kSecAttrService, kSecReturnData }; CFTypeRef existing_password = NULL; CFDictionaryRef query; CFDictionaryRef pass_dict; OSStatus status; const void * values[] = { kSecClassGenericPassword, unique_id_str, kCFBooleanTrue }; query = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemCopyMatching(query, &existing_password); CFRelease(query); if (status != noErr) { goto done; } if (existing_password != NULL && CFEqual(password, existing_password)) { /* nothing to do */ goto done; } query = CFDictionaryCreate(NULL, keys, values, (sizeof(keys) / sizeof(*keys)) - 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); pass_dict = CFDictionaryCreate(NULL, (const void * *)&kSecValueData, (const void * *)&password, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); status = SecItemUpdate(query, pass_dict); CFRelease(query); CFRelease(pass_dict); done: my_CFRelease(&existing_password); return (status); } #else /* TARGET_OS_EMBEDDED */ #include #include #include #include #include const CFStringRef kEAPSecKeychainPropPassword = CFSTR("Password"); const CFStringRef kEAPSecKeychainPropLabel = CFSTR("Label"); const CFStringRef kEAPSecKeychainPropDescription = CFSTR("Description"); const CFStringRef kEAPSecKeychainPropAccount = CFSTR("Account"); const CFStringRef kEAPSecKeychainPropTrustedApplications = CFSTR("TrustedApplications"); const CFStringRef kEAPSecKeychainPropAllowRootAccess = CFSTR("AllowRootAccess"); #include #include #include #include #define MY_KEYCHAIN_ATTR_MAX 6 typedef struct { SecKeychainAttributeInfo info; UInt32 tag[MY_KEYCHAIN_ATTR_MAX]; UInt32 format[MY_KEYCHAIN_ATTR_MAX]; } mySecKeychainAttributeInfo; typedef struct { SecKeychainAttributeList list; SecKeychainAttribute attr[MY_KEYCHAIN_ATTR_MAX]; } mySecKeychainAttributeList; typedef struct CFStringSecItemTag { const CFStringRef * key; UInt32 tag; } CFStringSecItemTag; STATIC const CFStringSecItemTag prop_tag_tbl[] = { { &kEAPSecKeychainPropAccount, kSecAccountItemAttr }, { &kEAPSecKeychainPropLabel, kSecLabelItemAttr }, { &kEAPSecKeychainPropDescription, kSecDescriptionItemAttr }, }; STATIC const int prop_tag_tbl_size = (sizeof(prop_tag_tbl) / sizeof(prop_tag_tbl[0])); STATIC void mySecKeychainAttributeInfoInit(mySecKeychainAttributeInfo * attr_info) { attr_info->info.count = 0; attr_info->info.tag = attr_info->tag; attr_info->info.format = attr_info->format; return; } STATIC Boolean mySecKeychainAttributeInfoAdd(mySecKeychainAttributeInfo * attr_info, UInt32 tag) { int count = attr_info->info.count; if (count >= MY_KEYCHAIN_ATTR_MAX) { fprintf(stderr, "Trying to add attribute %d but list is full\n", (int)tag); return (FALSE); } attr_info->format[count] = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr_info->tag[count] = tag; attr_info->info.count++; return (TRUE); } STATIC void mySecKeychainAttributeListInit(mySecKeychainAttributeList * attr_list) { attr_list->list.count = 0; attr_list->list.attr = attr_list->attr; return; } STATIC Boolean mySecKeychainAttributeListAdd(mySecKeychainAttributeList * attr_list, UInt32 attr_tag, CFDataRef attr_data) { SecKeychainAttribute * attr; int count = attr_list->list.count; if (count >= MY_KEYCHAIN_ATTR_MAX) { fprintf(stderr, "Trying to add attribute %d but list is full\n", (int)attr_tag); return (FALSE); } attr = attr_list->attr + count; attr->tag = attr_tag; attr->length = CFDataGetLength(attr_data); attr->data = (void *)CFDataGetBytePtr(attr_data); attr_list->list.count++; return (TRUE); } STATIC Boolean mySecKeychainAttributeListAddFromDict(mySecKeychainAttributeList * attr_list, CFDictionaryRef attrs) { int i; Boolean ret = TRUE; const CFStringSecItemTag * tbl; for (i = 0, tbl = prop_tag_tbl; i < prop_tag_tbl_size; i++, tbl++) { CFDataRef val = CFDictionaryGetValue(attrs, *tbl->key); if (val != NULL) { ret = mySecKeychainAttributeListAdd(attr_list, tbl->tag, val); if (ret == FALSE) { fprintf(stderr, "mySecKeychainAttributeListAddFromDict() " "failed to add %d\n", (int)tbl->tag); break; } } } return (ret); } PRIVATE_EXTERN OSStatus EAPSecAccessCreateWithTrustedApplications(CFArrayRef trusted_apps, CFDataRef label_data, SecAccessRef * ret_access) { CFStringRef label = NULL; OSStatus status; if (label_data != NULL) { label = my_CFStringCreateWithData(label_data); } else { label = CFSTR("--unspecified--"); CFRetain(label); } status = SecAccessCreate(label, trusted_apps, ret_access); my_CFRelease(&label); return (status); } PRIVATE_EXTERN OSStatus EAPSecKeychainItemSetAccessForTrustedApplications(SecKeychainItemRef item, CFArrayRef trusted_apps) { CFArrayRef app_list = NULL; CFMutableArrayRef app_list_data = NULL; SecACLRef acl; CFArrayRef acl_list = NULL; SecAccessRef access = NULL; CFStringRef prompt_description = NULL; SecKeychainPromptSelector prompt_selector; OSStatus status; status = SecKeychainItemCopyAccess(item, &access); if (status != noErr) { status = SecAccessCreate(CFSTR("--unspecified--"), trusted_apps, &access); if (status != noErr) { goto done; } } else { acl_list = SecAccessCopyMatchingACLList(access, kSecACLAuthorizationDecrypt); if (acl_list == NULL) { status = errSecAllocate; goto done; } acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list, 0); status = SecACLCopyContents(acl, &app_list, &prompt_description, &prompt_selector); if (status == noErr && app_list != NULL) { int count; int i; CFRange r; CFMutableArrayRef new_list = NULL; r.location = 0; r.length = CFArrayGetCount(app_list); app_list_data = CFArrayCreateMutable(NULL, r.length, &kCFTypeArrayCallBacks); for (i = 0; i < r.length; i++) { SecTrustedApplicationRef app; CFDataRef data = NULL; app = (SecTrustedApplicationRef) CFArrayGetValueAtIndex(app_list, i); (void)SecTrustedApplicationCopyExternalRepresentation(app, &data); if (data != NULL) { CFArrayAppendValue(app_list_data, data); CFRelease(data); } } /* in case it changed */ r.length = CFArrayGetCount(app_list_data); count = CFArrayGetCount(trusted_apps); for (i = 0; i < count; i++) { bool already_there; SecTrustedApplicationRef app; CFDataRef data = NULL; app = (SecTrustedApplicationRef) CFArrayGetValueAtIndex(trusted_apps, i); (void)SecTrustedApplicationCopyExternalRepresentation(app, &data); if (data == NULL) { continue; } already_there = CFArrayContainsValue(app_list_data, r, data); CFRelease(data); if (already_there) { continue; } if (new_list == NULL) { new_list = CFArrayCreateMutableCopy(NULL, 0, app_list); } CFArrayAppendValue(new_list, app); } if (new_list == NULL) { /* no modifications required */ goto done; } status = SecACLSetContents(acl, new_list, prompt_description, prompt_selector); CFRelease(new_list); if (status != noErr) { goto done; } } else { status = SecACLSetContents(acl, trusted_apps, prompt_description, prompt_selector); if (status != noErr) { goto done; } } } status = SecKeychainItemSetAccess(item, access); done: my_CFRelease(&access); my_CFRelease(&app_list); my_CFRelease(&app_list_data); my_CFRelease(&prompt_description); my_CFRelease(&acl_list); return (status); } STATIC OSStatus KeychainPasswordItemCopy(SecKeychainRef keychain, CFStringRef unique_id_str, SecKeychainItemRef * ret_item) { SecKeychainItemRef item; OSStatus status; CFDataRef unique_id; unique_id = CFStringCreateExternalRepresentation(NULL, unique_id_str, kCFStringEncodingUTF8, 0); status = SecKeychainFindGenericPassword(keychain, CFDataGetLength(unique_id), (const char *)CFDataGetBytePtr(unique_id), 0, NULL, NULL, NULL, &item); CFRelease(unique_id); *ret_item = NULL; if (status != noErr) { #if TEST_EAPKEYCHAINUTIL fprintf(stderr, "SecKeychainFindGenericPassword failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); #endif /* TEST_EAPKEYCHAINUTIL */ } else { *ret_item = item; } return (status); } STATIC CFStringRef SecKeychainAttrTypeGetCFString(SecKeychainAttrType type) { int i; const CFStringSecItemTag * tbl; for (i = 0, tbl = prop_tag_tbl; i < prop_tag_tbl_size; i++, tbl++) { if (tbl->tag == type) { return (*tbl->key); } } return (NULL); } STATIC OSStatus KeychainItemCopyInfo(SecKeychainItemRef item, CFArrayRef keys, CFDictionaryRef * ret_attrs) { CFMutableDictionaryRef attrs = NULL; int i; void * password_data = NULL; UInt32 password_length = 0; void * * password_data_p; UInt32 * password_length_p; CFRange range = CFRangeMake(0, CFArrayGetCount(keys)); mySecKeychainAttributeInfo req_attr_info; SecKeychainAttributeInfo * req_attr_info_p; SecKeychainAttributeList * ret_attr_list = NULL; SecKeychainAttributeList * *ret_attr_list_p; OSStatus status; mySecKeychainAttributeInfoInit(&req_attr_info); if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropAccount)) { mySecKeychainAttributeInfoAdd(&req_attr_info, kSecAccountItemAttr); } if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropLabel)) { mySecKeychainAttributeInfoAdd(&req_attr_info, kSecLabelItemAttr); } if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropDescription)) { mySecKeychainAttributeInfoAdd(&req_attr_info, kSecDescriptionItemAttr); } if (CFArrayContainsValue(keys, range, kEAPSecKeychainPropPassword)) { password_data_p = &password_data; password_length_p = &password_length; } else { password_data_p = NULL; password_length_p = NULL; } if (req_attr_info.info.count != 0) { req_attr_info_p = &req_attr_info.info; ret_attr_list_p = &ret_attr_list; } else { req_attr_info_p = NULL; ret_attr_list_p = NULL; } status = SecKeychainItemCopyAttributesAndData(item, req_attr_info_p, NULL, ret_attr_list_p, password_length_p, password_data_p); if (status != noErr) { goto done; } attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (password_data != NULL && password_length != 0) { CFDataRef password; password = CFDataCreate(NULL, password_data, password_length); CFDictionarySetValue(attrs, kEAPSecKeychainPropPassword, password); CFRelease(password); } if (ret_attr_list != NULL) { SecKeychainAttribute * attr = ret_attr_list->attr; for (i = 0; i < ret_attr_list->count; i++, attr++) { CFStringRef key; CFDataRef val; if (attr->data == NULL || attr->length == 0) { continue; } key = SecKeychainAttrTypeGetCFString(attr->tag); if (key == NULL) { /* shouldn't happen */ continue; } val = CFDataCreate(NULL, attr->data, attr->length); CFDictionarySetValue(attrs, key, val); CFRelease(val); } } SecKeychainItemFreeAttributesAndData(ret_attr_list, password_data); done: *ret_attrs = attrs; return (status); } OSStatus EAPSecKeychainPasswordItemRemove(SecKeychainRef keychain, CFStringRef unique_id_str) { SecKeychainItemRef item; OSStatus status; status = KeychainPasswordItemCopy(keychain, unique_id_str, &item); if (status != noErr) { goto done; } status = SecKeychainItemDelete(item); CFRelease(item); if (status != noErr) { fprintf(stderr, "SecKeychainItemDelete() failed: %s (%d)\n", EAPSecurityErrorString(status), (int)status); } done: return (status); } OSStatus EAPSecKeychainPasswordItemCopy2(SecKeychainRef keychain, CFStringRef unique_id_str, CFArrayRef keys, CFDictionaryRef * ret_values) { SecKeychainItemRef item = NULL; OSStatus status; *ret_values = NULL; status = KeychainPasswordItemCopy(keychain, unique_id_str, &item); if (status == noErr) { status = KeychainItemCopyInfo(item, keys, ret_values); } if (item != NULL) { CFRelease(item); } return (status); } OSStatus EAPSecKeychainPasswordItemCopy(SecKeychainRef keychain, CFStringRef unique_id_str, CFDataRef * ret_password) { CFDataRef password; CFDictionaryRef props; CFArrayRef req_props; OSStatus status; *ret_password = NULL; req_props = CFArrayCreate(NULL, (const void * *)&kEAPSecKeychainPropPassword, 1, &kCFTypeArrayCallBacks); status = EAPSecKeychainPasswordItemCopy2(keychain, unique_id_str, req_props, &props); CFRelease(req_props); if (status != noErr) { return (status); } password = CFDictionaryGetValue(props, kEAPSecKeychainPropPassword); if (password == NULL) { status = errSecItemNotFound; } else { *ret_password = CFRetain(password); } CFRelease(props); return (status); } OSStatus EAPSecKeychainPasswordItemCreateWithAccess(SecKeychainRef keychain, SecAccessRef access, CFStringRef unique_id_str, CFDataRef label, CFDataRef description, CFDataRef user, CFDataRef password) { mySecKeychainAttributeList attr_list; OSStatus status; CFDataRef unique_id; unique_id = CFStringCreateExternalRepresentation(NULL, unique_id_str, kCFStringEncodingUTF8, 0); mySecKeychainAttributeListInit(&attr_list); mySecKeychainAttributeListAdd(&attr_list, kSecServiceItemAttr, unique_id); if (label != NULL) { mySecKeychainAttributeListAdd(&attr_list, kSecLabelItemAttr, label); } if (description != NULL) { mySecKeychainAttributeListAdd(&attr_list, kSecDescriptionItemAttr, description); } if (user != NULL) { mySecKeychainAttributeListAdd(&attr_list, kSecAccountItemAttr, user); } status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attr_list.list, CFDataGetLength(password), CFDataGetBytePtr(password), keychain, access, NULL); CFRelease(unique_id); return (status); } OSStatus EAPSecKeychainPasswordItemCreate(SecKeychainRef keychain, CFStringRef unique_id_str, CFDictionaryRef attrs) { CFBooleanRef allow_root; SecAccessRef access = NULL; mySecKeychainAttributeList attr_list; CFDataRef password; void * password_data; UInt32 password_length; OSStatus status; CFArrayRef trusted_apps; CFDataRef unique_id = NULL; password = CFDictionaryGetValue(attrs, kEAPSecKeychainPropPassword); if (password != NULL) { /* set it */ password_length = CFDataGetLength(password); password_data = (void *)CFDataGetBytePtr(password); } else { /* don't set it */ password_length = 0; password_data = NULL; } trusted_apps = CFDictionaryGetValue(attrs, kEAPSecKeychainPropTrustedApplications); allow_root = CFDictionaryGetValue(attrs, kEAPSecKeychainPropAllowRootAccess); if (trusted_apps != NULL) { CFDataRef label_data; label_data = CFDictionaryGetValue(attrs, kEAPSecKeychainPropLabel); status = EAPSecAccessCreateWithTrustedApplications(trusted_apps, label_data, &access); if (status != noErr) { fprintf(stderr, "EAPSecKeychainPasswordItemCreate " "failed to get SecAccess for Trusted Apps, %d\n", (int)status); goto done; } } else if (allow_root != NULL && CFBooleanGetValue(allow_root)) { CFErrorRef error; access = SecAccessCreateWithOwnerAndACL(0, 0, kSecUseOnlyUID, NULL, &error); if (access == NULL) { status = errSecAllocate; if (error != NULL) { fprintf(stderr, "EAPSecKeychainPasswordItemCreate(): " "SecAccessCreateWithOwnerAndACL() failed %d\n", (int)CFErrorGetCode(error)); CFRelease(error); } goto done; } } mySecKeychainAttributeListInit(&attr_list); unique_id = CFStringCreateExternalRepresentation(NULL, unique_id_str, kCFStringEncodingUTF8, 0); mySecKeychainAttributeListAdd(&attr_list, kSecServiceItemAttr, unique_id); if (mySecKeychainAttributeListAddFromDict(&attr_list, attrs) == FALSE) { status = errSecBufferTooSmall; } else { status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attr_list.list, password_length, password_data, keychain, access, NULL); } done: my_CFRelease(&unique_id); my_CFRelease(&access); return (status); } OSStatus EAPSecKeychainPasswordItemCreateUnique(SecKeychainRef keychain, CFDictionaryRef attrs, CFStringRef * ret_unique_id) { OSStatus status; CFStringRef unique_id_str; unique_id_str = my_CFUUIDStringCreate(NULL); status = EAPSecKeychainPasswordItemCreate(keychain, unique_id_str, attrs); if (status == noErr && ret_unique_id != NULL) { *ret_unique_id = unique_id_str; } else { if (ret_unique_id != NULL) { *ret_unique_id = NULL; } CFRelease(unique_id_str); } return (status); } OSStatus EAPSecKeychainPasswordItemSet(SecKeychainRef keychain, CFStringRef unique_id_str, CFDataRef password) { SecKeychainItemRef item; OSStatus status; status = KeychainPasswordItemCopy(keychain, unique_id_str, &item); if (status != noErr) { return (status); } status = SecKeychainItemModifyAttributesAndData(item, NULL, CFDataGetLength(password), CFDataGetBytePtr(password)); CFRelease(item); return (status); } OSStatus EAPSecKeychainPasswordItemSet2(SecKeychainRef keychain, CFStringRef unique_id_str, CFDictionaryRef attrs) { mySecKeychainAttributeList attr_list; SecKeychainItemRef item; CFDataRef password; void * password_data; UInt32 password_length; OSStatus status; status = KeychainPasswordItemCopy(keychain, unique_id_str, &item); if (status != noErr) { return (status); } mySecKeychainAttributeListInit(&attr_list); if (mySecKeychainAttributeListAddFromDict(&attr_list, attrs) == FALSE) { status = errSecBufferTooSmall; goto done; } password = CFDictionaryGetValue(attrs, kEAPSecKeychainPropPassword); if (password != NULL) { /* set it */ password_length = CFDataGetLength(password); password_data = (void *)CFDataGetBytePtr(password); } else { /* don't set it */ password_length = 0; password_data = NULL; } status = SecKeychainItemModifyAttributesAndData(item, &attr_list.list, password_length, password_data); done: CFRelease(item); return (status); } #endif /* TARGET_OS_EMBEDDED */ OSStatus EAPSecKeychainPasswordItemCreateUniqueWithAccess(SecKeychainRef keychain, SecAccessRef access, CFDataRef label, CFDataRef description, CFDataRef user, CFDataRef password, CFStringRef * ret_unique_id) { OSStatus status; CFStringRef unique_id_str; unique_id_str = my_CFUUIDStringCreate(NULL); status = EAPSecKeychainPasswordItemCreateWithAccess(keychain, access, unique_id_str, label, description, user, password); if (status == noErr && ret_unique_id != NULL) { *ret_unique_id = unique_id_str; } else { if (ret_unique_id != NULL) { *ret_unique_id = NULL; } CFRelease(unique_id_str); } return (status); } #ifdef TEST_EAPKEYCHAINUTIL #if ! TARGET_OS_EMBEDDED /* * Create a SecAccessRef with a custom form. * Both the owner and the ACL set allow free access to root, * but nothing to anyone else. * NOTE: This is not the easiest way to build up CSSM data structures. * But it's a way that does not depend on any outside software layers * (other than CSSM and Security's Sec* layer, of course). */ static OSStatus SecKeychainCopySystemKeychain(SecKeychainRef * ret_keychain) { SecKeychainRef keychain = NULL; OSStatus status; status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain); if (status != noErr && keychain != NULL) { CFRelease(keychain); keychain = NULL; } *ret_keychain = keychain; return (status); } #endif /* ! TARGET_OS_EMBEDDED */ #if TARGET_OS_EMBEDDED #define SYS_OPT " " #else /* TARGET_OS_EMBEDDED */ #define SYS_OPT " [system] " #define HAS_KEYCHAINS #endif /* TARGET_OS_EMBEDDED */ static void usage(const char * progname) { fprintf(stderr, "%s" SYS_OPT "create