Imported Upstream version 2.21.2 upstream/2.21.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:16:54 +0000 (15:16 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:16:54 +0000 (15:16 +0900)
13 files changed:
Documentation/RelNotes/2.17.4.txt [new file with mode: 0644]
Documentation/RelNotes/2.18.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.19.4.txt [new file with mode: 0644]
Documentation/RelNotes/2.20.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.21.2.txt [new file with mode: 0644]
GIT-VERSION-GEN
RelNotes
credential.c
credential.h
fsck.c
t/lib-credential.sh
t/t0300-credentials.sh
t/t7416-submodule-dash-url.sh

diff --git a/Documentation/RelNotes/2.17.4.txt b/Documentation/RelNotes/2.17.4.txt
new file mode 100644 (file)
index 0000000..7d794ca
--- /dev/null
@@ -0,0 +1,16 @@
+Git v2.17.4 Release Notes
+=========================
+
+This release is to address the security issue: CVE-2020-5260
+
+Fixes since v2.17.3
+-------------------
+
+ * With a crafted URL that contains a newline in it, the credential
+   helper machinery can be fooled to give credential information for
+   a wrong host.  The attack has been made impossible by forbidding
+   a newline character in any value passed via the credential
+   protocol.
+
+Credit for finding the vulnerability goes to Felix Wilhelm of Google
+Project Zero.
diff --git a/Documentation/RelNotes/2.18.3.txt b/Documentation/RelNotes/2.18.3.txt
new file mode 100644 (file)
index 0000000..25143f0
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.18.3 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.17.4; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.19.4.txt b/Documentation/RelNotes/2.19.4.txt
new file mode 100644 (file)
index 0000000..35d0ae5
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.19.4 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.17.4; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.20.3.txt b/Documentation/RelNotes/2.20.3.txt
new file mode 100644 (file)
index 0000000..f6eccd1
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.20.3 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.17.4; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.21.2.txt b/Documentation/RelNotes/2.21.2.txt
new file mode 100644 (file)
index 0000000..a0fb83b
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.21.2 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.17.4; see
+the release notes for that version for details.
index 7e5ba89..9fa5ebb 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.21.1
+DEF_VER=v2.21.2
 
 LF='
 '
index 0466feb..ce5750f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.21.1.txt
\ No newline at end of file
+Documentation/RelNotes/2.21.2.txt
\ No newline at end of file
index 62be651..2482382 100644 (file)
@@ -195,6 +195,8 @@ static void credential_write_item(FILE *fp, const char *key, const char *value)
 {
        if (!value)
                return;
+       if (strchr(value, '\n'))
+               die("credential value for %s contains newline", key);
        fprintf(fp, "%s=%s\n", key, value);
 }
 
@@ -322,7 +324,22 @@ void credential_reject(struct credential *c)
        c->approved = 0;
 }
 
-void credential_from_url(struct credential *c, const char *url)
+static int check_url_component(const char *url, int quiet,
+                              const char *name, const char *value)
+{
+       if (!value)
+               return 0;
+       if (!strchr(value, '\n'))
+               return 0;
+
+       if (!quiet)
+               warning(_("url contains a newline in its %s component: %s"),
+                       name, url);
+       return -1;
+}
+
+int credential_from_url_gently(struct credential *c, const char *url,
+                              int quiet)
 {
        const char *at, *colon, *cp, *slash, *host, *proto_end;
 
@@ -336,7 +353,7 @@ void credential_from_url(struct credential *c, const char *url)
         */
        proto_end = strstr(url, "://");
        if (!proto_end)
-               return;
+               return 0;
        cp = proto_end + 3;
        at = strchr(cp, '@');
        colon = strchr(cp, ':');
@@ -371,4 +388,21 @@ void credential_from_url(struct credential *c, const char *url)
                while (p > c->path && *p == '/')
                        *p-- = '\0';
        }
+
+       if (check_url_component(url, quiet, "username", c->username) < 0 ||
+           check_url_component(url, quiet, "password", c->password) < 0 ||
+           check_url_component(url, quiet, "protocol", c->protocol) < 0 ||
+           check_url_component(url, quiet, "host", c->host) < 0 ||
+           check_url_component(url, quiet, "path", c->path) < 0)
+               return -1;
+
+       return 0;
+}
+
+void credential_from_url(struct credential *c, const char *url)
+{
+       if (credential_from_url_gently(c, url, 0) < 0) {
+               warning(_("skipping credential lookup for url: %s"), url);
+               credential_clear(c);
+       }
 }
index 6b0cd16..122a23c 100644 (file)
@@ -28,7 +28,23 @@ void credential_reject(struct credential *);
 
 int credential_read(struct credential *, FILE *);
 void credential_write(const struct credential *, FILE *);
+
+/*
+ * Parse a url into a credential struct, replacing any existing contents.
+ *
+ * Ifthe url can't be parsed (e.g., a missing "proto://" component), the
+ * resulting credential will be empty but we'll still return success from the
+ * "gently" form.
+ *
+ * If we encounter a component which cannot be represented as a credential
+ * value (e.g., because it contains a newline), the "gently" form will return
+ * an error but leave the broken state in the credential object for further
+ * examination.  The non-gentle form will issue a warning to stderr and return
+ * an empty credential.
+ */
 void credential_from_url(struct credential *, const char *url);
+int credential_from_url_gently(struct credential *, const char *url, int quiet);
+
 int credential_match(const struct credential *have,
                     const struct credential *want);
 
diff --git a/fsck.c b/fsck.c
index 35bfb7f..6e5b3af 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -15,6 +15,7 @@
 #include "packfile.h"
 #include "submodule-config.h"
 #include "config.h"
+#include "credential.h"
 #include "help.h"
 
 static struct oidset gitmodules_found = OIDSET_INIT;
@@ -982,6 +983,19 @@ static int fsck_tag(struct tag *tag, const char *data,
        return fsck_tag_buffer(tag, data, size, options);
 }
 
+static int check_submodule_url(const char *url)
+{
+       struct credential c = CREDENTIAL_INIT;
+       int ret;
+
+       if (looks_like_command_line_option(url))
+               return -1;
+
+       ret = credential_from_url_gently(&c, url, 1);
+       credential_clear(&c);
+       return ret;
+}
+
 struct fsck_gitmodules_data {
        struct object *obj;
        struct fsck_options *options;
@@ -1006,7 +1020,7 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
                                    "disallowed submodule name: %s",
                                    name);
        if (!strcmp(key, "url") && value &&
-           looks_like_command_line_option(value))
+           check_submodule_url(value) < 0)
                data->ret |= report(data->options, data->obj,
                                    FSCK_MSG_GITMODULES_URL,
                                    "disallowed submodule url: %s",
index 937b831..bb88cc0 100755 (executable)
@@ -19,7 +19,7 @@ check() {
                false
        fi &&
        test_cmp expect-stdout stdout &&
-       test_cmp expect-stderr stderr
+       test_i18ncmp expect-stderr stderr
 }
 
 read_chunk() {
index 82eaaea..b9c0f1f 100755 (executable)
@@ -308,4 +308,18 @@ test_expect_success 'empty helper spec resets helper list' '
        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
+'
+
 test_done
index 5ba041f..41431b1 100755 (executable)
@@ -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