[PATCH 3/4] osxkeychain: erase matching passwords only
- From
Bo Anderson via GitGitGadget <gitgitgadget@gmail.com>
- Date
- Feb 17, 2024, 23:34 UTC
- Message-ID
- <f7ac228aae69941032d904c3c6222216786c1d0e.1708212896.git.gitgitgadget@gmail.com>
- In-Reply-To
- <pull.1667.git.1708212896.gitgitgadget@gmail.com>
From: Bo Anderson <mail@boanderson.me>
Other credential helpers support deleting credentials that match a specified password. See 7144dee3ec (credential/libsecret: erase matching creds only, 2023-07-26) and cb626f8e5c (credential/wincred: erase matching creds only, 2023-07-26).
Support this in osxkeychain too by extracting, decrypting and comparing the stored password before deleting.
Fixes the following test failure with osxkeychain:
11 - helper (osxkeychain) does not erase a password distinct from
inputSigned-off-by: Bo Anderson <mail@boanderson.me> --- .../osxkeychain/git-credential-osxkeychain.c | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-)
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index e9cee3aed45..9e742796336 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -169,9 +169,55 @@ static OSStatus find_internet_password(void) return result; } +static OSStatus delete_ref(const void *itemRef) +{ + CFArrayRef item_ref_list; + CFDictionaryRef delete_query; + OSStatus result; + + item_ref_list = CFArrayCreate(kCFAllocatorDefault, + &itemRef, + 1, + &kCFTypeArrayCallBacks); + delete_query = create_dictionary(kCFAllocatorDefault, + kSecClass, kSecClassInternetPassword, + kSecMatchItemList, item_ref_list, + NULL); + + if (password) { + /* We only want to delete items with a matching password */ + CFIndex capacity; + CFMutableDictionaryRef query; + CFDataRef data; + + capacity = CFDictionaryGetCount(delete_query) + 1; + query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, + capacity, + delete_query); + CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); + result = SecItemCopyMatching(query, (CFTypeRef *)&data); + if (!result) { + if (CFEqual(data, password)) + result = SecItemDelete(delete_query); + + CFRelease(data); + } + + CFRelease(query); + } else { + result = SecItemDelete(delete_query); + } + + CFRelease(delete_query); + CFRelease(item_ref_list); + + return result; +} + static OSStatus delete_internet_password(void) { CFDictionaryRef attrs; + CFArrayRef refs; OSStatus result; /* @@ -183,10 +229,18 @@ static OSStatus delete_internet_password(void) return -1; attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll, + kSecReturnRef, kCFBooleanTrue, NULL); - result = SecItemDelete(attrs); + result = SecItemCopyMatching(attrs, (CFTypeRef *)&refs); CFRelease(attrs); + if (!result) { + for (CFIndex i = 0; !result && i < CFArrayGetCount(refs); i++) + result = delete_ref(CFArrayGetValueAtIndex(refs, i)); + + CFRelease(refs); + } + /* We consider not found to not be an error */ if (result == errSecItemNotFound) result = errSecSuccess;
-- gitgitgadget