]> git.ipfire.org Git - thirdparty/git.git/commitdiff
osxkeychain: erase matching passwords only
authorBo Anderson <mail@boanderson.me>
Sat, 17 Feb 2024 23:34:55 +0000 (23:34 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 Apr 2024 22:38:20 +0000 (15:38 -0700)
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>
contrib/credential/osxkeychain/git-credential-osxkeychain.c

index e9cee3aed4591d2422b6f59513e1950a8fa90c26..9e742796336d5be10c3ffc4cb7f9634537bbe946 100644 (file)
@@ -169,9 +169,55 @@ out:
        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;