git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 4/4] osxkeychain: port to generic credential helper implementation

From
Philipp A. Hartmann <pah@qo.cx>
Date
Aug 23, 2012, 16:57 UTC
Message-ID
<1345741068-11004-5-git-send-email-pah@qo.cx>
In-Reply-To
<1345741068-11004-1-git-send-email-pah@qo.cx>
From: "Philipp A. Hartmann" <pah@qo.cx>

This reduces code duplication in the osxkeychain helper by basing the implementation on the generic helper implementation.

Alongside, the return codes of the helper are tightened to be more consistent in corner cases and the memory containing cleartext passwords is explicitly cleared when possible.

Signed-off-by: Philipp A. Hartmann <pah@qo.cx>
---
 contrib/credential/helper/credential_helper.h      |    9 +
 contrib/credential/osxkeychain/Makefile            |   22 ++-
 .../osxkeychain/git-credential-osxkeychain.c       |  183 ++++++++------------
 3 files changed, 96 insertions(+), 118 deletions(-)
diff --git a/contrib/credential/helper/credential_helper.h b/contrib/credential/helper/credential_helper.h
index 8266078..76b6e50 100644
--- a/contrib/credential/helper/credential_helper.h
+++ b/contrib/credential/helper/credential_helper.h
@@ -113,4 +113,13 @@ static inline char *xstrdup(const char *str)
 	return ret;
 }
 
+static inline char *xstrndup(const char *str, size_t len)
+{
+	char *ret = strndup(str,len);
+	if (!ret)
+		die_errno(errno);
+
+	return ret;
+}
+
 #endif /* CREDENTIAL_HELPER_H_INCLUDED_ */
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
index 4b3a08a..64ee7c5 100644
--- a/contrib/credential/osxkeychain/Makefile
+++ b/contrib/credential/osxkeychain/Makefile
@@ -1,4 +1,5 @@
-all:: git-credential-osxkeychain
+MAIN:=git-credential-osxkeychain
+all:: $(MAIN)
 
 CC = gcc
 RM = rm -f
@@ -7,11 +8,20 @@ CFLAGS = -g -O2 -Wall
 -include ../../../config.mak.autogen
 -include ../../../config.mak
 
-git-credential-osxkeychain: git-credential-osxkeychain.o
-	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -Wl,-framework -Wl,Security
+INCS:=
+LIBS:=-Wl,-framework -Wl,Security
+HELPER:=../helper
+VPATH +=$(HELPER)
 
-git-credential-osxkeychain.o: git-credential-osxkeychain.c
-	$(CC) -c $(CFLAGS) $<
+SRCS:=$(MAIN).c
+SRCS+=credential_helper.c
+OBJS:=$(SRCS:.c=.o)
+
+%.o: %.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -I$(HELPER) $(INCS) -o $@ -c $<
+
+$(MAIN): $(OBJS)
+	$(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
 
 clean:
-	$(RM) git-credential-osxkeychain git-credential-osxkeychain.o
+	@$(RM) $(MAIN) $(OBJS)
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 6beed12..60bd973 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -1,53 +1,40 @@
+
+#include <credential_helper.h>
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <Security/Security.h>
 
 static SecProtocolType protocol;
-static char *host;
-static char *path;
-static char *username;
-static char *password;
-static UInt16 port;
-
-static void die(const char *err, ...)
-{
-	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);
-}
-
-static void *xstrdup(const char *s1)
-{
-	void *ret = strdup(s1);
-	if (!ret)
-		die("Out of memory");
-	return ret;
-}
 
 #define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
-#define KEYCHAIN_ARGS \
+#define KEYCHAIN_ARGS(c) \
 	NULL, /* default keychain */ \
-	KEYCHAIN_ITEM(host), \
+	KEYCHAIN_ITEM(c->host), \
 	0, NULL, /* account domain */ \
-	KEYCHAIN_ITEM(username), \
-	KEYCHAIN_ITEM(path), \
-	port, \
+	KEYCHAIN_ITEM(c->username), \
+	KEYCHAIN_ITEM(c->path), \
+	(UInt16) c->port, \
 	protocol, \
 	kSecAuthenticationTypeDefault
 
-static void write_item(const char *what, const char *buf, int len)
+static int prepare_internet_password(struct credential *c)
 {
-	printf("%s=", what);
-	fwrite(buf, 1, len, stdout);
-	putchar('\n');
+	if (!c->protocol)
+		return -1;
+	else if (!strcmp(c->protocol, "https"))
+		protocol = kSecProtocolTypeHTTPS;
+	else if (!strcmp(c->protocol, "http"))
+		protocol = kSecProtocolTypeHTTP;
+	else /* we don't yet handle other protocols */
+		return -1;
+
+	return 0;
 }
 
-static void find_username_in_item(SecKeychainItemRef item)
+static void
+find_username_in_item(SecKeychainItemRef item, struct credential *c)
 {
 	SecKeychainAttributeList list;
 	SecKeychainAttribute attr;
@@ -59,27 +46,37 @@ static void find_username_in_item(SecKeychainItemRef item)
 	if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
 		return;
 
-	write_item("username", attr.data, attr.length);
+	free(c->username);
+	c->username = xstrndup(attr.data, attr.length);
+
 	SecKeychainItemFreeContent(&list, NULL);
 }
 
-static void find_internet_password(void)
+static int find_internet_password(struct credential *c)
 {
 	void *buf;
 	UInt32 len;
 	SecKeychainItemRef item;
 
-	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
-		return;
+	/* Silently ignore unsupported protocols */
+	if (prepare_internet_password(c))
+		return EXIT_SUCCESS;
 
-	write_item("password", buf, len);
-	if (!username)
-		find_username_in_item(item);
+	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS(c), &len, &buf, &item))
+		return EXIT_SUCCESS;
+
+	free_password(c->password);
+	c->password = xstrndup(buf, len);
+	memset(buf,len,'\0');
+
+	if (!c->username)
+		find_username_in_item(item, c);
 
 	SecKeychainItemFreeContent(NULL, buf);
+	return EXIT_SUCCESS;
 }
 
-static void delete_internet_password(void)
+static int delete_internet_password(struct credential *c)
 {
 	SecKeychainItemRef item;
 
@@ -88,86 +85,48 @@ static void delete_internet_password(void)
 	 * will give us; if you want to do something more fancy, use the
 	 * Keychain manager.
 	 */
-	if (!protocol || !host)
-		return;
+	if (!c->protocol || !c->host)
+		return EXIT_FAILURE;
 
-	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
-		return;
+	/* Silently ignore unsupported protocols */
+	if (prepare_internet_password(c))
+		return EXIT_SUCCESS;
+
+	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS(c), 0, NULL, &item))
+		return EXIT_SUCCESS;
 
-	SecKeychainItemDelete(item);
+	if (!SecKeychainItemDelete(item))
+		return EXIT_SUCCESS;
+
+	return EXIT_FAILURE;
 }
 
-static void add_internet_password(void)
+static int add_internet_password(struct credential *c)
 {
 	/* Only store complete credentials */
-	if (!protocol || !host || !username || !password)
-		return;
+	if (!c->protocol || !c->host || !c->username || !c->password)
+		return EXIT_FAILURE;
+
+	if (prepare_internet_password(c))
+		return EXIT_FAILURE;
 
 	if (SecKeychainAddInternetPassword(
-	      KEYCHAIN_ARGS,
-	      KEYCHAIN_ITEM(password),
+	      KEYCHAIN_ARGS(c),
+	      KEYCHAIN_ITEM(c->password),
 	      NULL))
-		return;
-}
+		return EXIT_FAILURE;
 
-static void read_credential(void)
-{
-	char buf[1024];
-
-	while (fgets(buf, sizeof(buf), stdin)) {
-		char *v;
-
-		if (!strcmp(buf, "\n"))
-			break;
-		buf[strlen(buf)-1] = '\0';
-
-		v = strchr(buf, '=');
-		if (!v)
-			die("bad input: %s", buf);
-		*v++ = '\0';
-
-		if (!strcmp(buf, "protocol")) {
-			if (!strcmp(v, "https"))
-				protocol = kSecProtocolTypeHTTPS;
-			else if (!strcmp(v, "http"))
-				protocol = kSecProtocolTypeHTTP;
-			else /* we don't yet handle other protocols */
-				exit(0);
-		}
-		else if (!strcmp(buf, "host")) {
-			char *colon = strchr(v, ':');
-			if (colon) {
-				*colon++ = '\0';
-				port = atoi(colon);
-			}
-			host = xstrdup(v);
-		}
-		else if (!strcmp(buf, "path"))
-			path = xstrdup(v);
-		else if (!strcmp(buf, "username"))
-			username = xstrdup(v);
-		else if (!strcmp(buf, "password"))
-			password = xstrdup(v);
-	}
+	return EXIT_SUCCESS;
 }
 
-int main(int argc, const char **argv)
+/*
+ * Table with helper operation callbacks, used by generic
+ * credential helper main function.
+ */
+struct credential_operation const credential_helper_ops[] =
 {
-	const char *usage =
-		"Usage: git credential-osxkeychain <get|store|erase>";
-
-	if (!argv[1])
-		die(usage);
-
-	read_credential();
-
-	if (!strcmp(argv[1], "get"))
-		find_internet_password();
-	else if (!strcmp(argv[1], "store"))
-		add_internet_password();
-	else if (!strcmp(argv[1], "erase"))
-		delete_internet_password();
-	/* otherwise, ignore unknown action */
-
-	return 0;
-}
+	{ "get",   find_internet_password   },
+	{ "store", add_internet_password    },
+	{ "erase", delete_internet_password },
+	CREDENTIAL_OP_END
+};
-- 
1.7.10.4
Previous: Philipp A. HartmannNext: Junio C Hamano
Message 5 of 16 in “contrib: GnomeKeyring support + generic helper implementation”
  1. contrib: GnomeKeyring support + generic helper implementationPhilipp A. Hartmann, Aug 23, 2012
  2. 1/4 contrib: add credential helper for GnomeKeyringPhilipp A. Hartmann, Aug 23, 2012
  3. 2/4 contrib: add generic credential helperPhilipp A. Hartmann, Aug 23, 2012
  4. 3/4 gnome-keyring: port to generic helper implementationPhilipp A. Hartmann, Aug 23, 2012
  5. 4/4 osxkeychain: port to generic credential helper implementationPhilipp A. Hartmann, Aug 23, 2012
  6. Junio C HamanoAug 24, 2012
  7. Jeff KingAug 24, 2012
  8. Junio C HamanoAug 24, 2012
  9. Junio C HamanoAug 26, 2012
  10. Philipp A. HartmannAug 26, 2012
  11. 5/4 wincred: port to generic credential helper (UNTESTED)Philipp A. Hartmann, Aug 26, 2012
  12. 5/4 wincred: port to generic credential helper (UNTESTED)Philipp A. Hartmann, Aug 26, 2012
  13. Erik Faye-LundAug 30, 2012
  14. Junio C HamanoAug 30, 2012
  15. Erik Faye-LundAug 31, 2012
  16. 5/4 wincred: port to generic credential helperPhilipp A. Hartmann, Sep 1, 2012

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.