From 9a6bbee8006c24b46a85d29e7b38cfa79e9ab21b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 11 Mar 2020 17:53:41 -0400 Subject: credential: avoid writing values with newlines The credential protocol that we use to speak to helpers can't represent values with newlines in them. This was an intentional design choice to keep the protocol simple, since none of the values we pass should generally have newlines. However, if we _do_ encounter a newline in a value, we blindly transmit it in credential_write(). Such values may break the protocol syntax, or worse, inject new valid lines into the protocol stream. The most likely way for a newline to end up in a credential struct is by decoding a URL with a percent-encoded newline. However, since the bug occurs at the moment we write the value to the protocol, we'll catch it there. That should leave no possibility of accidentally missing a code path that can trigger the problem. At this level of the code we have little choice but to die(). However, since we'd not ever expect to see this case outside of a malicious URL, that's an acceptable outcome. Reported-by: Felix Wilhelm --- t/t0300-credentials.sh | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 't') diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index 03bd31e9f2..15cc3c5abb 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -309,4 +309,10 @@ test_expect_success 'empty helper spec resets helper list' ' EOF ' +test_expect_success 'url parser rejects embedded newlines' ' + test_must_fail git credential fill <<-\EOF + url=https://one.example.com?%0ahost=two.example.com/ + EOF +' + test_done -- cgit v1.2.3 From 17f1c0b8c7e447aa62f85dc355bb48133d2812f2 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 11 Mar 2020 18:11:37 -0400 Subject: t/lib-credential: use test_i18ncmp to check stderr The credential tests have a "check" function which feeds some input to git-credential and checks the stdout and stderr. We look for exact matches in the output. For stdout, this makes sense; the output is the credential protocol. But for stderr, we may be showing various diagnostic messages, or the prompts fed to the askpass program, which could be translated. Let's mark them as such. --- t/lib-credential.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 't') diff --git a/t/lib-credential.sh b/t/lib-credential.sh index 937b831ea6..bb88cc0108 100755 --- a/t/lib-credential.sh +++ b/t/lib-credential.sh @@ -19,7 +19,7 @@ check() { false fi && test_cmp expect-stdout stdout && - test_cmp expect-stderr stderr + test_i18ncmp expect-stderr stderr } read_chunk() { -- cgit v1.2.3 From c716fe4bd917e013bf376a678b3a924447777b2d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 12 Mar 2020 01:31:11 -0400 Subject: credential: detect unrepresentable values when parsing urls The credential protocol can't represent newlines in values, but URLs can embed percent-encoded newlines in various components. A previous commit taught the low-level writing routines to die() when encountering this, but we can be a little friendlier to the user by detecting them earlier and handling them gracefully. This patch teaches credential_from_url() to notice such components, issue a warning, and blank the credential (which will generally result in prompting the user for a username and password). We blank the whole credential in this case. Another option would be to blank only the invalid component. However, we're probably better off not feeding a partially-parsed URL result to a credential helper. We don't know how a given helper would handle it, so we're better off to err on the side of matching nothing rather than something unexpected. The die() call in credential_write() is _probably_ impossible to reach after this patch. Values should end up in credential structs only by URL parsing (which is covered here), or by reading credential protocol input (which by definition cannot read a newline into a value). But we should definitely keep the low-level check, as it's our final and most accurate line of defense against protocol injection attacks. Arguably it could become a BUG(), but it probably doesn't matter much either way. Note that the public interface of credential_from_url() grows a little more than we need here. We'll use the extra flexibility in a future patch to help fsck catch these cases. --- t/t0300-credentials.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 't') diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index 15cc3c5abb..3bec445cac 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -309,9 +309,17 @@ test_expect_success 'empty helper spec resets helper list' ' EOF ' -test_expect_success 'url parser rejects embedded newlines' ' - test_must_fail git credential fill <<-\EOF +test_expect_success 'url parser ignores embedded newlines' ' + check fill <<-EOF url=https://one.example.com?%0ahost=two.example.com/ + -- + username=askpass-username + password=askpass-password + -- + warning: url contains a newline in its host component: https://one.example.com?%0ahost=two.example.com/ + warning: skipping credential lookup for url: https://one.example.com?%0ahost=two.example.com/ + askpass: Username: + askpass: Password: EOF ' -- cgit v1.2.3 From 07259e74ec1237c836874342c65650bdee8a3993 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 11 Mar 2020 18:48:24 -0400 Subject: fsck: detect gitmodules URLs with embedded newlines The credential protocol can't handle values with newlines. We already detect and block any such URLs from being used with credential helpers, but let's also add an fsck check to detect and block gitmodules files with such URLs. That will let us notice the problem earlier when transfer.fsckObjects is turned on. And in particular it will prevent bad objects from spreading, which may protect downstream users running older versions of Git. We'll file this under the existing gitmodulesUrl flag, which covers URLs with option injection. There's really no need to distinguish the exact flaw in the URL in this context. Likewise, I've expanded the description of t7416 to cover all types of bogus URLs. --- t/t7416-submodule-dash-url.sh | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 't') diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh index 5ba041f537..41431b1ac3 100755 --- a/t/t7416-submodule-dash-url.sh +++ b/t/t7416-submodule-dash-url.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='check handling of .gitmodule url with dash' +test_description='check handling of disallowed .gitmodule urls' . ./test-lib.sh test_expect_success 'create submodule with protected dash in url' ' @@ -60,4 +60,20 @@ test_expect_success 'trailing backslash is handled correctly' ' test_i18ngrep ! "unknown option" err ' +test_expect_success 'fsck rejects embedded newline in url' ' + # create an orphan branch to avoid existing .gitmodules objects + git checkout --orphan newline && + cat >.gitmodules <<-\EOF && + [submodule "foo"] + url = "https://one.example.com?%0ahost=two.example.com/foo.git" + EOF + git add .gitmodules && + git commit -m "gitmodules with newline" && + test_when_finished "rm -rf dst" && + git init --bare dst && + git -C dst config transfer.fsckObjects true && + test_must_fail git push dst HEAD 2>err && + grep gitmodulesUrl err +' + test_done -- cgit v1.2.3