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 inmaint49ba83fb6(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>
							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 */ | ||||
| } | ||||
|  | ||||
| typedef int (*daemon_service_fn)(void); | ||||
| typedef int (*daemon_service_fn)(const struct argv_array *env); | ||||
| struct daemon_service { | ||||
| 	const char *name; | ||||
| 	const char *config_name; | ||||
|  | @ -363,7 +363,7 @@ error_return: | |||
| } | ||||
|  | ||||
| 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; | ||||
| 	int enabled = service->enabled; | ||||
|  | @ -422,7 +422,7 @@ static int run_service(const char *dir, struct daemon_service *service, | |||
| 	 */ | ||||
| 	signal(SIGTERM, SIG_IGN); | ||||
|  | ||||
| 	return service->fn(); | ||||
| 	return service->fn(env); | ||||
| } | ||||
|  | ||||
| static void copy_to_log(int fd) | ||||
|  | @ -462,25 +462,34 @@ static int run_service_command(struct child_process *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; | ||||
| 	argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL); | ||||
| 	argv_array_pushf(&cld.args, "--timeout=%u", timeout); | ||||
|  | ||||
| 	argv_array_pushv(&cld.env_array, env->argv); | ||||
|  | ||||
| 	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; | ||||
| 	argv_array_push(&cld.args, "upload-archive"); | ||||
|  | ||||
| 	argv_array_pushv(&cld.env_array, env->argv); | ||||
|  | ||||
| 	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; | ||||
| 	argv_array_push(&cld.args, "receive-pack"); | ||||
|  | ||||
| 	argv_array_pushv(&cld.env_array, env->argv); | ||||
|  | ||||
| 	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. | ||||
|  * | ||||
|  * 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; | ||||
| 	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) | ||||
| 			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; | ||||
| 	char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT"); | ||||
| 	struct hostinfo hi; | ||||
| 	struct argv_array env = ARGV_ARRAY_INIT; | ||||
|  | ||||
| 	hostinfo_init(&hi); | ||||
|  | ||||
|  | @ -716,8 +766,9 @@ static int execute(void) | |||
| 		pktlen--; | ||||
| 	} | ||||
|  | ||||
| 	/* parse additional args hidden behind a NUL byte */ | ||||
| 	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++) { | ||||
| 		struct daemon_service *s = &(daemon_service[i]); | ||||
|  | @ -730,13 +781,15 @@ static int execute(void) | |||
| 			 * Note: The directory here is probably context sensitive, | ||||
| 			 * 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); | ||||
| 			argv_array_clear(&env); | ||||
| 			return rc; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	hostinfo_clear(&hi); | ||||
| 	argv_array_clear(&env); | ||||
| 	logerror("Protocol error: '%s'", line); | ||||
| 	return -1; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Brandon Williams
						Brandon Williams