2 * A git credential helper that interface with Windows' Credential Manager
12 static void die(const char *err
, ...)
16 va_start(params
, err
);
17 vsnprintf(msg
, sizeof(msg
), err
, params
);
18 fprintf(stderr
, "%s\n", msg
);
23 static void *xmalloc(size_t size
)
25 void *ret
= malloc(size
);
33 static char *xstrdup(const char *str
)
35 char *ret
= strdup(str
);
41 /* MinGW doesn't have wincred.h, so we need to define stuff */
43 typedef struct _CREDENTIAL_ATTRIBUTEW
{
48 } CREDENTIAL_ATTRIBUTEW
, *PCREDENTIAL_ATTRIBUTEW
;
50 typedef struct _CREDENTIALW
{
56 DWORD CredentialBlobSize
;
57 LPBYTE CredentialBlob
;
60 PCREDENTIAL_ATTRIBUTEW Attributes
;
63 } CREDENTIALW
, *PCREDENTIALW
;
65 #define CRED_TYPE_GENERIC 1
66 #define CRED_PERSIST_LOCAL_MACHINE 2
67 #define CRED_MAX_ATTRIBUTES 64
69 typedef BOOL (WINAPI
*CredWriteWT
)(PCREDENTIALW
, DWORD
);
70 typedef BOOL (WINAPI
*CredUnPackAuthenticationBufferWT
)(DWORD
, PVOID
, DWORD
,
71 LPWSTR
, DWORD
*, LPWSTR
, DWORD
*, LPWSTR
, DWORD
*);
72 typedef BOOL (WINAPI
*CredEnumerateWT
)(LPCWSTR
, DWORD
, DWORD
*,
74 typedef BOOL (WINAPI
*CredPackAuthenticationBufferWT
)(DWORD
, LPWSTR
, LPWSTR
,
76 typedef VOID (WINAPI
*CredFreeT
)(PVOID
);
77 typedef BOOL (WINAPI
*CredDeleteWT
)(LPCWSTR
, DWORD
, DWORD
);
79 static HMODULE advapi
, credui
;
80 static CredWriteWT CredWriteW
;
81 static CredUnPackAuthenticationBufferWT CredUnPackAuthenticationBufferW
;
82 static CredEnumerateWT CredEnumerateW
;
83 static CredPackAuthenticationBufferWT CredPackAuthenticationBufferW
;
84 static CredFreeT CredFree
;
85 static CredDeleteWT CredDeleteW
;
87 static void load_cred_funcs(void)
90 advapi
= LoadLibrary("advapi32.dll");
91 credui
= LoadLibrary("credui.dll");
92 if (!advapi
|| !credui
)
93 die("failed to load DLLs");
95 /* get function pointers */
96 CredWriteW
= (CredWriteWT
)GetProcAddress(advapi
, "CredWriteW");
97 CredUnPackAuthenticationBufferW
= (CredUnPackAuthenticationBufferWT
)
98 GetProcAddress(credui
, "CredUnPackAuthenticationBufferW");
99 CredEnumerateW
= (CredEnumerateWT
)GetProcAddress(advapi
,
101 CredPackAuthenticationBufferW
= (CredPackAuthenticationBufferWT
)
102 GetProcAddress(credui
, "CredPackAuthenticationBufferW");
103 CredFree
= (CredFreeT
)GetProcAddress(advapi
, "CredFree");
104 CredDeleteW
= (CredDeleteWT
)GetProcAddress(advapi
, "CredDeleteW");
105 if (!CredWriteW
|| !CredUnPackAuthenticationBufferW
||
106 !CredEnumerateW
|| !CredPackAuthenticationBufferW
|| !CredFree
||
108 die("failed to load functions");
111 static char target_buf
[1024];
112 static char *protocol
, *host
, *path
, *username
;
113 static WCHAR
*wusername
, *password
, *target
;
115 static void write_item(const char *what
, WCHAR
*wbuf
)
118 int len
= WideCharToMultiByte(CP_UTF8
, 0, wbuf
, -1, NULL
, 0, NULL
,
122 if (!WideCharToMultiByte(CP_UTF8
, 0, wbuf
, -1, buf
, len
, NULL
, FALSE
))
123 die("WideCharToMultiByte failed!");
126 fwrite(buf
, 1, len
- 1, stdout
);
131 static int match_attr(const CREDENTIALW
*cred
, const WCHAR
*keyword
,
138 for (i
= 0; i
< cred
->AttributeCount
; ++i
)
139 if (!wcscmp(cred
->Attributes
[i
].Keyword
, keyword
))
140 return !strcmp((const char *)cred
->Attributes
[i
].Value
,
143 return 0; /* not found */
146 static int match_cred(const CREDENTIALW
*cred
)
148 return (!wusername
|| !wcscmp(wusername
, cred
->UserName
)) &&
149 match_attr(cred
, L
"git_protocol", protocol
) &&
150 match_attr(cred
, L
"git_host", host
) &&
151 match_attr(cred
, L
"git_path", path
);
154 static void get_credential(void)
156 WCHAR
*user_buf
, *pass_buf
;
157 DWORD user_buf_size
= 0, pass_buf_size
= 0;
158 CREDENTIALW
**creds
, *cred
= NULL
;
162 if (!CredEnumerateW(L
"git:*", 0, &num_creds
, &creds
))
165 /* search for the first credential that matches username */
166 for (i
= 0; i
< num_creds
; ++i
)
167 if (match_cred(creds
[i
])) {
174 CredUnPackAuthenticationBufferW(0, cred
->CredentialBlob
,
175 cred
->CredentialBlobSize
, NULL
, &user_buf_size
, NULL
, NULL
,
176 NULL
, &pass_buf_size
);
178 user_buf
= xmalloc(user_buf_size
* sizeof(WCHAR
));
179 pass_buf
= xmalloc(pass_buf_size
* sizeof(WCHAR
));
181 if (!CredUnPackAuthenticationBufferW(0, cred
->CredentialBlob
,
182 cred
->CredentialBlobSize
, user_buf
, &user_buf_size
, NULL
, NULL
,
183 pass_buf
, &pass_buf_size
))
184 die("CredUnPackAuthenticationBuffer failed");
188 /* zero-terminate (sizes include zero-termination) */
189 user_buf
[user_buf_size
- 1] = L
'\0';
190 pass_buf
[pass_buf_size
- 1] = L
'\0';
192 write_item("username", user_buf
);
193 write_item("password", pass_buf
);
199 static void write_attr(CREDENTIAL_ATTRIBUTEW
*attr
, const WCHAR
*keyword
,
202 attr
->Keyword
= (LPWSTR
)keyword
;
204 attr
->ValueSize
= strlen(value
) + 1; /* store zero-termination */
205 attr
->Value
= (LPBYTE
)value
;
208 static void store_credential(void)
212 DWORD auth_buf_size
= 0;
213 CREDENTIAL_ATTRIBUTEW attrs
[CRED_MAX_ATTRIBUTES
];
215 if (!wusername
|| !password
)
218 /* query buffer size */
219 CredPackAuthenticationBufferW(0, wusername
, password
,
220 NULL
, &auth_buf_size
);
222 auth_buf
= xmalloc(auth_buf_size
);
224 if (!CredPackAuthenticationBufferW(0, wusername
, password
,
225 auth_buf
, &auth_buf_size
))
226 die("CredPackAuthenticationBuffer failed");
229 cred
.Type
= CRED_TYPE_GENERIC
;
230 cred
.TargetName
= target
;
231 cred
.Comment
= L
"saved by git-credential-wincred";
232 cred
.CredentialBlobSize
= auth_buf_size
;
233 cred
.CredentialBlob
= auth_buf
;
234 cred
.Persist
= CRED_PERSIST_LOCAL_MACHINE
;
235 cred
.AttributeCount
= 1;
236 cred
.Attributes
= attrs
;
237 cred
.TargetAlias
= NULL
;
238 cred
.UserName
= wusername
;
240 write_attr(attrs
, L
"git_protocol", protocol
);
243 write_attr(attrs
+ cred
.AttributeCount
, L
"git_host", host
);
244 cred
.AttributeCount
++;
248 write_attr(attrs
+ cred
.AttributeCount
, L
"git_path", path
);
249 cred
.AttributeCount
++;
252 if (!CredWriteW(&cred
, 0))
253 die("CredWrite failed");
256 static void erase_credential(void)
262 if (!CredEnumerateW(L
"git:*", 0, &num_creds
, &creds
))
265 for (i
= 0; i
< num_creds
; ++i
) {
266 if (match_cred(creds
[i
]))
267 CredDeleteW(creds
[i
]->TargetName
, creds
[i
]->Type
, 0);
273 static WCHAR
*utf8_to_utf16_dup(const char *str
)
275 int wlen
= MultiByteToWideChar(CP_UTF8
, 0, str
, -1, NULL
, 0);
276 WCHAR
*wstr
= xmalloc(sizeof(WCHAR
) * wlen
);
277 MultiByteToWideChar(CP_UTF8
, 0, str
, -1, wstr
, wlen
);
281 static void read_credential(void)
285 while (fgets(buf
, sizeof(buf
), stdin
)) {
288 if (!strcmp(buf
, "\n"))
290 buf
[strlen(buf
)-1] = '\0';
292 v
= strchr(buf
, '=');
294 die("bad input: %s", buf
);
297 if (!strcmp(buf
, "protocol"))
298 protocol
= xstrdup(v
);
299 else if (!strcmp(buf
, "host"))
301 else if (!strcmp(buf
, "path"))
303 else if (!strcmp(buf
, "username")) {
304 username
= xstrdup(v
);
305 wusername
= utf8_to_utf16_dup(v
);
306 } else if (!strcmp(buf
, "password"))
307 password
= utf8_to_utf16_dup(v
);
309 die("unrecognized input");
313 int main(int argc
, char *argv
[])
316 "Usage: git credential-wincred <get|store|erase>\n";
321 /* git use binary pipes to avoid CRLF-issues */
322 _setmode(_fileno(stdin
), _O_BINARY
);
323 _setmode(_fileno(stdout
), _O_BINARY
);
329 if (!protocol
|| !(host
|| path
))
332 /* prepare 'target', the unique key for the credential */
333 strncat(target_buf
, "git:", sizeof(target_buf
));
334 strncat(target_buf
, protocol
, sizeof(target_buf
));
335 strncat(target_buf
, "://", sizeof(target_buf
));
337 strncat(target_buf
, username
, sizeof(target_buf
));
338 strncat(target_buf
, "@", sizeof(target_buf
));
341 strncat(target_buf
, host
, sizeof(target_buf
));
343 strncat(target_buf
, "/", sizeof(target_buf
));
344 strncat(target_buf
, path
, sizeof(target_buf
));
347 target
= utf8_to_utf16_dup(target_buf
);
349 if (!strcmp(argv
[1], "get"))
351 else if (!strcmp(argv
[1], "store"))
353 else if (!strcmp(argv
[1], "erase"))
355 /* otherwise, ignore unknown action */