All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] wincred: fix line split of secret blob content
@ 2026-10-09 13:45 Marc Becker via GitGitGadget
  2026-10-09 21:16 ` Junio C Hamano
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Marc Becker via GitGitGadget @ 2026-10-09 13:45 UTC (permalink / raw)
  To: git; +Cc: Marc Becker, Marc Becker

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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-10-14 20:01 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 13:45 [PATCH] wincred: fix line split of secret blob content Marc Becker via GitGitGadget
2026-10-09 21:16 ` 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
2026-10-14 18:16 ` [PATCH v4] " Marc Becker via GitGitGadget
2026-10-14 20:00 ` [PATCH v5] " Marc Becker via GitGitGadget

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.