Skip to content

Commit e3cef40

Browse files
Bo98gitster
authored andcommitted
osxkeychain: erase matching passwords only
Other credential helpers support deleting credentials that match a specified password. See 7144dee (credential/libsecret: erase matching creds only, 2023-07-26) and cb626f8 (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 input Signed-off-by: Bo Anderson <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9032bca commit e3cef40

File tree

1 file changed

+55
-1
lines changed

1 file changed

+55
-1
lines changed

contrib/credential/osxkeychain/git-credential-osxkeychain.c

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,55 @@ static OSStatus find_internet_password(void)
169169
return result;
170170
}
171171

172+
static OSStatus delete_ref(const void *itemRef)
173+
{
174+
CFArrayRef item_ref_list;
175+
CFDictionaryRef delete_query;
176+
OSStatus result;
177+
178+
item_ref_list = CFArrayCreate(kCFAllocatorDefault,
179+
&itemRef,
180+
1,
181+
&kCFTypeArrayCallBacks);
182+
delete_query = create_dictionary(kCFAllocatorDefault,
183+
kSecClass, kSecClassInternetPassword,
184+
kSecMatchItemList, item_ref_list,
185+
NULL);
186+
187+
if (password) {
188+
/* We only want to delete items with a matching password */
189+
CFIndex capacity;
190+
CFMutableDictionaryRef query;
191+
CFDataRef data;
192+
193+
capacity = CFDictionaryGetCount(delete_query) + 1;
194+
query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
195+
capacity,
196+
delete_query);
197+
CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
198+
result = SecItemCopyMatching(query, (CFTypeRef *)&data);
199+
if (!result) {
200+
if (CFEqual(data, password))
201+
result = SecItemDelete(delete_query);
202+
203+
CFRelease(data);
204+
}
205+
206+
CFRelease(query);
207+
} else {
208+
result = SecItemDelete(delete_query);
209+
}
210+
211+
CFRelease(delete_query);
212+
CFRelease(item_ref_list);
213+
214+
return result;
215+
}
216+
172217
static OSStatus delete_internet_password(void)
173218
{
174219
CFDictionaryRef attrs;
220+
CFArrayRef refs;
175221
OSStatus result;
176222

177223
/*
@@ -183,10 +229,18 @@ static OSStatus delete_internet_password(void)
183229
return -1;
184230

185231
attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll,
232+
kSecReturnRef, kCFBooleanTrue,
186233
NULL);
187-
result = SecItemDelete(attrs);
234+
result = SecItemCopyMatching(attrs, (CFTypeRef *)&refs);
188235
CFRelease(attrs);
189236

237+
if (!result) {
238+
for (CFIndex i = 0; !result && i < CFArrayGetCount(refs); i++)
239+
result = delete_ref(CFArrayGetValueAtIndex(refs, i));
240+
241+
CFRelease(refs);
242+
}
243+
190244
/* We consider not found to not be an error */
191245
if (result == errSecItemNotFound)
192246
result = errSecSuccess;

0 commit comments

Comments
 (0)