From ac042352b4910134c4c4d74b68f30061bde145b1 Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Sun, 15 Sep 2024 15:44:12 +0800
Subject: [PATCH] Revert breaking osxkeychain

This reverts commit e1ab45b2dab51f94db9548666dfd7af626d2aa7e.

This reverts commit fcf5b74e59c1c0d18a8e8e939475007b3b5f83ad.

This reverts commit d5b35bba86e6fdf0484ea71bf5b8ef1167f14015.

This reverts commit e3cef40db89f5a7c91f4e9d6c4959ca1e41f4647.

This reverts commit 9032bcad82f45a403e4a8de86e1fcb4bfd1ab282.

This reverts commit 9abe31f5f161be4d69118bdfae00103cd6efa510.

This reverts commit b201316835bbf2c49c2780f23cfd6146f6b8d1a2.

---
--- a/contrib/credential/osxkeychain/Makefile
+++ b/contrib/credential/osxkeychain/Makefile
@@ -1,13 +1,26 @@
 # The default target of this Makefile is...
 all:: git-credential-osxkeychain
 
-git-credential-osxkeychain:
-	$(MAKE) -C ../../.. contrib/credential/osxkeychain/git-credential-osxkeychain
+prefix ?= /usr/local
+gitexecdir ?= $(prefix)/libexec/git-core
 
-install:
-	$(MAKE) -C ../../.. install-git-credential-osxkeychain
+CC ?= gcc
+CFLAGS ?= -g -O2 -Wall
+INSTALL ?= install
+RM ?= rm -f
 
+%.o: %.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
+
+git-credential-osxkeychain: git-credential-osxkeychain.o
+	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) \
+		-framework Security -framework CoreFoundation
+
+install: git-credential-osxkeychain
+	$(INSTALL) -d -m 755 $(DESTDIR)$(gitexecdir)
+	$(INSTALL) -m 755 $< $(DESTDIR)$(gitexecdir)
+
 clean:
-	$(MAKE) -C ../../.. clean-git-credential-osxkeychain
+	$(RM) git-credential-osxkeychain git-credential-osxkeychain.o
 
-.PHONY: all git-credential-osxkeychain install clean
+.PHONY: all install clean
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -2,390 +2,113 @@
 #include <string.h>
 #include <stdlib.h>
 #include <Security/Security.h>
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "wrapper.h"
 
-#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 CFDataRef password_expiry_utc;
-static CFDataRef oauth_refresh_token;
-static char *state_seen;
+static SecProtocolType protocol;
+static char *host;
+static char *path;
+static char *username;
+static char *password;
+static UInt16 port;
 
-static void clear_credential(void)
+__attribute__((format (printf, 1, 2)))
+static void die(const char *err, ...)
 {
-	if (host) {
-		CFRelease(host);
-		host = NULL;
-	}
-	if (port) {
-		CFRelease(port);
-		port = NULL;
-	}
-	if (path) {
-		CFRelease(path);
-		path = NULL;
-	}
-	if (username) {
-		CFRelease(username);
-		username = NULL;
-	}
-	if (password) {
-		CFRelease(password);
-		password = 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;
-	}
+	char msg[4096];
+	va_list params;
+	va_start(params, err);
+	vsnprintf(msg, sizeof(msg), err, params);
+	fprintf(stderr, "%s\n", msg);
+	va_end(params);
+	exit(1);
 }
 
-#define STRING_WITH_LENGTH(s) s, sizeof(s) - 1
-
-static CFDictionaryRef create_dictionary(CFAllocatorRef allocator, ...)
+static void *xstrdup(const char *s1)
 {
-	va_list args;
-	const void *key;
-	CFMutableDictionaryRef result;
-
-	result = CFDictionaryCreateMutable(allocator,
-					   0,
-					   &kCFTypeDictionaryKeyCallBacks,
-					   &kCFTypeDictionaryValueCallBacks);
-
-
-	va_start(args, allocator);
-	while ((key = va_arg(args, const void *)) != NULL) {
-		const void *value;
-		value = va_arg(args, const void *);
-		if (value)
-			CFDictionarySetValue(result, key, value);
-	}
-	va_end(args);
-
-	return result;
+	void *ret = strdup(s1);
+	if (!ret)
+		die("Out of memory");
+	return ret;
 }
 
-#define CREATE_SEC_ATTRIBUTES(...) \
-	create_dictionary(kCFAllocatorDefault, \
-			  kSecClass, kSecClassInternetPassword, \
-			  kSecAttrServer, host, \
-			  kSecAttrAccount, username, \
-			  kSecAttrPath, path, \
-			  kSecAttrPort, port, \
-			  kSecAttrProtocol, protocol, \
-			  kSecAttrAuthenticationType, \
-			  kSecAttrAuthenticationTypeDefault, \
-			  __VA_ARGS__);
+#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
+#define KEYCHAIN_ARGS \
+	NULL, /* default keychain */ \
+	KEYCHAIN_ITEM(host), \
+	0, NULL, /* account domain */ \
+	KEYCHAIN_ITEM(username), \
+	KEYCHAIN_ITEM(path), \
+	port, \
+	protocol, \
+	kSecAuthenticationTypeDefault
 
-static void write_item(const char *what, const char *buf, size_t len)
+static void write_item(const char *what, const char *buf, int len)
 {
 	printf("%s=", what);
 	fwrite(buf, 1, len, stdout);
 	putchar('\n');
 }
 
-static void write_item_strbuf(struct strbuf *sb, const char *what, const char *buf, int n)
+static void find_username_in_item(SecKeychainItemRef item)
 {
-	char s[32];
+	SecKeychainAttributeList list;
+	SecKeychainAttribute attr;
 
-	xsnprintf(s, sizeof(s), "__%s=", what);
-	strbuf_add(sb, s, strlen(s));
-	strbuf_add(sb, buf, n);
-}
+	list.count = 1;
+	list.attr = &attr;
+	attr.tag = kSecAccountItemAttr;
 
-static void write_item_strbuf_cfstring(struct strbuf *sb, const char *what, CFStringRef ref)
-{
-	char *buf;
-	int len;
-
-	if (!ref)
+	if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
 		return;
-	len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(ref), ENCODING) + 1;
-	buf = xmalloc(len);
-	if (CFStringGetCString(ref, buf, len, ENCODING))
-		write_item_strbuf(sb, what, buf, strlen(buf));
-	free(buf);
-}
 
-static void write_item_strbuf_cfnumber(struct strbuf *sb, const char *what, CFNumberRef ref)
-{
-	short n;
-	char buf[32];
-
-	if (!ref)
-		return;
-	if (!CFNumberGetValue(ref, kCFNumberShortType, &n))
-		return;
-	xsnprintf(buf, sizeof(buf), "%d", n);
-	write_item_strbuf(sb, what, buf, strlen(buf));
+	write_item("username", attr.data, attr.length);
+	SecKeychainItemFreeContent(&list, NULL);
 }
 
-static void write_item_strbuf_cfdata(struct strbuf *sb, const char *what, CFDataRef ref)
+static void find_internet_password(void)
 {
-	char *buf;
-	int len;
+	void *buf;
+	UInt32 len;
+	SecKeychainItemRef item;
 
-	if (!ref)
+	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
 		return;
-	buf = (char *)CFDataGetBytePtr(ref);
-	if (!buf || strlen(buf) == 0)
-		return;
-	len = CFDataGetLength(ref);
-	write_item_strbuf(sb, what, buf, len);
-}
 
-static void encode_state_seen(struct strbuf *sb)
-{
-	strbuf_add(sb, "osxkeychain:seen=", strlen("osxkeychain:seen="));
-	write_item_strbuf_cfstring(sb, "host", host);
-	write_item_strbuf_cfnumber(sb, "port", port);
-	write_item_strbuf_cfstring(sb, "path", path);
-	write_item_strbuf_cfstring(sb, "username", username);
-	write_item_strbuf_cfdata(sb, "password", password);
-}
-
-static void find_username_in_item(CFDictionaryRef item)
-{
-	CFStringRef account_ref;
-	char *username_buf;
-	CFIndex buffer_len;
-
-	account_ref = CFDictionaryGetValue(item, kSecAttrAccount);
-	if (!account_ref)
-	{
-		write_item("username", "", 0);
-		return;
-	}
-	username = CFStringCreateCopy(kCFAllocatorDefault, account_ref);
-
-	username_buf = (char *)CFStringGetCStringPtr(account_ref, ENCODING);
-	if (username_buf)
-	{
-		write_item("username", username_buf, strlen(username_buf));
-		return;
-	}
-
-	/* If we can't get a CString pointer then
-	 * we need to allocate our own buffer */
-	buffer_len = CFStringGetMaximumSizeForEncoding(
-			CFStringGetLength(account_ref), ENCODING) + 1;
-	username_buf = xmalloc(buffer_len);
-	if (CFStringGetCString(account_ref,
-				username_buf,
-				buffer_len,
-				ENCODING)) {
-		write_item("username", username_buf, strlen(username_buf));
-	}
-	free(username_buf);
-}
-
-static OSStatus find_internet_password(void)
-{
-	CFDictionaryRef attrs;
-	CFDictionaryRef item;
-	CFDataRef data;
-	OSStatus result;
-
-	attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitOne,
-				      kSecReturnAttributes, kCFBooleanTrue,
-				      kSecReturnData, kCFBooleanTrue,
-				      NULL);
-	result = SecItemCopyMatching(attrs, (CFTypeRef *)&item);
-	if (result) {
-		goto out;
-	}
-
-	data = CFDictionaryGetValue(item, kSecValueData);
-	password = CFDataCreateCopy(kCFAllocatorDefault, data);
-
-	write_item("password",
-		   (const char *)CFDataGetBytePtr(data),
-		   CFDataGetLength(data));
+	write_item("password", buf, len);
 	if (!username)
 		find_username_in_item(item);
 
-	CFRelease(item);
-
-	write_item("capability[]", "state", strlen("state"));
-	{
-		struct strbuf sb;
-
-		strbuf_init(&sb, 1024);
-		encode_state_seen(&sb);
-		write_item("state[]", sb.buf, strlen(sb.buf));
-		strbuf_release(&sb);
-	}
-
-out:
-	CFRelease(attrs);
-
-	/* We consider not found to not be an error */
-	if (result == errSecItemNotFound)
-		result = errSecSuccess;
-
-	return result;
+	SecKeychainItemFreeContent(NULL, buf);
 }
 
-static OSStatus delete_ref(const void *itemRef)
+static void delete_internet_password(void)
 {
-	CFArrayRef item_ref_list;
-	CFDictionaryRef delete_query;
-	OSStatus result;
+	SecKeychainItemRef item;
 
-	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) {
-			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);
-		}
-
-		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;
-
 	/*
 	 * Require at least a protocol and host for removal, which is what git
 	 * will give us; if you want to do something more fancy, use the
 	 * Keychain manager.
 	 */
 	if (!protocol || !host)
-		return -1;
+		return;
 
-	attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll,
-				      kSecReturnRef, kCFBooleanTrue,
-				      NULL);
-	result = SecItemCopyMatching(attrs, (CFTypeRef *)&refs);
-	CFRelease(attrs);
+	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
+		return;
 
-	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;
-
-	return result;
+	SecKeychainItemDelete(item);
 }
 
-static OSStatus add_internet_password(void)
+static void add_internet_password(void)
 {
-	CFMutableDataRef data;
-	CFDictionaryRef attrs;
-	OSStatus result;
-
 	/* Only store complete credentials */
 	if (!protocol || !host || !username || !password)
-		return -1;
+		return;
 
-	if (state_seen) {
-		struct strbuf sb;
-
-		strbuf_init(&sb, 1024);
-		encode_state_seen(&sb);
-		if (!strcmp(state_seen, sb.buf)) {
-			strbuf_release(&sb);
-			return errSecSuccess;
-		}
-		strbuf_release(&sb);
-	}
-
-	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);
-	if (result == errSecDuplicateItem) {
-		CFDictionaryRef query;
-		query = CREATE_SEC_ATTRIBUTES(NULL);
-		result = SecItemUpdate(query, attrs);
-		CFRelease(query);
-	}
-
-	CFRelease(data);
-	CFRelease(attrs);
-
-	return result;
+	if (SecKeychainAddInternetPassword(
+	      KEYCHAIN_ARGS,
+	      KEYCHAIN_ITEM(password),
+	      NULL))
+		return;
 }
 
 static void read_credential(void)
@@ -408,65 +131,36 @@
 
 		if (!strcmp(buf, "protocol")) {
 			if (!strcmp(v, "imap"))
-				protocol = kSecAttrProtocolIMAP;
+				protocol = kSecProtocolTypeIMAP;
 			else if (!strcmp(v, "imaps"))
-				protocol = kSecAttrProtocolIMAPS;
+				protocol = kSecProtocolTypeIMAPS;
 			else if (!strcmp(v, "ftp"))
-				protocol = kSecAttrProtocolFTP;
+				protocol = kSecProtocolTypeFTP;
 			else if (!strcmp(v, "ftps"))
-				protocol = kSecAttrProtocolFTPS;
+				protocol = kSecProtocolTypeFTPS;
 			else if (!strcmp(v, "https"))
-				protocol = kSecAttrProtocolHTTPS;
+				protocol = kSecProtocolTypeHTTPS;
 			else if (!strcmp(v, "http"))
-				protocol = kSecAttrProtocolHTTP;
+				protocol = kSecProtocolTypeHTTP;
 			else if (!strcmp(v, "smtp"))
-				protocol = kSecAttrProtocolSMTP;
-			else {
-				/* we don't yet handle other protocols */
-				clear_credential();
+				protocol = kSecProtocolTypeSMTP;
+			else /* we don't yet handle other protocols */
 				exit(0);
-			}
 		}
 		else if (!strcmp(buf, "host")) {
 			char *colon = strchr(v, ':');
 			if (colon) {
-				UInt16 port_i;
 				*colon++ = '\0';
-				port_i = atoi(colon);
-				port = CFNumberCreate(kCFAllocatorDefault,
-						      kCFNumberShortType,
-						      &port_i);
+				port = atoi(colon);
 			}
-			host = CFStringCreateWithCString(kCFAllocatorDefault,
-							 v,
-							 ENCODING);
+			host = xstrdup(v);
 		}
 		else if (!strcmp(buf, "path"))
-			path = CFStringCreateWithCString(kCFAllocatorDefault,
-							 v,
-							 ENCODING);
+			path = xstrdup(v);
 		else if (!strcmp(buf, "username"))
-			username = CFStringCreateWithCString(
-					kCFAllocatorDefault,
-					v,
-					ENCODING);
+			username = xstrdup(v);
 		else if (!strcmp(buf, "password"))
-			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));
-		else if (!strcmp(buf, "state[]")) {
-			int len = strlen("osxkeychain:seen=");
-			if (!strncmp(v, "osxkeychain:seen=", len))
-				state_seen = xstrdup(v);
-		}
+			password = xstrdup(v);
 		/*
 		 * Ignore other lines; we don't know what they mean, but
 		 * this future-proofs us when later versions of git do
@@ -479,33 +173,21 @@
 
 int main(int argc, const char **argv)
 {
-	OSStatus result = 0;
 	const char *usage =
 		"usage: git credential-osxkeychain <get|store|erase>";
 
 	if (argc < 2 || !*argv[1])
 		die("%s", usage);
 
-	if (open(argv[0], O_RDONLY | O_EXLOCK) == -1)
-		die("failed to lock %s", argv[0]);
-
 	read_credential();
 
 	if (!strcmp(argv[1], "get"))
-		result = find_internet_password();
+		find_internet_password();
 	else if (!strcmp(argv[1], "store"))
-		result = add_internet_password();
+		add_internet_password();
 	else if (!strcmp(argv[1], "erase"))
-		result = delete_internet_password();
+		delete_internet_password();
 	/* otherwise, ignore unknown action */
 
-	if (result)
-		die("failed to %s: %d", argv[1], (int)result);
-
-	clear_credential();
-
-	if (state_seen)
-		free(state_seen);
-
 	return 0;
 }
