daemon: recognize hidden request arguments
A normal request to git-daemon is structured as "command path/to/repo\0host=..\0" and due to a bug introduced in49ba83fb6
(Add virtualization support to git-daemon, 2006-09-19) we aren't able to place any extra arguments (separated by NULs) besides the host otherwise the parsing of those arguments would enter an infinite loop. This bug was fixed in73bb33a94
(daemon: Strictly parse the "extra arg" part of the command, 2009-06-04) but a check was put in place to disallow extra arguments so that new clients wouldn't trigger this bug in older servers. In order to get around this limitation teach git-daemon to recognize additional request arguments hidden behind a second NUL byte. Requests can then be structured like: "command path/to/repo\0host=..\0\0version=1\0key=value\0". git-daemon can then parse out the extra arguments and set 'GIT_PROTOCOL' accordingly. By placing these extra arguments behind a second NUL byte we can skirt around both the infinite loop bug in49ba83fb6
(Add virtualization support to git-daemon, 2006-09-19) as well as the explicit disallowing of extra arguments introduced in73bb33a94
(daemon: Strictly parse the "extra arg" part of the command, 2009-06-04) because both of these versions of git-daemon check for a single NUL byte after the host argument before terminating the argument parsing. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
373d70efb2
commit
dfe422d04d
71
daemon.c
71
daemon.c
@ -282,7 +282,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
|
|||||||
return NULL; /* Fallthrough. Deny by default */
|
return NULL; /* Fallthrough. Deny by default */
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef int (*daemon_service_fn)(void);
|
typedef int (*daemon_service_fn)(const struct argv_array *env);
|
||||||
struct daemon_service {
|
struct daemon_service {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *config_name;
|
const char *config_name;
|
||||||
@ -363,7 +363,7 @@ error_return:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int run_service(const char *dir, struct daemon_service *service,
|
static int run_service(const char *dir, struct daemon_service *service,
|
||||||
struct hostinfo *hi)
|
struct hostinfo *hi, const struct argv_array *env)
|
||||||
{
|
{
|
||||||
const char *path;
|
const char *path;
|
||||||
int enabled = service->enabled;
|
int enabled = service->enabled;
|
||||||
@ -422,7 +422,7 @@ static int run_service(const char *dir, struct daemon_service *service,
|
|||||||
*/
|
*/
|
||||||
signal(SIGTERM, SIG_IGN);
|
signal(SIGTERM, SIG_IGN);
|
||||||
|
|
||||||
return service->fn();
|
return service->fn(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copy_to_log(int fd)
|
static void copy_to_log(int fd)
|
||||||
@ -462,25 +462,34 @@ static int run_service_command(struct child_process *cld)
|
|||||||
return finish_command(cld);
|
return finish_command(cld);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int upload_pack(void)
|
static int upload_pack(const struct argv_array *env)
|
||||||
{
|
{
|
||||||
struct child_process cld = CHILD_PROCESS_INIT;
|
struct child_process cld = CHILD_PROCESS_INIT;
|
||||||
argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
|
argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
|
||||||
argv_array_pushf(&cld.args, "--timeout=%u", timeout);
|
argv_array_pushf(&cld.args, "--timeout=%u", timeout);
|
||||||
|
|
||||||
|
argv_array_pushv(&cld.env_array, env->argv);
|
||||||
|
|
||||||
return run_service_command(&cld);
|
return run_service_command(&cld);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int upload_archive(void)
|
static int upload_archive(const struct argv_array *env)
|
||||||
{
|
{
|
||||||
struct child_process cld = CHILD_PROCESS_INIT;
|
struct child_process cld = CHILD_PROCESS_INIT;
|
||||||
argv_array_push(&cld.args, "upload-archive");
|
argv_array_push(&cld.args, "upload-archive");
|
||||||
|
|
||||||
|
argv_array_pushv(&cld.env_array, env->argv);
|
||||||
|
|
||||||
return run_service_command(&cld);
|
return run_service_command(&cld);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int receive_pack(void)
|
static int receive_pack(const struct argv_array *env)
|
||||||
{
|
{
|
||||||
struct child_process cld = CHILD_PROCESS_INIT;
|
struct child_process cld = CHILD_PROCESS_INIT;
|
||||||
argv_array_push(&cld.args, "receive-pack");
|
argv_array_push(&cld.args, "receive-pack");
|
||||||
|
|
||||||
|
argv_array_pushv(&cld.env_array, env->argv);
|
||||||
|
|
||||||
return run_service_command(&cld);
|
return run_service_command(&cld);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,8 +582,11 @@ static void canonicalize_client(struct strbuf *out, const char *in)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the host as supplied by the client connection.
|
* Read the host as supplied by the client connection.
|
||||||
|
*
|
||||||
|
* Returns a pointer to the character after the NUL byte terminating the host
|
||||||
|
* arguemnt, or 'extra_args' if there is no host arguemnt.
|
||||||
*/
|
*/
|
||||||
static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
|
static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
|
||||||
{
|
{
|
||||||
char *val;
|
char *val;
|
||||||
int vallen;
|
int vallen;
|
||||||
@ -602,6 +614,43 @@ static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
|
|||||||
if (extra_args < end && *extra_args)
|
if (extra_args < end && *extra_args)
|
||||||
die("Invalid request");
|
die("Invalid request");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return extra_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
|
||||||
|
char *extra_args, int buflen)
|
||||||
|
{
|
||||||
|
const char *end = extra_args + buflen;
|
||||||
|
struct strbuf git_protocol = STRBUF_INIT;
|
||||||
|
|
||||||
|
/* First look for the host argument */
|
||||||
|
extra_args = parse_host_arg(hi, extra_args, buflen);
|
||||||
|
|
||||||
|
/* Look for additional arguments places after a second NUL byte */
|
||||||
|
for (; extra_args < end; extra_args += strlen(extra_args) + 1) {
|
||||||
|
const char *arg = extra_args;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the extra arguments, adding most to 'git_protocol'
|
||||||
|
* which will be used to set the 'GIT_PROTOCOL' envvar in the
|
||||||
|
* service that will be run.
|
||||||
|
*
|
||||||
|
* If there ends up being a particular arg in the future that
|
||||||
|
* git-daemon needs to parse specificly (like the 'host' arg)
|
||||||
|
* then it can be parsed here and not added to 'git_protocol'.
|
||||||
|
*/
|
||||||
|
if (*arg) {
|
||||||
|
if (git_protocol.len > 0)
|
||||||
|
strbuf_addch(&git_protocol, ':');
|
||||||
|
strbuf_addstr(&git_protocol, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (git_protocol.len > 0)
|
||||||
|
argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
|
||||||
|
git_protocol.buf);
|
||||||
|
strbuf_release(&git_protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -695,6 +744,7 @@ static int execute(void)
|
|||||||
int pktlen, len, i;
|
int pktlen, len, i;
|
||||||
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
|
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
|
||||||
struct hostinfo hi;
|
struct hostinfo hi;
|
||||||
|
struct argv_array env = ARGV_ARRAY_INIT;
|
||||||
|
|
||||||
hostinfo_init(&hi);
|
hostinfo_init(&hi);
|
||||||
|
|
||||||
@ -716,8 +766,9 @@ static int execute(void)
|
|||||||
pktlen--;
|
pktlen--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* parse additional args hidden behind a NUL byte */
|
||||||
if (len != pktlen)
|
if (len != pktlen)
|
||||||
parse_host_arg(&hi, line + len + 1, pktlen - len - 1);
|
parse_extra_args(&hi, &env, line + len + 1, pktlen - len - 1);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
|
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
|
||||||
struct daemon_service *s = &(daemon_service[i]);
|
struct daemon_service *s = &(daemon_service[i]);
|
||||||
@ -730,13 +781,15 @@ static int execute(void)
|
|||||||
* Note: The directory here is probably context sensitive,
|
* Note: The directory here is probably context sensitive,
|
||||||
* and might depend on the actual service being performed.
|
* and might depend on the actual service being performed.
|
||||||
*/
|
*/
|
||||||
int rc = run_service(arg, s, &hi);
|
int rc = run_service(arg, s, &hi, &env);
|
||||||
hostinfo_clear(&hi);
|
hostinfo_clear(&hi);
|
||||||
|
argv_array_clear(&env);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostinfo_clear(&hi);
|
hostinfo_clear(&hi);
|
||||||
|
argv_array_clear(&env);
|
||||||
logerror("Protocol error: '%s'", line);
|
logerror("Protocol error: '%s'", line);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user