osxkeychain: erase matching passwords only
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 input Signed-off-by: Bo Anderson <mail@boanderson.me> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Bo Anderson committed
Feb 17, 2024 at 23:34 UTC
e3cef40db89f5a7c91f4e9d6c4959ca1e41f4647
1 file changed
+55
-1
contrib/credential/osxkeychain/git-credential-osxkeychain.c
+55
-1
@@ -169,9 +169,55 @@ out:
169
return result;
170
}
171
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
+
217
static OSStatus delete_internet_password(void)
218
{
219
CFDictionaryRef attrs;
220
+ CFArrayRef refs;
221
OSStatus result;
222
223
/*
@@ -183,10 +229,18 @@ static OSStatus delete_internet_password(void)
229
return -1;
230
231
attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll,
232
+ kSecReturnRef, kCFBooleanTrue,
233
NULL);
187
- result = SecItemDelete(attrs);
234
+ result = SecItemCopyMatching(attrs, (CFTypeRef *)&refs);
235
CFRelease(attrs);
236
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
+
244
/* We consider not found to not be an error */
245
if (result == errSecItemNotFound)
246
result = errSecSuccess;