diff options
13 files changed, 1667 insertions, 0 deletions
diff --git a/meta/recipes-devtools/git/git.inc b/meta/recipes-devtools/git/git.inc index 176423e972..a0ce1626a1 100644 --- a/meta/recipes-devtools/git/git.inc +++ b/meta/recipes-devtools/git/git.inc | |||
| @@ -9,6 +9,18 @@ PROVIDES_append_class-native = " git-replacement-native" | |||
| 9 | SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \ | 9 | SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \ |
| 10 | ${KERNELORG_MIRROR}/software/scm/git/git-manpages-${PV}.tar.gz;name=manpages \ | 10 | ${KERNELORG_MIRROR}/software/scm/git/git-manpages-${PV}.tar.gz;name=manpages \ |
| 11 | file://CVE-2020-5260.patch \ | 11 | file://CVE-2020-5260.patch \ |
| 12 | file://0001-t-lib-credential-use-test_i18ncmp-to-check-stderr.patch \ | ||
| 13 | file://0002-credential-detect-unrepresentable-values-when-parsin.patch \ | ||
| 14 | file://0003-fsck-detect-gitmodules-URLs-with-embedded-newlines.patch \ | ||
| 15 | file://CVE-2020-11008-1.patch \ | ||
| 16 | file://CVE-2020-11008-2.patch \ | ||
| 17 | file://CVE-2020-11008-3.patch \ | ||
| 18 | file://CVE-2020-11008-4.patch \ | ||
| 19 | file://CVE-2020-11008-5.patch \ | ||
| 20 | file://CVE-2020-11008-6.patch \ | ||
| 21 | file://CVE-2020-11008-7.patch \ | ||
| 22 | file://CVE-2020-11008-8.patch \ | ||
| 23 | file://CVE-2020-11008-9.patch \ | ||
| 12 | " | 24 | " |
| 13 | 25 | ||
| 14 | S = "${WORKDIR}/git-${PV}" | 26 | S = "${WORKDIR}/git-${PV}" |
diff --git a/meta/recipes-devtools/git/git/0001-t-lib-credential-use-test_i18ncmp-to-check-stderr.patch b/meta/recipes-devtools/git/git/0001-t-lib-credential-use-test_i18ncmp-to-check-stderr.patch new file mode 100644 index 0000000000..6eb3c16aef --- /dev/null +++ b/meta/recipes-devtools/git/git/0001-t-lib-credential-use-test_i18ncmp-to-check-stderr.patch | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | From 70ef9c6ce884b2d466d3d36563f1d2aa31b56443 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jeff King <peff@peff.net> | ||
| 3 | Date: Wed, 11 Mar 2020 18:11:37 -0400 | ||
| 4 | Subject: [PATCH 01/12] t/lib-credential: use test_i18ncmp to check stderr | ||
| 5 | |||
| 6 | The credential tests have a "check" function which feeds some input to | ||
| 7 | git-credential and checks the stdout and stderr. We look for exact | ||
| 8 | matches in the output. For stdout, this makes sense; the output is | ||
| 9 | the credential protocol. But for stderr, we may be showing various | ||
| 10 | diagnostic messages, or the prompts fed to the askpass program, which | ||
| 11 | could be translated. Let's mark them as such. | ||
| 12 | |||
| 13 | Upstream-Status: Backport | ||
| 14 | |||
| 15 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 16 | --- | ||
| 17 | t/lib-credential.sh | 2 +- | ||
| 18 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 19 | |||
| 20 | diff --git a/t/lib-credential.sh b/t/lib-credential.sh | ||
| 21 | index 937b831..bb88cc0 100755 | ||
| 22 | --- a/t/lib-credential.sh | ||
| 23 | +++ b/t/lib-credential.sh | ||
| 24 | @@ -19,7 +19,7 @@ check() { | ||
| 25 | false | ||
| 26 | fi && | ||
| 27 | test_cmp expect-stdout stdout && | ||
| 28 | - test_cmp expect-stderr stderr | ||
| 29 | + test_i18ncmp expect-stderr stderr | ||
| 30 | } | ||
| 31 | |||
| 32 | read_chunk() { | ||
| 33 | -- | ||
| 34 | 1.9.1 | ||
| 35 | |||
diff --git a/meta/recipes-devtools/git/git/0002-credential-detect-unrepresentable-values-when-parsin.patch b/meta/recipes-devtools/git/git/0002-credential-detect-unrepresentable-values-when-parsin.patch new file mode 100644 index 0000000000..a9b7348ef7 --- /dev/null +++ b/meta/recipes-devtools/git/git/0002-credential-detect-unrepresentable-values-when-parsin.patch | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | From 43803880b954a020dbffa5250a5b7fd893442c7c Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jeff King <peff@peff.net> | ||
| 3 | Date: Thu, 12 Mar 2020 01:31:11 -0400 | ||
| 4 | Subject: [PATCH 02/12] credential: detect unrepresentable values when parsing | ||
| 5 | urls | ||
| 6 | |||
| 7 | The credential protocol can't represent newlines in values, but URLs can | ||
| 8 | embed percent-encoded newlines in various components. A previous commit | ||
| 9 | taught the low-level writing routines to die() when encountering this, | ||
| 10 | but we can be a little friendlier to the user by detecting them earlier | ||
| 11 | and handling them gracefully. | ||
| 12 | |||
| 13 | This patch teaches credential_from_url() to notice such components, | ||
| 14 | issue a warning, and blank the credential (which will generally result | ||
| 15 | in prompting the user for a username and password). We blank the whole | ||
| 16 | credential in this case. Another option would be to blank only the | ||
| 17 | invalid component. However, we're probably better off not feeding a | ||
| 18 | partially-parsed URL result to a credential helper. We don't know how a | ||
| 19 | given helper would handle it, so we're better off to err on the side of | ||
| 20 | matching nothing rather than something unexpected. | ||
| 21 | |||
| 22 | The die() call in credential_write() is _probably_ impossible to reach | ||
| 23 | after this patch. Values should end up in credential structs only by URL | ||
| 24 | parsing (which is covered here), or by reading credential protocol input | ||
| 25 | (which by definition cannot read a newline into a value). But we should | ||
| 26 | definitely keep the low-level check, as it's our final and most accurate | ||
| 27 | line of defense against protocol injection attacks. Arguably it could | ||
| 28 | become a BUG(), but it probably doesn't matter much either way. | ||
| 29 | |||
| 30 | Note that the public interface of credential_from_url() grows a little | ||
| 31 | more than we need here. We'll use the extra flexibility in a future | ||
| 32 | patch to help fsck catch these cases. | ||
| 33 | |||
| 34 | Upstream-Status: Backport | ||
| 35 | |||
| 36 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 37 | --- | ||
| 38 | credential.c | 36 ++++++++++++++++++++++++++++++++++-- | ||
| 39 | credential.h | 16 ++++++++++++++++ | ||
| 40 | t/t0300-credentials.sh | 12 ++++++++++-- | ||
| 41 | 3 files changed, 60 insertions(+), 4 deletions(-) | ||
| 42 | |||
| 43 | diff --git a/credential.c b/credential.c | ||
| 44 | index a79aff0..2482382 100644 | ||
| 45 | --- a/credential.c | ||
| 46 | +++ b/credential.c | ||
| 47 | @@ -324,7 +324,22 @@ void credential_reject(struct credential *c) | ||
| 48 | c->approved = 0; | ||
| 49 | } | ||
| 50 | |||
| 51 | -void credential_from_url(struct credential *c, const char *url) | ||
| 52 | +static int check_url_component(const char *url, int quiet, | ||
| 53 | + const char *name, const char *value) | ||
| 54 | +{ | ||
| 55 | + if (!value) | ||
| 56 | + return 0; | ||
| 57 | + if (!strchr(value, '\n')) | ||
| 58 | + return 0; | ||
| 59 | + | ||
| 60 | + if (!quiet) | ||
| 61 | + warning(_("url contains a newline in its %s component: %s"), | ||
| 62 | + name, url); | ||
| 63 | + return -1; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +int credential_from_url_gently(struct credential *c, const char *url, | ||
| 67 | + int quiet) | ||
| 68 | { | ||
| 69 | const char *at, *colon, *cp, *slash, *host, *proto_end; | ||
| 70 | |||
| 71 | @@ -338,7 +353,7 @@ void credential_from_url(struct credential *c, const char *url) | ||
| 72 | */ | ||
| 73 | proto_end = strstr(url, "://"); | ||
| 74 | if (!proto_end) | ||
| 75 | - return; | ||
| 76 | + return 0; | ||
| 77 | cp = proto_end + 3; | ||
| 78 | at = strchr(cp, '@'); | ||
| 79 | colon = strchr(cp, ':'); | ||
| 80 | @@ -373,4 +388,21 @@ void credential_from_url(struct credential *c, const char *url) | ||
| 81 | while (p > c->path && *p == '/') | ||
| 82 | *p-- = '\0'; | ||
| 83 | } | ||
| 84 | + | ||
| 85 | + if (check_url_component(url, quiet, "username", c->username) < 0 || | ||
| 86 | + check_url_component(url, quiet, "password", c->password) < 0 || | ||
| 87 | + check_url_component(url, quiet, "protocol", c->protocol) < 0 || | ||
| 88 | + check_url_component(url, quiet, "host", c->host) < 0 || | ||
| 89 | + check_url_component(url, quiet, "path", c->path) < 0) | ||
| 90 | + return -1; | ||
| 91 | + | ||
| 92 | + return 0; | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +void credential_from_url(struct credential *c, const char *url) | ||
| 96 | +{ | ||
| 97 | + if (credential_from_url_gently(c, url, 0) < 0) { | ||
| 98 | + warning(_("skipping credential lookup for url: %s"), url); | ||
| 99 | + credential_clear(c); | ||
| 100 | + } | ||
| 101 | } | ||
| 102 | diff --git a/credential.h b/credential.h | ||
| 103 | index 6b0cd16..122a23c 100644 | ||
| 104 | --- a/credential.h | ||
| 105 | +++ b/credential.h | ||
| 106 | @@ -28,7 +28,23 @@ struct credential { | ||
| 107 | |||
| 108 | int credential_read(struct credential *, FILE *); | ||
| 109 | void credential_write(const struct credential *, FILE *); | ||
| 110 | + | ||
| 111 | +/* | ||
| 112 | + * Parse a url into a credential struct, replacing any existing contents. | ||
| 113 | + * | ||
| 114 | + * Ifthe url can't be parsed (e.g., a missing "proto://" component), the | ||
| 115 | + * resulting credential will be empty but we'll still return success from the | ||
| 116 | + * "gently" form. | ||
| 117 | + * | ||
| 118 | + * If we encounter a component which cannot be represented as a credential | ||
| 119 | + * value (e.g., because it contains a newline), the "gently" form will return | ||
| 120 | + * an error but leave the broken state in the credential object for further | ||
| 121 | + * examination. The non-gentle form will issue a warning to stderr and return | ||
| 122 | + * an empty credential. | ||
| 123 | + */ | ||
| 124 | void credential_from_url(struct credential *, const char *url); | ||
| 125 | +int credential_from_url_gently(struct credential *, const char *url, int quiet); | ||
| 126 | + | ||
| 127 | int credential_match(const struct credential *have, | ||
| 128 | const struct credential *want); | ||
| 129 | |||
| 130 | diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh | ||
| 131 | index 26f3c3a..b9c0f1f 100755 | ||
| 132 | --- a/t/t0300-credentials.sh | ||
| 133 | +++ b/t/t0300-credentials.sh | ||
| 134 | @@ -308,9 +308,17 @@ test_expect_success 'empty helper spec resets helper list' ' | ||
| 135 | EOF | ||
| 136 | ' | ||
| 137 | |||
| 138 | -test_expect_success 'url parser rejects embedded newlines' ' | ||
| 139 | - test_must_fail git credential fill <<-\EOF | ||
| 140 | +test_expect_success 'url parser ignores embedded newlines' ' | ||
| 141 | + check fill <<-EOF | ||
| 142 | url=https://one.example.com?%0ahost=two.example.com/ | ||
| 143 | + -- | ||
| 144 | + username=askpass-username | ||
| 145 | + password=askpass-password | ||
| 146 | + -- | ||
| 147 | + warning: url contains a newline in its host component: https://one.example.com?%0ahost=two.example.com/ | ||
| 148 | + warning: skipping credential lookup for url: https://one.example.com?%0ahost=two.example.com/ | ||
| 149 | + askpass: Username: | ||
| 150 | + askpass: Password: | ||
| 151 | EOF | ||
| 152 | ' | ||
| 153 | |||
| 154 | -- | ||
| 155 | 1.9.1 | ||
| 156 | |||
diff --git a/meta/recipes-devtools/git/git/0003-fsck-detect-gitmodules-URLs-with-embedded-newlines.patch b/meta/recipes-devtools/git/git/0003-fsck-detect-gitmodules-URLs-with-embedded-newlines.patch new file mode 100644 index 0000000000..23931e6313 --- /dev/null +++ b/meta/recipes-devtools/git/git/0003-fsck-detect-gitmodules-URLs-with-embedded-newlines.patch | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | From 1c9f8cedd34302575db40016231bdf502f17901e Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Li Zhou <li.zhou@windriver.com> | ||
| 3 | Date: Mon, 27 Apr 2020 13:49:39 +0800 | ||
| 4 | Subject: [PATCH 03/12] fsck: detect gitmodules URLs with embedded newlines | ||
| 5 | |||
| 6 | The credential protocol can't handle values with newlines. We already | ||
| 7 | detect and block any such URLs from being used with credential helpers, | ||
| 8 | but let's also add an fsck check to detect and block gitmodules files | ||
| 9 | with such URLs. That will let us notice the problem earlier when | ||
| 10 | transfer.fsckObjects is turned on. And in particular it will prevent bad | ||
| 11 | objects from spreading, which may protect downstream users running older | ||
| 12 | versions of Git. | ||
| 13 | |||
| 14 | We'll file this under the existing gitmodulesUrl flag, which covers URLs | ||
| 15 | with option injection. There's really no need to distinguish the exact | ||
| 16 | flaw in the URL in this context. Likewise, I've expanded the description | ||
| 17 | of t7416 to cover all types of bogus URLs. | ||
| 18 | |||
| 19 | Upstream-Status: Backport | ||
| 20 | |||
| 21 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 22 | --- | ||
| 23 | fsck.c | 16 +++++++++++++++- | ||
| 24 | t/t7416-submodule-dash-url.sh | 18 +++++++++++++++++- | ||
| 25 | 2 files changed, 32 insertions(+), 2 deletions(-) | ||
| 26 | |||
| 27 | diff --git a/fsck.c b/fsck.c | ||
| 28 | index ef8b343..ea46eea 100644 | ||
| 29 | --- a/fsck.c | ||
| 30 | +++ b/fsck.c | ||
| 31 | @@ -15,6 +15,7 @@ | ||
| 32 | #include "packfile.h" | ||
| 33 | #include "submodule-config.h" | ||
| 34 | #include "config.h" | ||
| 35 | +#include "credential.h" | ||
| 36 | #include "help.h" | ||
| 37 | |||
| 38 | static struct oidset gitmodules_found = OIDSET_INIT; | ||
| 39 | @@ -947,6 +948,19 @@ static int fsck_tag(struct tag *tag, const char *data, | ||
| 40 | return fsck_tag_buffer(tag, data, size, options); | ||
| 41 | } | ||
| 42 | |||
| 43 | +static int check_submodule_url(const char *url) | ||
| 44 | +{ | ||
| 45 | + struct credential c = CREDENTIAL_INIT; | ||
| 46 | + int ret; | ||
| 47 | + | ||
| 48 | + if (looks_like_command_line_option(url)) | ||
| 49 | + return -1; | ||
| 50 | + | ||
| 51 | + ret = credential_from_url_gently(&c, url, 1); | ||
| 52 | + credential_clear(&c); | ||
| 53 | + return ret; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | struct fsck_gitmodules_data { | ||
| 57 | struct object *obj; | ||
| 58 | struct fsck_options *options; | ||
| 59 | @@ -971,7 +985,7 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata) | ||
| 60 | "disallowed submodule name: %s", | ||
| 61 | name); | ||
| 62 | if (!strcmp(key, "url") && value && | ||
| 63 | - looks_like_command_line_option(value)) | ||
| 64 | + check_submodule_url(value) < 0) | ||
| 65 | data->ret |= report(data->options, data->obj, | ||
| 66 | FSCK_MSG_GITMODULES_URL, | ||
| 67 | "disallowed submodule url: %s", | ||
| 68 | diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh | ||
| 69 | index 5ba041f..41431b1 100755 | ||
| 70 | --- a/t/t7416-submodule-dash-url.sh | ||
| 71 | +++ b/t/t7416-submodule-dash-url.sh | ||
| 72 | @@ -1,6 +1,6 @@ | ||
| 73 | #!/bin/sh | ||
| 74 | |||
| 75 | -test_description='check handling of .gitmodule url with dash' | ||
| 76 | +test_description='check handling of disallowed .gitmodule urls' | ||
| 77 | . ./test-lib.sh | ||
| 78 | |||
| 79 | test_expect_success 'create submodule with protected dash in url' ' | ||
| 80 | @@ -60,4 +60,20 @@ test_expect_success 'trailing backslash is handled correctly' ' | ||
| 81 | test_i18ngrep ! "unknown option" err | ||
| 82 | ' | ||
| 83 | |||
| 84 | +test_expect_success 'fsck rejects embedded newline in url' ' | ||
| 85 | + # create an orphan branch to avoid existing .gitmodules objects | ||
| 86 | + git checkout --orphan newline && | ||
| 87 | + cat >.gitmodules <<-\EOF && | ||
| 88 | + [submodule "foo"] | ||
| 89 | + url = "https://one.example.com?%0ahost=two.example.com/foo.git" | ||
| 90 | + EOF | ||
| 91 | + git add .gitmodules && | ||
| 92 | + git commit -m "gitmodules with newline" && | ||
| 93 | + test_when_finished "rm -rf dst" && | ||
| 94 | + git init --bare dst && | ||
| 95 | + git -C dst config transfer.fsckObjects true && | ||
| 96 | + test_must_fail git push dst HEAD 2>err && | ||
| 97 | + grep gitmodulesUrl err | ||
| 98 | +' | ||
| 99 | + | ||
| 100 | test_done | ||
| 101 | -- | ||
| 102 | 1.9.1 | ||
| 103 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-1.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-1.patch new file mode 100644 index 0000000000..9cf98ea7b4 --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-1.patch | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | From 863f8067d8b4012904ca3bb881c659ac9894df97 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Li Zhou <li.zhou@windriver.com> | ||
| 3 | Date: Mon, 27 Apr 2020 14:36:03 +0800 | ||
| 4 | Subject: [PATCH 04/12] t0300: make "quit" helper more realistic | ||
| 5 | |||
| 6 | We test a toy credential helper that writes "quit=1" and confirms that | ||
| 7 | we stop running other helpers. However, that helper is unrealistic in | ||
| 8 | that it does not bother to read its stdin at all. | ||
| 9 | |||
| 10 | For now we don't send any input to it, because we feed git-credential a | ||
| 11 | blank credential. But that will change in the next patch, which will | ||
| 12 | cause this test to racily fail, as git-credential will get SIGPIPE | ||
| 13 | writing to the helper rather than exiting because it was asked to. | ||
| 14 | |||
| 15 | Let's make this one-off helper more like our other sample helpers, and | ||
| 16 | have it source the "dump" script. That will read stdin, fixing the | ||
| 17 | SIGPIPE problem. But it will also write what it sees to stderr. We can | ||
| 18 | make the test more robust by checking that output, which confirms that | ||
| 19 | we do run the quit helper, don't run any other helpers, and exit for the | ||
| 20 | reason we expected. | ||
| 21 | |||
| 22 | Signed-off-by: Jeff King <peff@peff.net> | ||
| 23 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 24 | |||
| 25 | Upstream-Status: Backport | ||
| 26 | CVE: CVE-2020-11008 (1) | ||
| 27 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 28 | --- | ||
| 29 | t/t0300-credentials.sh | 17 ++++++++++++++--- | ||
| 30 | 1 file changed, 14 insertions(+), 3 deletions(-) | ||
| 31 | |||
| 32 | diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh | ||
| 33 | index b9c0f1f..0206b3b 100755 | ||
| 34 | --- a/t/t0300-credentials.sh | ||
| 35 | +++ b/t/t0300-credentials.sh | ||
| 36 | @@ -22,6 +22,11 @@ test_expect_success 'setup helper scripts' ' | ||
| 37 | exit 0 | ||
| 38 | EOF | ||
| 39 | |||
| 40 | + write_script git-credential-quit <<-\EOF && | ||
| 41 | + . ./dump | ||
| 42 | + echo quit=1 | ||
| 43 | + EOF | ||
| 44 | + | ||
| 45 | write_script git-credential-verbatim <<-\EOF && | ||
| 46 | user=$1; shift | ||
| 47 | pass=$1; shift | ||
| 48 | @@ -291,10 +296,16 @@ test_expect_success 'http paths can be part of context' ' | ||
| 49 | |||
| 50 | test_expect_success 'helpers can abort the process' ' | ||
| 51 | test_must_fail git \ | ||
| 52 | - -c credential.helper="!f() { echo quit=1; }; f" \ | ||
| 53 | + -c credential.helper=quit \ | ||
| 54 | -c credential.helper="verbatim foo bar" \ | ||
| 55 | - credential fill >stdout && | ||
| 56 | - test_must_be_empty stdout | ||
| 57 | + credential fill >stdout 2>stderr && | ||
| 58 | + >expect && | ||
| 59 | + test_cmp expect stdout && | ||
| 60 | + cat >expect <<-\EOF && | ||
| 61 | + quit: get | ||
| 62 | + fatal: credential helper '\''quit'\'' told us to quit | ||
| 63 | + EOF | ||
| 64 | + test_i18ncmp expect stderr | ||
| 65 | ' | ||
| 66 | |||
| 67 | test_expect_success 'empty helper spec resets helper list' ' | ||
| 68 | -- | ||
| 69 | 1.9.1 | ||
| 70 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-2.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-2.patch new file mode 100644 index 0000000000..c752e3d431 --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-2.patch | |||
| @@ -0,0 +1,292 @@ | |||
| 1 | From 5588659069214aa0f7fea75a69687078e2f7a817 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jeff King <peff@peff.net> | ||
| 3 | Date: Sat, 18 Apr 2020 20:47:30 -0700 | ||
| 4 | Subject: [PATCH 05/12] t0300: use more realistic inputs | ||
| 5 | |||
| 6 | Many of the tests in t0300 give partial inputs to git-credential, | ||
| 7 | omitting a protocol or hostname. We're checking only high-level things | ||
| 8 | like whether and how helpers are invoked at all, and we don't care about | ||
| 9 | specific hosts. However, in preparation for tightening up the rules | ||
| 10 | about when we're willing to run a helper, let's start using input that's | ||
| 11 | a bit more realistic: pretend as if http://example.com is being | ||
| 12 | examined. | ||
| 13 | |||
| 14 | This shouldn't change the point of any of the tests, but do note we have | ||
| 15 | to adjust the expected output to accommodate this (filling a credential | ||
| 16 | will repeat back the protocol/host fields to stdout, and the helper | ||
| 17 | debug messages and askpass prompt will change on stderr). | ||
| 18 | |||
| 19 | Signed-off-by: Jeff King <peff@peff.net> | ||
| 20 | Reviewed-by: Taylor Blau <me@ttaylorr.com> | ||
| 21 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 22 | |||
| 23 | Upstream-Status: Backport | ||
| 24 | CVE: CVE-2020-11008 (2) | ||
| 25 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 26 | --- | ||
| 27 | t/t0300-credentials.sh | 89 +++++++++++++++++++++++++++++++++++++++++++++++--- | ||
| 28 | 1 file changed, 85 insertions(+), 4 deletions(-) | ||
| 29 | |||
| 30 | diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh | ||
| 31 | index 0206b3b..f4c5d7f 100755 | ||
| 32 | --- a/t/t0300-credentials.sh | ||
| 33 | +++ b/t/t0300-credentials.sh | ||
| 34 | @@ -40,43 +40,71 @@ test_expect_success 'setup helper scripts' ' | ||
| 35 | |||
| 36 | test_expect_success 'credential_fill invokes helper' ' | ||
| 37 | check fill "verbatim foo bar" <<-\EOF | ||
| 38 | + protocol=http | ||
| 39 | + host=example.com | ||
| 40 | -- | ||
| 41 | + protocol=http | ||
| 42 | + host=example.com | ||
| 43 | username=foo | ||
| 44 | password=bar | ||
| 45 | -- | ||
| 46 | verbatim: get | ||
| 47 | + verbatim: protocol=http | ||
| 48 | + verbatim: host=example.com | ||
| 49 | EOF | ||
| 50 | ' | ||
| 51 | |||
| 52 | test_expect_success 'credential_fill invokes multiple helpers' ' | ||
| 53 | check fill useless "verbatim foo bar" <<-\EOF | ||
| 54 | + protocol=http | ||
| 55 | + host=example.com | ||
| 56 | -- | ||
| 57 | + protocol=http | ||
| 58 | + host=example.com | ||
| 59 | username=foo | ||
| 60 | password=bar | ||
| 61 | -- | ||
| 62 | useless: get | ||
| 63 | + useless: protocol=http | ||
| 64 | + useless: host=example.com | ||
| 65 | verbatim: get | ||
| 66 | + verbatim: protocol=http | ||
| 67 | + verbatim: host=example.com | ||
| 68 | EOF | ||
| 69 | ' | ||
| 70 | |||
| 71 | test_expect_success 'credential_fill stops when we get a full response' ' | ||
| 72 | check fill "verbatim one two" "verbatim three four" <<-\EOF | ||
| 73 | + protocol=http | ||
| 74 | + host=example.com | ||
| 75 | -- | ||
| 76 | + protocol=http | ||
| 77 | + host=example.com | ||
| 78 | username=one | ||
| 79 | password=two | ||
| 80 | -- | ||
| 81 | verbatim: get | ||
| 82 | + verbatim: protocol=http | ||
| 83 | + verbatim: host=example.com | ||
| 84 | EOF | ||
| 85 | ' | ||
| 86 | |||
| 87 | test_expect_success 'credential_fill continues through partial response' ' | ||
| 88 | check fill "verbatim one \"\"" "verbatim two three" <<-\EOF | ||
| 89 | + protocol=http | ||
| 90 | + host=example.com | ||
| 91 | -- | ||
| 92 | + protocol=http | ||
| 93 | + host=example.com | ||
| 94 | username=two | ||
| 95 | password=three | ||
| 96 | -- | ||
| 97 | verbatim: get | ||
| 98 | + verbatim: protocol=http | ||
| 99 | + verbatim: host=example.com | ||
| 100 | verbatim: get | ||
| 101 | + verbatim: protocol=http | ||
| 102 | + verbatim: host=example.com | ||
| 103 | verbatim: username=one | ||
| 104 | EOF | ||
| 105 | ' | ||
| 106 | @@ -102,14 +130,20 @@ test_expect_success 'credential_fill passes along metadata' ' | ||
| 107 | |||
| 108 | test_expect_success 'credential_approve calls all helpers' ' | ||
| 109 | check approve useless "verbatim one two" <<-\EOF | ||
| 110 | + protocol=http | ||
| 111 | + host=example.com | ||
| 112 | username=foo | ||
| 113 | password=bar | ||
| 114 | -- | ||
| 115 | -- | ||
| 116 | useless: store | ||
| 117 | + useless: protocol=http | ||
| 118 | + useless: host=example.com | ||
| 119 | useless: username=foo | ||
| 120 | useless: password=bar | ||
| 121 | verbatim: store | ||
| 122 | + verbatim: protocol=http | ||
| 123 | + verbatim: host=example.com | ||
| 124 | verbatim: username=foo | ||
| 125 | verbatim: password=bar | ||
| 126 | EOF | ||
| 127 | @@ -117,6 +151,8 @@ test_expect_success 'credential_approve calls all helpers' ' | ||
| 128 | |||
| 129 | test_expect_success 'do not bother storing password-less credential' ' | ||
| 130 | check approve useless <<-\EOF | ||
| 131 | + protocol=http | ||
| 132 | + host=example.com | ||
| 133 | username=foo | ||
| 134 | -- | ||
| 135 | -- | ||
| 136 | @@ -126,14 +162,20 @@ test_expect_success 'do not bother storing password-less credential' ' | ||
| 137 | |||
| 138 | test_expect_success 'credential_reject calls all helpers' ' | ||
| 139 | check reject useless "verbatim one two" <<-\EOF | ||
| 140 | + protocol=http | ||
| 141 | + host=example.com | ||
| 142 | username=foo | ||
| 143 | password=bar | ||
| 144 | -- | ||
| 145 | -- | ||
| 146 | useless: erase | ||
| 147 | + useless: protocol=http | ||
| 148 | + useless: host=example.com | ||
| 149 | useless: username=foo | ||
| 150 | useless: password=bar | ||
| 151 | verbatim: erase | ||
| 152 | + verbatim: protocol=http | ||
| 153 | + verbatim: host=example.com | ||
| 154 | verbatim: username=foo | ||
| 155 | verbatim: password=bar | ||
| 156 | EOF | ||
| 157 | @@ -141,33 +183,49 @@ test_expect_success 'credential_reject calls all helpers' ' | ||
| 158 | |||
| 159 | test_expect_success 'usernames can be preserved' ' | ||
| 160 | check fill "verbatim \"\" three" <<-\EOF | ||
| 161 | + protocol=http | ||
| 162 | + host=example.com | ||
| 163 | username=one | ||
| 164 | -- | ||
| 165 | + protocol=http | ||
| 166 | + host=example.com | ||
| 167 | username=one | ||
| 168 | password=three | ||
| 169 | -- | ||
| 170 | verbatim: get | ||
| 171 | + verbatim: protocol=http | ||
| 172 | + verbatim: host=example.com | ||
| 173 | verbatim: username=one | ||
| 174 | EOF | ||
| 175 | ' | ||
| 176 | |||
| 177 | test_expect_success 'usernames can be overridden' ' | ||
| 178 | check fill "verbatim two three" <<-\EOF | ||
| 179 | + protocol=http | ||
| 180 | + host=example.com | ||
| 181 | username=one | ||
| 182 | -- | ||
| 183 | + protocol=http | ||
| 184 | + host=example.com | ||
| 185 | username=two | ||
| 186 | password=three | ||
| 187 | -- | ||
| 188 | verbatim: get | ||
| 189 | + verbatim: protocol=http | ||
| 190 | + verbatim: host=example.com | ||
| 191 | verbatim: username=one | ||
| 192 | EOF | ||
| 193 | ' | ||
| 194 | |||
| 195 | test_expect_success 'do not bother completing already-full credential' ' | ||
| 196 | check fill "verbatim three four" <<-\EOF | ||
| 197 | + protocol=http | ||
| 198 | + host=example.com | ||
| 199 | username=one | ||
| 200 | password=two | ||
| 201 | -- | ||
| 202 | + protocol=http | ||
| 203 | + host=example.com | ||
| 204 | username=one | ||
| 205 | password=two | ||
| 206 | -- | ||
| 207 | @@ -179,23 +237,31 @@ test_expect_success 'do not bother completing already-full credential' ' | ||
| 208 | # askpass helper is run, we know the internal getpass is working. | ||
| 209 | test_expect_success 'empty helper list falls back to internal getpass' ' | ||
| 210 | check fill <<-\EOF | ||
| 211 | + protocol=http | ||
| 212 | + host=example.com | ||
| 213 | -- | ||
| 214 | + protocol=http | ||
| 215 | + host=example.com | ||
| 216 | username=askpass-username | ||
| 217 | password=askpass-password | ||
| 218 | -- | ||
| 219 | - askpass: Username: | ||
| 220 | - askpass: Password: | ||
| 221 | + askpass: Username for '\''http://example.com'\'': | ||
| 222 | + askpass: Password for '\''http://askpass-username@example.com'\'': | ||
| 223 | EOF | ||
| 224 | ' | ||
| 225 | |||
| 226 | test_expect_success 'internal getpass does not ask for known username' ' | ||
| 227 | check fill <<-\EOF | ||
| 228 | + protocol=http | ||
| 229 | + host=example.com | ||
| 230 | username=foo | ||
| 231 | -- | ||
| 232 | + protocol=http | ||
| 233 | + host=example.com | ||
| 234 | username=foo | ||
| 235 | password=askpass-password | ||
| 236 | -- | ||
| 237 | - askpass: Password: | ||
| 238 | + askpass: Password for '\''http://foo@example.com'\'': | ||
| 239 | EOF | ||
| 240 | ' | ||
| 241 | |||
| 242 | @@ -207,7 +273,11 @@ HELPER="!f() { | ||
| 243 | test_expect_success 'respect configured credentials' ' | ||
| 244 | test_config credential.helper "$HELPER" && | ||
| 245 | check fill <<-\EOF | ||
| 246 | + protocol=http | ||
| 247 | + host=example.com | ||
| 248 | -- | ||
| 249 | + protocol=http | ||
| 250 | + host=example.com | ||
| 251 | username=foo | ||
| 252 | password=bar | ||
| 253 | -- | ||
| 254 | @@ -298,11 +368,16 @@ test_expect_success 'helpers can abort the process' ' | ||
| 255 | test_must_fail git \ | ||
| 256 | -c credential.helper=quit \ | ||
| 257 | -c credential.helper="verbatim foo bar" \ | ||
| 258 | - credential fill >stdout 2>stderr && | ||
| 259 | + credential fill >stdout 2>stderr <<-\EOF && | ||
| 260 | + protocol=http | ||
| 261 | + host=example.com | ||
| 262 | + EOF | ||
| 263 | >expect && | ||
| 264 | test_cmp expect stdout && | ||
| 265 | cat >expect <<-\EOF && | ||
| 266 | quit: get | ||
| 267 | + quit: protocol=http | ||
| 268 | + quit: host=example.com | ||
| 269 | fatal: credential helper '\''quit'\'' told us to quit | ||
| 270 | EOF | ||
| 271 | test_i18ncmp expect stderr | ||
| 272 | @@ -311,11 +386,17 @@ test_expect_success 'helpers can abort the process' ' | ||
| 273 | test_expect_success 'empty helper spec resets helper list' ' | ||
| 274 | test_config credential.helper "verbatim file file" && | ||
| 275 | check fill "" "verbatim cmdline cmdline" <<-\EOF | ||
| 276 | + protocol=http | ||
| 277 | + host=example.com | ||
| 278 | -- | ||
| 279 | + protocol=http | ||
| 280 | + host=example.com | ||
| 281 | username=cmdline | ||
| 282 | password=cmdline | ||
| 283 | -- | ||
| 284 | verbatim: get | ||
| 285 | + verbatim: protocol=http | ||
| 286 | + verbatim: host=example.com | ||
| 287 | EOF | ||
| 288 | ' | ||
| 289 | |||
| 290 | -- | ||
| 291 | 1.9.1 | ||
| 292 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-3.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-3.patch new file mode 100644 index 0000000000..c17e883d6c --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-3.patch | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | From 22f28251ae575dd7a60f7a46853469025d004ca7 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jeff King <peff@peff.net> | ||
| 3 | Date: Sat, 18 Apr 2020 20:48:05 -0700 | ||
| 4 | Subject: [PATCH 06/12] credential: parse URL without host as empty host, not | ||
| 5 | unset | ||
| 6 | |||
| 7 | We may feed a URL like "cert:///path/to/cert.pem" into the credential | ||
| 8 | machinery to get the key for a client-side certificate. That | ||
| 9 | credential has no hostname field, which is about to be disallowed (to | ||
| 10 | avoid confusion with protocols where a helper _would_ expect a | ||
| 11 | hostname). | ||
| 12 | |||
| 13 | This means as of the next patch, credential helpers won't work for | ||
| 14 | unlocking certs. Let's fix that by doing two things: | ||
| 15 | |||
| 16 | - when we parse a url with an empty host, set the host field to the | ||
| 17 | empty string (asking only to match stored entries with an empty | ||
| 18 | host) rather than NULL (asking to match _any_ host). | ||
| 19 | |||
| 20 | - when we build a cert:// credential by hand, similarly assign an | ||
| 21 | empty string | ||
| 22 | |||
| 23 | It's the latter that is more likely to impact real users in practice, | ||
| 24 | since it's what's used for http connections. But we don't have good | ||
| 25 | infrastructure to test it. | ||
| 26 | |||
| 27 | The url-parsing version will help anybody using git-credential in a | ||
| 28 | script, and is easy to test. | ||
| 29 | |||
| 30 | Signed-off-by: Jeff King <peff@peff.net> | ||
| 31 | Reviewed-by: Taylor Blau <me@ttaylorr.com> | ||
| 32 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 33 | |||
| 34 | Upstream-Status: Backport | ||
| 35 | CVE: CVE-2020-11008 (3) | ||
| 36 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 37 | --- | ||
| 38 | credential.c | 3 +-- | ||
| 39 | http.c | 1 + | ||
| 40 | t/t0300-credentials.sh | 17 +++++++++++++++++ | ||
| 41 | 3 files changed, 19 insertions(+), 2 deletions(-) | ||
| 42 | |||
| 43 | diff --git a/credential.c b/credential.c | ||
| 44 | index 2482382..f2413ce 100644 | ||
| 45 | --- a/credential.c | ||
| 46 | +++ b/credential.c | ||
| 47 | @@ -376,8 +376,7 @@ int credential_from_url_gently(struct credential *c, const char *url, | ||
| 48 | |||
| 49 | if (proto_end - url > 0) | ||
| 50 | c->protocol = xmemdupz(url, proto_end - url); | ||
| 51 | - if (slash - host > 0) | ||
| 52 | - c->host = url_decode_mem(host, slash - host); | ||
| 53 | + c->host = url_decode_mem(host, slash - host); | ||
| 54 | /* Trim leading and trailing slashes from path */ | ||
| 55 | while (*slash == '/') | ||
| 56 | slash++; | ||
| 57 | diff --git a/http.c b/http.c | ||
| 58 | index 27aa0a3..c4dfdac 100644 | ||
| 59 | --- a/http.c | ||
| 60 | +++ b/http.c | ||
| 61 | @@ -558,6 +558,7 @@ static int has_cert_password(void) | ||
| 62 | return 0; | ||
| 63 | if (!cert_auth.password) { | ||
| 64 | cert_auth.protocol = xstrdup("cert"); | ||
| 65 | + cert_auth.host = xstrdup(""); | ||
| 66 | cert_auth.username = xstrdup(""); | ||
| 67 | cert_auth.path = xstrdup(ssl_cert); | ||
| 68 | credential_fill(&cert_auth); | ||
| 69 | diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh | ||
| 70 | index f4c5d7f..1c1010b 100755 | ||
| 71 | --- a/t/t0300-credentials.sh | ||
| 72 | +++ b/t/t0300-credentials.sh | ||
| 73 | @@ -414,4 +414,21 @@ test_expect_success 'url parser ignores embedded newlines' ' | ||
| 74 | EOF | ||
| 75 | ' | ||
| 76 | |||
| 77 | +test_expect_success 'host-less URLs are parsed as empty host' ' | ||
| 78 | + check fill "verbatim foo bar" <<-\EOF | ||
| 79 | + url=cert:///path/to/cert.pem | ||
| 80 | + -- | ||
| 81 | + protocol=cert | ||
| 82 | + host= | ||
| 83 | + path=path/to/cert.pem | ||
| 84 | + username=foo | ||
| 85 | + password=bar | ||
| 86 | + -- | ||
| 87 | + verbatim: get | ||
| 88 | + verbatim: protocol=cert | ||
| 89 | + verbatim: host= | ||
| 90 | + verbatim: path=path/to/cert.pem | ||
| 91 | + EOF | ||
| 92 | +' | ||
| 93 | + | ||
| 94 | test_done | ||
| 95 | -- | ||
| 96 | 1.9.1 | ||
| 97 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-4.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-4.patch new file mode 100644 index 0000000000..14e23466d4 --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-4.patch | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | From f8bf7099379990ad974c1ca8f51e1f28bf18cf2a Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jeff King <peff@peff.net> | ||
| 3 | Date: Sat, 18 Apr 2020 20:50:48 -0700 | ||
| 4 | Subject: [PATCH 07/12] credential: refuse to operate when missing host or | ||
| 5 | protocol | ||
| 6 | |||
| 7 | The credential helper protocol was designed to be very flexible: the | ||
| 8 | fields it takes as input are treated as a pattern, and any missing | ||
| 9 | fields are taken as wildcards. This allows unusual things like: | ||
| 10 | |||
| 11 | echo protocol=https | git credential reject | ||
| 12 | |||
| 13 | to delete all stored https credentials (assuming the helpers themselves | ||
| 14 | treat the input that way). But when helpers are invoked automatically by | ||
| 15 | Git, this flexibility works against us. If for whatever reason we don't | ||
| 16 | have a "host" field, then we'd match _any_ host. When you're filling a | ||
| 17 | credential to send to a remote server, this is almost certainly not what | ||
| 18 | you want. | ||
| 19 | |||
| 20 | Prevent this at the layer that writes to the credential helper. Add a | ||
| 21 | check to the credential API that the host and protocol are always passed | ||
| 22 | in, and add an assertion to the credential_write function that speaks | ||
| 23 | credential helper protocol to be doubly sure. | ||
| 24 | |||
| 25 | There are a few ways this can be triggered in practice: | ||
| 26 | |||
| 27 | - the "git credential" command passes along arbitrary credential | ||
| 28 | parameters it reads from stdin. | ||
| 29 | |||
| 30 | - until the previous patch, when the host field of a URL is empty, we | ||
| 31 | would leave it unset (rather than setting it to the empty string) | ||
| 32 | |||
| 33 | - a URL like "example.com/foo.git" is treated by curl as if "http://" | ||
| 34 | was present, but our parser sees it as a non-URL and leaves all | ||
| 35 | fields unset | ||
| 36 | |||
| 37 | - the recent fix for URLs with embedded newlines blanks the URL but | ||
| 38 | otherwise continues. Rather than having the desired effect of | ||
| 39 | looking up no credential at all, many helpers will return _any_ | ||
| 40 | credential | ||
| 41 | |||
| 42 | Our earlier test for an embedded newline didn't catch this because it | ||
| 43 | only checked that the credential was cleared, but didn't configure an | ||
| 44 | actual helper. Configuring the "verbatim" helper in the test would show | ||
| 45 | that it is invoked (it's obviously a silly helper which doesn't look at | ||
| 46 | its input, but the point is that it shouldn't be run at all). Since | ||
| 47 | we're switching this case to die(), we don't need to bother with a | ||
| 48 | helper. We can see the new behavior just by checking that the operation | ||
| 49 | fails. | ||
| 50 | |||
| 51 | We'll add new tests covering partial input as well (these can be | ||
| 52 | triggered through various means with url-parsing, but it's simpler to | ||
| 53 | just check them directly, as we know we are covered even if the url | ||
| 54 | parser changes behavior in the future). | ||
| 55 | |||
| 56 | [jn: changed to die() instead of logging and showing a manual | ||
| 57 | username/password prompt] | ||
| 58 | |||
| 59 | Reported-by: Carlo Arenas <carenas@gmail.com> | ||
| 60 | Signed-off-by: Jeff King <peff@peff.net> | ||
| 61 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 62 | |||
| 63 | Upstream-Status: Backport | ||
| 64 | CVE: CVE-2020-11008 (4) | ||
| 65 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 66 | --- | ||
| 67 | credential.c | 20 ++++++++++++++------ | ||
| 68 | t/t0300-credentials.sh | 34 ++++++++++++++++++++++++++-------- | ||
| 69 | 2 files changed, 40 insertions(+), 14 deletions(-) | ||
| 70 | |||
| 71 | diff --git a/credential.c b/credential.c | ||
| 72 | index f2413ce..e08ed84 100644 | ||
| 73 | --- a/credential.c | ||
| 74 | +++ b/credential.c | ||
| 75 | @@ -89,6 +89,11 @@ static int proto_is_http(const char *s) | ||
| 76 | |||
| 77 | static void credential_apply_config(struct credential *c) | ||
| 78 | { | ||
| 79 | + if (!c->host) | ||
| 80 | + die(_("refusing to work with credential missing host field")); | ||
| 81 | + if (!c->protocol) | ||
| 82 | + die(_("refusing to work with credential missing protocol field")); | ||
| 83 | + | ||
| 84 | if (c->configured) | ||
| 85 | return; | ||
| 86 | git_config(credential_config_callback, c); | ||
| 87 | @@ -191,8 +196,11 @@ int credential_read(struct credential *c, FILE *fp) | ||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | -static void credential_write_item(FILE *fp, const char *key, const char *value) | ||
| 92 | +static void credential_write_item(FILE *fp, const char *key, const char *value, | ||
| 93 | + int required) | ||
| 94 | { | ||
| 95 | + if (!value && required) | ||
| 96 | + BUG("credential value for %s is missing", key); | ||
| 97 | if (!value) | ||
| 98 | return; | ||
| 99 | if (strchr(value, '\n')) | ||
| 100 | @@ -202,11 +210,11 @@ static void credential_write_item(FILE *fp, const char *key, const char *value) | ||
| 101 | |||
| 102 | void credential_write(const struct credential *c, FILE *fp) | ||
| 103 | { | ||
| 104 | - credential_write_item(fp, "protocol", c->protocol); | ||
| 105 | - credential_write_item(fp, "host", c->host); | ||
| 106 | - credential_write_item(fp, "path", c->path); | ||
| 107 | - credential_write_item(fp, "username", c->username); | ||
| 108 | - credential_write_item(fp, "password", c->password); | ||
| 109 | + credential_write_item(fp, "protocol", c->protocol, 1); | ||
| 110 | + credential_write_item(fp, "host", c->host, 1); | ||
| 111 | + credential_write_item(fp, "path", c->path, 0); | ||
| 112 | + credential_write_item(fp, "username", c->username, 0); | ||
| 113 | + credential_write_item(fp, "password", c->password, 0); | ||
| 114 | } | ||
| 115 | |||
| 116 | static int run_credential_helper(struct credential *c, | ||
| 117 | diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh | ||
| 118 | index 1c1010b..646f845 100755 | ||
| 119 | --- a/t/t0300-credentials.sh | ||
| 120 | +++ b/t/t0300-credentials.sh | ||
| 121 | @@ -400,18 +400,16 @@ test_expect_success 'empty helper spec resets helper list' ' | ||
| 122 | EOF | ||
| 123 | ' | ||
| 124 | |||
| 125 | -test_expect_success 'url parser ignores embedded newlines' ' | ||
| 126 | - check fill <<-EOF | ||
| 127 | +test_expect_success 'url parser rejects embedded newlines' ' | ||
| 128 | + test_must_fail git credential fill 2>stderr <<-\EOF && | ||
| 129 | url=https://one.example.com?%0ahost=two.example.com/ | ||
| 130 | - -- | ||
| 131 | - username=askpass-username | ||
| 132 | - password=askpass-password | ||
| 133 | - -- | ||
| 134 | + EOF | ||
| 135 | + cat >expect <<-\EOF && | ||
| 136 | warning: url contains a newline in its host component: https://one.example.com?%0ahost=two.example.com/ | ||
| 137 | warning: skipping credential lookup for url: https://one.example.com?%0ahost=two.example.com/ | ||
| 138 | - askpass: Username: | ||
| 139 | - askpass: Password: | ||
| 140 | + fatal: refusing to work with credential missing host field | ||
| 141 | EOF | ||
| 142 | + test_i18ncmp expect stderr | ||
| 143 | ' | ||
| 144 | |||
| 145 | test_expect_success 'host-less URLs are parsed as empty host' ' | ||
| 146 | @@ -431,4 +429,24 @@ test_expect_success 'host-less URLs are parsed as empty host' ' | ||
| 147 | EOF | ||
| 148 | ' | ||
| 149 | |||
| 150 | +test_expect_success 'credential system refuses to work with missing host' ' | ||
| 151 | + test_must_fail git credential fill 2>stderr <<-\EOF && | ||
| 152 | + protocol=http | ||
| 153 | + EOF | ||
| 154 | + cat >expect <<-\EOF && | ||
| 155 | + fatal: refusing to work with credential missing host field | ||
| 156 | + EOF | ||
| 157 | + test_i18ncmp expect stderr | ||
| 158 | +' | ||
| 159 | + | ||
| 160 | +test_expect_success 'credential system refuses to work with missing protocol' ' | ||
| 161 | + test_must_fail git credential fill 2>stderr <<-\EOF && | ||
| 162 | + host=example.com | ||
| 163 | + EOF | ||
| 164 | + cat >expect <<-\EOF && | ||
| 165 | + fatal: refusing to work with credential missing protocol field | ||
| 166 | + EOF | ||
| 167 | + test_i18ncmp expect stderr | ||
| 168 | +' | ||
| 169 | + | ||
| 170 | test_done | ||
| 171 | -- | ||
| 172 | 1.9.1 | ||
| 173 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-5.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-5.patch new file mode 100644 index 0000000000..60f8d59082 --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-5.patch | |||
| @@ -0,0 +1,211 @@ | |||
| 1 | From 3431abe8c0f64f4049a31298c0b1056baa7d81dc Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Li Zhou <li.zhou@windriver.com> | ||
| 3 | Date: Mon, 27 Apr 2020 14:45:49 +0800 | ||
| 4 | Subject: [PATCH 08/12] fsck: convert gitmodules url to URL passed to curl | ||
| 5 | |||
| 6 | In 07259e74ec1 (fsck: detect gitmodules URLs with embedded newlines, | ||
| 7 | 2020-03-11), git fsck learned to check whether URLs in .gitmodules could | ||
| 8 | be understood by the credential machinery when they are handled by | ||
| 9 | git-remote-curl. | ||
| 10 | |||
| 11 | However, the check is overbroad: it checks all URLs instead of only | ||
| 12 | URLs that would be passed to git-remote-curl. In principle a git:// or | ||
| 13 | file:/// URL does not need to follow the same conventions as an http:// | ||
| 14 | URL; in particular, git:// and file:// protocols are not succeptible to | ||
| 15 | issues in the credential API because they do not support attaching | ||
| 16 | credentials. | ||
| 17 | |||
| 18 | In the HTTP case, the URL in .gitmodules does not always match the URL | ||
| 19 | that would be passed to git-remote-curl and the credential machinery: | ||
| 20 | Git's URL syntax allows specifying a remote helper followed by a "::" | ||
| 21 | delimiter and a URL to be passed to it, so that | ||
| 22 | |||
| 23 | git ls-remote http::https://example.com/repo.git | ||
| 24 | |||
| 25 | invokes git-remote-http with https://example.com/repo.git as its URL | ||
| 26 | argument. With today's checks, that distinction does not make a | ||
| 27 | difference, but for a check we are about to introduce (for empty URL | ||
| 28 | schemes) it will matter. | ||
| 29 | |||
| 30 | .gitmodules files also support relative URLs. To ensure coverage for the | ||
| 31 | https based embedded-newline attack, urldecode and check them directly | ||
| 32 | for embedded newlines. | ||
| 33 | |||
| 34 | Helped-by: Jeff King <peff@peff.net> | ||
| 35 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 36 | Reviewed-by: Jeff King <peff@peff.net> | ||
| 37 | |||
| 38 | Upstream-Status: Backport | ||
| 39 | CVE: CVE-2020-11008 (5) | ||
| 40 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 41 | --- | ||
| 42 | fsck.c | 94 ++++++++++++++++++++++++++++++++++++++++--- | ||
| 43 | t/t7416-submodule-dash-url.sh | 29 +++++++++++++ | ||
| 44 | 2 files changed, 118 insertions(+), 5 deletions(-) | ||
| 45 | |||
| 46 | diff --git a/fsck.c b/fsck.c | ||
| 47 | index ea46eea..0f21eb1 100644 | ||
| 48 | --- a/fsck.c | ||
| 49 | +++ b/fsck.c | ||
| 50 | @@ -9,6 +9,7 @@ | ||
| 51 | #include "tag.h" | ||
| 52 | #include "fsck.h" | ||
| 53 | #include "refs.h" | ||
| 54 | +#include "url.h" | ||
| 55 | #include "utf8.h" | ||
| 56 | #include "decorate.h" | ||
| 57 | #include "oidset.h" | ||
| 58 | @@ -948,17 +949,100 @@ static int fsck_tag(struct tag *tag, const char *data, | ||
| 59 | return fsck_tag_buffer(tag, data, size, options); | ||
| 60 | } | ||
| 61 | |||
| 62 | +/* | ||
| 63 | + * Like builtin/submodule--helper.c's starts_with_dot_slash, but without | ||
| 64 | + * relying on the platform-dependent is_dir_sep helper. | ||
| 65 | + * | ||
| 66 | + * This is for use in checking whether a submodule URL is interpreted as | ||
| 67 | + * relative to the current directory on any platform, since \ is a | ||
| 68 | + * directory separator on Windows but not on other platforms. | ||
| 69 | + */ | ||
| 70 | +static int starts_with_dot_slash(const char *str) | ||
| 71 | +{ | ||
| 72 | + return str[0] == '.' && (str[1] == '/' || str[1] == '\\'); | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +/* | ||
| 76 | + * Like starts_with_dot_slash, this is a variant of submodule--helper's | ||
| 77 | + * helper of the same name with the twist that it accepts backslash as a | ||
| 78 | + * directory separator even on non-Windows platforms. | ||
| 79 | + */ | ||
| 80 | +static int starts_with_dot_dot_slash(const char *str) | ||
| 81 | +{ | ||
| 82 | + return str[0] == '.' && starts_with_dot_slash(str + 1); | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +static int submodule_url_is_relative(const char *url) | ||
| 86 | +{ | ||
| 87 | + return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url); | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +/* | ||
| 91 | + * Check whether a transport is implemented by git-remote-curl. | ||
| 92 | + * | ||
| 93 | + * If it is, returns 1 and writes the URL that would be passed to | ||
| 94 | + * git-remote-curl to the "out" parameter. | ||
| 95 | + * | ||
| 96 | + * Otherwise, returns 0 and leaves "out" untouched. | ||
| 97 | + * | ||
| 98 | + * Examples: | ||
| 99 | + * http::https://example.com/repo.git -> 1, https://example.com/repo.git | ||
| 100 | + * https://example.com/repo.git -> 1, https://example.com/repo.git | ||
| 101 | + * git://example.com/repo.git -> 0 | ||
| 102 | + * | ||
| 103 | + * This is for use in checking for previously exploitable bugs that | ||
| 104 | + * required a submodule URL to be passed to git-remote-curl. | ||
| 105 | + */ | ||
| 106 | +static int url_to_curl_url(const char *url, const char **out) | ||
| 107 | +{ | ||
| 108 | + /* | ||
| 109 | + * We don't need to check for case-aliases, "http.exe", and so | ||
| 110 | + * on because in the default configuration, is_transport_allowed | ||
| 111 | + * prevents URLs with those schemes from being cloned | ||
| 112 | + * automatically. | ||
| 113 | + */ | ||
| 114 | + if (skip_prefix(url, "http::", out) || | ||
| 115 | + skip_prefix(url, "https::", out) || | ||
| 116 | + skip_prefix(url, "ftp::", out) || | ||
| 117 | + skip_prefix(url, "ftps::", out)) | ||
| 118 | + return 1; | ||
| 119 | + if (starts_with(url, "http://") || | ||
| 120 | + starts_with(url, "https://") || | ||
| 121 | + starts_with(url, "ftp://") || | ||
| 122 | + starts_with(url, "ftps://")) { | ||
| 123 | + *out = url; | ||
| 124 | + return 1; | ||
| 125 | + } | ||
| 126 | + return 0; | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | static int check_submodule_url(const char *url) | ||
| 130 | { | ||
| 131 | - struct credential c = CREDENTIAL_INIT; | ||
| 132 | - int ret; | ||
| 133 | + const char *curl_url; | ||
| 134 | |||
| 135 | if (looks_like_command_line_option(url)) | ||
| 136 | return -1; | ||
| 137 | |||
| 138 | - ret = credential_from_url_gently(&c, url, 1); | ||
| 139 | - credential_clear(&c); | ||
| 140 | - return ret; | ||
| 141 | + if (submodule_url_is_relative(url)) { | ||
| 142 | + /* | ||
| 143 | + * This could be appended to an http URL and url-decoded; | ||
| 144 | + * check for malicious characters. | ||
| 145 | + */ | ||
| 146 | + char *decoded = url_decode(url); | ||
| 147 | + int has_nl = !!strchr(decoded, '\n'); | ||
| 148 | + free(decoded); | ||
| 149 | + if (has_nl) | ||
| 150 | + return -1; | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + else if (url_to_curl_url(url, &curl_url)) { | ||
| 154 | + struct credential c = CREDENTIAL_INIT; | ||
| 155 | + int ret = credential_from_url_gently(&c, curl_url, 1); | ||
| 156 | + credential_clear(&c); | ||
| 157 | + return ret; | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | struct fsck_gitmodules_data { | ||
| 164 | diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh | ||
| 165 | index 41431b1..afdd255 100755 | ||
| 166 | --- a/t/t7416-submodule-dash-url.sh | ||
| 167 | +++ b/t/t7416-submodule-dash-url.sh | ||
| 168 | @@ -60,6 +60,20 @@ test_expect_success 'trailing backslash is handled correctly' ' | ||
| 169 | test_i18ngrep ! "unknown option" err | ||
| 170 | ' | ||
| 171 | |||
| 172 | +test_expect_success 'fsck permits embedded newline with unrecognized scheme' ' | ||
| 173 | + git checkout --orphan newscheme && | ||
| 174 | + cat >.gitmodules <<-\EOF && | ||
| 175 | + [submodule "foo"] | ||
| 176 | + url = "data://acjbkd%0akajfdickajkd" | ||
| 177 | + EOF | ||
| 178 | + git add .gitmodules && | ||
| 179 | + git commit -m "gitmodules with unrecognized scheme" && | ||
| 180 | + test_when_finished "rm -rf dst" && | ||
| 181 | + git init --bare dst && | ||
| 182 | + git -C dst config transfer.fsckObjects true && | ||
| 183 | + git push dst HEAD | ||
| 184 | +' | ||
| 185 | + | ||
| 186 | test_expect_success 'fsck rejects embedded newline in url' ' | ||
| 187 | # create an orphan branch to avoid existing .gitmodules objects | ||
| 188 | git checkout --orphan newline && | ||
| 189 | @@ -76,4 +90,19 @@ test_expect_success 'fsck rejects embedded newline in url' ' | ||
| 190 | grep gitmodulesUrl err | ||
| 191 | ' | ||
| 192 | |||
| 193 | +test_expect_success 'fsck rejects embedded newline in relative url' ' | ||
| 194 | + git checkout --orphan relative-newline && | ||
| 195 | + cat >.gitmodules <<-\EOF && | ||
| 196 | + [submodule "foo"] | ||
| 197 | + url = "./%0ahost=two.example.com/foo.git" | ||
| 198 | + EOF | ||
| 199 | + git add .gitmodules && | ||
| 200 | + git commit -m "relative url with newline" && | ||
| 201 | + test_when_finished "rm -rf dst" && | ||
| 202 | + git init --bare dst && | ||
| 203 | + git -C dst config transfer.fsckObjects true && | ||
| 204 | + test_must_fail git push dst HEAD 2>err && | ||
| 205 | + grep gitmodulesUrl err | ||
| 206 | +' | ||
| 207 | + | ||
| 208 | test_done | ||
| 209 | -- | ||
| 210 | 1.9.1 | ||
| 211 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-6.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-6.patch new file mode 100644 index 0000000000..6b36893030 --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-6.patch | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | From 883508bcebe87fbe7fb7392272e930c27c30fdc2 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jeff King <peff@peff.net> | ||
| 3 | Date: Sat, 18 Apr 2020 20:53:09 -0700 | ||
| 4 | Subject: [PATCH 09/12] credential: die() when parsing invalid urls | ||
| 5 | |||
| 6 | When we try to initialize credential loading by URL and find that the | ||
| 7 | URL is invalid, we set all fields to NULL in order to avoid acting on | ||
| 8 | malicious input. Later when we request credentials, we diagonse the | ||
| 9 | erroneous input: | ||
| 10 | |||
| 11 | fatal: refusing to work with credential missing host field | ||
| 12 | |||
| 13 | This is problematic in two ways: | ||
| 14 | |||
| 15 | - The message doesn't tell the user *why* we are missing the host | ||
| 16 | field, so they can't tell from this message alone how to recover. | ||
| 17 | There can be intervening messages after the original warning of | ||
| 18 | bad input, so the user may not have the context to put two and two | ||
| 19 | together. | ||
| 20 | |||
| 21 | - The error only occurs when we actually need to get a credential. If | ||
| 22 | the URL permits anonymous access, the only encouragement the user gets | ||
| 23 | to correct their bogus URL is a quiet warning. | ||
| 24 | |||
| 25 | This is inconsistent with the check we perform in fsck, where any use | ||
| 26 | of such a URL as a submodule is an error. | ||
| 27 | |||
| 28 | When we see such a bogus URL, let's not try to be nice and continue | ||
| 29 | without helpers. Instead, die() immediately. This is simpler and | ||
| 30 | obviously safe. And there's very little chance of disrupting a normal | ||
| 31 | workflow. | ||
| 32 | |||
| 33 | It's _possible_ that somebody has a legitimate URL with a raw newline in | ||
| 34 | it. It already wouldn't work with credential helpers, so this patch | ||
| 35 | steps that up from an inconvenience to "we will refuse to work with it | ||
| 36 | at all". If such a case does exist, we should figure out a way to work | ||
| 37 | with it (especially if the newline is only in the path component, which | ||
| 38 | we normally don't even pass to helpers). But until we see a real report, | ||
| 39 | we're better off being defensive. | ||
| 40 | |||
| 41 | Reported-by: Carlo Arenas <carenas@gmail.com> | ||
| 42 | Signed-off-by: Jeff King <peff@peff.net> | ||
| 43 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 44 | |||
| 45 | Upstream-Status: Backport | ||
| 46 | CVE: CVE-2020-11008 (6) | ||
| 47 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 48 | --- | ||
| 49 | credential.c | 6 ++---- | ||
| 50 | t/t0300-credentials.sh | 3 +-- | ||
| 51 | 2 files changed, 3 insertions(+), 6 deletions(-) | ||
| 52 | |||
| 53 | diff --git a/credential.c b/credential.c | ||
| 54 | index e08ed84..22649d5 100644 | ||
| 55 | --- a/credential.c | ||
| 56 | +++ b/credential.c | ||
| 57 | @@ -408,8 +408,6 @@ int credential_from_url_gently(struct credential *c, const char *url, | ||
| 58 | |||
| 59 | void credential_from_url(struct credential *c, const char *url) | ||
| 60 | { | ||
| 61 | - if (credential_from_url_gently(c, url, 0) < 0) { | ||
| 62 | - warning(_("skipping credential lookup for url: %s"), url); | ||
| 63 | - credential_clear(c); | ||
| 64 | - } | ||
| 65 | + if (credential_from_url_gently(c, url, 0) < 0) | ||
| 66 | + die(_("credential url cannot be parsed: %s"), url); | ||
| 67 | } | ||
| 68 | diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh | ||
| 69 | index 646f845..efed3ea 100755 | ||
| 70 | --- a/t/t0300-credentials.sh | ||
| 71 | +++ b/t/t0300-credentials.sh | ||
| 72 | @@ -406,8 +406,7 @@ test_expect_success 'url parser rejects embedded newlines' ' | ||
| 73 | EOF | ||
| 74 | cat >expect <<-\EOF && | ||
| 75 | warning: url contains a newline in its host component: https://one.example.com?%0ahost=two.example.com/ | ||
| 76 | - warning: skipping credential lookup for url: https://one.example.com?%0ahost=two.example.com/ | ||
| 77 | - fatal: refusing to work with credential missing host field | ||
| 78 | + fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/ | ||
| 79 | EOF | ||
| 80 | test_i18ncmp expect stderr | ||
| 81 | ' | ||
| 82 | -- | ||
| 83 | 1.9.1 | ||
| 84 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-7.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-7.patch new file mode 100644 index 0000000000..5e3b6f1454 --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-7.patch | |||
| @@ -0,0 +1,206 @@ | |||
| 1 | From 68acf8724e9cb2f67664dd980581c0022401daf0 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jonathan Nieder <jrnieder@gmail.com> | ||
| 3 | Date: Sat, 18 Apr 2020 20:54:13 -0700 | ||
| 4 | Subject: [PATCH 10/12] credential: treat URL without scheme as invalid | ||
| 5 | |||
| 6 | libcurl permits making requests without a URL scheme specified. In | ||
| 7 | this case, it guesses the URL from the hostname, so I can run | ||
| 8 | |||
| 9 | git ls-remote http::ftp.example.com/path/to/repo | ||
| 10 | |||
| 11 | and it would make an FTP request. | ||
| 12 | |||
| 13 | Any user intentionally using such a URL is likely to have made a typo. | ||
| 14 | Unfortunately, credential_from_url is not able to determine the host and | ||
| 15 | protocol in order to determine appropriate credentials to send, and | ||
| 16 | until "credential: refuse to operate when missing host or protocol", | ||
| 17 | this resulted in another host's credentials being leaked to the named | ||
| 18 | host. | ||
| 19 | |||
| 20 | Teach credential_from_url_gently to consider such a URL to be invalid | ||
| 21 | so that fsck can detect and block gitmodules files with such URLs, | ||
| 22 | allowing server operators to avoid serving them to downstream users | ||
| 23 | running older versions of Git. | ||
| 24 | |||
| 25 | This also means that when such URLs are passed on the command line, Git | ||
| 26 | will print a clearer error so affected users can switch to the simpler | ||
| 27 | URL that explicitly specifies the host and protocol they intend. | ||
| 28 | |||
| 29 | One subtlety: .gitmodules files can contain relative URLs, representing | ||
| 30 | a URL relative to the URL they were cloned from. The relative URL | ||
| 31 | resolver used for .gitmodules can follow ".." components out of the path | ||
| 32 | part and past the host part of a URL, meaning that such a relative URL | ||
| 33 | can be used to traverse from a https://foo.example.com/innocent | ||
| 34 | superproject to a https::attacker.example.com/exploit submodule. | ||
| 35 | Fortunately a leading ':' in the first path component after a series of | ||
| 36 | leading './' and '../' components is unlikely to show up in other | ||
| 37 | contexts, so we can catch this by detecting that pattern. | ||
| 38 | |||
| 39 | Reported-by: Jeff King <peff@peff.net> | ||
| 40 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 41 | Reviewed-by: Jeff King <peff@peff.net> | ||
| 42 | |||
| 43 | Upstream-Status: Backport | ||
| 44 | CVE: CVE-2020-11008 (7) | ||
| 45 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 46 | --- | ||
| 47 | credential.c | 7 +++++-- | ||
| 48 | fsck.c | 47 +++++++++++++++++++++++++++++++++++++++++-- | ||
| 49 | t/t5550-http-fetch-dumb.sh | 7 ++----- | ||
| 50 | t/t7416-submodule-dash-url.sh | 32 +++++++++++++++++++++++++++++ | ||
| 51 | 4 files changed, 84 insertions(+), 9 deletions(-) | ||
| 52 | |||
| 53 | diff --git a/credential.c b/credential.c | ||
| 54 | index 22649d5..1e1aed5 100644 | ||
| 55 | --- a/credential.c | ||
| 56 | +++ b/credential.c | ||
| 57 | @@ -360,8 +360,11 @@ int credential_from_url_gently(struct credential *c, const char *url, | ||
| 58 | * (3) proto://<user>:<pass>@<host>/... | ||
| 59 | */ | ||
| 60 | proto_end = strstr(url, "://"); | ||
| 61 | - if (!proto_end) | ||
| 62 | - return 0; | ||
| 63 | + if (!proto_end) { | ||
| 64 | + if (!quiet) | ||
| 65 | + warning(_("url has no scheme: %s"), url); | ||
| 66 | + return -1; | ||
| 67 | + } | ||
| 68 | cp = proto_end + 3; | ||
| 69 | at = strchr(cp, '@'); | ||
| 70 | colon = strchr(cp, ':'); | ||
| 71 | diff --git a/fsck.c b/fsck.c | ||
| 72 | index 0f21eb1..30eac29 100644 | ||
| 73 | --- a/fsck.c | ||
| 74 | +++ b/fsck.c | ||
| 75 | @@ -978,6 +978,34 @@ static int submodule_url_is_relative(const char *url) | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | + * Count directory components that a relative submodule URL should chop | ||
| 80 | + * from the remote_url it is to be resolved against. | ||
| 81 | + * | ||
| 82 | + * In other words, this counts "../" components at the start of a | ||
| 83 | + * submodule URL. | ||
| 84 | + * | ||
| 85 | + * Returns the number of directory components to chop and writes a | ||
| 86 | + * pointer to the next character of url after all leading "./" and | ||
| 87 | + * "../" components to out. | ||
| 88 | + */ | ||
| 89 | +static int count_leading_dotdots(const char *url, const char **out) | ||
| 90 | +{ | ||
| 91 | + int result = 0; | ||
| 92 | + while (1) { | ||
| 93 | + if (starts_with_dot_dot_slash(url)) { | ||
| 94 | + result++; | ||
| 95 | + url += strlen("../"); | ||
| 96 | + continue; | ||
| 97 | + } | ||
| 98 | + if (starts_with_dot_slash(url)) { | ||
| 99 | + url += strlen("./"); | ||
| 100 | + continue; | ||
| 101 | + } | ||
| 102 | + *out = url; | ||
| 103 | + return result; | ||
| 104 | + } | ||
| 105 | +} | ||
| 106 | +/* | ||
| 107 | * Check whether a transport is implemented by git-remote-curl. | ||
| 108 | * | ||
| 109 | * If it is, returns 1 and writes the URL that would be passed to | ||
| 110 | @@ -1024,15 +1052,30 @@ static int check_submodule_url(const char *url) | ||
| 111 | return -1; | ||
| 112 | |||
| 113 | if (submodule_url_is_relative(url)) { | ||
| 114 | + char *decoded; | ||
| 115 | + const char *next; | ||
| 116 | + int has_nl; | ||
| 117 | + | ||
| 118 | /* | ||
| 119 | * This could be appended to an http URL and url-decoded; | ||
| 120 | * check for malicious characters. | ||
| 121 | */ | ||
| 122 | - char *decoded = url_decode(url); | ||
| 123 | - int has_nl = !!strchr(decoded, '\n'); | ||
| 124 | + decoded = url_decode(url); | ||
| 125 | + has_nl = !!strchr(decoded, '\n'); | ||
| 126 | + | ||
| 127 | free(decoded); | ||
| 128 | if (has_nl) | ||
| 129 | return -1; | ||
| 130 | + | ||
| 131 | + /* | ||
| 132 | + * URLs which escape their root via "../" can overwrite | ||
| 133 | + * the host field and previous components, resolving to | ||
| 134 | + * URLs like https::example.com/submodule.git that were | ||
| 135 | + * susceptible to CVE-2020-11008. | ||
| 136 | + */ | ||
| 137 | + if (count_leading_dotdots(url, &next) > 0 && | ||
| 138 | + *next == ':') | ||
| 139 | + return -1; | ||
| 140 | } | ||
| 141 | |||
| 142 | else if (url_to_curl_url(url, &curl_url)) { | ||
| 143 | diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh | ||
| 144 | index b811d89..1c9e5d3 100755 | ||
| 145 | --- a/t/t5550-http-fetch-dumb.sh | ||
| 146 | +++ b/t/t5550-http-fetch-dumb.sh | ||
| 147 | @@ -321,11 +321,8 @@ test_expect_success 'git client does not send an empty Accept-Language' ' | ||
| 148 | ' | ||
| 149 | |||
| 150 | test_expect_success 'remote-http complains cleanly about malformed urls' ' | ||
| 151 | - # do not actually issue "list" or other commands, as we do not | ||
| 152 | - # want to rely on what curl would actually do with such a broken | ||
| 153 | - # URL. This is just about making sure we do not segfault during | ||
| 154 | - # initialization. | ||
| 155 | - test_must_fail git remote-http http::/example.com/repo.git | ||
| 156 | + test_must_fail git remote-http http::/example.com/repo.git 2>stderr && | ||
| 157 | + test_i18ngrep "url has no scheme" stderr | ||
| 158 | ' | ||
| 159 | |||
| 160 | test_expect_success 'redirects can be forbidden/allowed' ' | ||
| 161 | diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh | ||
| 162 | index afdd255..249dc3d 100755 | ||
| 163 | --- a/t/t7416-submodule-dash-url.sh | ||
| 164 | +++ b/t/t7416-submodule-dash-url.sh | ||
| 165 | @@ -60,6 +60,38 @@ test_expect_success 'trailing backslash is handled correctly' ' | ||
| 166 | test_i18ngrep ! "unknown option" err | ||
| 167 | ' | ||
| 168 | |||
| 169 | +test_expect_success 'fsck rejects missing URL scheme' ' | ||
| 170 | + git checkout --orphan missing-scheme && | ||
| 171 | + cat >.gitmodules <<-\EOF && | ||
| 172 | + [submodule "foo"] | ||
| 173 | + url = http::one.example.com/foo.git | ||
| 174 | + EOF | ||
| 175 | + git add .gitmodules && | ||
| 176 | + test_tick && | ||
| 177 | + git commit -m "gitmodules with missing URL scheme" && | ||
| 178 | + test_when_finished "rm -rf dst" && | ||
| 179 | + git init --bare dst && | ||
| 180 | + git -C dst config transfer.fsckObjects true && | ||
| 181 | + test_must_fail git push dst HEAD 2>err && | ||
| 182 | + grep gitmodulesUrl err | ||
| 183 | +' | ||
| 184 | + | ||
| 185 | +test_expect_success 'fsck rejects relative URL resolving to missing scheme' ' | ||
| 186 | + git checkout --orphan relative-missing-scheme && | ||
| 187 | + cat >.gitmodules <<-\EOF && | ||
| 188 | + [submodule "foo"] | ||
| 189 | + url = "..\\../.\\../:one.example.com/foo.git" | ||
| 190 | + EOF | ||
| 191 | + git add .gitmodules && | ||
| 192 | + test_tick && | ||
| 193 | + git commit -m "gitmodules with relative URL that strips off scheme" && | ||
| 194 | + test_when_finished "rm -rf dst" && | ||
| 195 | + git init --bare dst && | ||
| 196 | + git -C dst config transfer.fsckObjects true && | ||
| 197 | + test_must_fail git push dst HEAD 2>err && | ||
| 198 | + grep gitmodulesUrl err | ||
| 199 | +' | ||
| 200 | + | ||
| 201 | test_expect_success 'fsck permits embedded newline with unrecognized scheme' ' | ||
| 202 | git checkout --orphan newscheme && | ||
| 203 | cat >.gitmodules <<-\EOF && | ||
| 204 | -- | ||
| 205 | 1.9.1 | ||
| 206 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-8.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-8.patch new file mode 100644 index 0000000000..935d47795f --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-8.patch | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | From 5e06d0781a963d62413ae7eab4eb78cc7195af8b Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jonathan Nieder <jrnieder@gmail.com> | ||
| 3 | Date: Sat, 18 Apr 2020 20:54:57 -0700 | ||
| 4 | Subject: [PATCH 11/12] credential: treat URL with empty scheme as invalid | ||
| 5 | |||
| 6 | Until "credential: refuse to operate when missing host or protocol", | ||
| 7 | Git's credential handling code interpreted URLs with empty scheme to | ||
| 8 | mean "give me credentials matching this host for any protocol". | ||
| 9 | |||
| 10 | Luckily libcurl does not recognize such URLs (it tries to look for a | ||
| 11 | protocol named "" and fails). Just in case that changes, let's reject | ||
| 12 | them within Git as well. This way, credential_from_url is guaranteed to | ||
| 13 | always produce a "struct credential" with protocol and host set. | ||
| 14 | |||
| 15 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 16 | |||
| 17 | Upstream-Status: Backport | ||
| 18 | CVE: CVE-2020-11008 (8) | ||
| 19 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 20 | --- | ||
| 21 | credential.c | 5 ++--- | ||
| 22 | t/t5550-http-fetch-dumb.sh | 9 +++++++++ | ||
| 23 | t/t7416-submodule-dash-url.sh | 32 ++++++++++++++++++++++++++++++++ | ||
| 24 | 3 files changed, 43 insertions(+), 3 deletions(-) | ||
| 25 | |||
| 26 | diff --git a/credential.c b/credential.c | ||
| 27 | index 1e1aed5..cf11cc9 100644 | ||
| 28 | --- a/credential.c | ||
| 29 | +++ b/credential.c | ||
| 30 | @@ -360,7 +360,7 @@ int credential_from_url_gently(struct credential *c, const char *url, | ||
| 31 | * (3) proto://<user>:<pass>@<host>/... | ||
| 32 | */ | ||
| 33 | proto_end = strstr(url, "://"); | ||
| 34 | - if (!proto_end) { | ||
| 35 | + if (!proto_end || proto_end == url) { | ||
| 36 | if (!quiet) | ||
| 37 | warning(_("url has no scheme: %s"), url); | ||
| 38 | return -1; | ||
| 39 | @@ -385,8 +385,7 @@ int credential_from_url_gently(struct credential *c, const char *url, | ||
| 40 | host = at + 1; | ||
| 41 | } | ||
| 42 | |||
| 43 | - if (proto_end - url > 0) | ||
| 44 | - c->protocol = xmemdupz(url, proto_end - url); | ||
| 45 | + c->protocol = xmemdupz(url, proto_end - url); | ||
| 46 | c->host = url_decode_mem(host, slash - host); | ||
| 47 | /* Trim leading and trailing slashes from path */ | ||
| 48 | while (*slash == '/') | ||
| 49 | diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh | ||
| 50 | index 1c9e5d3..ea2688b 100755 | ||
| 51 | --- a/t/t5550-http-fetch-dumb.sh | ||
| 52 | +++ b/t/t5550-http-fetch-dumb.sh | ||
| 53 | @@ -325,6 +325,15 @@ test_expect_success 'remote-http complains cleanly about malformed urls' ' | ||
| 54 | test_i18ngrep "url has no scheme" stderr | ||
| 55 | ' | ||
| 56 | |||
| 57 | +# NEEDSWORK: Writing commands to git-remote-curl can race against the latter | ||
| 58 | +# erroring out, producing SIGPIPE. Remove "ok=sigpipe" once transport-helper has | ||
| 59 | +# learned to handle early remote helper failures more cleanly. | ||
| 60 | +test_expect_success 'remote-http complains cleanly about empty scheme' ' | ||
| 61 | + test_must_fail ok=sigpipe git ls-remote \ | ||
| 62 | + http::${HTTPD_URL#http}/dumb/repo.git 2>stderr && | ||
| 63 | + test_i18ngrep "url has no scheme" stderr | ||
| 64 | +' | ||
| 65 | + | ||
| 66 | test_expect_success 'redirects can be forbidden/allowed' ' | ||
| 67 | test_must_fail git -c http.followRedirects=false \ | ||
| 68 | clone $HTTPD_URL/dumb-redir/repo.git dumb-redir && | ||
| 69 | diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh | ||
| 70 | index 249dc3d..9309040 100755 | ||
| 71 | --- a/t/t7416-submodule-dash-url.sh | ||
| 72 | +++ b/t/t7416-submodule-dash-url.sh | ||
| 73 | @@ -92,6 +92,38 @@ test_expect_success 'fsck rejects relative URL resolving to missing scheme' ' | ||
| 74 | grep gitmodulesUrl err | ||
| 75 | ' | ||
| 76 | |||
| 77 | +test_expect_success 'fsck rejects empty URL scheme' ' | ||
| 78 | + git checkout --orphan empty-scheme && | ||
| 79 | + cat >.gitmodules <<-\EOF && | ||
| 80 | + [submodule "foo"] | ||
| 81 | + url = http::://one.example.com/foo.git | ||
| 82 | + EOF | ||
| 83 | + git add .gitmodules && | ||
| 84 | + test_tick && | ||
| 85 | + git commit -m "gitmodules with empty URL scheme" && | ||
| 86 | + test_when_finished "rm -rf dst" && | ||
| 87 | + git init --bare dst && | ||
| 88 | + git -C dst config transfer.fsckObjects true && | ||
| 89 | + test_must_fail git push dst HEAD 2>err && | ||
| 90 | + grep gitmodulesUrl err | ||
| 91 | +' | ||
| 92 | + | ||
| 93 | +test_expect_success 'fsck rejects relative URL resolving to empty scheme' ' | ||
| 94 | + git checkout --orphan relative-empty-scheme && | ||
| 95 | + cat >.gitmodules <<-\EOF && | ||
| 96 | + [submodule "foo"] | ||
| 97 | + url = ../../../:://one.example.com/foo.git | ||
| 98 | + EOF | ||
| 99 | + git add .gitmodules && | ||
| 100 | + test_tick && | ||
| 101 | + git commit -m "relative gitmodules URL resolving to empty scheme" && | ||
| 102 | + test_when_finished "rm -rf dst" && | ||
| 103 | + git init --bare dst && | ||
| 104 | + git -C dst config transfer.fsckObjects true && | ||
| 105 | + test_must_fail git push dst HEAD 2>err && | ||
| 106 | + grep gitmodulesUrl err | ||
| 107 | +' | ||
| 108 | + | ||
| 109 | test_expect_success 'fsck permits embedded newline with unrecognized scheme' ' | ||
| 110 | git checkout --orphan newscheme && | ||
| 111 | cat >.gitmodules <<-\EOF && | ||
| 112 | -- | ||
| 113 | 1.9.1 | ||
| 114 | |||
diff --git a/meta/recipes-devtools/git/git/CVE-2020-11008-9.patch b/meta/recipes-devtools/git/git/CVE-2020-11008-9.patch new file mode 100644 index 0000000000..22292dbbbf --- /dev/null +++ b/meta/recipes-devtools/git/git/CVE-2020-11008-9.patch | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | From 2e084e25fa454c58a600c9434f776f2150037a76 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Jonathan Nieder <jrnieder@gmail.com> | ||
| 3 | Date: Sat, 18 Apr 2020 20:57:22 -0700 | ||
| 4 | Subject: [PATCH 12/12] fsck: reject URL with empty host in .gitmodules | ||
| 5 | |||
| 6 | Git's URL parser interprets | ||
| 7 | |||
| 8 | https:///example.com/repo.git | ||
| 9 | |||
| 10 | to have no host and a path of "example.com/repo.git". Curl, on the | ||
| 11 | other hand, internally redirects it to https://example.com/repo.git. As | ||
| 12 | a result, until "credential: parse URL without host as empty host, not | ||
| 13 | unset", tricking a user into fetching from such a URL would cause Git to | ||
| 14 | send credentials for another host to example.com. | ||
| 15 | |||
| 16 | Teach fsck to block and detect .gitmodules files using such a URL to | ||
| 17 | prevent sharing them with Git versions that are not yet protected. | ||
| 18 | |||
| 19 | A relative URL in a .gitmodules file could also be used to trigger this. | ||
| 20 | The relative URL resolver used for .gitmodules does not normalize | ||
| 21 | sequences of slashes and can follow ".." components out of the path part | ||
| 22 | and to the host part of a URL, meaning that such a relative URL can be | ||
| 23 | used to traverse from a https://foo.example.com/innocent superproject to | ||
| 24 | a https:///attacker.example.com/exploit submodule. Fortunately, | ||
| 25 | redundant extra slashes in .gitmodules are rare, so we can catch this by | ||
| 26 | detecting one after a leading sequence of "./" and "../" components. | ||
| 27 | |||
| 28 | Helped-by: Jeff King <peff@peff.net> | ||
| 29 | Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> | ||
| 30 | Reviewed-by: Jeff King <peff@peff.net> | ||
| 31 | |||
| 32 | Upstream-Status: Backport | ||
| 33 | CVE: CVE-2020-11008 (9) | ||
| 34 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 35 | --- | ||
| 36 | fsck.c | 10 +++++++--- | ||
| 37 | t/t7416-submodule-dash-url.sh | 32 ++++++++++++++++++++++++++++++++ | ||
| 38 | 2 files changed, 39 insertions(+), 3 deletions(-) | ||
| 39 | |||
| 40 | diff --git a/fsck.c b/fsck.c | ||
| 41 | index 30eac29..00077b1 100644 | ||
| 42 | --- a/fsck.c | ||
| 43 | +++ b/fsck.c | ||
| 44 | @@ -1070,17 +1070,21 @@ static int check_submodule_url(const char *url) | ||
| 45 | /* | ||
| 46 | * URLs which escape their root via "../" can overwrite | ||
| 47 | * the host field and previous components, resolving to | ||
| 48 | - * URLs like https::example.com/submodule.git that were | ||
| 49 | + * URLs like https::example.com/submodule.git and | ||
| 50 | + * https:///example.com/submodule.git that were | ||
| 51 | * susceptible to CVE-2020-11008. | ||
| 52 | */ | ||
| 53 | if (count_leading_dotdots(url, &next) > 0 && | ||
| 54 | - *next == ':') | ||
| 55 | + (*next == ':' || *next == '/')) | ||
| 56 | return -1; | ||
| 57 | } | ||
| 58 | |||
| 59 | else if (url_to_curl_url(url, &curl_url)) { | ||
| 60 | struct credential c = CREDENTIAL_INIT; | ||
| 61 | - int ret = credential_from_url_gently(&c, curl_url, 1); | ||
| 62 | + int ret = 0; | ||
| 63 | + if (credential_from_url_gently(&c, curl_url, 1) || | ||
| 64 | + !*c.host) | ||
| 65 | + ret = -1; | ||
| 66 | credential_clear(&c); | ||
| 67 | return ret; | ||
| 68 | } | ||
| 69 | diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh | ||
| 70 | index 9309040..eec96e0 100755 | ||
| 71 | --- a/t/t7416-submodule-dash-url.sh | ||
| 72 | +++ b/t/t7416-submodule-dash-url.sh | ||
| 73 | @@ -124,6 +124,38 @@ test_expect_success 'fsck rejects relative URL resolving to empty scheme' ' | ||
| 74 | grep gitmodulesUrl err | ||
| 75 | ' | ||
| 76 | |||
| 77 | +test_expect_success 'fsck rejects empty hostname' ' | ||
| 78 | + git checkout --orphan empty-host && | ||
| 79 | + cat >.gitmodules <<-\EOF && | ||
| 80 | + [submodule "foo"] | ||
| 81 | + url = http:///one.example.com/foo.git | ||
| 82 | + EOF | ||
| 83 | + git add .gitmodules && | ||
| 84 | + test_tick && | ||
| 85 | + git commit -m "gitmodules with extra slashes" && | ||
| 86 | + test_when_finished "rm -rf dst" && | ||
| 87 | + git init --bare dst && | ||
| 88 | + git -C dst config transfer.fsckObjects true && | ||
| 89 | + test_must_fail git push dst HEAD 2>err && | ||
| 90 | + grep gitmodulesUrl err | ||
| 91 | +' | ||
| 92 | + | ||
| 93 | +test_expect_success 'fsck rejects relative url that produced empty hostname' ' | ||
| 94 | + git checkout --orphan messy-relative && | ||
| 95 | + cat >.gitmodules <<-\EOF && | ||
| 96 | + [submodule "foo"] | ||
| 97 | + url = ../../..//one.example.com/foo.git | ||
| 98 | + EOF | ||
| 99 | + git add .gitmodules && | ||
| 100 | + test_tick && | ||
| 101 | + git commit -m "gitmodules abusing relative_path" && | ||
| 102 | + test_when_finished "rm -rf dst" && | ||
| 103 | + git init --bare dst && | ||
| 104 | + git -C dst config transfer.fsckObjects true && | ||
| 105 | + test_must_fail git push dst HEAD 2>err && | ||
| 106 | + grep gitmodulesUrl err | ||
| 107 | +' | ||
| 108 | + | ||
| 109 | test_expect_success 'fsck permits embedded newline with unrecognized scheme' ' | ||
| 110 | git checkout --orphan newscheme && | ||
| 111 | cat >.gitmodules <<-\EOF && | ||
| 112 | -- | ||
| 113 | 1.9.1 | ||
| 114 | |||
