From 056169494f82bbf659f8896c130ba410b4b0ee93 Mon Sep 17 00:00:00 2001 From: nvk <797193+nvk@users.noreply.github.com> Date: Sat, 25 Apr 2026 10:28:20 -0600 Subject: [PATCH] macOS: migrate deprecated SecKeychain APIs to SecItem --- envchain.c | 2 +- envchain_osx.c | 607 +++++++++++++++++++++++-------------------------- 2 files changed, 282 insertions(+), 327 deletions(-) diff --git a/envchain.c b/envchain.c index e9a6ed3..2e31a0c 100644 --- a/envchain.c +++ b/envchain.c @@ -68,7 +68,7 @@ envchain_abort_with_help(void) " Enable noecho mode when prompting values. Requires stdin to be a terminal.\n" "\n" " --require-passphrase (-p), --no-require-passphrase (-P):\n" - " Replace the item's ACL list to require passphrase (or not).\n" + " Configure whether the item prompts for access.\n" " Leave as is when both options are omitted.\n" , envchain_name, version, envchain_name, envchain_name, envchain_name, envchain_name diff --git a/envchain_osx.c b/envchain_osx.c index 76fb3df..e4c2d3c 100644 --- a/envchain_osx.c +++ b/envchain_osx.c @@ -8,20 +8,7 @@ #define ENVCHAIN_SERVICE_PREFIX "envchain-" #define ENVCHAIN_ITEM_DESCRIPTION "envchain" -SecKeychainRef envchain_keychain = NULL; - -typedef struct { - envchain_search_callback search_callback; - envchain_namespace_search_callback namespace_callback; - void *data; -} envchain_search_values_applier_data; - -typedef struct { - envchain_namespace_search_callback callback; - int head_index; - char** names; - void *data; -} envchain_search_namespaces_context; +/* Legacy SecKeychainRef removed - using modern SecItem API */ /* misc */ @@ -49,18 +36,6 @@ envchain_fail_osstatus(OSStatus status) } -static char* -envchain_generate_service_name(const char *name) -{ - char *service_name; - asprintf(&service_name, "%s%s", ENVCHAIN_SERVICE_PREFIX, name); - if (service_name == NULL) { - fprintf(stderr, "Failed to generate service_name\n"); - exit(10); - } - return service_name; -} - static CFStringRef envchain_generate_service_name_cf(const char *name) { @@ -70,220 +45,216 @@ envchain_generate_service_name_cf(const char *name) ); } -static char* -envchain_get_self_path(void) +static void +envchain_log_cferror(const char *message, CFErrorRef error) { - uint32_t pathlen = 0; - char *selfpath = malloc(sizeof(char) * 255); - char *selfrealpath; - if (_NSGetExecutablePath(selfpath, &pathlen) < 0) { - selfpath = realloc(selfpath, sizeof(char) * pathlen); - if (_NSGetExecutablePath(selfpath, &pathlen) < 0) { - fprintf(stderr, "NSGetExecutablePath something went wrong :/\n"); - exit(10); - } - } + CFStringRef desc = NULL; + char buf[1024]; - selfrealpath = realpath(selfpath, NULL); - if (selfrealpath == NULL) { - fprintf(stderr, "Error during retrieve executable path of itself: %s\n", strerror(errno)); - exit(1); + if (error == NULL) { + fprintf(stderr, "%s\n", message); + return; } - free(selfpath); + desc = CFErrorCopyDescription(error); + if (desc != NULL && + CFStringGetCString(desc, buf, sizeof(buf), kCFStringEncodingUTF8)) { + fprintf(stderr, "%s: %s\n", message, buf); + } + else { + fprintf(stderr, "%s\n", message); + } - return selfrealpath; + if (desc != NULL) CFRelease(desc); } -static CFArrayRef -envchain_self_trusted_app_list(void) +static OSStatus +envchain_create_user_presence_acl(SecAccessControlRef *acl_out) { - char* selfpath = envchain_get_self_path(); - OSStatus status; - SecTrustedApplicationRef app; - CFArrayRef list = NULL; - - status = SecTrustedApplicationCreateFromPath(selfpath, &app); - if (status != noErr) goto fail; - - SecTrustedApplicationRef apps[] = {app}; - list = CFArrayCreate(NULL, (void*)apps, 1, &kCFTypeArrayCallBacks); - -fail: - if (app != NULL) CFRelease(app); - if (status != noErr) envchain_fail_osstatus(status); + CFErrorRef error = NULL; + SecAccessControlRef acl = NULL; + + acl = SecAccessControlCreateWithFlags( + kCFAllocatorDefault, + kSecAttrAccessibleWhenUnlockedThisDeviceOnly, + kSecAccessControlUserPresence, + &error); + if (acl == NULL) { + envchain_log_cferror("Error creating prompting access control", error); + if (error != NULL) CFRelease(error); + return errSecParam; + } - return list; + if (error != NULL) CFRelease(error); + *acl_out = acl; + return errSecSuccess; } - -static void -envchain_search_values_applier(const void *raw_ref, void *raw_context) +static OSStatus +envchain_create_self_trusted_access(CFStringRef desc, SecAccessRef *access_out) { OSStatus status; - envchain_search_values_applier_data *context = (envchain_search_values_applier_data*) raw_context; - SecKeychainItemRef ref = (SecKeychainItemRef) raw_ref; - - SecKeychainAttribute attr = {kSecAccountItemAttr, 0, NULL}; - if (context->search_callback) { - attr.tag = kSecAccountItemAttr; - } - else { - attr.tag = kSecServiceItemAttr; - } - SecKeychainAttributeList list = {1, &attr}; - SecItemClass klass; - UInt32 len, keylen = 0; - char* rawvalue = NULL; - char* rawkey = NULL; - char* value = NULL; - char* key = NULL; - - if (context->search_callback) { - status = SecKeychainItemCopyContent( - ref, &klass, &list, &len, (void*)&rawvalue - ); - } - else { - status = SecKeychainItemCopyContent( - ref, &klass, &list, &len, NULL - ); - } - - if (status != noErr) goto fail; - - for(UInt32 i = 0; i < list.count; i++) { - SecKeychainAttribute attr = list.attr[i]; - if (attr.tag == kSecAccountItemAttr || attr.tag == kSecServiceItemAttr) { - rawkey = (char*)attr.data; - keylen = attr.length; - key = malloc(keylen+1); - if (key == NULL) goto fail; +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + status = SecAccessCreate(desc, NULL, access_out); +#if defined(__clang__) +#pragma clang diagnostic pop +#endif - memcpy(key, rawkey, keylen); - key[keylen] = '\0'; + return status; +} - if (attr.tag == kSecServiceItemAttr) { - if (strncmp(key, ENVCHAIN_SERVICE_PREFIX, strlen(ENVCHAIN_SERVICE_PREFIX)) == 0) { - keylen = keylen - strlen(ENVCHAIN_SERVICE_PREFIX); - char* service_name = malloc(keylen + 1); +static OSStatus +envchain_preflight_access_mode(CFStringRef desc, int require_passphrase) +{ + OSStatus status = errSecSuccess; - memcpy(service_name, &key[strlen(ENVCHAIN_SERVICE_PREFIX)], keylen); - service_name[keylen] = '\0'; + if (require_passphrase == 1) { + SecAccessControlRef acl = NULL; - free(key); - key = service_name; - } - } - break; - } + status = envchain_create_user_presence_acl(&acl); + if (acl != NULL) CFRelease(acl); } + else if (require_passphrase == 0) { + SecAccessRef access = NULL; - if (rawkey == NULL) { - fprintf(stderr, "Can't find account name\n"); - goto ensure; + status = envchain_create_self_trusted_access(desc, &access); + if (access != NULL) CFRelease(access); } - if (context->search_callback) { - value = malloc(len+1); - if (value == NULL) goto fail; - memcpy(value,rawvalue,len); - value[len] = '\0'; - context->search_callback(key, value, context->data); - } - else { - context->namespace_callback(key, context->data); - } + return status; +} - goto ensure; -fail: - fprintf(stderr, "Something wrong during searching value\n"); - if (errno) fprintf(stderr, "errno: %s\n", strerror(errno)); -ensure: - if (value) { - memset(value, 0, len); - free(value); - } - if (key) { - memset(key, 0, keylen); - free(key); +static OSStatus +envchain_add_item_with_access(CFStringRef svc, CFStringRef acct, + CFDataRef val, CFStringRef desc, + int require_passphrase) +{ + OSStatus status = errSecSuccess; + CFMutableDictionaryRef add = NULL; + SecAccessControlRef acl = NULL; + SecAccessRef access = NULL; + + add = CFDictionaryCreateMutable( + NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(add, kSecClass, kSecClassGenericPassword); + CFDictionarySetValue(add, kSecAttrService, svc); + CFDictionarySetValue(add, kSecAttrAccount, acct); + CFDictionarySetValue(add, kSecValueData, val); + CFDictionarySetValue(add, kSecAttrDescription, desc); + + if (require_passphrase == 1) { + status = envchain_create_user_presence_acl(&acl); + if (status != errSecSuccess) goto cleanup; + CFDictionarySetValue(add, kSecAttrAccessControl, acl); } - if (context->search_callback) { - SecKeychainItemFreeContent(&list, rawvalue); + else if (require_passphrase == 0) { + status = envchain_create_self_trusted_access(desc, &access); + if (status != errSecSuccess) goto cleanup; + CFDictionarySetValue(add, kSecAttrAccess, access); } - return; + + status = SecItemAdd(add, NULL); + +cleanup: + if (acl != NULL) CFRelease(acl); + if (access != NULL) CFRelease(access); + if (add != NULL) CFRelease(add); + return status; } -static void -envchain_search_namespaces_uniqufier(const char* name, void *raw_context) +static OSStatus +envchain_copy_item_data(CFStringRef svc, CFStringRef acct, CFDataRef *data_out) { - envchain_search_namespaces_context* context = (envchain_search_namespaces_context*)raw_context; + OSStatus status; + CFTypeRef result = NULL; + CFMutableDictionaryRef query = CFDictionaryCreateMutable( + NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - char* item = malloc((strlen(name) * sizeof(char)) + 1); - strcpy(item, name); + CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); + CFDictionarySetValue(query, kSecAttrService, svc); + CFDictionarySetValue(query, kSecAttrAccount, acct); + CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); + CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne); - context->names[context->head_index] = item; - context->head_index++; + status = SecItemCopyMatching(query, &result); + CFRelease(query); + + if (status != errSecSuccess) return status; + + *data_out = (CFDataRef)result; + return errSecSuccess; } +/* Legacy code removed: envchain_get_self_path, envchain_self_trusted_app_list, + envchain_search_values_applier, envchain_search_namespaces_uniqufier. + All read/write/search paths now use modern SecItem API. */ + int envchain_search_namespaces(envchain_namespace_search_callback callback, void *data) { OSStatus status; - CFArrayRef items = NULL; - CFStringRef description = CFStringCreateWithCString(NULL, ENVCHAIN_ITEM_DESCRIPTION, kCFStringEncodingUTF8); - - const void *query_keys[] = { - kSecClass, kSecAttrDescription, - kSecReturnRef, kSecMatchLimit - }; - const void *query_vals[] = { - kSecClassGenericPassword, description, - kCFBooleanTrue, kSecMatchLimitAll - }; - - CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, - query_keys, query_vals, sizeof(query_keys) / sizeof(query_keys[0]), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - status = SecItemCopyMatching(query, (CFTypeRef *)&items); - if (status != errSecItemNotFound && status != noErr) goto fail; - - if (status == errSecItemNotFound || CFArrayGetCount(items) == 0) { + CFTypeRef results = NULL; + CFStringRef description = CFStringCreateWithCString( + NULL, ENVCHAIN_ITEM_DESCRIPTION, kCFStringEncodingUTF8); + + CFMutableDictionaryRef query = CFDictionaryCreateMutable( + NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); + CFDictionarySetValue(query, kSecAttrDescription, description); + CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue); + CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); + + status = SecItemCopyMatching(query, &results); + CFRelease(query); + CFRelease(description); + + if (status == errSecItemNotFound) return 0; + if (status != errSecSuccess) { + envchain_fail_osstatus(status); return 0; } - - char** names = malloc(sizeof(char*) * CFArrayGetCount(items)); - if (names == NULL) { - fprintf(stderr, "malloc fail (names)\n"); - goto fail; + + CFArrayRef items = (CFArrayRef)results; + CFIndex count = CFArrayGetCount(items); + size_t prefix_len = strlen(ENVCHAIN_SERVICE_PREFIX); + + /* Extract unique namespace names from service attributes */ + char **names = malloc(sizeof(char*) * count); + int name_count = 0; + + for (CFIndex i = 0; i < count; i++) { + CFDictionaryRef item = CFArrayGetValueAtIndex(items, i); + CFStringRef svc_cf = CFDictionaryGetValue(item, kSecAttrService); + if (svc_cf == NULL) continue; + + char svc_buf[1024]; + if (!CFStringGetCString(svc_cf, svc_buf, sizeof(svc_buf), kCFStringEncodingUTF8)) + continue; + + if (strncmp(svc_buf, ENVCHAIN_SERVICE_PREFIX, prefix_len) != 0) + continue; + + names[name_count] = strdup(svc_buf + prefix_len); + name_count++; } - envchain_search_namespaces_context context = {callback, 0, names, data}; - envchain_search_values_applier_data applier_context = {NULL, envchain_search_namespaces_uniqufier, &context}; - CFArrayApplyFunction( - items, CFRangeMake(0, CFArrayGetCount(items)), - &envchain_search_values_applier, &applier_context - ); + CFRelease(items); - qsort(names, CFArrayGetCount(items), sizeof(char*), envchain_sortcmp_str); - char *prev_name = NULL; - for(int i = 0; i < CFArrayGetCount(items); i++) { - if (!prev_name || strcmp(prev_name, names[i]) != 0) + /* Sort and deduplicate */ + qsort(names, name_count, sizeof(char*), envchain_sortcmp_str); + char *prev = NULL; + for (int i = 0; i < name_count; i++) { + if (!prev || strcmp(prev, names[i]) != 0) callback(names[i], data); - prev_name = names[i]; + prev = names[i]; } - for(int i = 0; i < CFArrayGetCount(items); i++) free(names[i]); - + for (int i = 0; i < name_count; i++) free(names[i]); free(names); -fail: - if (items != NULL) CFRelease(items); - if (query != NULL) CFRelease(query); - if (description != NULL) CFRelease(description); - if (status != noErr) envchain_fail_osstatus(status); - return 0; } @@ -292,170 +263,154 @@ envchain_search_values(const char *name, envchain_search_callback callback, void { OSStatus status; CFStringRef service_name = envchain_generate_service_name_cf(name); - CFArrayRef items = NULL; + CFTypeRef results = NULL; - const void *query_keys[] = { - kSecClass, kSecAttrService, - kSecReturnRef, kSecMatchLimit - }; - const void *query_vals[] = { - kSecClassGenericPassword, service_name, - kCFBooleanTrue, kSecMatchLimitAll - }; + CFMutableDictionaryRef query = CFDictionaryCreateMutable( + NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); + CFDictionarySetValue(query, kSecAttrService, service_name); + CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue); + CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); + CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); - CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, - query_keys, query_vals, sizeof(query_keys) / sizeof(query_keys[0]), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + status = SecItemCopyMatching(query, &results); - status = SecItemCopyMatching(query, (CFTypeRef *)&items); - if (status != errSecItemNotFound && status != noErr) goto fail; - - if (status == errSecItemNotFound || CFArrayGetCount(items) == 0) { + if (status == errSecItemNotFound) { fprintf(stderr, "WARNING: namespace `%s` not defined.\n" " You can set via running `%s --set %s SOME_ENV_NAME`.\n\n", name, envchain_name, name ); + CFRelease(query); + CFRelease(service_name); + return 1; + } + if (status != errSecSuccess) { + CFRelease(query); + CFRelease(service_name); + envchain_fail_osstatus(status); return 1; } - - envchain_search_values_applier_data context = {callback, NULL, data}; - CFArrayApplyFunction( - items, CFRangeMake(0, CFArrayGetCount(items)), - &envchain_search_values_applier, &context - ); -fail: - if (items != NULL) CFRelease(items); - if (query != NULL) CFRelease(query); - if (service_name != NULL) CFRelease(service_name); - if (status != noErr) envchain_fail_osstatus(status); + CFArrayRef items = (CFArrayRef)results; + CFIndex count = CFArrayGetCount(items); - return 0; -} + for (CFIndex i = 0; i < count; i++) { + CFDictionaryRef item = CFArrayGetValueAtIndex(items, i); -static int -envchain_find_value(const char *name, const char *key, SecKeychainItemRef *ref) -{ - OSStatus status; - char *service_name = envchain_generate_service_name(name); - - status = SecKeychainFindGenericPassword( - envchain_keychain, - strlen(service_name), service_name, - strlen(key), key, - NULL, NULL, - ref - ); + CFStringRef acct_cf = CFDictionaryGetValue(item, kSecAttrAccount); + CFDataRef val_cf = CFDictionaryGetValue(item, kSecValueData); - free(service_name); + if (acct_cf == NULL || val_cf == NULL) continue; - if (status != noErr && status != errSecItemNotFound) { - if (ref != NULL) CFRelease(ref); - envchain_fail_osstatus(status); + char acct_buf[1024]; + if (!CFStringGetCString(acct_cf, acct_buf, sizeof(acct_buf), kCFStringEncodingUTF8)) + continue; + + CFIndex val_len = CFDataGetLength(val_cf); + char *value = malloc(val_len + 1); + if (value == NULL) continue; + memcpy(value, CFDataGetBytePtr(val_cf), val_len); + value[val_len] = '\0'; + + callback(acct_buf, value, data); + + memset(value, 0, val_len); + free(value); } - return status == errSecItemNotFound ? 0 : 1; + CFRelease(items); + CFRelease(query); + CFRelease(service_name); + + return 0; } void envchain_save_value(const char *name, const char *key, char *value, int require_passphrase) { - char *service_name = envchain_generate_service_name(name); + CFStringRef svc = envchain_generate_service_name_cf(name); + CFStringRef acct = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); + CFDataRef val = CFDataCreate(NULL, (const UInt8 *)value, strlen(value)); + CFStringRef desc = CFStringCreateWithCString( + NULL, ENVCHAIN_ITEM_DESCRIPTION, kCFStringEncodingUTF8); + CFDataRef old_val = NULL; OSStatus status; - SecKeychainItemRef ref = NULL; - SecAccessRef access_ref = NULL; - CFArrayRef acl_list = nil; - - if (envchain_find_value(name, key, &ref) == 0) { - status = SecKeychainAddGenericPassword( - envchain_keychain, - strlen(service_name), service_name, - strlen(key), key, - strlen(value), value, - &ref - ); - } - else { - status = SecKeychainItemModifyAttributesAndData( - ref, - NULL, - strlen(value), value - ); - } + OSStatus restore_status; - free(service_name); - - if (status != noErr) goto fail; - - /* Set description */ - SecKeychainAttribute attr_desc = { - kSecDescriptionItemAttr, strlen(ENVCHAIN_ITEM_DESCRIPTION), ENVCHAIN_ITEM_DESCRIPTION}; - SecKeychainAttributeList attrs = {1, &attr_desc}; - status = SecKeychainItemModifyAttributesAndData( - ref, - &attrs, - strlen(value), value - ); + /* Build base query for existence check */ + CFMutableDictionaryRef match = CFDictionaryCreateMutable( + NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(match, kSecClass, kSecClassGenericPassword); + CFDictionarySetValue(match, kSecAttrService, svc); + CFDictionarySetValue(match, kSecAttrAccount, acct); - if (status != noErr) goto fail; - - if (require_passphrase >= 0) { - CFArrayRef app_list = NULL; - CFStringRef desc = NULL; - - status = SecKeychainItemCopyAccess(ref, &access_ref); - if (status != noErr) goto fail; - - acl_list = SecAccessCopyMatchingACLList( - access_ref, kSecACLAuthorizationDecrypt - ); - SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list, 0); - - if (acl == NULL) { - fprintf(stderr, "error: There's no ACL?\n"); - goto passfail; - } + status = SecItemCopyMatching(match, NULL); - SecKeychainPromptSelector prompt; - status = SecACLCopyContents(acl, &app_list, &desc, &prompt); - if (status != noErr) goto passfail; - if (app_list != NULL) CFRelease(app_list); - - if(require_passphrase == 1) { - if (prompt == 0) prompt = 0x100; - prompt |= kSecKeychainPromptRequirePassphase; - app_list = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); + if (status == errSecItemNotFound) { + status = envchain_add_item_with_access(svc, acct, val, desc, require_passphrase); + } + else if (status == errSecSuccess) { + if (require_passphrase < 0) { + /* Existing item - update value in place */ + CFMutableDictionaryRef update = CFDictionaryCreateMutable( + NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(update, kSecValueData, val); + CFDictionarySetValue(update, kSecAttrDescription, desc); + + status = SecItemUpdate(match, update); + CFRelease(update); } else { - prompt = 0; - app_list = envchain_self_trusted_app_list(); + status = envchain_preflight_access_mode(desc, require_passphrase); + if (status != errSecSuccess) goto cleanup; + + status = envchain_copy_item_data(svc, acct, &old_val); + if (status != errSecSuccess) goto cleanup; + + status = SecItemDelete(match); + if (status != errSecSuccess) goto cleanup; + + status = envchain_add_item_with_access(svc, acct, val, desc, require_passphrase); + if (status != errSecSuccess) { + fprintf(stderr, + "%s: failed to update item access mode, restoring previous value without prompt\n", + envchain_name); + restore_status = envchain_add_item_with_access(svc, acct, old_val, desc, 0); + if (restore_status != errSecSuccess) { + fprintf(stderr, "%s: failed to restore previous item: %d\n", + envchain_name, (int)restore_status); + } + } } - - status = SecACLSetContents(acl, app_list, desc, prompt); - if (status != noErr) goto passfail; - - status = SecKeychainItemSetAccess(ref, access_ref); - -passfail: - if (app_list != NULL) CFRelease(app_list); - if (desc != NULL) CFRelease(desc); - if (status != noErr) goto fail; } -fail: - if (ref != NULL) { CFRelease(ref); } - if (access_ref != NULL) { CFRelease(access_ref); } - if (acl_list != NULL) { CFRelease(acl_list); } - if (status != noErr) envchain_fail_osstatus(status); +cleanup: + CFRelease(match); + CFRelease(svc); + CFRelease(acct); + CFRelease(val); + CFRelease(desc); + if (old_val != NULL) CFRelease(old_val); - return; + if (status != errSecSuccess) envchain_fail_osstatus(status); } void -envchain_delete_value(const char *name, const char *key) { - SecKeychainItemRef ref = NULL; - if (envchain_find_value(name, key, &ref) != 0) { - SecKeychainItemDelete(ref); - } +envchain_delete_value(const char *name, const char *key) +{ + CFStringRef svc = envchain_generate_service_name_cf(name); + CFStringRef acct = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); + + CFMutableDictionaryRef query = CFDictionaryCreateMutable( + NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); + CFDictionarySetValue(query, kSecAttrService, svc); + CFDictionarySetValue(query, kSecAttrAccount, acct); + + SecItemDelete(query); + + CFRelease(query); + CFRelease(svc); + CFRelease(acct); }