Compare commits
24 Commits
Author | SHA256 | Date | |
---|---|---|---|
972e527526 | |||
29c41e30f3 | |||
d282f59a19 | |||
449f6b4529 | |||
25bd9d7139 | |||
c9ef1e03d7 | |||
cc31c19dde | |||
b54fb42c5f | |||
b812e06057 | |||
c7cd65c0b5 | |||
0b3439a103 | |||
81007fc644 | |||
9e8e6a8473 | |||
919cbfe376 | |||
3242813d6e | |||
616a142099 | |||
b6b6dcc2aa | |||
135bc78bbd | |||
|
4f638d656e | ||
4e674a788f | |||
64cea07b17 | |||
483dbc7bc8 | |||
e0ea61d6e0 | |||
b80926ddde |
98
CVE-2024-50349-1.patch
Normal file
98
CVE-2024-50349-1.patch
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
From c903985bf7e772e2d08275c1a95c8a55ab011577 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Johannes Schindelin <johannes.schindelin@gmx.de>
|
||||||
|
Date: Thu, 7 Nov 2024 08:57:52 +0100
|
||||||
|
Subject: [PATCH 1/2] credential_format(): also encode <host>[:<port>]
|
||||||
|
|
||||||
|
An upcoming change wants to sanitize the credential password prompt
|
||||||
|
where a URL is displayed that may potentially come from a `.gitmodules`
|
||||||
|
file. To this end, the `credential_format()` function is employed.
|
||||||
|
|
||||||
|
To sanitize the host name (and optional port) part of the URL, we need a
|
||||||
|
new mode of the `strbuf_add_percentencode()` function because the
|
||||||
|
current mode is both too strict and too lenient: too strict because it
|
||||||
|
encodes `:`, `[` and `]` (which should be left unencoded in
|
||||||
|
`<host>:<port>` and in IPv6 addresses), and too lenient because it does
|
||||||
|
not encode invalid host name characters `/`, `_` and `~`.
|
||||||
|
|
||||||
|
So let's introduce and use a new mode specifically to encode the host
|
||||||
|
name and optional port part of a URI, leaving alpha-numerical
|
||||||
|
characters, periods, colons and brackets alone and encoding all others.
|
||||||
|
|
||||||
|
This only leads to a change of behavior for URLs that contain invalid
|
||||||
|
host names.
|
||||||
|
|
||||||
|
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
|
||||||
|
---
|
||||||
|
credential.c | 3 ++-
|
||||||
|
strbuf.c | 4 +++-
|
||||||
|
strbuf.h | 1 +
|
||||||
|
t/t0300-credentials.sh | 13 +++++++++++++
|
||||||
|
4 files changed, 19 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/credential.c b/credential.c
|
||||||
|
index f32011343f..572f1785da 100644
|
||||||
|
--- a/credential.c
|
||||||
|
+++ b/credential.c
|
||||||
|
@@ -164,7 +164,8 @@ static void credential_format(struct credential *c, struct strbuf *out)
|
||||||
|
strbuf_addch(out, '@');
|
||||||
|
}
|
||||||
|
if (c->host)
|
||||||
|
- strbuf_addstr(out, c->host);
|
||||||
|
+ strbuf_add_percentencode(out, c->host,
|
||||||
|
+ STRBUF_ENCODE_HOST_AND_PORT);
|
||||||
|
if (c->path) {
|
||||||
|
strbuf_addch(out, '/');
|
||||||
|
strbuf_add_percentencode(out, c->path, 0);
|
||||||
|
diff --git a/strbuf.c b/strbuf.c
|
||||||
|
index c383f41a3c..756b96c561 100644
|
||||||
|
--- a/strbuf.c
|
||||||
|
+++ b/strbuf.c
|
||||||
|
@@ -492,7 +492,9 @@ void strbuf_add_percentencode(struct strbuf *dst, const char *src, int flags)
|
||||||
|
unsigned char ch = src[i];
|
||||||
|
if (ch <= 0x1F || ch >= 0x7F ||
|
||||||
|
(ch == '/' && (flags & STRBUF_ENCODE_SLASH)) ||
|
||||||
|
- strchr(URL_UNSAFE_CHARS, ch))
|
||||||
|
+ ((flags & STRBUF_ENCODE_HOST_AND_PORT) ?
|
||||||
|
+ !isalnum(ch) && !strchr("-.:[]", ch) :
|
||||||
|
+ !!strchr(URL_UNSAFE_CHARS, ch)))
|
||||||
|
strbuf_addf(dst, "%%%02X", (unsigned char)ch);
|
||||||
|
else
|
||||||
|
strbuf_addch(dst, ch);
|
||||||
|
diff --git a/strbuf.h b/strbuf.h
|
||||||
|
index f6dbb9681e..f9f8bb0381 100644
|
||||||
|
--- a/strbuf.h
|
||||||
|
+++ b/strbuf.h
|
||||||
|
@@ -380,6 +380,7 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb,
|
||||||
|
void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
|
||||||
|
|
||||||
|
#define STRBUF_ENCODE_SLASH 1
|
||||||
|
+#define STRBUF_ENCODE_HOST_AND_PORT 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the contents of a string to a strbuf, percent-encoding any characters
|
||||||
|
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
|
||||||
|
index c66d91e82d..cb91be1427 100755
|
||||||
|
--- a/t/t0300-credentials.sh
|
||||||
|
+++ b/t/t0300-credentials.sh
|
||||||
|
@@ -514,6 +514,19 @@ test_expect_success 'match percent-encoded values in username' '
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
+test_expect_success 'match percent-encoded values in hostname' '
|
||||||
|
+ test_config "credential.https://a%20b%20c/.helper" "$HELPER" &&
|
||||||
|
+ check fill <<-\EOF
|
||||||
|
+ url=https://a b c/
|
||||||
|
+ --
|
||||||
|
+ protocol=https
|
||||||
|
+ host=a b c
|
||||||
|
+ username=foo
|
||||||
|
+ password=bar
|
||||||
|
+ --
|
||||||
|
+ EOF
|
||||||
|
+'
|
||||||
|
+
|
||||||
|
test_expect_success 'fetch with multiple path components' '
|
||||||
|
test_unconfig credential.helper &&
|
||||||
|
test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
|
||||||
|
--
|
||||||
|
2.47.1
|
314
CVE-2024-50349-2.patch
Normal file
314
CVE-2024-50349-2.patch
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
From 7725b8100ffbbff2750ee4d61a0fcc1f53a086e8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Johannes Schindelin <johannes.schindelin@gmx.de>
|
||||||
|
Date: Wed, 30 Oct 2024 13:26:10 +0100
|
||||||
|
Subject: [PATCH 2/2] credential: sanitize the user prompt
|
||||||
|
|
||||||
|
When asking the user interactively for credentials, we want to avoid
|
||||||
|
misleading them e.g. via control sequences that pretend that the URL
|
||||||
|
targets a trusted host when it does not.
|
||||||
|
|
||||||
|
While Git learned, over the course of the preceding commits, to disallow
|
||||||
|
URLs containing URL-encoded control characters by default, credential
|
||||||
|
helpers are still allowed to specify values very freely (apart from Line
|
||||||
|
Feed and NUL characters, anything is allowed), and this would allow,
|
||||||
|
say, a username containing control characters to be specified that would
|
||||||
|
then be displayed in the interactive terminal prompt asking the user for
|
||||||
|
the password, potentially sending those control characters directly to
|
||||||
|
the terminal. This is undesirable because control characters can be used
|
||||||
|
to mislead users to divulge secret information to untrusted sites.
|
||||||
|
|
||||||
|
To prevent such an attack vector, let's add a `git_prompt()` that forces
|
||||||
|
the displayed text to be sanitized, i.e. displaying question marks
|
||||||
|
instead of control characters.
|
||||||
|
|
||||||
|
Note: While this commit's diff changes a lot of `user@host` strings to
|
||||||
|
`user%40host`, which may look suspicious on the surface, there is a good
|
||||||
|
reason for that: this string specifies a user name, not a
|
||||||
|
<username>@<hostname> combination! In the context of t5541, the actual
|
||||||
|
combination looks like this: `user%40@127.0.0.1:5541`. Therefore, these
|
||||||
|
string replacements document a net improvement introduced by this
|
||||||
|
commit, as `user@host@127.0.0.1` could have left readers wondering where
|
||||||
|
the user name ends and where the host name begins.
|
||||||
|
|
||||||
|
Hinted-at-by: Jeff King <peff@peff.net>
|
||||||
|
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
|
||||||
|
---
|
||||||
|
Documentation/config/credential.txt | 6 ++++++
|
||||||
|
credential.c | 7 ++++++-
|
||||||
|
credential.h | 4 +++-
|
||||||
|
t/t0300-credentials.sh | 20 ++++++++++++++++++++
|
||||||
|
t/t5541-http-push-smart.sh | 6 +++---
|
||||||
|
t/t5550-http-fetch-dumb.sh | 14 +++++++-------
|
||||||
|
t/t5551-http-fetch-smart.sh | 16 ++++++++--------
|
||||||
|
7 files changed, 53 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
Index: b/Documentation/config/credential.txt
|
||||||
|
===================================================================
|
||||||
|
--- a/Documentation/config/credential.txt
|
||||||
|
+++ b/Documentation/config/credential.txt
|
||||||
|
@@ -14,6 +14,12 @@ credential.useHttpPath::
|
||||||
|
or https URL to be important. Defaults to false. See
|
||||||
|
linkgit:gitcredentials[7] for more information.
|
||||||
|
|
||||||
|
+credential.sanitizePrompt::
|
||||||
|
+ By default, user names and hosts that are shown as part of the
|
||||||
|
+ password prompt are not allowed to contain control characters (they
|
||||||
|
+ will be URL-encoded by default). Configure this setting to `false` to
|
||||||
|
+ override that behavior.
|
||||||
|
+
|
||||||
|
credential.username::
|
||||||
|
If no username is set for a network authentication, use this username
|
||||||
|
by default. See credential.<context>.* below, and
|
||||||
|
Index: b/credential.c
|
||||||
|
===================================================================
|
||||||
|
--- a/credential.c
|
||||||
|
+++ b/credential.c
|
||||||
|
@@ -125,6 +125,8 @@ static int credential_config_callback(co
|
||||||
|
}
|
||||||
|
else if (!strcmp(key, "usehttppath"))
|
||||||
|
c->use_http_path = git_config_bool(var, value);
|
||||||
|
+ else if (!strcmp(key, "sanitizeprompt"))
|
||||||
|
+ c->sanitize_prompt = git_config_bool(var, value);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -237,7 +239,10 @@ static char *credential_ask_one(const ch
|
||||||
|
struct strbuf prompt = STRBUF_INIT;
|
||||||
|
char *r;
|
||||||
|
|
||||||
|
- credential_describe(c, &desc);
|
||||||
|
+ if (c->sanitize_prompt)
|
||||||
|
+ credential_format(c, &desc);
|
||||||
|
+ else
|
||||||
|
+ credential_describe(c, &desc);
|
||||||
|
if (desc.len)
|
||||||
|
strbuf_addf(&prompt, "%s for '%s': ", what, desc.buf);
|
||||||
|
else
|
||||||
|
Index: b/credential.h
|
||||||
|
===================================================================
|
||||||
|
--- a/credential.h
|
||||||
|
+++ b/credential.h
|
||||||
|
@@ -168,7 +168,8 @@ struct credential {
|
||||||
|
multistage: 1,
|
||||||
|
quit:1,
|
||||||
|
use_http_path:1,
|
||||||
|
- username_from_proto:1;
|
||||||
|
+ username_from_proto:1,
|
||||||
|
+ sanitize_prompt:1;
|
||||||
|
|
||||||
|
struct credential_capability capa_authtype;
|
||||||
|
struct credential_capability capa_state;
|
||||||
|
@@ -195,6 +196,7 @@ struct credential {
|
||||||
|
.wwwauth_headers = STRVEC_INIT, \
|
||||||
|
.state_headers = STRVEC_INIT, \
|
||||||
|
.state_headers_to_send = STRVEC_INIT, \
|
||||||
|
+ .sanitize_prompt = 1, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize a credential structure, setting all fields to empty. */
|
||||||
|
Index: b/t/t0300-credentials.sh
|
||||||
|
===================================================================
|
||||||
|
--- a/t/t0300-credentials.sh
|
||||||
|
+++ b/t/t0300-credentials.sh
|
||||||
|
@@ -77,6 +77,10 @@ test_expect_success 'setup helper script
|
||||||
|
test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
|
||||||
|
EOF
|
||||||
|
|
||||||
|
+ write_script git-credential-cntrl-in-username <<-\EOF &&
|
||||||
|
+ printf "username=\\007latrix Lestrange\\n"
|
||||||
|
+ EOF
|
||||||
|
+
|
||||||
|
PATH="$PWD:$PATH"
|
||||||
|
'
|
||||||
|
|
||||||
|
@@ -1008,4 +1012,20 @@ test_expect_success 'credential config w
|
||||||
|
test_grep "skipping credential lookup for key" stderr
|
||||||
|
'
|
||||||
|
|
||||||
|
+BEL="$(printf '\007')"
|
||||||
|
+
|
||||||
|
+test_expect_success 'interactive prompt is sanitized' '
|
||||||
|
+ check fill cntrl-in-username <<-EOF
|
||||||
|
+ protocol=https
|
||||||
|
+ host=example.org
|
||||||
|
+ --
|
||||||
|
+ protocol=https
|
||||||
|
+ host=example.org
|
||||||
|
+ username=${BEL}latrix Lestrange
|
||||||
|
+ password=askpass-password
|
||||||
|
+ --
|
||||||
|
+ askpass: Password for ${SQ}https://%07latrix%20Lestrange@example.org${SQ}:
|
||||||
|
+ EOF
|
||||||
|
+'
|
||||||
|
+
|
||||||
|
test_done
|
||||||
|
Index: b/t/t5541-http-push-smart.sh
|
||||||
|
===================================================================
|
||||||
|
--- a/t/t5541-http-push-smart.sh
|
||||||
|
+++ b/t/t5541-http-push-smart.sh
|
||||||
|
@@ -343,7 +343,7 @@ test_expect_success 'push over smart htt
|
||||||
|
git push "$HTTPD_URL"/auth/smart/test_repo.git &&
|
||||||
|
git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
|
||||||
|
log -1 --format=%s >actual &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
@@ -355,7 +355,7 @@ test_expect_success 'push to auth-only-f
|
||||||
|
git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
|
||||||
|
git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
|
||||||
|
log -1 --format=%s >actual &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
@@ -385,7 +385,7 @@ test_expect_success 'push into half-auth
|
||||||
|
git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
|
||||||
|
git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
|
||||||
|
log -1 --format=%s >actual &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
Index: b/t/t5550-http-fetch-dumb.sh
|
||||||
|
===================================================================
|
||||||
|
--- a/t/t5550-http-fetch-dumb.sh
|
||||||
|
+++ b/t/t5550-http-fetch-dumb.sh
|
||||||
|
@@ -111,13 +111,13 @@ test_expect_success 'http auth can use u
|
||||||
|
test_expect_success 'http auth can use just user in URL' '
|
||||||
|
set_askpass wrong pass@host &&
|
||||||
|
git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
|
||||||
|
- expect_askpass pass user@host
|
||||||
|
+ expect_askpass pass user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'http auth can request both user and pass' '
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
|
||||||
|
- expect_askpass both user@host
|
||||||
|
+ expect_askpass both user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'http auth respects credential helper config' '
|
||||||
|
@@ -135,14 +135,14 @@ test_expect_success 'http auth can get u
|
||||||
|
test_config_global "credential.$HTTPD_URL.username" user@host &&
|
||||||
|
set_askpass wrong pass@host &&
|
||||||
|
git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
|
||||||
|
- expect_askpass pass user@host
|
||||||
|
+ expect_askpass pass user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'configured username does not override URL' '
|
||||||
|
test_config_global "credential.$HTTPD_URL.username" wrong &&
|
||||||
|
set_askpass wrong pass@host &&
|
||||||
|
git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
|
||||||
|
- expect_askpass pass user@host
|
||||||
|
+ expect_askpass pass user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'set up repo with http submodules' '
|
||||||
|
@@ -163,7 +163,7 @@ test_expect_success 'cmdline credential
|
||||||
|
set_askpass wrong pass@host &&
|
||||||
|
git -c "credential.$HTTPD_URL.username=user@host" \
|
||||||
|
clone --recursive super super-clone &&
|
||||||
|
- expect_askpass pass user@host
|
||||||
|
+ expect_askpass pass user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cmdline credential config passes submodule via fetch' '
|
||||||
|
@@ -174,7 +174,7 @@ test_expect_success 'cmdline credential
|
||||||
|
git -C super-clone \
|
||||||
|
-c "credential.$HTTPD_URL.username=user@host" \
|
||||||
|
fetch --recurse-submodules &&
|
||||||
|
- expect_askpass pass user@host
|
||||||
|
+ expect_askpass pass user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cmdline credential config passes submodule update' '
|
||||||
|
@@ -191,7 +191,7 @@ test_expect_success 'cmdline credential
|
||||||
|
git -C super-clone \
|
||||||
|
-c "credential.$HTTPD_URL.username=user@host" \
|
||||||
|
submodule update &&
|
||||||
|
- expect_askpass pass user@host
|
||||||
|
+ expect_askpass pass user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fetch changes via http' '
|
||||||
|
Index: b/t/t5551-http-fetch-smart.sh
|
||||||
|
===================================================================
|
||||||
|
--- a/t/t5551-http-fetch-smart.sh
|
||||||
|
+++ b/t/t5551-http-fetch-smart.sh
|
||||||
|
@@ -181,7 +181,7 @@ test_expect_success 'clone from password
|
||||||
|
echo two >expect &&
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
git --git-dir=smart-auth log -1 --format=%s >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
@@ -199,7 +199,7 @@ test_expect_success 'clone from auth-onl
|
||||||
|
echo two >expect &&
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
git --git-dir=half-auth log -1 --format=%s >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
@@ -224,14 +224,14 @@ test_expect_success 'redirects send auth
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
git -c credential.useHttpPath=true \
|
||||||
|
clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
|
||||||
|
- expect_askpass both user@host auth/smart/repo.git
|
||||||
|
+ expect_askpass both user%40host auth/smart/repo.git
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'GIT_TRACE_CURL redacts auth details' '
|
||||||
|
rm -rf redact-auth trace &&
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
GIT_TRACE_CURL="$(pwd)/trace" git clone --bare "$HTTPD_URL/auth/smart/repo.git" redact-auth &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
|
||||||
|
# Ensure that there is no "Basic" followed by a base64 string, but that
|
||||||
|
# the auth details are redacted
|
||||||
|
@@ -243,7 +243,7 @@ test_expect_success 'GIT_CURL_VERBOSE re
|
||||||
|
rm -rf redact-auth trace &&
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
GIT_CURL_VERBOSE=1 git clone --bare "$HTTPD_URL/auth/smart/repo.git" redact-auth 2>trace &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
|
||||||
|
# Ensure that there is no "Basic" followed by a base64 string, but that
|
||||||
|
# the auth details are redacted
|
||||||
|
@@ -256,7 +256,7 @@ test_expect_success 'GIT_TRACE_CURL does
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
GIT_TRACE_REDACT=0 GIT_TRACE_CURL="$(pwd)/trace" \
|
||||||
|
git clone --bare "$HTTPD_URL/auth/smart/repo.git" redact-auth &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
|
||||||
|
grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace
|
||||||
|
'
|
||||||
|
@@ -570,7 +570,7 @@ test_expect_success 'http auth remembers
|
||||||
|
# the first request prompts the user...
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
git ls-remote "$HTTPD_URL/auth/smart/repo.git" >/dev/null &&
|
||||||
|
- expect_askpass both user@host &&
|
||||||
|
+ expect_askpass both user%40host &&
|
||||||
|
|
||||||
|
# ...and the second one uses the stored value rather than
|
||||||
|
# prompting the user.
|
||||||
|
@@ -601,7 +601,7 @@ test_expect_success 'http auth forgets b
|
||||||
|
# us to prompt the user again.
|
||||||
|
set_askpass user@host pass@host &&
|
||||||
|
git ls-remote "$HTTPD_URL/auth/smart/repo.git" >/dev/null &&
|
||||||
|
- expect_askpass both user@host
|
||||||
|
+ expect_askpass both user%40host
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'client falls back from v2 to v0 to match server' '
|
193
CVE-2024-52006.patch
Normal file
193
CVE-2024-52006.patch
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
From b01b9b81d36759cdcd07305e78765199e1bc2060 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Johannes Schindelin <johannes.schindelin@gmx.de>
|
||||||
|
Date: Mon, 4 Nov 2024 14:48:22 +0100
|
||||||
|
Subject: [PATCH] credential: disallow Carriage Returns in the protocol by
|
||||||
|
default
|
||||||
|
|
||||||
|
While Git has documented that the credential protocol is line-based,
|
||||||
|
with newlines as terminators, the exact shape of a newline has not been
|
||||||
|
documented.
|
||||||
|
|
||||||
|
From Git's perspective, which is firmly rooted in the Linux ecosystem,
|
||||||
|
it is clear that "a newline" means a Line Feed character.
|
||||||
|
|
||||||
|
However, even Git's credential protocol respects Windows line endings
|
||||||
|
(a Carriage Return character followed by a Line Feed character, "CR/LF")
|
||||||
|
by virtue of using `strbuf_getline()`.
|
||||||
|
|
||||||
|
There is a third category of line endings that has been used originally
|
||||||
|
by MacOS, and that is respected by the default line readers of .NET and
|
||||||
|
node.js: bare Carriage Returns.
|
||||||
|
|
||||||
|
Git cannot handle those, and what is worse: Git's remedy against
|
||||||
|
CVE-2020-5260 does not catch when credential helpers are used that
|
||||||
|
interpret bare Carriage Returns as newlines.
|
||||||
|
|
||||||
|
Git Credential Manager addressed this as CVE-2024-50338, but other
|
||||||
|
credential helpers may still be vulnerable. So let's not only disallow
|
||||||
|
Line Feed characters as part of the values in the credential protocol,
|
||||||
|
but also disallow Carriage Return characters.
|
||||||
|
|
||||||
|
In the unlikely event that a credential helper relies on Carriage
|
||||||
|
Returns in the protocol, introduce an escape hatch via the
|
||||||
|
`credential.protectProtocol` config setting.
|
||||||
|
|
||||||
|
This addresses CVE-2024-52006.
|
||||||
|
|
||||||
|
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
|
||||||
|
---
|
||||||
|
Documentation/config/credential.txt | 5 +++++
|
||||||
|
credential.c | 21 ++++++++++++++-------
|
||||||
|
credential.h | 4 +++-
|
||||||
|
t/t0300-credentials.sh | 16 ++++++++++++++++
|
||||||
|
4 files changed, 38 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
Index: b/Documentation/config/credential.txt
|
||||||
|
===================================================================
|
||||||
|
--- a/Documentation/config/credential.txt
|
||||||
|
+++ b/Documentation/config/credential.txt
|
||||||
|
@@ -20,6 +20,11 @@ credential.sanitizePrompt::
|
||||||
|
will be URL-encoded by default). Configure this setting to `false` to
|
||||||
|
override that behavior.
|
||||||
|
|
||||||
|
+credential.protectProtocol::
|
||||||
|
+ By default, Carriage Return characters are not allowed in the protocol
|
||||||
|
+ that is used when Git talks to a credential helper. This setting allows
|
||||||
|
+ users to override this default.
|
||||||
|
+
|
||||||
|
credential.username::
|
||||||
|
If no username is set for a network authentication, use this username
|
||||||
|
by default. See credential.<context>.* below, and
|
||||||
|
Index: b/credential.c
|
||||||
|
===================================================================
|
||||||
|
--- a/credential.c
|
||||||
|
+++ b/credential.c
|
||||||
|
@@ -127,6 +127,8 @@ static int credential_config_callback(co
|
||||||
|
c->use_http_path = git_config_bool(var, value);
|
||||||
|
else if (!strcmp(key, "sanitizeprompt"))
|
||||||
|
c->sanitize_prompt = git_config_bool(var, value);
|
||||||
|
+ else if (!strcmp(key, "protectprotocol"))
|
||||||
|
+ c->protect_protocol = git_config_bool(var, value);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -361,7 +363,8 @@ int credential_read(struct credential *c
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static void credential_write_item(FILE *fp, const char *key, const char *value,
|
||||||
|
+static void credential_write_item(const struct credential *c,
|
||||||
|
+ FILE *fp, const char *key, const char *value,
|
||||||
|
int required)
|
||||||
|
{
|
||||||
|
if (!value && required)
|
||||||
|
@@ -370,6 +373,10 @@ static void credential_write_item(FILE *
|
||||||
|
return;
|
||||||
|
if (strchr(value, '\n'))
|
||||||
|
die("credential value for %s contains newline", key);
|
||||||
|
+ if (c->protect_protocol && strchr(value, '\r'))
|
||||||
|
+ die("credential value for %s contains carriage return\n"
|
||||||
|
+ "If this is intended, set `credential.protectProtocol=false`",
|
||||||
|
+ key);
|
||||||
|
fprintf(fp, "%s=%s\n", key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -377,34 +384,34 @@ void credential_write(const struct crede
|
||||||
|
enum credential_op_type op_type)
|
||||||
|
{
|
||||||
|
if (credential_has_capability(&c->capa_authtype, op_type))
|
||||||
|
- credential_write_item(fp, "capability[]", "authtype", 0);
|
||||||
|
+ credential_write_item(c, fp, "capability[]", "authtype", 0);
|
||||||
|
if (credential_has_capability(&c->capa_state, op_type))
|
||||||
|
- credential_write_item(fp, "capability[]", "state", 0);
|
||||||
|
+ credential_write_item(c, fp, "capability[]", "state", 0);
|
||||||
|
|
||||||
|
if (credential_has_capability(&c->capa_authtype, op_type)) {
|
||||||
|
- credential_write_item(fp, "authtype", c->authtype, 0);
|
||||||
|
- credential_write_item(fp, "credential", c->credential, 0);
|
||||||
|
+ credential_write_item(c, fp, "authtype", c->authtype, 0);
|
||||||
|
+ credential_write_item(c, fp, "credential", c->credential, 0);
|
||||||
|
if (c->ephemeral)
|
||||||
|
- credential_write_item(fp, "ephemeral", "1", 0);
|
||||||
|
+ credential_write_item(c, fp, "ephemeral", "1", 0);
|
||||||
|
}
|
||||||
|
- credential_write_item(fp, "protocol", c->protocol, 1);
|
||||||
|
- credential_write_item(fp, "host", c->host, 1);
|
||||||
|
- credential_write_item(fp, "path", c->path, 0);
|
||||||
|
- credential_write_item(fp, "username", c->username, 0);
|
||||||
|
- credential_write_item(fp, "password", c->password, 0);
|
||||||
|
- credential_write_item(fp, "oauth_refresh_token", c->oauth_refresh_token, 0);
|
||||||
|
+ credential_write_item(c, fp, "protocol", c->protocol, 1);
|
||||||
|
+ credential_write_item(c, fp, "host", c->host, 1);
|
||||||
|
+ credential_write_item(c, fp, "path", c->path, 0);
|
||||||
|
+ credential_write_item(c, fp, "username", c->username, 0);
|
||||||
|
+ credential_write_item(c, fp, "password", c->password, 0);
|
||||||
|
+ credential_write_item(c, fp, "oauth_refresh_token", c->oauth_refresh_token, 0);
|
||||||
|
if (c->password_expiry_utc != TIME_MAX) {
|
||||||
|
char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
|
||||||
|
- credential_write_item(fp, "password_expiry_utc", s, 0);
|
||||||
|
+ credential_write_item(c, fp, "password_expiry_utc", s, 0);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
|
||||||
|
- credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
|
||||||
|
+ credential_write_item(c, fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
|
||||||
|
if (credential_has_capability(&c->capa_state, op_type)) {
|
||||||
|
if (c->multistage)
|
||||||
|
- credential_write_item(fp, "continue", "1", 0);
|
||||||
|
+ credential_write_item(c, fp, "continue", "1", 0);
|
||||||
|
for (size_t i = 0; i < c->state_headers_to_send.nr; i++)
|
||||||
|
- credential_write_item(fp, "state[]", c->state_headers_to_send.v[i], 0);
|
||||||
|
+ credential_write_item(c, fp, "state[]", c->state_headers_to_send.v[i], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Index: b/credential.h
|
||||||
|
===================================================================
|
||||||
|
--- a/credential.h
|
||||||
|
+++ b/credential.h
|
||||||
|
@@ -169,7 +169,8 @@ struct credential {
|
||||||
|
quit:1,
|
||||||
|
use_http_path:1,
|
||||||
|
username_from_proto:1,
|
||||||
|
- sanitize_prompt:1;
|
||||||
|
+ sanitize_prompt:1,
|
||||||
|
+ protect_protocol:1;
|
||||||
|
|
||||||
|
struct credential_capability capa_authtype;
|
||||||
|
struct credential_capability capa_state;
|
||||||
|
@@ -197,6 +198,7 @@ struct credential {
|
||||||
|
.state_headers = STRVEC_INIT, \
|
||||||
|
.state_headers_to_send = STRVEC_INIT, \
|
||||||
|
.sanitize_prompt = 1, \
|
||||||
|
+ .protect_protocol = 1, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize a credential structure, setting all fields to empty. */
|
||||||
|
Index: b/t/t0300-credentials.sh
|
||||||
|
===================================================================
|
||||||
|
--- a/t/t0300-credentials.sh
|
||||||
|
+++ b/t/t0300-credentials.sh
|
||||||
|
@@ -903,6 +903,22 @@ test_expect_success 'url parser rejects
|
||||||
|
test_cmp expect stderr
|
||||||
|
'
|
||||||
|
|
||||||
|
+test_expect_success 'url parser rejects embedded carriage returns' '
|
||||||
|
+ test_config credential.helper "!true" &&
|
||||||
|
+ test_must_fail git credential fill 2>stderr <<-\EOF &&
|
||||||
|
+ url=https://example%0d.com/
|
||||||
|
+ EOF
|
||||||
|
+ cat >expect <<-\EOF &&
|
||||||
|
+ fatal: credential value for host contains carriage return
|
||||||
|
+ If this is intended, set `credential.protectProtocol=false`
|
||||||
|
+ EOF
|
||||||
|
+ test_cmp expect stderr &&
|
||||||
|
+ GIT_ASKPASS=true \
|
||||||
|
+ git -c credential.protectProtocol=false credential fill <<-\EOF
|
||||||
|
+ url=https://example%0d.com/
|
||||||
|
+ EOF
|
||||||
|
+'
|
||||||
|
+
|
||||||
|
test_expect_success 'host-less URLs are parsed as empty host' '
|
||||||
|
check fill "verbatim foo bar" <<-\EOF
|
||||||
|
url=cert:///path/to/cert.pem
|
Binary file not shown.
BIN
git-2.46.0.tar.xz
(Stored with Git LFS)
BIN
git-2.46.0.tar.xz
(Stored with Git LFS)
Binary file not shown.
BIN
git-2.46.1.tar.sign
Normal file
BIN
git-2.46.1.tar.sign
Normal file
Binary file not shown.
BIN
git-2.46.1.tar.xz
(Stored with Git LFS)
Normal file
BIN
git-2.46.1.tar.xz
(Stored with Git LFS)
Normal file
Binary file not shown.
60
git.changes
60
git.changes
@@ -1,3 +1,63 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu Jan 16 22:29:07 UTC 2025 - Antonio Teixeira <antonio.teixeira@suse.com>
|
||||||
|
|
||||||
|
- Add CVE-2024-50349-1.patch, CVE-2024-50349-2.patch
|
||||||
|
* CVE-2024-50349: passwords for trusted sites could be sent to untrusted
|
||||||
|
sites (bsc#1235600)
|
||||||
|
- Add CVE-2024-52006.patch
|
||||||
|
* CVE-2024-52006: Carriage Returns via the credential protocol to credential
|
||||||
|
helpers (bsc#1235601)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri Sep 20 08:18:30 UTC 2024 - Dominique Leuenberger <dimstar@opensuse.org>
|
||||||
|
|
||||||
|
- Update to version 2.46.1;
|
||||||
|
* "git checkout --ours" (no other arguments) complained that the
|
||||||
|
option is incompatible with branch switching, which is technically
|
||||||
|
correct, but found confusing by some users. It now says that the
|
||||||
|
user needs to give pathspec to specify what paths to checkout.
|
||||||
|
* It has been documented that we avoid "VAR=VAL shell_func" and why.
|
||||||
|
* "git add -p" by users with diff.suppressBlankEmpty set to true
|
||||||
|
failed to parse the patch that represents an unmodified empty line
|
||||||
|
with an empty line (not a line with a single space on it), which
|
||||||
|
has been corrected.
|
||||||
|
* "git rebase --help" referred to "offset" (the difference between
|
||||||
|
the location a change was taken from and the change gets replaced)
|
||||||
|
incorrectly and called it "fuzz", which has been corrected.
|
||||||
|
* "git notes add -m '' --allow-empty" and friends that take prepared
|
||||||
|
data to create notes should not invoke an editor, but it started
|
||||||
|
doing so since Git 2.42, which has been corrected.
|
||||||
|
* An expensive operation to prepare tracing was done in re-encoding
|
||||||
|
code path even when the tracing was not requested, which has been
|
||||||
|
corrected.
|
||||||
|
* Perforce tests have been updated.
|
||||||
|
* The credential helper to talk to OSX keychain sometimes sent
|
||||||
|
garbage bytes after the username, which has been corrected.
|
||||||
|
* A recent update broke "git ls-remote" used outside a repository,
|
||||||
|
which has been corrected.
|
||||||
|
* "git config --value=foo --fixed-value section.key newvalue" barfed
|
||||||
|
when the existing value in the configuration file used the
|
||||||
|
valueless true syntax, which has been corrected.
|
||||||
|
* "git reflog expire" failed to honor annotated tags when computing
|
||||||
|
reachable commits.
|
||||||
|
* A flakey test and incorrect calls to strtoX() functions have been
|
||||||
|
fixed.
|
||||||
|
* Follow-up on 2.45.1 regression fix.
|
||||||
|
* "git rev-list ... | git diff-tree -p --remerge-diff --stdin" should
|
||||||
|
behave more or less like "git log -p --remerge-diff" but instead it
|
||||||
|
crashed, forgetting to prepare a temporary object store needed.
|
||||||
|
* The patch parser in "git patch-id" has been tightened to avoid
|
||||||
|
getting confused by lines that look like a patch header in the log
|
||||||
|
message.
|
||||||
|
* "git bundle unbundle" outside a repository triggered a BUG()
|
||||||
|
unnecessarily, which has been corrected.
|
||||||
|
* The code forgot to discard unnecessary in-core commit buffer data
|
||||||
|
for commits that "git log --skip=<number>" traversed but omitted
|
||||||
|
from the output, which has been corrected.
|
||||||
|
* "git verify-pack" and "git index-pack" started dying outside a
|
||||||
|
repository, which has been corrected.
|
||||||
|
* A corner case bug in "git stash" was fixed.
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Wed Aug 28 08:33:45 UTC 2024 - Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>
|
Wed Aug 28 08:33:45 UTC 2024 - Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>
|
||||||
|
|
||||||
|
9
git.spec
9
git.spec
@@ -43,7 +43,7 @@
|
|||||||
%bcond_with asciidoctor
|
%bcond_with asciidoctor
|
||||||
%endif
|
%endif
|
||||||
Name: git
|
Name: git
|
||||||
Version: 2.46.0
|
Version: 2.46.1
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: Fast, scalable, distributed revision control system
|
Summary: Fast, scalable, distributed revision control system
|
||||||
License: GPL-2.0-only
|
License: GPL-2.0-only
|
||||||
@@ -70,6 +70,13 @@ Patch8: git-asciidoc.patch
|
|||||||
Patch10: setup-don-t-fail-if-commondir-reference-is-deleted.patch
|
Patch10: setup-don-t-fail-if-commondir-reference-is-deleted.patch
|
||||||
# PATCH-FIX-OPENSUSE CVE-2024-24577.patch boo#1219660 antonio.teixeira@suse.com
|
# PATCH-FIX-OPENSUSE CVE-2024-24577.patch boo#1219660 antonio.teixeira@suse.com
|
||||||
Patch11: CVE-2024-24577.patch
|
Patch11: CVE-2024-24577.patch
|
||||||
|
# PATCH-FIX-UPSTREAM antonio.teixeira@suse.com bsc#1235600
|
||||||
|
# passwords for trusted sites could be sent to untrusted sites
|
||||||
|
Patch12: CVE-2024-50349-1.patch
|
||||||
|
Patch13: CVE-2024-50349-2.patch
|
||||||
|
# PATCH-FIX-UPSTREAM antonio.teixeira@suse.com bsc#1235601
|
||||||
|
# Carriage Returns via the credential protocol to credential helpers
|
||||||
|
Patch14: CVE-2024-52006.patch
|
||||||
BuildRequires: fdupes
|
BuildRequires: fdupes
|
||||||
BuildRequires: gpg2
|
BuildRequires: gpg2
|
||||||
BuildRequires: libcurl-devel
|
BuildRequires: libcurl-devel
|
||||||
|
Reference in New Issue
Block a user