contrib / credential / osxkeychain / git-credential-osxkeychain.con commit Merge branch 'gj/push-more-verbose-advice' (289ca27)
   1#include <stdio.h>
   2#include <string.h>
   3#include <stdlib.h>
   4#include <Security/Security.h>
   5
   6static SecProtocolType protocol;
   7static char *host;
   8static char *path;
   9static char *username;
  10static char *password;
  11static UInt16 port;
  12
  13static void die(const char *err, ...)
  14{
  15        char msg[4096];
  16        va_list params;
  17        va_start(params, err);
  18        vsnprintf(msg, sizeof(msg), err, params);
  19        fprintf(stderr, "%s\n", msg);
  20        va_end(params);
  21        exit(1);
  22}
  23
  24static void *xstrdup(const char *s1)
  25{
  26        void *ret = strdup(s1);
  27        if (!ret)
  28                die("Out of memory");
  29        return ret;
  30}
  31
  32#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
  33#define KEYCHAIN_ARGS \
  34        NULL, /* default keychain */ \
  35        KEYCHAIN_ITEM(host), \
  36        0, NULL, /* account domain */ \
  37        KEYCHAIN_ITEM(username), \
  38        KEYCHAIN_ITEM(path), \
  39        port, \
  40        protocol, \
  41        kSecAuthenticationTypeDefault
  42
  43static void write_item(const char *what, const char *buf, int len)
  44{
  45        printf("%s=", what);
  46        fwrite(buf, 1, len, stdout);
  47        putchar('\n');
  48}
  49
  50static void find_username_in_item(SecKeychainItemRef item)
  51{
  52        SecKeychainAttributeList list;
  53        SecKeychainAttribute attr;
  54
  55        list.count = 1;
  56        list.attr = &attr;
  57        attr.tag = kSecAccountItemAttr;
  58
  59        if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
  60                return;
  61
  62        write_item("username", attr.data, attr.length);
  63        SecKeychainItemFreeContent(&list, NULL);
  64}
  65
  66static void find_internet_password(void)
  67{
  68        void *buf;
  69        UInt32 len;
  70        SecKeychainItemRef item;
  71
  72        if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
  73                return;
  74
  75        write_item("password", buf, len);
  76        if (!username)
  77                find_username_in_item(item);
  78
  79        SecKeychainItemFreeContent(NULL, buf);
  80}
  81
  82static void delete_internet_password(void)
  83{
  84        SecKeychainItemRef item;
  85
  86        /*
  87         * Require at least a protocol and host for removal, which is what git
  88         * will give us; if you want to do something more fancy, use the
  89         * Keychain manager.
  90         */
  91        if (!protocol || !host)
  92                return;
  93
  94        if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
  95                return;
  96
  97        SecKeychainItemDelete(item);
  98}
  99
 100static void add_internet_password(void)
 101{
 102        /* Only store complete credentials */
 103        if (!protocol || !host || !username || !password)
 104                return;
 105
 106        if (SecKeychainAddInternetPassword(
 107              KEYCHAIN_ARGS,
 108              KEYCHAIN_ITEM(password),
 109              NULL))
 110                return;
 111}
 112
 113static void read_credential(void)
 114{
 115        char buf[1024];
 116
 117        while (fgets(buf, sizeof(buf), stdin)) {
 118                char *v;
 119
 120                if (!strcmp(buf, "\n"))
 121                        break;
 122                buf[strlen(buf)-1] = '\0';
 123
 124                v = strchr(buf, '=');
 125                if (!v)
 126                        die("bad input: %s", buf);
 127                *v++ = '\0';
 128
 129                if (!strcmp(buf, "protocol")) {
 130                        if (!strcmp(v, "imap"))
 131                                protocol = kSecProtocolTypeIMAP;
 132                        else if (!strcmp(v, "imaps"))
 133                                protocol = kSecProtocolTypeIMAPS;
 134                        else if (!strcmp(v, "ftp"))
 135                                protocol = kSecProtocolTypeFTP;
 136                        else if (!strcmp(v, "ftps"))
 137                                protocol = kSecProtocolTypeFTPS;
 138                        else if (!strcmp(v, "https"))
 139                                protocol = kSecProtocolTypeHTTPS;
 140                        else if (!strcmp(v, "http"))
 141                                protocol = kSecProtocolTypeHTTP;
 142                        else if (!strcmp(v, "smtp"))
 143                                protocol = kSecProtocolTypeSMTP;
 144                        else /* we don't yet handle other protocols */
 145                                exit(0);
 146                }
 147                else if (!strcmp(buf, "host")) {
 148                        char *colon = strchr(v, ':');
 149                        if (colon) {
 150                                *colon++ = '\0';
 151                                port = atoi(colon);
 152                        }
 153                        host = xstrdup(v);
 154                }
 155                else if (!strcmp(buf, "path"))
 156                        path = xstrdup(v);
 157                else if (!strcmp(buf, "username"))
 158                        username = xstrdup(v);
 159                else if (!strcmp(buf, "password"))
 160                        password = xstrdup(v);
 161        }
 162}
 163
 164int main(int argc, const char **argv)
 165{
 166        const char *usage =
 167                "usage: git credential-osxkeychain <get|store|erase>";
 168
 169        if (!argv[1])
 170                die(usage);
 171
 172        read_credential();
 173
 174        if (!strcmp(argv[1], "get"))
 175                find_internet_password();
 176        else if (!strcmp(argv[1], "store"))
 177                add_internet_password();
 178        else if (!strcmp(argv[1], "erase"))
 179                delete_internet_password();
 180        /* otherwise, ignore unknown action */
 181
 182        return 0;
 183}