From: "Marc Becker via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Marc Becker <becm@gmx.de>, Marc Becker <becm@gmx.de>
Subject: [PATCH] wincred: fix line split of secret blob content
Date: Fri, 09 Oct 2026 13:45:18 +0000 [thread overview]
Message-ID: <pull.2251.git.1791553518774.gitgitgadget@gmail.com> (raw)
From: Marc Becker <becm@gmx.de>
operate on immutable blob data (wcsncpy_s still had invalid target size)
split on newline character to avoid bleed-over on multi-line content
Signed-off-by: Marc Becker <becm@gmx.de>
---
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(-)
diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c
index 22eb27ca31..584f457774 100644
--- a/contrib/credential/wincred/git-credential-wincred.c
+++ b/contrib/credential/wincred/git-credential-wincred.c
@@ -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
--
gitgitgadget
next reply other threads:[~2026-10-09 13:45 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 13:45 Marc Becker via GitGitGadget [this message]
2026-10-09 21:16 ` [PATCH] wincred: fix line split of secret blob content Junio C Hamano
2026-10-10 11:47 ` [PATCH v2] wincred: refactor credential blob processing Marc Becker via GitGitGadget
2026-10-12 21:27 ` [PATCH v3] " Marc Becker via GitGitGadget
2026-10-12 21:57 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.2251.git.1791553518774.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=becm@gmx.de \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox