|
@@ -0,0 +1,211 @@
|
|
|
+From 3431abe8c0f64f4049a31298c0b1056baa7d81dc Mon Sep 17 00:00:00 2001
|
|
|
+From: Li Zhou <li.zhou@windriver.com>
|
|
|
+Date: Mon, 27 Apr 2020 14:45:49 +0800
|
|
|
+Subject: [PATCH 08/12] fsck: convert gitmodules url to URL passed to curl
|
|
|
+
|
|
|
+In 07259e74ec1 (fsck: detect gitmodules URLs with embedded newlines,
|
|
|
+2020-03-11), git fsck learned to check whether URLs in .gitmodules could
|
|
|
+be understood by the credential machinery when they are handled by
|
|
|
+git-remote-curl.
|
|
|
+
|
|
|
+However, the check is overbroad: it checks all URLs instead of only
|
|
|
+URLs that would be passed to git-remote-curl. In principle a git:// or
|
|
|
+file:/// URL does not need to follow the same conventions as an http://
|
|
|
+URL; in particular, git:// and file:// protocols are not succeptible to
|
|
|
+issues in the credential API because they do not support attaching
|
|
|
+credentials.
|
|
|
+
|
|
|
+In the HTTP case, the URL in .gitmodules does not always match the URL
|
|
|
+that would be passed to git-remote-curl and the credential machinery:
|
|
|
+Git's URL syntax allows specifying a remote helper followed by a "::"
|
|
|
+delimiter and a URL to be passed to it, so that
|
|
|
+
|
|
|
+ git ls-remote http::https://example.com/repo.git
|
|
|
+
|
|
|
+invokes git-remote-http with https://example.com/repo.git as its URL
|
|
|
+argument. With today's checks, that distinction does not make a
|
|
|
+difference, but for a check we are about to introduce (for empty URL
|
|
|
+schemes) it will matter.
|
|
|
+
|
|
|
+.gitmodules files also support relative URLs. To ensure coverage for the
|
|
|
+https based embedded-newline attack, urldecode and check them directly
|
|
|
+for embedded newlines.
|
|
|
+
|
|
|
+Helped-by: Jeff King <peff@peff.net>
|
|
|
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
|
|
|
+Reviewed-by: Jeff King <peff@peff.net>
|
|
|
+
|
|
|
+Upstream-Status: Backport
|
|
|
+CVE: CVE-2020-11008 (5)
|
|
|
+Signed-off-by: Li Zhou <li.zhou@windriver.com>
|
|
|
+---
|
|
|
+ fsck.c | 94 ++++++++++++++++++++++++++++++++++++++++---
|
|
|
+ t/t7416-submodule-dash-url.sh | 29 +++++++++++++
|
|
|
+ 2 files changed, 118 insertions(+), 5 deletions(-)
|
|
|
+
|
|
|
+diff --git a/fsck.c b/fsck.c
|
|
|
+index ea46eea..0f21eb1 100644
|
|
|
+--- a/fsck.c
|
|
|
++++ b/fsck.c
|
|
|
+@@ -9,6 +9,7 @@
|
|
|
+ #include "tag.h"
|
|
|
+ #include "fsck.h"
|
|
|
+ #include "refs.h"
|
|
|
++#include "url.h"
|
|
|
+ #include "utf8.h"
|
|
|
+ #include "decorate.h"
|
|
|
+ #include "oidset.h"
|
|
|
+@@ -948,17 +949,100 @@ static int fsck_tag(struct tag *tag, const char *data,
|
|
|
+ return fsck_tag_buffer(tag, data, size, options);
|
|
|
+ }
|
|
|
+
|
|
|
++/*
|
|
|
++ * Like builtin/submodule--helper.c's starts_with_dot_slash, but without
|
|
|
++ * relying on the platform-dependent is_dir_sep helper.
|
|
|
++ *
|
|
|
++ * This is for use in checking whether a submodule URL is interpreted as
|
|
|
++ * relative to the current directory on any platform, since \ is a
|
|
|
++ * directory separator on Windows but not on other platforms.
|
|
|
++ */
|
|
|
++static int starts_with_dot_slash(const char *str)
|
|
|
++{
|
|
|
++ return str[0] == '.' && (str[1] == '/' || str[1] == '\\');
|
|
|
++}
|
|
|
++
|
|
|
++/*
|
|
|
++ * Like starts_with_dot_slash, this is a variant of submodule--helper's
|
|
|
++ * helper of the same name with the twist that it accepts backslash as a
|
|
|
++ * directory separator even on non-Windows platforms.
|
|
|
++ */
|
|
|
++static int starts_with_dot_dot_slash(const char *str)
|
|
|
++{
|
|
|
++ return str[0] == '.' && starts_with_dot_slash(str + 1);
|
|
|
++}
|
|
|
++
|
|
|
++static int submodule_url_is_relative(const char *url)
|
|
|
++{
|
|
|
++ return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
|
|
|
++}
|
|
|
++
|
|
|
++/*
|
|
|
++ * Check whether a transport is implemented by git-remote-curl.
|
|
|
++ *
|
|
|
++ * If it is, returns 1 and writes the URL that would be passed to
|
|
|
++ * git-remote-curl to the "out" parameter.
|
|
|
++ *
|
|
|
++ * Otherwise, returns 0 and leaves "out" untouched.
|
|
|
++ *
|
|
|
++ * Examples:
|
|
|
++ * http::https://example.com/repo.git -> 1, https://example.com/repo.git
|
|
|
++ * https://example.com/repo.git -> 1, https://example.com/repo.git
|
|
|
++ * git://example.com/repo.git -> 0
|
|
|
++ *
|
|
|
++ * This is for use in checking for previously exploitable bugs that
|
|
|
++ * required a submodule URL to be passed to git-remote-curl.
|
|
|
++ */
|
|
|
++static int url_to_curl_url(const char *url, const char **out)
|
|
|
++{
|
|
|
++ /*
|
|
|
++ * We don't need to check for case-aliases, "http.exe", and so
|
|
|
++ * on because in the default configuration, is_transport_allowed
|
|
|
++ * prevents URLs with those schemes from being cloned
|
|
|
++ * automatically.
|
|
|
++ */
|
|
|
++ if (skip_prefix(url, "http::", out) ||
|
|
|
++ skip_prefix(url, "https::", out) ||
|
|
|
++ skip_prefix(url, "ftp::", out) ||
|
|
|
++ skip_prefix(url, "ftps::", out))
|
|
|
++ return 1;
|
|
|
++ if (starts_with(url, "http://") ||
|
|
|
++ starts_with(url, "https://") ||
|
|
|
++ starts_with(url, "ftp://") ||
|
|
|
++ starts_with(url, "ftps://")) {
|
|
|
++ *out = url;
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
+ static int check_submodule_url(const char *url)
|
|
|
+ {
|
|
|
+- struct credential c = CREDENTIAL_INIT;
|
|
|
+- int ret;
|
|
|
++ const char *curl_url;
|
|
|
+
|
|
|
+ if (looks_like_command_line_option(url))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+- ret = credential_from_url_gently(&c, url, 1);
|
|
|
+- credential_clear(&c);
|
|
|
+- return ret;
|
|
|
++ if (submodule_url_is_relative(url)) {
|
|
|
++ /*
|
|
|
++ * This could be appended to an http URL and url-decoded;
|
|
|
++ * check for malicious characters.
|
|
|
++ */
|
|
|
++ char *decoded = url_decode(url);
|
|
|
++ int has_nl = !!strchr(decoded, '\n');
|
|
|
++ free(decoded);
|
|
|
++ if (has_nl)
|
|
|
++ return -1;
|
|
|
++ }
|
|
|
++
|
|
|
++ else if (url_to_curl_url(url, &curl_url)) {
|
|
|
++ struct credential c = CREDENTIAL_INIT;
|
|
|
++ int ret = credential_from_url_gently(&c, curl_url, 1);
|
|
|
++ credential_clear(&c);
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
++
|
|
|
++ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct fsck_gitmodules_data {
|
|
|
+diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
|
|
|
+index 41431b1..afdd255 100755
|
|
|
+--- a/t/t7416-submodule-dash-url.sh
|
|
|
++++ b/t/t7416-submodule-dash-url.sh
|
|
|
+@@ -60,6 +60,20 @@ test_expect_success 'trailing backslash is handled correctly' '
|
|
|
+ test_i18ngrep ! "unknown option" err
|
|
|
+ '
|
|
|
+
|
|
|
++test_expect_success 'fsck permits embedded newline with unrecognized scheme' '
|
|
|
++ git checkout --orphan newscheme &&
|
|
|
++ cat >.gitmodules <<-\EOF &&
|
|
|
++ [submodule "foo"]
|
|
|
++ url = "data://acjbkd%0akajfdickajkd"
|
|
|
++ EOF
|
|
|
++ git add .gitmodules &&
|
|
|
++ git commit -m "gitmodules with unrecognized scheme" &&
|
|
|
++ test_when_finished "rm -rf dst" &&
|
|
|
++ git init --bare dst &&
|
|
|
++ git -C dst config transfer.fsckObjects true &&
|
|
|
++ git push dst HEAD
|
|
|
++'
|
|
|
++
|
|
|
+ test_expect_success 'fsck rejects embedded newline in url' '
|
|
|
+ # create an orphan branch to avoid existing .gitmodules objects
|
|
|
+ git checkout --orphan newline &&
|
|
|
+@@ -76,4 +90,19 @@ test_expect_success 'fsck rejects embedded newline in url' '
|
|
|
+ grep gitmodulesUrl err
|
|
|
+ '
|
|
|
+
|
|
|
++test_expect_success 'fsck rejects embedded newline in relative url' '
|
|
|
++ git checkout --orphan relative-newline &&
|
|
|
++ cat >.gitmodules <<-\EOF &&
|
|
|
++ [submodule "foo"]
|
|
|
++ url = "./%0ahost=two.example.com/foo.git"
|
|
|
++ EOF
|
|
|
++ git add .gitmodules &&
|
|
|
++ git commit -m "relative url 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
|
|
|
+--
|
|
|
+1.9.1
|
|
|
+
|