From: M Hickford <mirth.hickford@xxxxxxxxx> d208bfd (credential: new attribute password_expiry_utc, 2023-02-18) and a5c76569e7 (credential: new attribute oauth_refresh_token) introduced new credential attributes. Similar to 7144dee3 (credential/libsecret: erase matching creds only, 2023-07-26), we encode the new attributes in the secret, separated by newline: hunter2 password_expiry_utc=1684189401 oauth_refresh_token=xyzzy This is extensible and backwards compatible. The credential protocol already assumes that attribute values do not contain newlines. Signed-off-by: M Hickford <mirth.hickford@xxxxxxxxx> --- [RFC] contrib/credential/osxkeychain: store new attributes Is any keen MacOS user interested in building and testing this RFC patch? I personally don't have a MacOS machine, so haven't tried building it. Fixes are surely necessary. Once it builds, you can test the feature with: GIT_TEST_CREDENTIAL_HELPER=osxkeychain ./t0303-credential-external.sh The feature would help git-credential-oauth users on MacOS https://github.com/hickford/git-credential-oauth/issues/42 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1663%2Fhickford%2Fosxkeychain-v1 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1663/hickford/osxkeychain-v1 Pull-Request: https://github.com/gitgitgadget/git/pull/1663 .../osxkeychain/git-credential-osxkeychain.c | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index 5f2e5f16c88..25ffa84f4ba 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -8,6 +8,8 @@ static char *host; static char *path; static char *username; static char *password; +static char *password_expiry_utc; +static char *oauth_refresh_token; static UInt16 port; __attribute__((format (printf, 1, 2))) @@ -22,6 +24,17 @@ static void die(const char *err, ...) exit(1); } + +static void *xmalloc(size_t size) +{ + void *ret = malloc(size); + if (!ret && !size) + ret = malloc(1); + if (!ret) + die("Out of memory"); + return ret; +} + static void *xstrdup(const char *s1) { void *ret = strdup(s1); @@ -69,11 +82,27 @@ static void find_internet_password(void) void *buf; UInt32 len; SecKeychainItemRef item; + char *line; + char *remaining_lines; + char *part; + char *remaining_parts; if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item)) return; - write_item("password", buf, len); + line = strtok_r(buf, "\n", &remaining_lines); + write_item("password", line, strlen(line)); + while(line != NULL) { + part = strtok_r(line, "=", &remaining_parts); + if (!strcmp(part, "oauth_refresh_token")) { + write_item("oauth_refresh_token", remaining_parts, strlen(remaining_parts)); + } + if (!strcmp(part, "password_expiry_utc")) { + write_item("password_expiry_utc", remaining_parts, strlen(remaining_parts)); + } + line = strtok_r(NULL, "\n", &remaining_lines); + } + if (!username) find_username_in_item(item); @@ -100,13 +129,32 @@ static void delete_internet_password(void) static void add_internet_password(void) { + int len; + /* Only store complete credentials */ if (!protocol || !host || !username || !password) return; + char *secret; + if (password_expiry_utc && oauth_refresh_token) { + len = strlen(password) + strlen(password_expiry_utc) + strlen(oauth_refresh_token) + strlen("\npassword_expiry_utc=\noauth_refresh_token="); + secret = xmalloc(len); + snprintf(secret, len, len, "%s\npassword_expiry_utc=%s\noauth_refresh_token=%s", password, oauth_refresh_token); + } else if (oauth_refresh_token) { + len = strlen(password) + strlen(oauth_refresh_token) + strlen("\noauth_refresh_token="); + secret = xmalloc(len); + snprintf(secret, len, len, "%s\noauth_refresh_token=%s", password, oauth_refresh_token); + } else if (password_expiry_utc) { + len = strlen(password) + strlen(password_expiry_utc) + strlen("\npassword_expiry_utc="); + secret = xmalloc(len); + snprintf(secret, len, len, "%s\npassword_expiry_utc=%s", password, password_expiry_utc); + } else { + secret = xstrdup(password); + } + if (SecKeychainAddInternetPassword( KEYCHAIN_ARGS, - KEYCHAIN_ITEM(password), + KEYCHAIN_ITEM(secret), NULL)) return; } @@ -161,6 +209,10 @@ static void read_credential(void) username = xstrdup(v); else if (!strcmp(buf, "password")) password = xstrdup(v); + else if (!strcmp(buf, "password_expiry_utc")) + password_expiry_utc = xstrdup(v); + else if (!strcmp(buf, "oauth_refresh_token")) + oauth_refresh_token = xstrdup(v); /* * Ignore other lines; we don't know what they mean, but * this future-proofs us when later versions of git do base-commit: c875e0b8e036c12cfbf6531962108a063c7a821c -- gitgitgadget