From: Taylor Blau <me@ttaylorr.com>
To: git@vger.kernel.org
Cc: Jeff King <peff@peff.net>, Junio C Hamano <gitster@pobox.com>,
Matthew John Cheetham <mjcheetham@outlook.com>,
Johannes Schindelin <Johannes.Schindelin@gmx.de>,
Derrick Stolee <derrickstolee@github.com>
Subject: [PATCH 7/7] contrib/credential: embiggen fixed-size buffer in wincred
Date: Mon, 1 May 2023 11:54:06 -0400 [thread overview]
Message-ID: <59a934a256a177e1d47f05a772f53f40ff015ced.1682956419.git.me@ttaylorr.com> (raw)
In-Reply-To: <cover.1682956419.git.me@ttaylorr.com>
As in previous commits, harden the wincred credential helper against the
aforementioned protocol injection attack.
Unlike the approached used for osxkeychain and libsecret, where a
fixed-size buffer was replaced with `getline()`, we must take a
different approach here. There is no `getline()` equivalent in Windows,
and the function is not available to us with ordinary compiler settings.
Instead, allocate a larger (still fixed-size) buffer in which to process
each line. The value of 100 KiB is chosen to match the maximum-length
header that curl will allow, CURL_MAX_HTTP_HEADER.
To ensure that we are reading complete lines at a time, and that we
aren't susceptible to a similar injection attack (albeit with more
padding), ensure that each read terminates at a newline (i.e., that no
line is more than 100 KiB long).
Note that it isn't sufficient to turn the old loop into something like:
while (len && strchr("\r\n", buf[len - 1])) {
buf[--len] = 0;
ends_in_newline = 1;
}
because if an attacker sends something like:
[aaaaa.....]\r
host=example.com\r\n
the credential helper would fill its buffer after reading up through the
first '\r', call fgets() again, and then see "host=example.com\r\n" on
its line.
Note that the original code was written in a way that would trim an
arbitrary number of "\r" and "\n" from the end of the string. We should
get only a single "\n" (since the point of `fgets()` is to return the
buffer to us when it sees one), and likewise would not expect to see
more than one associated "\r". The new code trims a single "\r\n", which
matches the original intent.
[1]: https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html
Tested-by: Matthew John Cheetham <mjcheetham@outlook.com>
Helped-by: Matthew John Cheetham <mjcheetham@outlook.com>
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
.../wincred/git-credential-wincred.c | 21 +++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c
index ead6e267c7..32ebef7f65 100644
--- a/contrib/credential/wincred/git-credential-wincred.c
+++ b/contrib/credential/wincred/git-credential-wincred.c
@@ -249,16 +249,27 @@ static WCHAR *utf8_to_utf16_dup(const char *str)
return wstr;
}
+#define KB (1024)
+
static void read_credential(void)
{
- char buf[1024];
+ size_t alloc = 100 * KB;
+ char *buf = calloc(alloc, sizeof(*buf));
- while (fgets(buf, sizeof(buf), stdin)) {
+ while (fgets(buf, alloc, stdin)) {
char *v;
- int len = strlen(buf);
+ size_t len = strlen(buf);
+ int ends_in_newline = 0;
/* strip trailing CR / LF */
- while (len && strchr("\r\n", buf[len - 1]))
+ if (len && buf[len - 1] == '\n') {
buf[--len] = 0;
+ ends_in_newline = 1;
+ }
+ if (len && buf[len - 1] == '\r')
+ buf[--len] = 0;
+
+ if (!ends_in_newline)
+ die("bad input: %s", buf);
if (!*buf)
break;
@@ -284,6 +295,8 @@ static void read_credential(void)
* learn new lines, and the helpers are updated to match.
*/
}
+
+ free(buf);
}
int main(int argc, char *argv[])
--
2.40.1.452.gb3cd41c833
next prev parent reply other threads:[~2023-05-01 15:54 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-05-01 15:53 [PATCH 0/7] contrib/credential: avoid protocol injection attacks Taylor Blau
2023-05-01 15:53 ` [PATCH 1/7] credential.c: store "wwwauth[]" values in `credential_read()` Taylor Blau
2023-05-01 15:53 ` [PATCH 2/7] t/lib-credential.sh: ensure credential helpers handle long headers Taylor Blau
2023-05-01 15:53 ` [PATCH 3/7] contrib/credential: avoid fixed-size buffer in osxkeychain Taylor Blau
2023-05-01 15:53 ` [PATCH 4/7] contrib/credential: remove 'gnome-keyring' credential helper Taylor Blau
2023-05-01 15:54 ` [PATCH 5/7] contrib/credential: .gitignore libsecret build artifacts Taylor Blau
2023-05-01 15:54 ` [PATCH 6/7] contrib/credential: avoid fixed-size buffer in libsecret Taylor Blau
2023-05-01 15:54 ` Taylor Blau [this message]
2023-05-05 15:24 ` [PATCH 0/7] contrib/credential: avoid protocol injection attacks Derrick Stolee
2023-05-05 17:46 ` Taylor Blau
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=59a934a256a177e1d47f05a772f53f40ff015ced.1682956419.git.me@ttaylorr.com \
--to=me@ttaylorr.com \
--cc=Johannes.Schindelin@gmx.de \
--cc=derrickstolee@github.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=mjcheetham@outlook.com \
--cc=peff@peff.net \
/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;
as well as URLs for NNTP newsgroup(s).