diff options
author | Jonathan Nieder <jrnieder@gmail.com> | 2020-04-18 20:54:13 -0700 |
---|---|---|
committer | Jonathan Nieder <jrnieder@gmail.com> | 2020-04-19 16:10:58 -0700 |
commit | c44088ecc4b0722636e0a305f9608d3047197282 (patch) | |
tree | a971e4a0899abe18ccac120fb345502aaf628722 /fsck.c | |
parent | fe29a9b7b0236d3d45c254965580d6aff7fa8504 (diff) | |
download | git-c44088ecc4b0722636e0a305f9608d3047197282.tar.gz |
credential: treat URL without scheme as invalid
libcurl permits making requests without a URL scheme specified. In
this case, it guesses the URL from the hostname, so I can run
git ls-remote http::ftp.example.com/path/to/repo
and it would make an FTP request.
Any user intentionally using such a URL is likely to have made a typo.
Unfortunately, credential_from_url is not able to determine the host and
protocol in order to determine appropriate credentials to send, and
until "credential: refuse to operate when missing host or protocol",
this resulted in another host's credentials being leaked to the named
host.
Teach credential_from_url_gently to consider such a URL to be invalid
so that fsck can detect and block gitmodules files with such URLs,
allowing server operators to avoid serving them to downstream users
running older versions of Git.
This also means that when such URLs are passed on the command line, Git
will print a clearer error so affected users can switch to the simpler
URL that explicitly specifies the host and protocol they intend.
One subtlety: .gitmodules files can contain relative URLs, representing
a URL relative to the URL they were cloned from. The relative URL
resolver used for .gitmodules can follow ".." components out of the path
part and past the host part of a URL, meaning that such a relative URL
can be used to traverse from a https://foo.example.com/innocent
superproject to a https::attacker.example.com/exploit submodule.
Fortunately a leading ':' in the first path component after a series of
leading './' and '../' components is unlikely to show up in other
contexts, so we can catch this by detecting that pattern.
Reported-by: Jeff King <peff@peff.net>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Jeff King <peff@peff.net>
Diffstat (limited to 'fsck.c')
-rw-r--r-- | fsck.c | 47 |
1 files changed, 45 insertions, 2 deletions
@@ -972,6 +972,34 @@ static int submodule_url_is_relative(const char *url) } /* + * Count directory components that a relative submodule URL should chop + * from the remote_url it is to be resolved against. + * + * In other words, this counts "../" components at the start of a + * submodule URL. + * + * Returns the number of directory components to chop and writes a + * pointer to the next character of url after all leading "./" and + * "../" components to out. + */ +static int count_leading_dotdots(const char *url, const char **out) +{ + int result = 0; + while (1) { + if (starts_with_dot_dot_slash(url)) { + result++; + url += strlen("../"); + continue; + } + if (starts_with_dot_slash(url)) { + url += strlen("./"); + continue; + } + *out = url; + return result; + } +} +/* * Check whether a transport is implemented by git-remote-curl. * * If it is, returns 1 and writes the URL that would be passed to @@ -1018,15 +1046,30 @@ static int check_submodule_url(const char *url) return -1; if (submodule_url_is_relative(url)) { + char *decoded; + const char *next; + int has_nl; + /* * 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'); + decoded = url_decode(url); + has_nl = !!strchr(decoded, '\n'); + free(decoded); if (has_nl) return -1; + + /* + * URLs which escape their root via "../" can overwrite + * the host field and previous components, resolving to + * URLs like https::example.com/submodule.git that were + * susceptible to CVE-2020-11008. + */ + if (count_leading_dotdots(url, &next) > 0 && + *next == ':') + return -1; } else if (url_to_curl_url(url, &curl_url)) { |