Skip to content

Commit dfe422d

Browse files
bmwillgitster
authored andcommitted
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 in 49ba83f (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 in 73bb33a (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 in 49ba83f (Add virtualization support to git-daemon, 2006-09-19) as well as the explicit disallowing of extra arguments introduced in 73bb33a (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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 373d70e commit dfe422d

File tree

1 file changed

+62
-9
lines changed

1 file changed

+62
-9
lines changed

daemon.c

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
282282
return NULL; /* Fallthrough. Deny by default */
283283
}
284284

285-
typedef int (*daemon_service_fn)(void);
285+
typedef int (*daemon_service_fn)(const struct argv_array *env);
286286
struct daemon_service {
287287
const char *name;
288288
const char *config_name;
@@ -363,7 +363,7 @@ static int run_access_hook(struct daemon_service *service, const char *dir,
363363
}
364364

365365
static int run_service(const char *dir, struct daemon_service *service,
366-
struct hostinfo *hi)
366+
struct hostinfo *hi, const struct argv_array *env)
367367
{
368368
const char *path;
369369
int enabled = service->enabled;
@@ -422,7 +422,7 @@ static int run_service(const char *dir, struct daemon_service *service,
422422
*/
423423
signal(SIGTERM, SIG_IGN);
424424

425-
return service->fn();
425+
return service->fn(env);
426426
}
427427

428428
static void copy_to_log(int fd)
@@ -462,25 +462,34 @@ static int run_service_command(struct child_process *cld)
462462
return finish_command(cld);
463463
}
464464

465-
static int upload_pack(void)
465+
static int upload_pack(const struct argv_array *env)
466466
{
467467
struct child_process cld = CHILD_PROCESS_INIT;
468468
argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
469469
argv_array_pushf(&cld.args, "--timeout=%u", timeout);
470+
471+
argv_array_pushv(&cld.env_array, env->argv);
472+
470473
return run_service_command(&cld);
471474
}
472475

473-
static int upload_archive(void)
476+
static int upload_archive(const struct argv_array *env)
474477
{
475478
struct child_process cld = CHILD_PROCESS_INIT;
476479
argv_array_push(&cld.args, "upload-archive");
480+
481+
argv_array_pushv(&cld.env_array, env->argv);
482+
477483
return run_service_command(&cld);
478484
}
479485

480-
static int receive_pack(void)
486+
static int receive_pack(const struct argv_array *env)
481487
{
482488
struct child_process cld = CHILD_PROCESS_INIT;
483489
argv_array_push(&cld.args, "receive-pack");
490+
491+
argv_array_pushv(&cld.env_array, env->argv);
492+
484493
return run_service_command(&cld);
485494
}
486495

@@ -573,8 +582,11 @@ static void canonicalize_client(struct strbuf *out, const char *in)
573582

574583
/*
575584
* Read the host as supplied by the client connection.
585+
*
586+
* Returns a pointer to the character after the NUL byte terminating the host
587+
* arguemnt, or 'extra_args' if there is no host arguemnt.
576588
*/
577-
static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
589+
static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
578590
{
579591
char *val;
580592
int vallen;
@@ -602,6 +614,43 @@ static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
602614
if (extra_args < end && *extra_args)
603615
die("Invalid request");
604616
}
617+
618+
return extra_args;
619+
}
620+
621+
static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
622+
char *extra_args, int buflen)
623+
{
624+
const char *end = extra_args + buflen;
625+
struct strbuf git_protocol = STRBUF_INIT;
626+
627+
/* First look for the host argument */
628+
extra_args = parse_host_arg(hi, extra_args, buflen);
629+
630+
/* Look for additional arguments places after a second NUL byte */
631+
for (; extra_args < end; extra_args += strlen(extra_args) + 1) {
632+
const char *arg = extra_args;
633+
634+
/*
635+
* Parse the extra arguments, adding most to 'git_protocol'
636+
* which will be used to set the 'GIT_PROTOCOL' envvar in the
637+
* service that will be run.
638+
*
639+
* If there ends up being a particular arg in the future that
640+
* git-daemon needs to parse specificly (like the 'host' arg)
641+
* then it can be parsed here and not added to 'git_protocol'.
642+
*/
643+
if (*arg) {
644+
if (git_protocol.len > 0)
645+
strbuf_addch(&git_protocol, ':');
646+
strbuf_addstr(&git_protocol, arg);
647+
}
648+
}
649+
650+
if (git_protocol.len > 0)
651+
argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
652+
git_protocol.buf);
653+
strbuf_release(&git_protocol);
605654
}
606655

607656
/*
@@ -695,6 +744,7 @@ static int execute(void)
695744
int pktlen, len, i;
696745
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
697746
struct hostinfo hi;
747+
struct argv_array env = ARGV_ARRAY_INIT;
698748

699749
hostinfo_init(&hi);
700750

@@ -716,8 +766,9 @@ static int execute(void)
716766
pktlen--;
717767
}
718768

769+
/* parse additional args hidden behind a NUL byte */
719770
if (len != pktlen)
720-
parse_host_arg(&hi, line + len + 1, pktlen - len - 1);
771+
parse_extra_args(&hi, &env, line + len + 1, pktlen - len - 1);
721772

722773
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
723774
struct daemon_service *s = &(daemon_service[i]);
@@ -730,13 +781,15 @@ static int execute(void)
730781
* Note: The directory here is probably context sensitive,
731782
* and might depend on the actual service being performed.
732783
*/
733-
int rc = run_service(arg, s, &hi);
784+
int rc = run_service(arg, s, &hi, &env);
734785
hostinfo_clear(&hi);
786+
argv_array_clear(&env);
735787
return rc;
736788
}
737789
}
738790

739791
hostinfo_clear(&hi);
792+
argv_array_clear(&env);
740793
logerror("Protocol error: '%s'", line);
741794
return -1;
742795
}

0 commit comments

Comments
 (0)