4 #include <Security/Security.h>
6 static SecProtocolType protocol
;
10 static char *password
;
13 static void die(const char *err
, ...)
17 va_start(params
, err
);
18 vsnprintf(msg
, sizeof(msg
), err
, params
);
19 fprintf(stderr
, "%s\n", msg
);
24 static void *xstrdup(const char *s1
)
26 void *ret
= strdup(s1
);
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), \
41 kSecAuthenticationTypeDefault
43 static void write_item(const char *what
, const char *buf
, int len
)
46 fwrite(buf
, 1, len
, stdout
);
50 static void find_username_in_item(SecKeychainItemRef item
)
52 SecKeychainAttributeList list
;
53 SecKeychainAttribute attr
;
57 attr
.tag
= kSecAccountItemAttr
;
59 if (SecKeychainItemCopyContent(item
, NULL
, &list
, NULL
, NULL
))
62 write_item("username", attr
.data
, attr
.length
);
63 SecKeychainItemFreeContent(&list
, NULL
);
66 static void find_internet_password(void)
70 SecKeychainItemRef item
;
72 if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS
, &len
, &buf
, &item
))
75 write_item("password", buf
, len
);
77 find_username_in_item(item
);
79 SecKeychainItemFreeContent(NULL
, buf
);
82 static void delete_internet_password(void)
84 SecKeychainItemRef item
;
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
91 if (!protocol
|| !host
)
94 if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS
, 0, NULL
, &item
))
97 SecKeychainItemDelete(item
);
100 static void add_internet_password(void)
102 /* Only store complete credentials */
103 if (!protocol
|| !host
|| !username
|| !password
)
106 if (SecKeychainAddInternetPassword(
108 KEYCHAIN_ITEM(password
),
113 static void read_credential(void)
117 while (fgets(buf
, sizeof(buf
), stdin
)) {
120 if (!strcmp(buf
, "\n"))
122 buf
[strlen(buf
)-1] = '\0';
124 v
= strchr(buf
, '=');
126 die("bad input: %s", buf
);
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 */
147 else if (!strcmp(buf
, "host")) {
148 char *colon
= strchr(v
, ':');
155 else if (!strcmp(buf
, "path"))
157 else if (!strcmp(buf
, "username"))
158 username
= xstrdup(v
);
159 else if (!strcmp(buf
, "password"))
160 password
= xstrdup(v
);
164 int main(int argc
, const char **argv
)
167 "usage: git credential-osxkeychain <get|store|erase>";
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 */