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 49ba83fb6 (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 73bb33a94 (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 49ba83fb6 (Add virtualization support to git-daemon, 2006-09-19) as well as the explicit disallowing of extra arguments introduced in 73bb33a94 (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>

Brandon Williams committed Oct 16, 2017 at 10:55 UTC dfe422d04db56e7306a78fcf5b8e93b6b7f60e34
1 file changed +62 -9
daemon.c
+62 -9
@@ -282,7 +282,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
282 return NULL; /* Fallthrough. Deny by default */
283 }
284
285 -typedef int (*daemon_service_fn)(void);
285 +typedef int (*daemon_service_fn)(const struct argv_array *env);
286 struct daemon_service {
287 const char *name;
288 const char *config_name;
@@ -363,7 +363,7 @@ error_return:
363 }
364
365 static int run_service(const char *dir, struct daemon_service *service,
366 - struct hostinfo *hi)
366 + struct hostinfo *hi, const struct argv_array *env)
367 {
368 const char *path;
369 int enabled = service->enabled;
@@ -422,7 +422,7 @@ static int run_service(const char *dir, struct daemon_service *service,
422 */
423 signal(SIGTERM, SIG_IGN);
424
425 - return service->fn();
425 + return service->fn(env);
426 }
427
428 static void copy_to_log(int fd)
@@ -462,25 +462,34 @@ static int run_service_command(struct child_process *cld)
462 return finish_command(cld);
463 }
464
465 -static int upload_pack(void)
465 +static int upload_pack(const struct argv_array *env)
466 {
467 struct child_process cld = CHILD_PROCESS_INIT;
468 argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
469 argv_array_pushf(&cld.args, "--timeout=%u", timeout);
470 +
471 + argv_array_pushv(&cld.env_array, env->argv);
472 +
473 return run_service_command(&cld);
474 }
475
473 -static int upload_archive(void)
476 +static int upload_archive(const struct argv_array *env)
477 {
478 struct child_process cld = CHILD_PROCESS_INIT;
479 argv_array_push(&cld.args, "upload-archive");
480 +
481 + argv_array_pushv(&cld.env_array, env->argv);
482 +
483 return run_service_command(&cld);
484 }
485
480 -static int receive_pack(void)
486 +static int receive_pack(const struct argv_array *env)
487 {
488 struct child_process cld = CHILD_PROCESS_INIT;
489 argv_array_push(&cld.args, "receive-pack");
490 +
491 + argv_array_pushv(&cld.env_array, env->argv);
492 +
493 return run_service_command(&cld);
494 }
495
@@ -573,8 +582,11 @@ static void canonicalize_client(struct strbuf *out, const char *in)
582
583 /*
584 * 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.
588 */
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)
590 {
591 char *val;
592 int vallen;
@@ -602,6 +614,43 @@ static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
614 if (extra_args < end && *extra_args)
615 die("Invalid request");
616 }
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);
654 }
655
656 /*
@@ -695,6 +744,7 @@ static int execute(void)
744 int pktlen, len, i;
745 char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
746 struct hostinfo hi;
747 + struct argv_array env = ARGV_ARRAY_INIT;
748
749 hostinfo_init(&hi);
750
@@ -716,8 +766,9 @@ static int execute(void)
766 pktlen--;
767 }
768
769 + /* parse additional args hidden behind a NUL byte */
770 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);
772
773 for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
774 struct daemon_service *s = &(daemon_service[i]);
@@ -730,13 +781,15 @@ static int execute(void)
781 * Note: The directory here is probably context sensitive,
782 * and might depend on the actual service being performed.
783 */
733 - int rc = run_service(arg, s, &hi);
784 + int rc = run_service(arg, s, &hi, &env);
785 hostinfo_clear(&hi);
786 + argv_array_clear(&env);
787 return rc;
788 }
789 }
790
791 hostinfo_clear(&hi);
792 + argv_array_clear(&env);
793 logerror("Protocol error: '%s'", line);
794 return -1;
795 }