Skip to content

Commit 27db5a0

Browse files
committed
Merge branch 'for-junio' of git://github.com/kusma/git
* 'for-junio' of git://github.com/kusma/git: wincred: improve compatibility with windows versions wincred: accept CRLF on stdin to simplify console usage
2 parents 13a2319 + 8b2d219 commit 27db5a0

File tree

1 file changed

+75
-131
lines changed

1 file changed

+75
-131
lines changed

contrib/credential/wincred/git-credential-wincred.c

Lines changed: 75 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
/* common helpers */
1111

12+
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
13+
1214
static void die(const char *err, ...)
1315
{
1416
char msg[4096];
@@ -30,14 +32,6 @@ static void *xmalloc(size_t size)
3032
return ret;
3133
}
3234

33-
static char *xstrdup(const char *str)
34-
{
35-
char *ret = strdup(str);
36-
if (!ret)
37-
die("Out of memory");
38-
return ret;
39-
}
40-
4135
/* MinGW doesn't have wincred.h, so we need to define stuff */
4236

4337
typedef struct _CREDENTIAL_ATTRIBUTEW {
@@ -67,95 +61,99 @@ typedef struct _CREDENTIALW {
6761
#define CRED_MAX_ATTRIBUTES 64
6862

6963
typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD);
70-
typedef BOOL (WINAPI *CredUnPackAuthenticationBufferWT)(DWORD, PVOID, DWORD,
71-
LPWSTR, DWORD *, LPWSTR, DWORD *, LPWSTR, DWORD *);
7264
typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *,
7365
PCREDENTIALW **);
74-
typedef BOOL (WINAPI *CredPackAuthenticationBufferWT)(DWORD, LPWSTR, LPWSTR,
75-
PBYTE, DWORD *);
7666
typedef VOID (WINAPI *CredFreeT)(PVOID);
7767
typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD);
7868

79-
static HMODULE advapi, credui;
69+
static HMODULE advapi;
8070
static CredWriteWT CredWriteW;
81-
static CredUnPackAuthenticationBufferWT CredUnPackAuthenticationBufferW;
8271
static CredEnumerateWT CredEnumerateW;
83-
static CredPackAuthenticationBufferWT CredPackAuthenticationBufferW;
8472
static CredFreeT CredFree;
8573
static CredDeleteWT CredDeleteW;
8674

8775
static void load_cred_funcs(void)
8876
{
8977
/* load DLLs */
9078
advapi = LoadLibrary("advapi32.dll");
91-
credui = LoadLibrary("credui.dll");
92-
if (!advapi || !credui)
93-
die("failed to load DLLs");
79+
if (!advapi)
80+
die("failed to load advapi32.dll");
9481

9582
/* get function pointers */
9683
CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW");
97-
CredUnPackAuthenticationBufferW = (CredUnPackAuthenticationBufferWT)
98-
GetProcAddress(credui, "CredUnPackAuthenticationBufferW");
9984
CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi,
10085
"CredEnumerateW");
101-
CredPackAuthenticationBufferW = (CredPackAuthenticationBufferWT)
102-
GetProcAddress(credui, "CredPackAuthenticationBufferW");
10386
CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree");
10487
CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW");
105-
if (!CredWriteW || !CredUnPackAuthenticationBufferW ||
106-
!CredEnumerateW || !CredPackAuthenticationBufferW || !CredFree ||
107-
!CredDeleteW)
88+
if (!CredWriteW || !CredEnumerateW || !CredFree || !CredDeleteW)
10889
die("failed to load functions");
10990
}
11091

111-
static char target_buf[1024];
112-
static char *protocol, *host, *path, *username;
113-
static WCHAR *wusername, *password, *target;
92+
static WCHAR *wusername, *password, *protocol, *host, *path, target[1024];
11493

115-
static void write_item(const char *what, WCHAR *wbuf)
94+
static void write_item(const char *what, LPCWSTR wbuf, int wlen)
11695
{
11796
char *buf;
118-
int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, NULL, 0, NULL,
97+
int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, NULL, 0, NULL,
11998
FALSE);
12099
buf = xmalloc(len);
121100

122-
if (!WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, len, NULL, FALSE))
101+
if (!WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, buf, len, NULL, FALSE))
123102
die("WideCharToMultiByte failed!");
124103

125104
printf("%s=", what);
126-
fwrite(buf, 1, len - 1, stdout);
105+
fwrite(buf, 1, len, stdout);
127106
putchar('\n');
128107
free(buf);
129108
}
130109

131-
static int match_attr(const CREDENTIALW *cred, const WCHAR *keyword,
132-
const char *want)
110+
/*
111+
* Match an (optional) expected string and a delimiter in the target string,
112+
* consuming the matched text by updating the target pointer.
113+
*/
114+
static int match_part(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
133115
{
134-
int i;
135-
if (!want)
136-
return 1;
137-
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,
141-
want);
142-
143-
return 0; /* not found */
116+
LPCWSTR delim_pos, start = *ptarget;
117+
int len;
118+
119+
/* find start of delimiter (or end-of-string if delim is empty) */
120+
if (*delim)
121+
delim_pos = wcsstr(start, delim);
122+
else
123+
delim_pos = start + wcslen(start);
124+
125+
/*
126+
* match text up to delimiter, or end of string (e.g. the '/' after
127+
* host is optional if not followed by a path)
128+
*/
129+
if (delim_pos)
130+
len = delim_pos - start;
131+
else
132+
len = wcslen(start);
133+
134+
/* update ptarget if we either found a delimiter or need a match */
135+
if (delim_pos || want)
136+
*ptarget = delim_pos ? delim_pos + wcslen(delim) : start + len;
137+
138+
return !want || (!wcsncmp(want, start, len) && !want[len]);
144139
}
145140

146141
static int match_cred(const CREDENTIALW *cred)
147142
{
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);
143+
LPCWSTR target = cred->TargetName;
144+
if (wusername && wcscmp(wusername, cred->UserName))
145+
return 0;
146+
147+
return match_part(&target, L"git", L":") &&
148+
match_part(&target, protocol, L"://") &&
149+
match_part(&target, wusername, L"@") &&
150+
match_part(&target, host, L"/") &&
151+
match_part(&target, path, L"");
152152
}
153153

154154
static void get_credential(void)
155155
{
156-
WCHAR *user_buf, *pass_buf;
157-
DWORD user_buf_size = 0, pass_buf_size = 0;
158-
CREDENTIALW **creds, *cred = NULL;
156+
CREDENTIALW **creds;
159157
DWORD num_creds;
160158
int i;
161159

@@ -165,90 +163,36 @@ static void get_credential(void)
165163
/* search for the first credential that matches username */
166164
for (i = 0; i < num_creds; ++i)
167165
if (match_cred(creds[i])) {
168-
cred = creds[i];
166+
write_item("username", creds[i]->UserName,
167+
wcslen(creds[i]->UserName));
168+
write_item("password",
169+
(LPCWSTR)creds[i]->CredentialBlob,
170+
creds[i]->CredentialBlobSize / sizeof(WCHAR));
169171
break;
170172
}
171-
if (!cred)
172-
return;
173-
174-
CredUnPackAuthenticationBufferW(0, cred->CredentialBlob,
175-
cred->CredentialBlobSize, NULL, &user_buf_size, NULL, NULL,
176-
NULL, &pass_buf_size);
177-
178-
user_buf = xmalloc(user_buf_size * sizeof(WCHAR));
179-
pass_buf = xmalloc(pass_buf_size * sizeof(WCHAR));
180-
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");
185173

186174
CredFree(creds);
187-
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';
191-
192-
write_item("username", user_buf);
193-
write_item("password", pass_buf);
194-
195-
free(user_buf);
196-
free(pass_buf);
197-
}
198-
199-
static void write_attr(CREDENTIAL_ATTRIBUTEW *attr, const WCHAR *keyword,
200-
const char *value)
201-
{
202-
attr->Keyword = (LPWSTR)keyword;
203-
attr->Flags = 0;
204-
attr->ValueSize = strlen(value) + 1; /* store zero-termination */
205-
attr->Value = (LPBYTE)value;
206175
}
207176

208177
static void store_credential(void)
209178
{
210179
CREDENTIALW cred;
211-
BYTE *auth_buf;
212-
DWORD auth_buf_size = 0;
213-
CREDENTIAL_ATTRIBUTEW attrs[CRED_MAX_ATTRIBUTES];
214180

215181
if (!wusername || !password)
216182
return;
217183

218-
/* query buffer size */
219-
CredPackAuthenticationBufferW(0, wusername, password,
220-
NULL, &auth_buf_size);
221-
222-
auth_buf = xmalloc(auth_buf_size);
223-
224-
if (!CredPackAuthenticationBufferW(0, wusername, password,
225-
auth_buf, &auth_buf_size))
226-
die("CredPackAuthenticationBuffer failed");
227-
228184
cred.Flags = 0;
229185
cred.Type = CRED_TYPE_GENERIC;
230186
cred.TargetName = target;
231187
cred.Comment = L"saved by git-credential-wincred";
232-
cred.CredentialBlobSize = auth_buf_size;
233-
cred.CredentialBlob = auth_buf;
188+
cred.CredentialBlobSize = (wcslen(password)) * sizeof(WCHAR);
189+
cred.CredentialBlob = (LPVOID)password;
234190
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
235-
cred.AttributeCount = 1;
236-
cred.Attributes = attrs;
191+
cred.AttributeCount = 0;
192+
cred.Attributes = NULL;
237193
cred.TargetAlias = NULL;
238194
cred.UserName = wusername;
239195

240-
write_attr(attrs, L"git_protocol", protocol);
241-
242-
if (host) {
243-
write_attr(attrs + cred.AttributeCount, L"git_host", host);
244-
cred.AttributeCount++;
245-
}
246-
247-
if (path) {
248-
write_attr(attrs + cred.AttributeCount, L"git_path", path);
249-
cred.AttributeCount++;
250-
}
251-
252196
if (!CredWriteW(&cred, 0))
253197
die("CredWrite failed");
254198
}
@@ -284,24 +228,26 @@ static void read_credential(void)
284228

285229
while (fgets(buf, sizeof(buf), stdin)) {
286230
char *v;
231+
int len = strlen(buf);
232+
/* strip trailing CR / LF */
233+
while (len && strchr("\r\n", buf[len - 1]))
234+
buf[--len] = 0;
287235

288-
if (!strcmp(buf, "\n"))
236+
if (!*buf)
289237
break;
290-
buf[strlen(buf)-1] = '\0';
291238

292239
v = strchr(buf, '=');
293240
if (!v)
294241
die("bad input: %s", buf);
295242
*v++ = '\0';
296243

297244
if (!strcmp(buf, "protocol"))
298-
protocol = xstrdup(v);
245+
protocol = utf8_to_utf16_dup(v);
299246
else if (!strcmp(buf, "host"))
300-
host = xstrdup(v);
247+
host = utf8_to_utf16_dup(v);
301248
else if (!strcmp(buf, "path"))
302-
path = xstrdup(v);
249+
path = utf8_to_utf16_dup(v);
303250
else if (!strcmp(buf, "username")) {
304-
username = xstrdup(v);
305251
wusername = utf8_to_utf16_dup(v);
306252
} else if (!strcmp(buf, "password"))
307253
password = utf8_to_utf16_dup(v);
@@ -330,22 +276,20 @@ int main(int argc, char *argv[])
330276
return 0;
331277

332278
/* 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));
336-
if (username) {
337-
strncat(target_buf, username, sizeof(target_buf));
338-
strncat(target_buf, "@", sizeof(target_buf));
279+
wcscpy(target, L"git:");
280+
wcsncat(target, protocol, ARRAY_SIZE(target));
281+
wcsncat(target, L"://", ARRAY_SIZE(target));
282+
if (wusername) {
283+
wcsncat(target, wusername, ARRAY_SIZE(target));
284+
wcsncat(target, L"@", ARRAY_SIZE(target));
339285
}
340286
if (host)
341-
strncat(target_buf, host, sizeof(target_buf));
287+
wcsncat(target, host, ARRAY_SIZE(target));
342288
if (path) {
343-
strncat(target_buf, "/", sizeof(target_buf));
344-
strncat(target_buf, path, sizeof(target_buf));
289+
wcsncat(target, L"/", ARRAY_SIZE(target));
290+
wcsncat(target, path, ARRAY_SIZE(target));
345291
}
346292

347-
target = utf8_to_utf16_dup(target_buf);
348-
349293
if (!strcmp(argv[1], "get"))
350294
get_credential();
351295
else if (!strcmp(argv[1], "store"))

0 commit comments

Comments
 (0)