| Viewing file:  git-credential-osxkeychain.c (3.77 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
#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;
 }
 
 |