1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
#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;
}
|