4 #include <Security/Security.h>
6 static SecProtocolType protocol
;
10 static char *password
;
13 __attribute__((format (printf
, 1, 2)))
14 static void die(const char *err
, ...)
18 va_start(params
, err
);
19 vsnprintf(msg
, sizeof(msg
), err
, params
);
20 fprintf(stderr
, "%s\n", msg
);
25 static void *xstrdup(const char *s1
)
27 void *ret
= strdup(s1
);
33 #define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
34 #define KEYCHAIN_ARGS \
35 NULL, /* default keychain */ \
36 KEYCHAIN_ITEM(host), \
37 0, NULL, /* account domain */ \
38 KEYCHAIN_ITEM(username), \
39 KEYCHAIN_ITEM(path), \
42 kSecAuthenticationTypeDefault
44 static void write_item(const char *what
, const char *buf
, int len
)
47 fwrite(buf
, 1, len
, stdout
);
51 static void find_username_in_item(SecKeychainItemRef item
)
53 SecKeychainAttributeList list
;
54 SecKeychainAttribute attr
;
58 attr
.tag
= kSecAccountItemAttr
;
60 if (SecKeychainItemCopyContent(item
, NULL
, &list
, NULL
, NULL
))
63 write_item("username", attr
.data
, attr
.length
);
64 SecKeychainItemFreeContent(&list
, NULL
);
67 static void find_internet_password(void)
71 SecKeychainItemRef item
;
73 if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS
, &len
, &buf
, &item
))
76 write_item("password", buf
, len
);
78 find_username_in_item(item
);
80 SecKeychainItemFreeContent(NULL
, buf
);
83 static void delete_internet_password(void)
85 SecKeychainItemRef item
;
88 * Require at least a protocol and host for removal, which is what git
89 * will give us; if you want to do something more fancy, use the
92 if (!protocol
|| !host
)
95 if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS
, 0, NULL
, &item
))
98 SecKeychainItemDelete(item
);
101 static void add_internet_password(void)
103 /* Only store complete credentials */
104 if (!protocol
|| !host
|| !username
|| !password
)
107 if (SecKeychainAddInternetPassword(
109 KEYCHAIN_ITEM(password
),
114 static void read_credential(void)
120 while ((line_len
= getline(&buf
, &alloc
, stdin
)) > 0) {
123 if (!strcmp(buf
, "\n"))
125 buf
[line_len
-1] = '\0';
127 v
= strchr(buf
, '=');
129 die("bad input: %s", buf
);
132 if (!strcmp(buf
, "protocol")) {
133 if (!strcmp(v
, "imap"))
134 protocol
= kSecProtocolTypeIMAP
;
135 else if (!strcmp(v
, "imaps"))
136 protocol
= kSecProtocolTypeIMAPS
;
137 else if (!strcmp(v
, "ftp"))
138 protocol
= kSecProtocolTypeFTP
;
139 else if (!strcmp(v
, "ftps"))
140 protocol
= kSecProtocolTypeFTPS
;
141 else if (!strcmp(v
, "https"))
142 protocol
= kSecProtocolTypeHTTPS
;
143 else if (!strcmp(v
, "http"))
144 protocol
= kSecProtocolTypeHTTP
;
145 else if (!strcmp(v
, "smtp"))
146 protocol
= kSecProtocolTypeSMTP
;
147 else /* we don't yet handle other protocols */
150 else if (!strcmp(buf
, "host")) {
151 char *colon
= strchr(v
, ':');
158 else if (!strcmp(buf
, "path"))
160 else if (!strcmp(buf
, "username"))
161 username
= xstrdup(v
);
162 else if (!strcmp(buf
, "password"))
163 password
= xstrdup(v
);
165 * Ignore other lines; we don't know what they mean, but
166 * this future-proofs us when later versions of git do
167 * learn new lines, and the helpers are updated to match.
174 int main(int argc
, const char **argv
)
177 "usage: git credential-osxkeychain <get|store|erase>";
184 if (!strcmp(argv
[1], "get"))
185 find_internet_password();
186 else if (!strcmp(argv
[1], "store"))
187 add_internet_password();
188 else if (!strcmp(argv
[1], "erase"))
189 delete_internet_password();
190 /* otherwise, ignore unknown action */