зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/daemon-interpolate' into maint
The "interpolated-path" option of "git daemon" inserted any string client declared on the "host=" capability request without checking. Sanitize and limit %H and %CH to a saner and a valid DNS name. * jk/daemon-interpolate: daemon: sanitize incoming virtual hostname t5570: test git-daemon's --interpolated-path option git_connect: let user override virtual-host we send to daemon
This commit is contained in:
Коммит
c722ba4814
12
connect.c
12
connect.c
|
@ -669,10 +669,20 @@ struct child_process *git_connect(int fd[2], const char *url,
|
||||||
printf("Diag: path=%s\n", path ? path : "NULL");
|
printf("Diag: path=%s\n", path ? path : "NULL");
|
||||||
conn = NULL;
|
conn = NULL;
|
||||||
} else if (protocol == PROTO_GIT) {
|
} else if (protocol == PROTO_GIT) {
|
||||||
|
/*
|
||||||
|
* Set up virtual host information based on where we will
|
||||||
|
* connect, unless the user has overridden us in
|
||||||
|
* the environment.
|
||||||
|
*/
|
||||||
|
char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST");
|
||||||
|
if (target_host)
|
||||||
|
target_host = xstrdup(target_host);
|
||||||
|
else
|
||||||
|
target_host = xstrdup(hostandport);
|
||||||
|
|
||||||
/* These underlying connection commands die() if they
|
/* These underlying connection commands die() if they
|
||||||
* cannot connect.
|
* cannot connect.
|
||||||
*/
|
*/
|
||||||
char *target_host = xstrdup(hostandport);
|
|
||||||
if (git_use_proxy(hostandport))
|
if (git_use_proxy(hostandport))
|
||||||
conn = git_proxy_connect(fd, hostandport);
|
conn = git_proxy_connect(fd, hostandport);
|
||||||
else
|
else
|
||||||
|
|
50
daemon.c
50
daemon.c
|
@ -484,6 +484,45 @@ static void parse_host_and_port(char *hostport, char **host,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sanitize a string from the client so that it's OK to be inserted into a
|
||||||
|
* filesystem path. Specifically, we disallow slashes, runs of "..", and
|
||||||
|
* trailing and leading dots, which means that the client cannot escape
|
||||||
|
* our base path via ".." traversal.
|
||||||
|
*/
|
||||||
|
static void sanitize_client_strbuf(struct strbuf *out, const char *in)
|
||||||
|
{
|
||||||
|
for (; *in; in++) {
|
||||||
|
if (*in == '/')
|
||||||
|
continue;
|
||||||
|
if (*in == '.' && (!out->len || out->buf[out->len - 1] == '.'))
|
||||||
|
continue;
|
||||||
|
strbuf_addch(out, *in);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (out->len && out->buf[out->len - 1] == '.')
|
||||||
|
strbuf_setlen(out, out->len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *sanitize_client(const char *in)
|
||||||
|
{
|
||||||
|
struct strbuf out = STRBUF_INIT;
|
||||||
|
sanitize_client_strbuf(&out, in);
|
||||||
|
return strbuf_detach(&out, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like sanitize_client, but we also perform any canonicalization
|
||||||
|
* to make life easier on the admin.
|
||||||
|
*/
|
||||||
|
static char *canonicalize_client(const char *in)
|
||||||
|
{
|
||||||
|
struct strbuf out = STRBUF_INIT;
|
||||||
|
sanitize_client_strbuf(&out, in);
|
||||||
|
strbuf_tolower(&out);
|
||||||
|
return strbuf_detach(&out, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the host as supplied by the client connection.
|
* Read the host as supplied by the client connection.
|
||||||
*/
|
*/
|
||||||
|
@ -505,10 +544,10 @@ static void parse_host_arg(char *extra_args, int buflen)
|
||||||
parse_host_and_port(val, &host, &port);
|
parse_host_and_port(val, &host, &port);
|
||||||
if (port) {
|
if (port) {
|
||||||
free(tcp_port);
|
free(tcp_port);
|
||||||
tcp_port = xstrdup(port);
|
tcp_port = sanitize_client(port);
|
||||||
}
|
}
|
||||||
free(hostname);
|
free(hostname);
|
||||||
hostname = xstrdup_tolower(host);
|
hostname = canonicalize_client(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* On to the next one */
|
/* On to the next one */
|
||||||
|
@ -541,8 +580,9 @@ static void parse_host_arg(char *extra_args, int buflen)
|
||||||
ip_address = xstrdup(addrbuf);
|
ip_address = xstrdup(addrbuf);
|
||||||
|
|
||||||
free(canon_hostname);
|
free(canon_hostname);
|
||||||
canon_hostname = xstrdup(ai->ai_canonname ?
|
canon_hostname = ai->ai_canonname ?
|
||||||
ai->ai_canonname : ip_address);
|
sanitize_client(ai->ai_canonname) :
|
||||||
|
xstrdup(ip_address);
|
||||||
|
|
||||||
freeaddrinfo(ai);
|
freeaddrinfo(ai);
|
||||||
}
|
}
|
||||||
|
@ -564,7 +604,7 @@ static void parse_host_arg(char *extra_args, int buflen)
|
||||||
addrbuf, sizeof(addrbuf));
|
addrbuf, sizeof(addrbuf));
|
||||||
|
|
||||||
free(canon_hostname);
|
free(canon_hostname);
|
||||||
canon_hostname = xstrdup(hent->h_name);
|
canon_hostname = sanitize_client(hent->h_name);
|
||||||
free(ip_address);
|
free(ip_address);
|
||||||
ip_address = xstrdup(addrbuf);
|
ip_address = xstrdup(addrbuf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,5 +141,32 @@ test_expect_success 'push disabled' "test_remote_error 'service not enab
|
||||||
test_expect_success 'read access denied' "test_remote_error -x 'no such repository' fetch repo.git "
|
test_expect_success 'read access denied' "test_remote_error -x 'no such repository' fetch repo.git "
|
||||||
test_expect_success 'not exported' "test_remote_error -n 'repository not exported' fetch repo.git "
|
test_expect_success 'not exported' "test_remote_error -n 'repository not exported' fetch repo.git "
|
||||||
|
|
||||||
|
stop_git_daemon
|
||||||
|
start_git_daemon --interpolated-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH/%H%D"
|
||||||
|
|
||||||
|
test_expect_success 'access repo via interpolated hostname' '
|
||||||
|
repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/localhost/interp.git" &&
|
||||||
|
git init --bare "$repo" &&
|
||||||
|
git push "$repo" HEAD &&
|
||||||
|
>"$repo"/git-daemon-export-ok &&
|
||||||
|
rm -rf tmp.git &&
|
||||||
|
GIT_OVERRIDE_VIRTUAL_HOST=localhost \
|
||||||
|
git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git &&
|
||||||
|
rm -rf tmp.git &&
|
||||||
|
GIT_OVERRIDE_VIRTUAL_HOST=LOCALHOST \
|
||||||
|
git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'hostname cannot break out of directory' '
|
||||||
|
rm -rf tmp.git &&
|
||||||
|
repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/../escape.git" &&
|
||||||
|
git init --bare "$repo" &&
|
||||||
|
git push "$repo" HEAD &&
|
||||||
|
>"$repo"/git-daemon-export-ok &&
|
||||||
|
test_must_fail \
|
||||||
|
env GIT_OVERRIDE_VIRTUAL_HOST=.. \
|
||||||
|
git clone --bare "$GIT_DAEMON_URL/escape.git" tmp.git
|
||||||
|
'
|
||||||
|
|
||||||
stop_git_daemon
|
stop_git_daemon
|
||||||
test_done
|
test_done
|
||||||
|
|
Загрузка…
Ссылка в новой задаче