]> git.ipfire.org Git - thirdparty/git.git/commitdiff
osxkeychain: store new attributes
authorBo Anderson <mail@boanderson.me>
Sat, 17 Feb 2024 23:34:56 +0000 (23:34 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 Apr 2024 22:38:20 +0000 (15:38 -0700)
d208bfdfef (credential: new attribute password_expiry_utc, 2023-02-18)
and a5c76569e7 (credential: new attribute oauth_refresh_token,
2023-04-21) introduced new credential attributes but support was missing
from git-credential-osxkeychain.

Support these attributes by appending the data to the password in the
keychain, separated by line breaks. Line breaks cannot appear in a git
credential password so it is an appropriate separator.

Fixes the remaining test failures with osxkeychain:

    18 - helper (osxkeychain) gets password_expiry_utc
    19 - helper (osxkeychain) overwrites when password_expiry_utc
    changes
    21 - helper (osxkeychain) gets oauth_refresh_token

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 9e742796336d5be10c3ffc4cb7f9634537bbe946..6a40917b1ef866a3f9f5295dd887797df58af777 100644 (file)
@@ -6,10 +6,12 @@
 #define ENCODING kCFStringEncodingUTF8
 static CFStringRef protocol; /* Stores constant strings - not memory managed */
 static CFStringRef host;
+static CFNumberRef port;
 static CFStringRef path;
 static CFStringRef username;
 static CFDataRef password;
-static CFNumberRef port;
+static CFDataRef password_expiry_utc;
+static CFDataRef oauth_refresh_token;
 
 static void clear_credential(void)
 {
@@ -17,6 +19,10 @@ static void clear_credential(void)
                CFRelease(host);
                host = NULL;
        }
+       if (port) {
+               CFRelease(port);
+               port = NULL;
+       }
        if (path) {
                CFRelease(path);
                path = NULL;
@@ -29,12 +35,18 @@ static void clear_credential(void)
                CFRelease(password);
                password = NULL;
        }
-       if (port) {
-               CFRelease(port);
-               port = NULL;
+       if (password_expiry_utc) {
+               CFRelease(password_expiry_utc);
+               password_expiry_utc = NULL;
+       }
+       if (oauth_refresh_token) {
+               CFRelease(oauth_refresh_token);
+               oauth_refresh_token = NULL;
        }
 }
 
+#define STRING_WITH_LENGTH(s) s, sizeof(s) - 1
+
 __attribute__((format (printf, 1, 2), __noreturn__))
 static void die(const char *err, ...)
 {
@@ -197,9 +209,27 @@ static OSStatus delete_ref(const void *itemRef)
                CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
                result = SecItemCopyMatching(query, (CFTypeRef *)&data);
                if (!result) {
-                       if (CFEqual(data, password))
+                       CFDataRef kc_password;
+                       const UInt8 *raw_data;
+                       const UInt8 *line;
+
+                       /* Don't match appended metadata */
+                       raw_data = CFDataGetBytePtr(data);
+                       line = memchr(raw_data, '\n', CFDataGetLength(data));
+                       if (line)
+                               kc_password = CFDataCreateWithBytesNoCopy(
+                                               kCFAllocatorDefault,
+                                               raw_data,
+                                               line - raw_data,
+                                               kCFAllocatorNull);
+                       else
+                               kc_password = data;
+
+                       if (CFEqual(kc_password, password))
                                result = SecItemDelete(delete_query);
 
+                       if (line)
+                               CFRelease(kc_password);
                        CFRelease(data);
                }
 
@@ -250,6 +280,7 @@ static OSStatus delete_internet_password(void)
 
 static OSStatus add_internet_password(void)
 {
+       CFMutableDataRef data;
        CFDictionaryRef attrs;
        OSStatus result;
 
@@ -257,7 +288,23 @@ static OSStatus add_internet_password(void)
        if (!protocol || !host || !username || !password)
                return -1;
 
-       attrs = CREATE_SEC_ATTRIBUTES(kSecValueData, password,
+       data = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, password);
+       if (password_expiry_utc) {
+               CFDataAppendBytes(data,
+                   (const UInt8 *)STRING_WITH_LENGTH("\npassword_expiry_utc="));
+               CFDataAppendBytes(data,
+                                 CFDataGetBytePtr(password_expiry_utc),
+                                 CFDataGetLength(password_expiry_utc));
+       }
+       if (oauth_refresh_token) {
+               CFDataAppendBytes(data,
+                   (const UInt8 *)STRING_WITH_LENGTH("\noauth_refresh_token="));
+               CFDataAppendBytes(data,
+                                 CFDataGetBytePtr(oauth_refresh_token),
+                                 CFDataGetLength(oauth_refresh_token));
+       }
+
+       attrs = CREATE_SEC_ATTRIBUTES(kSecValueData, data,
                                      NULL);
 
        result = SecItemAdd(attrs, NULL);
@@ -268,6 +315,7 @@ static OSStatus add_internet_password(void)
                CFRelease(query);
        }
 
+       CFRelease(data);
        CFRelease(attrs);
 
        return result;
@@ -339,6 +387,14 @@ static void read_credential(void)
                        password = CFDataCreate(kCFAllocatorDefault,
                                                (UInt8 *)v,
                                                strlen(v));
+               else if (!strcmp(buf, "password_expiry_utc"))
+                       password_expiry_utc = CFDataCreate(kCFAllocatorDefault,
+                                                          (UInt8 *)v,
+                                                          strlen(v));
+               else if (!strcmp(buf, "oauth_refresh_token"))
+                       oauth_refresh_token = CFDataCreate(kCFAllocatorDefault,
+                                                          (UInt8 *)v,
+                                                          strlen(v));
                /*
                 * Ignore other lines; we don't know what they mean, but
                 * this future-proofs us when later versions of git do