Add protocol imap, imaps, ftp and smtp for credential-osxkeychain. Signed-off-by: Xidorn Quan <quanxunzhen@gmail.com> Acked-by: John Szakmeister <john@szakmeister.net> Acked-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
			
				
	
	
		
			184 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			184 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#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 \
 | 
						|
	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, int len)
 | 
						|
{
 | 
						|
	printf("%s=", what);
 | 
						|
	fwrite(buf, 1, len, stdout);
 | 
						|
	putchar('\n');
 | 
						|
}
 | 
						|
 | 
						|
static void find_username_in_item(SecKeychainItemRef item)
 | 
						|
{
 | 
						|
	SecKeychainAttributeList list;
 | 
						|
	SecKeychainAttribute attr;
 | 
						|
 | 
						|
	list.count = 1;
 | 
						|
	list.attr = &attr;
 | 
						|
	attr.tag = kSecAccountItemAttr;
 | 
						|
 | 
						|
	if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
 | 
						|
		return;
 | 
						|
 | 
						|
	write_item("username", attr.data, attr.length);
 | 
						|
	SecKeychainItemFreeContent(&list, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void find_internet_password(void)
 | 
						|
{
 | 
						|
	void *buf;
 | 
						|
	UInt32 len;
 | 
						|
	SecKeychainItemRef item;
 | 
						|
 | 
						|
	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
 | 
						|
		return;
 | 
						|
 | 
						|
	write_item("password", buf, len);
 | 
						|
	if (!username)
 | 
						|
		find_username_in_item(item);
 | 
						|
 | 
						|
	SecKeychainItemFreeContent(NULL, buf);
 | 
						|
}
 | 
						|
 | 
						|
static void delete_internet_password(void)
 | 
						|
{
 | 
						|
	SecKeychainItemRef item;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 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;
 | 
						|
 | 
						|
	if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
 | 
						|
		return;
 | 
						|
 | 
						|
	SecKeychainItemDelete(item);
 | 
						|
}
 | 
						|
 | 
						|
static void add_internet_password(void)
 | 
						|
{
 | 
						|
	/* Only store complete credentials */
 | 
						|
	if (!protocol || !host || !username || !password)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (SecKeychainAddInternetPassword(
 | 
						|
	      KEYCHAIN_ARGS,
 | 
						|
	      KEYCHAIN_ITEM(password),
 | 
						|
	      NULL))
 | 
						|
		return;
 | 
						|
}
 | 
						|
 | 
						|
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, "imap"))
 | 
						|
				protocol = kSecProtocolTypeIMAP;
 | 
						|
			else if (!strcmp(v, "imaps"))
 | 
						|
				protocol = kSecProtocolTypeIMAPS;
 | 
						|
			else if (!strcmp(v, "ftp"))
 | 
						|
				protocol = kSecProtocolTypeFTP;
 | 
						|
			else if (!strcmp(v, "ftps"))
 | 
						|
				protocol = kSecProtocolTypeFTPS;
 | 
						|
			else if (!strcmp(v, "https"))
 | 
						|
				protocol = kSecProtocolTypeHTTPS;
 | 
						|
			else if (!strcmp(v, "http"))
 | 
						|
				protocol = kSecProtocolTypeHTTP;
 | 
						|
			else if (!strcmp(v, "smtp"))
 | 
						|
				protocol = kSecProtocolTypeSMTP;
 | 
						|
			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);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, const char **argv)
 | 
						|
{
 | 
						|
	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;
 | 
						|
}
 |