wincred: fix line split of secret blob content
PATCH 1/1 wincred: fix line split of secret blob content
From: Marc Becker <[email protected]>
operate on immutable blob data (wcsncpy_s still had invalid target size) split on newline character to avoid bleed-over on multi-line content
---
wincred: fix line split of secret blob content
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2251%2Fbecm%2Ffix-wincred-secret-linesplit-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2251/becm/fix-wincred-secret-linesplit-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2251
.../wincred/git-credential-wincred.c | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-)
contrib/credential/wincred/git-credential-wincred.c +55 -31
@@ -6,6 +6,7 @@#include <stdio.h> #include <io.h> #include <fcntl.h> +#include <wchar.h> #include <wincred.h> /* common helpers */
@@ -148,51 +149,74 @@ static void get_credential(void) {CREDENTIALW **creds;DWORD num_creds; -int i; -CREDENTIAL_ATTRIBUTEW *attr; -WCHAR *secret; -WCHAR *line; -WCHAR *remaining_lines; -WCHAR *part; -WCHAR *remaining_parts;if (!CredEnumerateW (L"git:*" ,0 , &num_creds, &creds))return ; -/* search for the first credential that matches username */ -for (i =0 ; i < num_creds; ++i) +/* search for the first credential that matches target and username */ +for (int i =0 ; i < num_creds; ++i) {if (match_cred (creds[i],0 )) { -write_item ("username" , creds[i]->UserName, - creds[i]->UserName ?wcslen (creds[i]->UserName) :0 ); -if (creds[i]->CredentialBlobSize >0 ) { - secret =xmalloc (creds[i]->CredentialBlobSize +sizeof (WCHAR )); -wcsncpy_s (secret, creds[i]->CredentialBlobSize, (LPCWSTR )creds[i]->CredentialBlob, creds[i]->CredentialBlobSize /sizeof (WCHAR )); - line =wcstok_s (secret,L"\r\n" , &remaining_lines); -write_item ("password" , line, line ?wcslen (line) :0 ); -while (line !=NULL ) { - part =wcstok_s (line,L"=" , &remaining_parts); -if (!wcscmp (part,L"oauth_refresh_token" )) { -write_item ("oauth_refresh_token" , remaining_parts, remaining_parts ?wcslen (remaining_parts) :0 ); - } - line =wcstok_s (NULL ,L"\r\n" , &remaining_lines); - } -free (secret); +LPCWSTR username = creds[i]->UserName; +LPCWSTR blob = (LPCWSTR )creds[i]->CredentialBlob; +LPCWSTR end; +DWORD wlen; + +write_item ("username" , username, username ?wcslen (username) :0 ); + + wlen = creds[i]->CredentialBlobSize /sizeof (WCHAR ); + +// check if content is single line +if ((end =wmemchr (blob,'\n' , wlen)) ==NULL ) { +write_item ("password" , blob, wlen); }else { -write_item ("password" , - (LPCWSTR )creds[i]->CredentialBlob, - creds[i]->CredentialBlobSize /sizeof (WCHAR )); +DWORD length = end++ - blob; + +// correct remaining size and drop carriage return at line end + wlen -= length +1 ; +if (length && blob[length -1 ] =='\r' ) { + --length; + } +write_item ("password" , blob, length); + +// key/value content starting on next line + blob = end; +do { +LPCWSTR value; + +// find line end +if ((end =wmemchr (blob,'\n' , wlen)) ==NULL ) { + length = wlen; + }else { + length = end++ - blob; +// correct remaining size and drop carriage return at line end + wlen -= length +1 ; +if (length && blob[length -1 ] =='\r' ) { + --length; + } + } +// find key/value separator for extended credential info +if ((value =wmemchr (blob,'=' , length)) !=NULL ) { +static const LPCWSTR refresh =L"oauth_refresh_token" ; +DWORD klen = value - blob; + +// write entries known to git credential protocol +if (klen ==wcslen (refresh) &&memcmp (blob, refresh, klen) ==0 ) { +write_item ("oauth_refresh_token" , value +1 , length - klen -1 ); + } + } + }while ((blob = end)); }for (int j =0 ; j < creds[i]->AttributeCount; j++) { - attr = creds[i]->Attributes + j; +CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j; +if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { -write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, - attr->ValueSize /sizeof (WCHAR )); +write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, attr->ValueSize /sizeof (WCHAR ));break ; } }break ; } - + }CredFree (creds); }
base-commit: 6de20f6092dcf9bdb1c8efe03db4b70c82b423dd
Signature · 1 line
gitgitgadget
Quoted 4 lines · Marc Becker via GitGitGadget
From: Marc Becker <[email protected]>
operate on immutable blob data (
wcsncpy_sstill had invalid target size) split on newline character to avoid bleed-over on multi-line content
This needs a bit more work to make it more readable than a bulleted list of lowercase fragments.
When in doubt, keep in mind that the usual way to compose a log message of this project is to:
- Give an observation on how the current system works in the present tense (so no need to say "Currently X is Y", or "Previously X was Y" to describe the state before your change; just "X is Y" is enough), and discuss what you perceive as a problem in it.
- Propose a solution (optional---often, problem description trivially leads to an obvious solution in reader's minds).
- Give commands to somebody editing the codebase to "make it so", instead of saying "This commit does X".
in this order.
- It mentions wcsncpy_s having an invalid target size, but does not explain why it was invalid or the consequences. Is the issue that wcsncpy_s expects the buffer size in wide characters, but was being passed a size in bytes, which obviously cannot always agree?
- It mentions "bleed-over on multi-line content", but does not describe the observable symptoms. Is the issue that when the password is empty, the skipping by wcstok_s delimiter would cause the oauth_refresh_token line to be erroneously parsed as the password?
- The final sentence should be an imperative command to the codebase, e.g., "Parse the blob in-place without copying and split lines manually using wmemchr()."
Quoted 49 lines
Signed-off-by Marc Becker <[email protected]>---
wincred: fix line split of secret blob contentPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2251%2Fbecm%2Ffix-wincred-secret-linesplit-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2251/becm/fix-wincred-secret-linesplit-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2251.../wincred/git-credential-wincred.c | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-)contrib/credential/wincred/git-credential-wincred.c +3
-14@@ -6,6 +6,7 @@#include <stdio.h> #include <io.h> #include <fcntl.h> +#include <wchar.h> #include <wincred.h> /* common helpers */ @@ -148,51 +149,74 @@ static void get_credential(void) {CREDENTIALW **creds;DWORD num_creds; -int i; -CREDENTIAL_ATTRIBUTEW *attr; -WCHAR *secret; -WCHAR *line; -WCHAR *remaining_lines; -WCHAR *part; -WCHAR *remaining_parts;if (!CredEnumerateW (L"git:*" ,0 , &num_creds, &creds))return ; -/* search for the first credential that matches username */ -for (i =0 ; i < num_creds; ++i) +/* search for the first credential that matches target and username */ +for (int i =0 ; i < num_creds; ++i) {if (match_cred (creds[i],0 )) { -write_item ("username" , creds[i]->UserName, - creds[i]->UserName ?wcslen (creds[i]->UserName) :0 ); -if (creds[i]->CredentialBlobSize >0 ) { - secret =xmalloc (creds[i]->CredentialBlobSize +sizeof (WCHAR )); -wcsncpy_s (secret, creds[i]->CredentialBlobSize, (LPCWSTR )creds[i]->CredentialBlob, creds[i]->CredentialBlobSize /sizeof (WCHAR ));
contrib/credential/wincred/git-credential-wincred.c static void get_credential(void) · line 179
Quoted 10 lines
contrib/credential/wincred/git-credential-wincred.c +0
-10- line =wcstok_s (secret,L"\r\n" , &remaining_lines); -write_item ("password" , line, line ?wcslen (line) :0 ); -while (line !=NULL ) { - part =wcstok_s (line,L"=" , &remaining_parts); -if (!wcscmp (part,L"oauth_refresh_token" )) { -write_item ("oauth_refresh_token" , remaining_parts, remaining_parts ?wcslen (remaining_parts) :0 ); - } - line =wcstok_s (NULL ,L"\r\n" , &remaining_lines); - } -free (secret);
The original was already bad, but this makes it even worse to have the code nested too deeply. Would separating out the body of the for loop into a separate helper function, or perhaps standard tricks like this
for (...) {
if (!match_cred(...))
continue;
... rest of the loop dedented by one tab stop ...
}make it readable?
contrib/credential/wincred/git-credential-wincred.c static void get_credential(void) · line 168
Quoted 10 lines
contrib/credential/wincred/git-credential-wincred.c +10
-0+LPCWSTR username = creds[i]->UserName; +LPCWSTR blob = (LPCWSTR )creds[i]->CredentialBlob; +LPCWSTR end; +DWORD wlen; + +write_item ("username" , username, username ?wcslen (username) :0 ); + + wlen = creds[i]->CredentialBlobSize /sizeof (WCHAR ); + +// check if content is single line
/* our single line comment should look like this */
contrib/credential/wincred/git-credential-wincred.c static void get_credential(void) · line 169
Quoted 1 line
contrib/credential/wincred/git-credential-wincred.c +1
-0+if ((end =wmemchr (blob,'\n' , wlen)) ==NULL ) {
I do not do Windows and I do not often deal with wchar_t, so I do not know how much practitioners of code like this one cares, but would it be better to make the fact clear that we are not dealing with a regular 'char' by writing a wchar_t literal like this as L'\n'? This is not a correctness suggestion, but a readability one. Having a function prototype would coerse the parameter types, so you may end up passing L'\n' either way.
contrib/credential/wincred/git-credential-wincred.c static void get_credential(void) · line 172
Quoted 6 lines
contrib/credential/wincred/git-credential-wincred.c +2
-3+write_item ("password" , blob, wlen); }else { -write_item ("password" , - (LPCWSTR )creds[i]->CredentialBlob, - creds[i]->CredentialBlobSize /sizeof (WCHAR )); +DWORD length = end++ - blob;
Here, "end" is of LPCWSTR type, aka "wchar_t *". So is "blob". The difference would give us how many wide characters are in there. That is not necessarily number of bytes starting at &blob[0].
contrib/credential/wincred/git-credential-wincred.c static void get_credential(void) · line 176
Quoted 3 lines
contrib/credential/wincred/git-credential-wincred.c +3
-0+// correct remaining size and drop carriage return at line end + wlen -= length +1 ; +if (length && blob[length -1 ] =='\r' ) {
This CR is also side, right?
Quoted 14 lines
contrib/credential/wincred/git-credential-wincred.c +14
-0+ --length; + } +write_item ("password" , blob, length); + +// key/value content starting on next line + blob = end; + do { + LPCWSTR value; + + // find line end + if ((end = wmemchr(blob, '\n', wlen)) == NULL) { + length = wlen; + } else { + length = end++ - blob;
contrib/credential/wincred/git-credential-wincred.c static void get_credential(void) · line 200
Quoted 10 lines
contrib/credential/wincred/git-credential-wincred.c +10
-0+// correct remaining size and drop carriage return at line end + wlen -= length +1 ; +if (length && blob[length -1 ] =='\r' ) { + --length; + } + } +// find key/value separator for extended credential info +if ((value =wmemchr (blob,'=' , length)) !=NULL ) { +static const LPCWSTR refresh =L"oauth_refresh_token" ; +DWORD klen = value - blob;
Value is also "wchar_t *", so klen counts the length in wchar_t, which may be wider than a byte. So is
contrib/credential/wincred/git-credential-wincred.c static void get_credential(void) · line 203
Quoted 2 lines
contrib/credential/wincred/git-credential-wincred.c +2
-0+// write entries known to git credential protocol +if (klen ==wcslen (refresh) &&memcmp (blob, refresh, klen) ==0 ) {
klen that counts number of wchar_t letters in refresh[] string.
So, is the memcmp() used to check if early part of blob[] match the refresh[] as a whole correct, or is it only checking an early half (or one fourth, depending on how much wider your wchar_t is compared to char) of the string?
Quoted 25 lines
contrib/credential/wincred/git-credential-wincred.c +8
-4+write_item ("oauth_refresh_token" , value +1 , length - klen -1 ); + } + } + }while ((blob = end)); }for (int j =0 ; j < creds[i]->AttributeCount; j++) { - attr = creds[i]->Attributes + j; +CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j; +if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { -write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, - attr->ValueSize /sizeof (WCHAR )); +write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, attr->ValueSize /sizeof (WCHAR ));break ; } }break ; } - + }CredFree (creds); }base-commit:
6de20f6092dcf9bdb1c8efe03db4b70c82b423dd
PATCH v2 1/1 wincred: refactor credential blob processing
In reply to Marc Becker via GitGitGadgetFrom: Marc Becker <[email protected]>
Invalid target size check (bytes instead of characters) for wcsncpy_s already led to memory corruption; d22a4884 just hid the error by making sure the target is always big enough.
Single invocation of wcstok_s only cuts out first hit delimiter. Consecutive items would always start with a line feed character if separation consists of CRLF.
Only exception is 1st item (due to following bug). Advancement to end of password line is missing. Password value is reused as extended credential item but likely filtered out due to value mismatch with accepted key.
Line split needs to be deterministic and code should be split up into smaller blocks.
Create separate methods for writing credential items and the complete credential blob content to tighten code in main credential loop. Reduce variable scope and nesting level. Use early continue/return to improve code readability.
Use wmemchr to reliably detect wide-character-newline in immutable blob data without need to create a further copy.
---
wincred: fix line split of secret blob content
Changes since v1:
* move credential blob dissection and output to separate methods (maintainer request)
* mark character constants as wide char
* consistently treat sizes/lengths as (wide) character count
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2251%2Fbecm%2Ffix-wincred-secret-linesplit-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2251/becm/fix-wincred-secret-linesplit-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/2251
Range-diff vs v1:
1: fb80cfc32a ! 1: 01a1039f70 wincred: fix line split of secret blob content
@@ Metadata
Author: Marc Becker <[email protected]>
## Commit message ##
- wincred: fix line split of secret blob content
+ wincred: refactor credential blob processing
- operate on immutable blob data (wcsncpy_s still had invalid target size)
- split on newline character to avoid bleed-over on multi-line content
+ Invalid target size check (bytes instead of characters) for `wcsncpy_s`
+ already led to memory corruption; d22a4884 just hid the error by making
+ sure the target is always big enough.
+ Single invocation of `wcstok_s` only cuts out first hit delimiter.
+ Consecutive items would always start with a line feed character if
+ separation consists of CRLF.
+ Only exception is 1st item (due to following bug).
+ Advancement to end of password line is missing. Password value is reused
+ as extended credential item but likely filtered out due to value
+ mismatch with accepted key.
+
+ Line split needs to be deterministic and code should be split up into
+ smaller blocks.
+
+ Create separate methods for writing credential items and the complete
+ credential blob content to tighten code in main credential loop.
+ Reduce variable scope and nesting level. Use early continue/return to
+ improve code readability.
+ Use `wmemchr` to reliably detect wide-character-newline in immutable
+ blob data without need to create a further copy.
Signed-off-by: Marc Becker <[email protected]>
@@ contrib/credential/wincred/git-credential-wincred.c
#include <wincred.h>
/* common helpers */
+@@ contrib/credential/wincred/git-credential-wincred.c: static void write_item(const char *what, LPCWSTR wbuf, int wlen)
+ free(buf);
+ }
+
++/*
++ * Write known credential item.
++ */
++static void write_credential_item(LPCWSTR data, int wlen)
++{
++ static const LPCWSTR refresh_token = L"oauth_refresh_token";
++ LPCWSTR value;
++ DWORD klen;
++ DWORD vlen;
++
++ /* find key/value separator for credential item */
++ if ((value = wmemchr(data, L'=', wlen)) == NULL)
++ return;
++ klen = value++ - data;
++ vlen = wlen - klen - 1;
++
++ /* write items known to git credential protocol */
++ if (klen == wcslen(refresh_token) && wmemcmp(data, refresh_token, klen) == 0)
++ write_item("oauth_refresh_token", value, vlen);
++}
++
++/*
++ * Write single credential block
++ * consisting of password and further (accepted) credential items.
++ */
++static void write_credential(const CREDENTIALW *cred)
++{
++ LPCWSTR end;
++ DWORD length;
++ LPCWSTR blob = (LPCWSTR)cred->CredentialBlob;
++ DWORD wlen = cred->CredentialBlobSize / sizeof(WCHAR);
++
++ /* check if content is single line */
++ if ((end = wmemchr(blob, L'\n', wlen)) == NULL) {
++ write_item("password", blob, wlen);
++ return;
++ }
++ /* determine current line length and remaining data size */
++ length = end++ - blob;
++ wlen -= length + 1;
++
++ /* skip carriage return at line end */
++ if (length && blob[length - 1] == L'\r')
++ --length;
++ write_item("password", blob, length);
++
++ while (1) {
++ /* key/value content starts on next line */
++ blob = end;
++
++ /* find line end */
++ if ((end = wmemchr(blob, L'\n', wlen)) == NULL) {
++ write_credential_item(blob, wlen);
++ return;
++ }
++ /* determine current line length and remaining data size */
++ length = end++ - blob;
++ wlen -= length + 1;
++
++ // skip carriage return at line end
++ if (length && blob[length - 1] == L'\r')
++ --length;
++ write_credential_item(blob, length);
++ }
++}
++
+ /*
+ * Match an (optional) expected string and a delimiter in the target string,
+ * consuming the matched text by updating the target pointer.
@@ contrib/credential/wincred/git-credential-wincred.c: static void get_credential(void)
{
CREDENTIALW **creds;
@@ contrib/credential/wincred/git-credential-wincred.c: static void get_credential(
- /* search for the first credential that matches username */
- for (i = 0; i < num_creds; ++i)
-+ /* search for the first credential that matches target and username */
-+ for (int i = 0; i < num_creds; ++i) {
- if (match_cred(creds[i], 0)) {
+- if (match_cred(creds[i], 0)) {
- write_item("username", creds[i]->UserName,
- creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
- if (creds[i]->CredentialBlobSize > 0) {
@@ contrib/credential/wincred/git-credential-wincred.c: static void get_credential(
- line = wcstok_s(NULL, L"\r\n", &remaining_lines);
- }
- free(secret);
-+ LPCWSTR username = creds[i]->UserName;
-+ LPCWSTR blob = (LPCWSTR)creds[i]->CredentialBlob;
-+ LPCWSTR end;
-+ DWORD wlen;
-+
-+ write_item("username", username, username ? wcslen(username) : 0);
-+
-+ wlen = creds[i]->CredentialBlobSize / sizeof(WCHAR);
-+
-+ // check if content is single line
-+ if ((end = wmemchr(blob, '\n', wlen)) == NULL) {
-+ write_item("password", blob, wlen);
- } else {
+- } else {
- write_item("password",
- (LPCWSTR)creds[i]->CredentialBlob,
- creds[i]->CredentialBlobSize / sizeof(WCHAR));
-+ DWORD length = end++ - blob;
+- }
+- for (int j = 0; j < creds[i]->AttributeCount; j++) {
+- attr = creds[i]->Attributes + j;
+- if (!wcscmp(attr->Keyword, L"git_password_expiry_utc")) {
+- write_item("password_expiry_utc", (LPCWSTR)attr->Value,
+- attr->ValueSize / sizeof(WCHAR));
+- break;
+- }
++ /* search for the first credential that matches target and username */
++ for (int i = 0; i < num_creds; ++i) {
++ LPCWSTR username;
+
-+ // correct remaining size and drop carriage return at line end
-+ wlen -= length + 1;
-+ if (length && blob[length - 1] == '\r') {
-+ --length;
-+ }
-+ write_item("password", blob, length);
++ if (!match_cred(creds[i], 0))
++ continue;
+
-+ // key/value content starting on next line
-+ blob = end;
-+ do {
-+ LPCWSTR value;
++ username = creds[i]->UserName;
++ write_item("username", username, username ? wcslen(username) : 0);
+
-+ // find line end
-+ if ((end = wmemchr(blob, '\n', wlen)) == NULL) {
-+ length = wlen;
-+ } else {
-+ length = end++ - blob;
-+ // correct remaining size and drop carriage return at line end
-+ wlen -= length + 1;
-+ if (length && blob[length - 1] == '\r') {
-+ --length;
-+ }
-+ }
-+ // find key/value separator for extended credential info
-+ if ((value = wmemchr(blob, '=', length)) != NULL) {
-+ static const LPCWSTR refresh = L"oauth_refresh_token";
-+ DWORD klen = value - blob;
++ write_credential(creds[i]);
+
-+ // write entries known to git credential protocol
-+ if (klen == wcslen(refresh) && memcmp(blob, refresh, klen) == 0) {
-+ write_item("oauth_refresh_token", value + 1, length - klen - 1);
-+ }
-+ }
-+ } while ((blob = end));
- }
- for (int j = 0; j < creds[i]->AttributeCount; j++) {
-- attr = creds[i]->Attributes + j;
-+ CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j;
++ for (int j = 0; j < creds[i]->AttributeCount; j++) {
++ CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j;
+
- if (!wcscmp(attr->Keyword, L"git_password_expiry_utc")) {
-- write_item("password_expiry_utc", (LPCWSTR)attr->Value,
-- attr->ValueSize / sizeof(WCHAR));
-+ write_item("password_expiry_utc", (LPCWSTR)attr->Value, attr->ValueSize / sizeof(WCHAR));
- break;
- }
++ if (!wcscmp(attr->Keyword, L"git_password_expiry_utc")) {
++ write_item("password_expiry_utc", (LPCWSTR)attr->Value, attr->ValueSize / sizeof(WCHAR));
++ break;
}
- break;
+- break;
}
-
++ break;
+ }
CredFree(creds);
}.../wincred/git-credential-wincred.c | 126 ++++++++++++------ 1 file changed, 87 insertions(+), 39 deletions(-)
contrib/credential/wincred/git-credential-wincred.c +87 -39
@@ -6,6 +6,7 @@#include <stdio.h> #include <io.h> #include <fcntl.h> +#include <wchar.h> #include <wincred.h> /* common helpers */
@@ -69,6 +70,72 @@ static void write_item(const char *what, LPCWSTR wbuf, int wlen)free (buf); } +/* +* Write known credential item. +*/ +static void write_credential_item (LPCWSTR data,int wlen) +{ +static const LPCWSTR refresh_token =L"oauth_refresh_token" ; +LPCWSTR value; +DWORD klen; +DWORD vlen; + +/* find key/value separator for credential item */ +if ((value =wmemchr (data,L'=' , wlen)) ==NULL ) +return ; + klen = value++ - data; + vlen = wlen - klen -1 ; + +/* write items known to git credential protocol */ +if (klen ==wcslen (refresh_token) &&wmemcmp (data, refresh_token, klen) ==0 ) +write_item ("oauth_refresh_token" , value, vlen); +} + +/* +* Write single credential block +* consisting of password and further (accepted) credential items. +*/ +static void write_credential (const CREDENTIALW *cred) +{ +LPCWSTR end; +DWORD length; +LPCWSTR blob = (LPCWSTR )cred->CredentialBlob; +DWORD wlen = cred->CredentialBlobSize /sizeof (WCHAR ); + +/* check if content is single line */ +if ((end =wmemchr (blob,L'\n' , wlen)) ==NULL ) { +write_item ("password" , blob, wlen); +return ; + } +/* determine current line length and remaining data size */ + length = end++ - blob; + wlen -= length +1 ; + +/* skip carriage return at line end */ +if (length && blob[length -1 ] ==L'\r' ) + --length; +write_item ("password" , blob, length); + +while (1 ) { +/* key/value content starts on next line */ + blob = end; + +/* find line end */ +if ((end =wmemchr (blob,L'\n' , wlen)) ==NULL ) { +write_credential_item (blob, wlen); +return ; + } +/* determine current line length and remaining data size */ + length = end++ - blob; + wlen -= length +1 ; + +// skip carriage return at line end +if (length && blob[length -1 ] ==L'\r' ) + --length; +write_credential_item (blob, length); + } +} +/* * Match an (optional) expected string and a delimiter in the target string, * consuming the matched text by updating the target pointer.
@@ -148,51 +215,32 @@ static void get_credential(void) {CREDENTIALW **creds;DWORD num_creds; -int i; -CREDENTIAL_ATTRIBUTEW *attr; -WCHAR *secret; -WCHAR *line; -WCHAR *remaining_lines; -WCHAR *part; -WCHAR *remaining_parts;if (!CredEnumerateW (L"git:*" ,0 , &num_creds, &creds))return ; -/* search for the first credential that matches username */ -for (i =0 ; i < num_creds; ++i) -if (match_cred (creds[i],0 )) { -write_item ("username" , creds[i]->UserName, - creds[i]->UserName ?wcslen (creds[i]->UserName) :0 ); -if (creds[i]->CredentialBlobSize >0 ) { - secret =xmalloc (creds[i]->CredentialBlobSize +sizeof (WCHAR )); -wcsncpy_s (secret, creds[i]->CredentialBlobSize, (LPCWSTR )creds[i]->CredentialBlob, creds[i]->CredentialBlobSize /sizeof (WCHAR )); - line =wcstok_s (secret,L"\r\n" , &remaining_lines); -write_item ("password" , line, line ?wcslen (line) :0 ); -while (line !=NULL ) { - part =wcstok_s (line,L"=" , &remaining_parts); -if (!wcscmp (part,L"oauth_refresh_token" )) { -write_item ("oauth_refresh_token" , remaining_parts, remaining_parts ?wcslen (remaining_parts) :0 ); - } - line =wcstok_s (NULL ,L"\r\n" , &remaining_lines); - } -free (secret); - }else { -write_item ("password" , - (LPCWSTR )creds[i]->CredentialBlob, - creds[i]->CredentialBlobSize /sizeof (WCHAR )); - } -for (int j =0 ; j < creds[i]->AttributeCount; j++) { - attr = creds[i]->Attributes + j; -if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { -write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, - attr->ValueSize /sizeof (WCHAR )); -break ; - } +/* search for the first credential that matches target and username */ +for (int i =0 ; i < num_creds; ++i) { +LPCWSTR username; + +if (!match_cred (creds[i],0 )) +continue ; + + username = creds[i]->UserName; +write_item ("username" , username, username ?wcslen (username) :0 ); + +write_credential (creds[i]); + +for (int j =0 ; j < creds[i]->AttributeCount; j++) { +CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j; + +if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { +write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, attr->ValueSize /sizeof (WCHAR )); +break ; } -break ; } - +break ; + }CredFree (creds); }
base-commit: 6de20f6092dcf9bdb1c8efe03db4b70c82b423dd
Signature · 1 line
gitgitgadget
PATCH v3 1/1 wincred: refactor credential blob processing
In reply to Marc Becker via GitGitGadgetFrom: Marc Becker <[email protected]>
Invalid target size check (bytes instead of characters) for wcsncpy_s already led to memory corruption; d22a4884 just hid the error by making sure the target is always big enough.
Single invocation of wcstok_s only cuts out first hit delimiter. Consecutive items would always start with a line feed character if separation consists of CRLF.
Only exception is 1st item (due to following bug). Advancement to end of password line is missing. Password value is reused as extended credential item but likely filtered out due to value mismatch with accepted key.
Line split needs to be deterministic and code should be split up into smaller blocks.
Create separate methods for writing credential items and the complete credential blob content to tighten code in main credential loop. Reduce variable scope and nesting level. Use early continue/return to improve code readability.
Use wmemchr to reliably detect wide-character-newline in immutable blob data without need to create a further copy.
---
wincred: fix line split of secret blob content
Changes since v1:
* move credential blob dissection and output to separate methods (maintainer request)
* mark character constants as wide char
* consistently treat sizes/lengths as (wide) character count
Changes since v2:
* adjust loop condition for credential items; loop body processes non-final lines
* fix remaining C++ style comment
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2251%2Fbecm%2Ffix-wincred-secret-linesplit-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2251/becm/fix-wincred-secret-linesplit-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/2251
Range-diff vs v2:
1: 01a1039f70 ! 1: c7b03346cc wincred: refactor credential blob processing
@@ contrib/credential/wincred/git-credential-wincred.c: static void write_item(cons
+ LPCWSTR blob = (LPCWSTR)cred->CredentialBlob;
+ DWORD wlen = cred->CredentialBlobSize / sizeof(WCHAR);
+
-+ /* check if content is single line */
++ /* check if content is single line (retain trailing carriage return) */
+ if ((end = wmemchr(blob, L'\n', wlen)) == NULL) {
+ write_item("password", blob, wlen);
+ return;
@@ contrib/credential/wincred/git-credential-wincred.c: static void write_item(cons
+ length = end++ - blob;
+ wlen -= length + 1;
+
-+ /* skip carriage return at line end */
++ /* trim carriage return at line end */
+ if (length && blob[length - 1] == L'\r')
+ --length;
+ write_item("password", blob, length);
++ blob = end;
+
-+ while (1) {
-+ /* key/value content starts on next line */
-+ blob = end;
-+
-+ /* find line end */
-+ if ((end = wmemchr(blob, L'\n', wlen)) == NULL) {
-+ write_credential_item(blob, wlen);
-+ return;
-+ }
-+ /* determine current line length and remaining data size */
++ /* find line separator */
++ while ((end = wmemchr(blob, L'\n', wlen)) != NULL) {
+ length = end++ - blob;
+ wlen -= length + 1;
+
-+ // skip carriage return at line end
++ /* trim carriage return at line end */
+ if (length && blob[length - 1] == L'\r')
+ --length;
++
++ /* write item and advance blob position */
+ write_credential_item(blob, length);
++ blob = end;
+ }
++ /* process final line (retain trailing carriage return) */
++ write_credential_item(blob, wlen);
+}
+
/*.../wincred/git-credential-wincred.c | 124 ++++++++++++------ 1 file changed, 85 insertions(+), 39 deletions(-)
contrib/credential/wincred/git-credential-wincred.c +85 -39
@@ -6,6 +6,7 @@#include <stdio.h> #include <io.h> #include <fcntl.h> +#include <wchar.h> #include <wincred.h> /* common helpers */
@@ -69,6 +70,70 @@ static void write_item(const char *what, LPCWSTR wbuf, int wlen)free (buf); } +/* +* Write known credential item. +*/ +static void write_credential_item (LPCWSTR data,int wlen) +{ +static const LPCWSTR refresh_token =L"oauth_refresh_token" ; +LPCWSTR value; +DWORD klen; +DWORD vlen; + +/* find key/value separator for credential item */ +if ((value =wmemchr (data,L'=' , wlen)) ==NULL ) +return ; + klen = value++ - data; + vlen = wlen - klen -1 ; + +/* write items known to git credential protocol */ +if (klen ==wcslen (refresh_token) &&wmemcmp (data, refresh_token, klen) ==0 ) +write_item ("oauth_refresh_token" , value, vlen); +} + +/* +* Write single credential block +* consisting of password and further (accepted) credential items. +*/ +static void write_credential (const CREDENTIALW *cred) +{ +LPCWSTR end; +DWORD length; +LPCWSTR blob = (LPCWSTR )cred->CredentialBlob; +DWORD wlen = cred->CredentialBlobSize /sizeof (WCHAR ); + +/* check if content is single line (retain trailing carriage return) */ +if ((end =wmemchr (blob,L'\n' , wlen)) ==NULL ) { +write_item ("password" , blob, wlen); +return ; + } +/* determine current line length and remaining data size */ + length = end++ - blob; + wlen -= length +1 ; + +/* trim carriage return at line end */ +if (length && blob[length -1 ] ==L'\r' ) + --length; +write_item ("password" , blob, length); + blob = end; + +/* find line separator */ +while ((end =wmemchr (blob,L'\n' , wlen)) !=NULL ) { + length = end++ - blob; + wlen -= length +1 ; + +/* trim carriage return at line end */ +if (length && blob[length -1 ] ==L'\r' ) + --length; + +/* write item and advance blob position */ +write_credential_item (blob, length); + blob = end; + } +/* process final line (retain trailing carriage return) */ +write_credential_item (blob, wlen); +} +/* * Match an (optional) expected string and a delimiter in the target string, * consuming the matched text by updating the target pointer.
@@ -148,51 +213,32 @@ static void get_credential(void) {CREDENTIALW **creds;DWORD num_creds; -int i; -CREDENTIAL_ATTRIBUTEW *attr; -WCHAR *secret; -WCHAR *line; -WCHAR *remaining_lines; -WCHAR *part; -WCHAR *remaining_parts;if (!CredEnumerateW (L"git:*" ,0 , &num_creds, &creds))return ; -/* search for the first credential that matches username */ -for (i =0 ; i < num_creds; ++i) -if (match_cred (creds[i],0 )) { -write_item ("username" , creds[i]->UserName, - creds[i]->UserName ?wcslen (creds[i]->UserName) :0 ); -if (creds[i]->CredentialBlobSize >0 ) { - secret =xmalloc (creds[i]->CredentialBlobSize +sizeof (WCHAR )); -wcsncpy_s (secret, creds[i]->CredentialBlobSize, (LPCWSTR )creds[i]->CredentialBlob, creds[i]->CredentialBlobSize /sizeof (WCHAR )); - line =wcstok_s (secret,L"\r\n" , &remaining_lines); -write_item ("password" , line, line ?wcslen (line) :0 ); -while (line !=NULL ) { - part =wcstok_s (line,L"=" , &remaining_parts); -if (!wcscmp (part,L"oauth_refresh_token" )) { -write_item ("oauth_refresh_token" , remaining_parts, remaining_parts ?wcslen (remaining_parts) :0 ); - } - line =wcstok_s (NULL ,L"\r\n" , &remaining_lines); - } -free (secret); - }else { -write_item ("password" , - (LPCWSTR )creds[i]->CredentialBlob, - creds[i]->CredentialBlobSize /sizeof (WCHAR )); - } -for (int j =0 ; j < creds[i]->AttributeCount; j++) { - attr = creds[i]->Attributes + j; -if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { -write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, - attr->ValueSize /sizeof (WCHAR )); -break ; - } +/* search for the first credential that matches target and username */ +for (int i =0 ; i < num_creds; ++i) { +LPCWSTR username; + +if (!match_cred (creds[i],0 )) +continue ; + + username = creds[i]->UserName; +write_item ("username" , username, username ?wcslen (username) :0 ); + +write_credential (creds[i]); + +for (int j =0 ; j < creds[i]->AttributeCount; j++) { +CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j; + +if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { +write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, attr->ValueSize /sizeof (WCHAR )); +break ; } -break ; } - +break ; + }CredFree (creds); }
base-commit: 6de20f6092dcf9bdb1c8efe03db4b70c82b423dd
Signature · 1 line
gitgitgadget
Re: [PATCH v3] wincred: refactor credential blob processing
Quoted 5 lines · Marc Becker via GitGitGadget
From: Marc Becker <[email protected]>
Invalid target size check (bytes instead of characters) for
wcsncpy_salready led to memory corruption;d22a4884just hid the error by making sure the target is always big enough.
While I can parse each sentence and understand every individual word, it remains unclear what they are trying to convey in the context of explaining this change. In my native tongue, there is a very appropriate expression to describe such a statement:
言語明瞭意味不明. It roughly translates to "clear language, obscure meaning".
I will leave this patch for those who work on Windows to review.
Thanks.
PATCH v4 1/1 wincred: refactor credential blob processing
In reply to Marc Becker via GitGitGadgetFrom: Marc Becker <[email protected]>
Implementation for splitting secret blob into separate lines and parsing those into key/value pairs has a couple of issues:
- invalid size limit to wcsncpy_s (missed during d22a4884)
- inconsistent detection of line separator (matches _CR_ and _LF_)
- failing to advance position after initial password line
- deep code nesting in loop body.
Line end detection needs to be deterministic and code can be split up into smaller functional blocks.
Create new methods for processing the matching credential and each supported key/value pair of a multi-line secret.
Reduce variable scope and nesting level. Use early continue/return to improve code readability.
Use wmemchr to reliably detect wide-character linefeed in immutable blob data without need to create a further copy.
---
wincred: fix line split of secret blob content
Changes since v3:
* update issue description in commit message to (hopefully) be less obscure
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2251%2Fbecm%2Ffix-wincred-secret-linesplit-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2251/becm/fix-wincred-secret-linesplit-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/2251
Range-diff vs v3:
1: c7b03346cc ! 1: 5fbd3987cf wincred: refactor credential blob processing
@@ Metadata
## Commit message ##
wincred: refactor credential blob processing
- Invalid target size check (bytes instead of characters) for wcsncpy_s
- already led to memory corruption; d22a4884 just hid the error by making
- sure the target is always big enough.
- Single invocation of wcstok_s only cuts out first hit delimiter.
- Consecutive items would always start with a line feed character if
- separation consists of CRLF.
- Only exception is 1st item (due to following bug).
- Advancement to end of password line is missing. Password value is reused
- as extended credential item but likely filtered out due to value
- mismatch with accepted key.
+ Implementation for splitting secret blob into separate lines and
+ parsing those into key/value pairs has a couple of issues:
+ - invalid size limit to wcsncpy_s (missed during d22a4884)
+ - inconsistent detection of line separator (matches _CR_ and _LF_)
+ - failing to advance position after initial password line
+ - deep code nesting in loop body.
- Line split needs to be deterministic and code should be split up into
- smaller blocks.
+ Line end detection needs to be deterministic and
+ code can be split up into smaller functional blocks.
- Create separate methods for writing credential items and the complete
- credential blob content to tighten code in main credential loop.
+ Create new methods for processing the matching credential and each
+ supported key/value pair of a multi-line secret.
Reduce variable scope and nesting level. Use early continue/return to improve code readability.
- Use wmemchr to reliably detect wide-character-newline in immutable
+ Use wmemchr to reliably detect wide-character linefeed in immutable
blob data without need to create a further copy.
.../wincred/git-credential-wincred.c | 124 ++++++++++++------ 1 file changed, 85 insertions(+), 39 deletions(-)
contrib/credential/wincred/git-credential-wincred.c +85 -39
@@ -6,6 +6,7 @@#include <stdio.h> #include <io.h> #include <fcntl.h> +#include <wchar.h> #include <wincred.h> /* common helpers */
@@ -69,6 +70,70 @@ static void write_item(const char *what, LPCWSTR wbuf, int wlen)free (buf); } +/* +* Write known credential item. +*/ +static void write_credential_item (LPCWSTR data,int wlen) +{ +static const LPCWSTR refresh_token =L"oauth_refresh_token" ; +LPCWSTR value; +DWORD klen; +DWORD vlen; + +/* find key/value separator for credential item */ +if ((value =wmemchr (data,L'=' , wlen)) ==NULL ) +return ; + klen = value++ - data; + vlen = wlen - klen -1 ; + +/* write items known to git credential protocol */ +if (klen ==wcslen (refresh_token) &&wmemcmp (data, refresh_token, klen) ==0 ) +write_item ("oauth_refresh_token" , value, vlen); +} + +/* +* Write single credential block +* consisting of password and further (accepted) credential items. +*/ +static void write_credential (const CREDENTIALW *cred) +{ +LPCWSTR end; +DWORD length; +LPCWSTR blob = (LPCWSTR )cred->CredentialBlob; +DWORD wlen = cred->CredentialBlobSize /sizeof (WCHAR ); + +/* check if content is single line (retain trailing carriage return) */ +if ((end =wmemchr (blob,L'\n' , wlen)) ==NULL ) { +write_item ("password" , blob, wlen); +return ; + } +/* determine current line length and remaining data size */ + length = end++ - blob; + wlen -= length +1 ; + +/* trim carriage return at line end */ +if (length && blob[length -1 ] ==L'\r' ) + --length; +write_item ("password" , blob, length); + blob = end; + +/* find line separator */ +while ((end =wmemchr (blob,L'\n' , wlen)) !=NULL ) { + length = end++ - blob; + wlen -= length +1 ; + +/* trim carriage return at line end */ +if (length && blob[length -1 ] ==L'\r' ) + --length; + +/* write item and advance blob position */ +write_credential_item (blob, length); + blob = end; + } +/* process final line (retain trailing carriage return) */ +write_credential_item (blob, wlen); +} +/* * Match an (optional) expected string and a delimiter in the target string, * consuming the matched text by updating the target pointer.
@@ -148,51 +213,32 @@ static void get_credential(void) {CREDENTIALW **creds;DWORD num_creds; -int i; -CREDENTIAL_ATTRIBUTEW *attr; -WCHAR *secret; -WCHAR *line; -WCHAR *remaining_lines; -WCHAR *part; -WCHAR *remaining_parts;if (!CredEnumerateW (L"git:*" ,0 , &num_creds, &creds))return ; -/* search for the first credential that matches username */ -for (i =0 ; i < num_creds; ++i) -if (match_cred (creds[i],0 )) { -write_item ("username" , creds[i]->UserName, - creds[i]->UserName ?wcslen (creds[i]->UserName) :0 ); -if (creds[i]->CredentialBlobSize >0 ) { - secret =xmalloc (creds[i]->CredentialBlobSize +sizeof (WCHAR )); -wcsncpy_s (secret, creds[i]->CredentialBlobSize, (LPCWSTR )creds[i]->CredentialBlob, creds[i]->CredentialBlobSize /sizeof (WCHAR )); - line =wcstok_s (secret,L"\r\n" , &remaining_lines); -write_item ("password" , line, line ?wcslen (line) :0 ); -while (line !=NULL ) { - part =wcstok_s (line,L"=" , &remaining_parts); -if (!wcscmp (part,L"oauth_refresh_token" )) { -write_item ("oauth_refresh_token" , remaining_parts, remaining_parts ?wcslen (remaining_parts) :0 ); - } - line =wcstok_s (NULL ,L"\r\n" , &remaining_lines); - } -free (secret); - }else { -write_item ("password" , - (LPCWSTR )creds[i]->CredentialBlob, - creds[i]->CredentialBlobSize /sizeof (WCHAR )); - } -for (int j =0 ; j < creds[i]->AttributeCount; j++) { - attr = creds[i]->Attributes + j; -if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { -write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, - attr->ValueSize /sizeof (WCHAR )); -break ; - } +/* search for the first credential that matches target and username */ +for (int i =0 ; i < num_creds; ++i) { +LPCWSTR username; + +if (!match_cred (creds[i],0 )) +continue ; + + username = creds[i]->UserName; +write_item ("username" , username, username ?wcslen (username) :0 ); + +write_credential (creds[i]); + +for (int j =0 ; j < creds[i]->AttributeCount; j++) { +CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j; + +if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { +write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, attr->ValueSize /sizeof (WCHAR )); +break ; } -break ; } - +break ; + }CredFree (creds); }
base-commit: 6de20f6092dcf9bdb1c8efe03db4b70c82b423dd
Signature · 1 line
gitgitgadget
PATCH v5 1/1 wincred: refactor credential blob processing
In reply to Marc Becker via GitGitGadgetFrom: Marc Becker <[email protected]>
Implementation for splitting secret blob into separate lines and parsing those into key/value pairs has a couple of issues:
- invalid size limit to wcsncpy_s (missed during d22a4884)
- inconsistent detection of line separator (matches _CR_ and _LF_)
- failing to advance position after initial password line
- deep code nesting in loop body.
Line end detection needs to be deterministic and code can be split up into smaller functional blocks.
Create new methods for processing the matching credential and each supported key/value pair of a multi-line secret.
Reduce variable scope and nesting level. Use early continue/return to improve code readability.
Use wmemchr to reliably detect wide-character linefeed in immutable blob data without need to create a further copy.
---
wincred: fix line split of secret blob content
Changes since v4:
* rename method to parse/format/write secret data to write_credential_secret
* pass only secret blob data to write_credential_secret
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2251%2Fbecm%2Ffix-wincred-secret-linesplit-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2251/becm/fix-wincred-secret-linesplit-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/2251
Range-diff vs v4:
1: 5fbd3987cf ! 1: 7a0b1d4d2d wincred: refactor credential blob processing
@@ contrib/credential/wincred/git-credential-wincred.c: static void write_item(cons
+ * Write single credential block
+ * consisting of password and further (accepted) credential items.
+ */
-+static void write_credential(const CREDENTIALW *cred)
++static void write_credential_secret(LPCWSTR blob, DWORD wlen)
+{
+ LPCWSTR end;
+ DWORD length;
-+ LPCWSTR blob = (LPCWSTR)cred->CredentialBlob;
-+ DWORD wlen = cred->CredentialBlobSize / sizeof(WCHAR);
+
+ /* check if content is single line (retain trailing carriage return) */
+ if ((end = wmemchr(blob, L'\n', wlen)) == NULL) {
@@ contrib/credential/wincred/git-credential-wincred.c: static void get_credential(
+ /* search for the first credential that matches target and username */
+ for (int i = 0; i < num_creds; ++i) {
+ LPCWSTR username;
++ LPCWSTR blob;
+
+ if (!match_cred(creds[i], 0))
+ continue;
@@ contrib/credential/wincred/git-credential-wincred.c: static void get_credential(
+ username = creds[i]->UserName;
+ write_item("username", username, username ? wcslen(username) : 0);
+
-+ write_credential(creds[i]);
++ blob = (LPCWSTR)creds[i]->CredentialBlob;
++ write_credential_secret(blob, creds[i]->CredentialBlobSize / sizeof(*blob));
+
+ for (int j = 0; j < creds[i]->AttributeCount; j++) {
+ CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j;.../wincred/git-credential-wincred.c | 124 ++++++++++++------ 1 file changed, 85 insertions(+), 39 deletions(-)
contrib/credential/wincred/git-credential-wincred.c +85 -39
@@ -6,6 +6,7 @@#include <stdio.h> #include <io.h> #include <fcntl.h> +#include <wchar.h> #include <wincred.h> /* common helpers */
@@ -69,6 +70,68 @@ static void write_item(const char *what, LPCWSTR wbuf, int wlen)free (buf); } +/* +* Write known credential item. +*/ +static void write_credential_item (LPCWSTR data,int wlen) +{ +static const LPCWSTR refresh_token =L"oauth_refresh_token" ; +LPCWSTR value; +DWORD klen; +DWORD vlen; + +/* find key/value separator for credential item */ +if ((value =wmemchr (data,L'=' , wlen)) ==NULL ) +return ; + klen = value++ - data; + vlen = wlen - klen -1 ; + +/* write items known to git credential protocol */ +if (klen ==wcslen (refresh_token) &&wmemcmp (data, refresh_token, klen) ==0 ) +write_item ("oauth_refresh_token" , value, vlen); +} + +/* +* Write single credential block +* consisting of password and further (accepted) credential items. +*/ +static void write_credential_secret (LPCWSTR blob,DWORD wlen) +{ +LPCWSTR end; +DWORD length; + +/* check if content is single line (retain trailing carriage return) */ +if ((end =wmemchr (blob,L'\n' , wlen)) ==NULL ) { +write_item ("password" , blob, wlen); +return ; + } +/* determine current line length and remaining data size */ + length = end++ - blob; + wlen -= length +1 ; + +/* trim carriage return at line end */ +if (length && blob[length -1 ] ==L'\r' ) + --length; +write_item ("password" , blob, length); + blob = end; + +/* find line separator */ +while ((end =wmemchr (blob,L'\n' , wlen)) !=NULL ) { + length = end++ - blob; + wlen -= length +1 ; + +/* trim carriage return at line end */ +if (length && blob[length -1 ] ==L'\r' ) + --length; + +/* write item and advance blob position */ +write_credential_item (blob, length); + blob = end; + } +/* process final line (retain trailing carriage return) */ +write_credential_item (blob, wlen); +} +/* * Match an (optional) expected string and a delimiter in the target string, * consuming the matched text by updating the target pointer.
@@ -148,51 +211,34 @@ static void get_credential(void) {CREDENTIALW **creds;DWORD num_creds; -int i; -CREDENTIAL_ATTRIBUTEW *attr; -WCHAR *secret; -WCHAR *line; -WCHAR *remaining_lines; -WCHAR *part; -WCHAR *remaining_parts;if (!CredEnumerateW (L"git:*" ,0 , &num_creds, &creds))return ; -/* search for the first credential that matches username */ -for (i =0 ; i < num_creds; ++i) -if (match_cred (creds[i],0 )) { -write_item ("username" , creds[i]->UserName, - creds[i]->UserName ?wcslen (creds[i]->UserName) :0 ); -if (creds[i]->CredentialBlobSize >0 ) { - secret =xmalloc (creds[i]->CredentialBlobSize +sizeof (WCHAR )); -wcsncpy_s (secret, creds[i]->CredentialBlobSize, (LPCWSTR )creds[i]->CredentialBlob, creds[i]->CredentialBlobSize /sizeof (WCHAR )); - line =wcstok_s (secret,L"\r\n" , &remaining_lines); -write_item ("password" , line, line ?wcslen (line) :0 ); -while (line !=NULL ) { - part =wcstok_s (line,L"=" , &remaining_parts); -if (!wcscmp (part,L"oauth_refresh_token" )) { -write_item ("oauth_refresh_token" , remaining_parts, remaining_parts ?wcslen (remaining_parts) :0 ); - } - line =wcstok_s (NULL ,L"\r\n" , &remaining_lines); - } -free (secret); - }else { -write_item ("password" , - (LPCWSTR )creds[i]->CredentialBlob, - creds[i]->CredentialBlobSize /sizeof (WCHAR )); - } -for (int j =0 ; j < creds[i]->AttributeCount; j++) { - attr = creds[i]->Attributes + j; -if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { -write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, - attr->ValueSize /sizeof (WCHAR )); -break ; - } +/* search for the first credential that matches target and username */ +for (int i =0 ; i < num_creds; ++i) { +LPCWSTR username; +LPCWSTR blob; + +if (!match_cred (creds[i],0 )) +continue ; + + username = creds[i]->UserName; +write_item ("username" , username, username ?wcslen (username) :0 ); + + blob = (LPCWSTR )creds[i]->CredentialBlob; +write_credential_secret (blob, creds[i]->CredentialBlobSize /sizeof (*blob)); + +for (int j =0 ; j < creds[i]->AttributeCount; j++) { +CREDENTIAL_ATTRIBUTEW *attr = creds[i]->Attributes + j; + +if (!wcscmp (attr->Keyword,L"git_password_expiry_utc" )) { +write_item ("password_expiry_utc" , (LPCWSTR )attr->Value, attr->ValueSize /sizeof (WCHAR )); +break ; } -break ; } - +break ; + }CredFree (creds); }
base-commit: 6de20f6092dcf9bdb1c8efe03db4b70c82b423dd
Signature · 1 line
gitgitgadget