diff --git a/7-bugfix-for-CVE-2024-50349.patch b/7-bugfix-for-CVE-2024-50349.patch new file mode 100644 index 0000000000000000000000000000000000000000..20a8add1e1a79beb2f8d5593723d36564e299387 --- /dev/null +++ b/7-bugfix-for-CVE-2024-50349.patch @@ -0,0 +1,313 @@ +From 7725b8100ffbbff2750ee4d61a0fcc1f53a086e8 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 30 Oct 2024 13:26:10 +0100 +Subject: [PATCH] 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 +@ 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 +Signed-off-by: Johannes Schindelin +--- + 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(-) + +diff --git a/Documentation/config/credential.txt b/Documentation/config/credential.txt +index 512f31876e17ed..fd8113d6d42f75 100644 +--- a/Documentation/config/credential.txt ++++ b/Documentation/config/credential.txt +@@ -25,6 +25,12 @@ credential.useHttpPath:: + credential.username:: + If no username is set for a network authentication, use this username + by default. See credential..* below, and ++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. ++ + linkgit:gitcredentials[7]. + + credential..*:: +diff --git a/credential.c b/credential.c +index 572f1785da7d3e..1392a54d5c2c3e 100644 +--- a/credential.c ++++ b/credential.c +@@ -21,6 +21,8 @@ static int credential_config_callback(const char *var, const char *value, + struct credential blank = CREDENTIAL_INIT; + memcpy(c, &blank, sizeof(*c)); + } ++ else if (!strcmp(key, "sanitizeprompt")) ++ c->sanitize_prompt = git_config_bool(var, value); + + void credential_clear(struct credential *c) + { +@@ -240,7 +242,10 @@ static char *credential_ask_one(const char *what, struct credential *c, + 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 +diff --git a/credential.h b/credential.h +index 935b28a70f16ec..0364d436d242d6 100644 +--- 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; +@@ -198,6 +198,7 @@ struct credential { + } + + /* Initialize a credential structure, setting all fields to empty. */ ++ .sanitize_prompt = 1, \ + void credential_init(struct credential *); + + /** +diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh +index cb91be1427f1d2..b62c70c193eff3 100755 +--- a/t/t0300-credentials.sh ++++ b/t/t0300-credentials.sh +@@ -77,6 +77,10 @@ test_expect_success 'setup helper scripts' ' + 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" + ' + +@@ -996,3 +996,19 @@ test_expect_success 'credential config with partial URLs' ' + ' + + test_done ++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 ++' ++ +diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh +index d0211cd8bef450..2cd2e1a0598760 100755 +--- a/t/t5541-http-push-smart.sh ++++ b/t/t5541-http-push-smart.sh +@@ -344,7 +344,7 @@ test_expect_success 'push over smart http with auth' ' + 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 + ' + +@@ -356,7 +356,7 @@ test_expect_success 'push to auth-only-for-push repo' ' + 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 + ' + +@@ -386,7 +386,7 @@ test_expect_success 'push into half-auth-complete requires password' ' + 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 + ' + +diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh +index 8f182a3cbfe73c..5d0e394609a08c 100755 +--- a/t/t5550-http-fetch-dumb.sh ++++ b/t/t5550-http-fetch-dumb.sh +@@ -112,13 +112,13 @@ test_expect_success 'http auth can use user/pass in URL' ' + 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' ' +@@ -136,14 +136,14 @@ test_expect_success 'http auth can get username from config' ' + 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' ' +@@ -164,7 +164,7 @@ test_expect_success 'cmdline credential config passes to submodule via clone' ' + 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' ' +@@ -175,7 +175,7 @@ test_expect_success 'cmdline credential config passes submodule via fetch' ' + 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' ' +@@ -192,7 +192,7 @@ test_expect_success 'cmdline credential config passes submodule update' ' + 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' ' +diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh +index 0908534f2561f4..8a27768dfba583 100755 +--- a/t/t5551-http-fetch-smart.sh ++++ b/t/t5551-http-fetch-smart.sh +@@ -182,7 +182,7 @@ test_expect_success 'clone from password-protected repository' ' + 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 + ' +@@ -222,7 +222,7 @@ test_expect_success 'clone from auth-only-for-objects repository' ' + 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 + ' +@@ -247,14 +247,14 @@ test_expect_success 'redirects send auth to new location' ' + 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 +@@ -266,7 +266,7 @@ test_expect_success 'GIT_CURL_VERBOSE redacts auth details' ' + 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 +@@ -279,7 +279,7 @@ test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_RE + 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 + ' +@@ -593,7 +593,7 @@ test_expect_success 'http auth remembers successful credentials' ' + # 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. +@@ -624,7 +624,7 @@ test_expect_success 'http auth forgets bogus credentials' ' + # 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' ' diff --git a/8-bugfix-for-CVE-2024-50349.patch b/8-bugfix-for-CVE-2024-50349.patch new file mode 100644 index 0000000000000000000000000000000000000000..ffe23e2d8a9210f98a9b1f727a6085c7b07fdf98 --- /dev/null +++ b/8-bugfix-for-CVE-2024-50349.patch @@ -0,0 +1,96 @@ +From c903985bf7e772e2d08275c1a95c8a55ab011577 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Thu, 7 Nov 2024 08:57:52 +0100 +Subject: [PATCH] credential_format(): also encode [:] + +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 +`:` 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 +--- + 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 f32011343f9400..572f1785da7d3e 100644 +--- a/credential.c ++++ b/credential.c +@@ -226,7 +226,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 c383f41a3c5ccc..756b96c56157c3 100644 +--- a/strbuf.c ++++ b/strbuf.c +@@ -495,7 +495,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 f6dbb9681ee768..f9f8bb0381b3c5 100644 +--- a/strbuf.h ++++ b/strbuf.h +@@ -359,6 +359,7 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb, + + /** + * Append the contents of a string to a strbuf, percent-encoding any characters ++#define STRBUF_ENCODE_HOST_AND_PORT 2 + * that are needed to be encoded for a URL. + * + * If STRBUF_ENCODE_SLASH is set in flags, percent-encode slashes. Otherwise, +diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh +index c66d91e82d8bc7..cb91be1427f1d2 100755 +--- a/t/t0300-credentials.sh ++++ b/t/t0300-credentials.sh +@@ -700,6 +700,19 @@ test_expect_success 'match percent-encoded values in username' ' + 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" && ++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 ++' ++ + check fill <<-\EOF + url=https://example.com/foo/repo.git + -- diff --git a/git.spec b/git.spec index 3a9a631fa4d7e0da4705dfd5df3918997aebce27..103ea0cdc1069d9048cc9fa592af20d0b6058657 100644 --- a/git.spec +++ b/git.spec @@ -1,4 +1,4 @@ -%define anolis_release 4 +%define anolis_release 5 %bcond_without docs %bcond_with linkcheck @@ -46,6 +46,8 @@ Patch4: git-test-apache-davlockdbtype-config.patch Patch5: 0005-bug-fix-CVE-2025-48386-avoid-buffer-overflow-in-wcsncat.patch # https://github.com/gitgitgadget/git/pull/1853 Patch6: 0004-backport-CVE-2024-52005.patch +Patch7: 7-bugfix-for-CVE-2024-50349.patch +Patch8: 8-bugfix-for-CVE-2024-50349.patch %if %{with docs} BuildRequires: /usr/bin/pod2man @@ -699,6 +701,9 @@ rmdir --ignore-fail-on-non-empty "$testdir" %changelog +* Fri Jul 11 2025 tomcruiseqi - 2.47.1-5 +- Fix CVE-2024-50349 + * Thu Jul 10 2025 wenxin - 2.47.1-4 - Add patch to fix CVE-2024-52005