1799 lines · 50.0 KB · C
RawDownload
/* * The socket based protocol for setting up a connection with rsyncd. * * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org> * Copyright (C) 2001-2002 Martin Pool <mbp@samba.org> * Copyright (C) 2002-2022 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h"#include "itypes.h"#include "ifuncs.h" extern int quiet;extern int dry_run;extern int output_motd;extern int list_only;extern int am_sender;extern int am_server;extern int am_daemon;extern int am_chrooted;extern int am_root;extern int msgs2stderr;extern int rsync_port;extern int protect_args;extern int ignore_errors;extern int preserve_xattrs;extern int kluge_around_eof;extern int munge_symlinks;extern int use_secure_symlinks;extern int open_noatime;extern int sanitize_paths;extern int daemon_config_filter_file;extern int numeric_ids;extern int filesfrom_fd;extern int remote_protocol;extern int protocol_version;extern int io_timeout;extern int no_detach;extern int write_batch;extern int old_style_args;extern int default_af_hint;extern int logfile_format_has_i;extern int logfile_format_has_o_or_i;extern char *bind_address;extern char *config_file;extern char *logfile_format;extern char *files_from;extern char *tmpdir;extern char *early_input_file;extern struct chmod_mode_struct *chmod_modes;extern filter_rule_list daemon_filter_list;#ifdef ICONV_OPTIONextern char *iconv_opt;extern iconv_t ic_send, ic_recv;#endifextern uid_t our_uid;extern gid_t our_gid; char *auth_user;char *daemon_auth_choices;/* read_args() enforces MAX_DAEMON_ARGS and reports "too many daemon arguments" * before a daemon client can grow argv without bound. */int read_only = 0;int module_id = -1;int pid_file_fd = -1;int early_input_len = 0;char *early_input = NULL;pid_t namecvt_pid = 0;struct chmod_mode_struct *daemon_chmod_modes; #define EARLY_INPUT_CMD "#early_input="#define EARLY_INPUT_CMDLEN (sizeof EARLY_INPUT_CMD - 1) /* Fallback bound on each peer-driven daemon handshake phase when no positive * "timeout" is configured.  A module value can shorten the pre-auth and * argument-read phases, but cannot extend either beyond this limit. */#define DAEMON_HANDSHAKE_TIMEOUT 60 static int daemon_handshake_timeout(int module){	int timeout = lp_timeout(module); 	/* "timeout" is parsed with atoi(), so negative values are possible. */	if (timeout <= 0 || timeout > DAEMON_HANDSHAKE_TIMEOUT)		timeout = DAEMON_HANDSHAKE_TIMEOUT;	return timeout;} /* module_dirlen is the length of the module_dir string when in daemon * mode and module_dir is not "/"; otherwise 0.  (Note that a chroot- * enabled module can have a non-"/" module_dir these days.) */char *module_dir = NULL;unsigned int module_dirlen = 0;/* An fd held open on the served module root, captured while the daemon is still * positioned there (and privileged) -- so the sender's directory scan can be * confined beneath the module by resolving module-relative paths against this fd, * without re-walking (and re-permission-checking) the absolute module path as the * dropped-privilege module uid.  -1 when not a daemon or not yet captured. */int module_dirfd = -1; char *full_module_path; static int rl_nulls = 0;static int namecvt_fd_req = -1, namecvt_fd_ans = -1; #ifdef HAVE_SIGACTIONstatic struct sigaction sigact;#endif static item_list gid_list = EMPTY_ITEM_LIST; /* Used when "reverse lookup" is off. */const char undetermined_hostname[] = "UNDETERMINED"; /** * Run a client connected to an rsyncd.  The alternative to this * function for remote-shell connections is do_cmd(). * * After negotiating which module to use and reading the server's * motd, this hands over to client_run().  Telling the server the * module will cause it to chroot/setuid/etc. * * Instead of doing a transfer, the client may at this stage instead * get a listing of remote modules and exit. * * @return -1 for error in startup, or the result of client_run(). * Either way, it eventually gets passed to exit_cleanup(). **/int start_socket_client(char *host, int remote_argc, char *remote_argv[],			int argc, char *argv[]){	int fd, ret;	char *p, *user = NULL; 	/* This is redundant with code in start_inband_exchange(), but this	 * short-circuits a problem in the client before we open a socket,	 * and the extra check won't hurt. */	if (**remote_argv == '/') {		rprintf(FERROR,			"ERROR: The remote path must start with a module name not a /\n");		return -1;	} 	if ((p = strrchr(host, '@')) != NULL) {		user = host;		host = p+1;		*p = '\0';	} 	fd = open_socket_out_wrapped(host, rsync_port, bind_address, default_af_hint);	if (fd == -1)		exit_cleanup(RERR_SOCKETIO); #ifdef ICONV_CONST	setup_iconv();#endif 	ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv); 	return ret ? ret : client_run(fd, fd, -1, argc, argv);} static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client){	int remote_sub = -1;	int our_sub = get_subprotocol_version(); 	output_daemon_greeting(f_out, am_client);	if (!am_client) {		char *motd = lp_motd_file();		if (motd && *motd) {			/* 'motd file = PATH': motd content is sent to every client, so			 * a planted symlink would leak the target's bytes.  Refuse			 * symlinks not owned by uid 0 or our euid. */			int motd_fd = open_no_attacker_symlinks(motd, O_RDONLY, 0);			FILE *f = motd_fd >= 0 ? fdopen(motd_fd, "r") : NULL;			if (!f && motd_fd >= 0) close(motd_fd);			while (f && !feof(f)) {				int len = fread(buf, 1, bufsiz - 1, f);				if (len > 0)					write_buf(f_out, buf, len);			}			if (f)				fclose(f);			write_sbuf(f_out, "\n");		}	} 	/* This strips the \n. */	if (!read_line_old(f_in, buf, bufsiz, 0)) {		if (am_client)			rprintf(FERROR, "rsync: did not see server greeting\n");		return -1;	} 	if (sscanf(buf, "@RSYNCD: %d.%d", &remote_protocol, &remote_sub) < 1) {		if (am_client)			rprintf(FERROR, "rsync: server sent \"%s\" rather than greeting\n", buf);		else			io_printf(f_out, "@ERROR: protocol startup error\n");		return -1;	} 	if (remote_sub < 0) {		if (remote_protocol >= 30) {			if (am_client)				rprintf(FERROR, "rsync: the server omitted the subprotocol value: %s\n", buf);			else				io_printf(f_out, "@ERROR: your client omitted the subprotocol value: %s\n", buf);			return -1;		}		remote_sub = 0;	} 	daemon_auth_choices = strchr(buf + 9, ' ');	if (daemon_auth_choices) {		char *cp;		daemon_auth_choices = strdup(daemon_auth_choices + 1);		if ((cp = strchr(daemon_auth_choices, '\n')) != NULL)			*cp = '\0';	} else if (remote_protocol > 31) {		if (am_client)			rprintf(FERROR, "rsync: the server omitted the digest name list: %s\n", buf);		else			io_printf(f_out, "@ERROR: your client omitted the digest name list: %s\n", buf);		return -1;	} 	if (protocol_version > remote_protocol) {		protocol_version = remote_protocol;		if (remote_sub)			protocol_version--;	} else if (protocol_version == remote_protocol) {		if (remote_sub != our_sub)			protocol_version--;	}#if SUBPROTOCOL_VERSION != 0	else if (protocol_version < remote_protocol) {		if (our_sub)			protocol_version--;	}#endif 	if (protocol_version >= 30)		rl_nulls = 1; 	return 0;} int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char *argv[]){	int i, modlen;	char line[BIGPATHBUFLEN];	char *sargs[MAX_ARGS];	int sargc = 0;	char *p, *modname; 	assert(argc > 0 && *argv != NULL); 	if (**argv == '/') {		rprintf(FERROR,			"ERROR: The remote path must start with a module name\n");		return -1;	} 	if (!(p = strchr(*argv, '/')))		modlen = strlen(*argv);	else		modlen = p - *argv; 	modname = new_array(char, modlen+1+1); /* room for '/' & '\0' */	strlcpy(modname, *argv, modlen + 1);	modname[modlen] = '/';	modname[modlen+1] = '\0'; 	if (!user)		user = getenv("USER");	if (!user)		user = getenv("LOGNAME"); 	if (exchange_protocols(f_in, f_out, line, sizeof line, 1) < 0) {		free(modname);		return -1;	} 	if (early_input_file) {		STRUCT_STAT st;		/* --early-input-file=PATH: refuse symlinks not owned by uid 0 or		 * our euid anywhere in the path. */		int ei_fd = open_no_attacker_symlinks(early_input_file, O_RDONLY, 0);		FILE *f = ei_fd >= 0 ? fdopen(ei_fd, "rb") : NULL;		if (!f && ei_fd >= 0) close(ei_fd);		if (!f || do_fstat(fileno(f), &st) < 0) {			rsyserr(FERROR, errno, "failed to open %s", early_input_file);			if (f)				fclose(f);			free(modname);			return -1;		}		early_input_len = st.st_size;		if (early_input_len > (int)sizeof line) {			rprintf(FERROR, "%s is > %d bytes.\n", early_input_file, (int)sizeof line);			fclose(f);			free(modname);			return -1;		}		if (early_input_len > 0) {			io_printf(f_out, EARLY_INPUT_CMD "%d\n", early_input_len);			while (early_input_len > 0) {				int len;				if (feof(f)) {					rprintf(FERROR, "Early EOF in %s\n", early_input_file);					fclose(f);					free(modname);					return -1;				}				len = fread(line, 1, early_input_len, f);				if (len > 0) {					write_buf(f_out, line, len);					early_input_len -= len;				}			}		}		fclose(f);	} 	server_options(sargs, &sargc); 	if (sargc >= MAX_ARGS - 2)		goto arg_overflow; 	sargs[sargc++] = "."; 	if (!old_style_args)		snprintf(line, sizeof line, " %.*s/", modlen, modname); 	while (argc > 0) {		if (sargc >= MAX_ARGS - 1) {		  arg_overflow:			rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");			exit_cleanup(RERR_SYNTAX);		}		if (strncmp(*argv, modname, modlen) == 0 && argv[0][modlen] == '\0')			sargs[sargc++] = modname; /* we send "modname/" */		else {			char *arg = *argv;			int extra_chars = *arg == '-' ? 2 : 0; /* a leading dash needs a "./" prefix. */			/* If --old-args was not specified, make sure that the arg won't split at a mod name! */			if (!old_style_args && (p = strstr(arg, line)) != NULL) {				do {					extra_chars += 2;				} while ((p = strstr(p+1, line)) != NULL);			}			if (extra_chars) {				char *f = arg;				char *t = arg = new_array(char, strlen(arg) + extra_chars + 1);				if (*f == '-') {					*t++ = '.';					*t++ = '/';				}				while (*f) {					if (*f == ' ' && strncmp(f, line, modlen+2) == 0) {						*t++ = '[';						*t++ = *f++;						*t++ = ']';					} else						*t++ = *f++;				}				*t = '\0';			}			sargs[sargc++] = arg;		}		argv++;		argc--;	} 	sargs[sargc] = NULL; 	if (DEBUG_GTE(CMD, 1))		print_child_argv("sending daemon args:", sargs); 	io_printf(f_out, "%.*s\n", modlen, modname); 	/* Old servers may just drop the connection here,	 rather than sending a proper EXIT command.  Yuck. */	kluge_around_eof = list_only && protocol_version < 25 ? 1 : 0; 	while (1) {		if (!read_line_old(f_in, line, sizeof line, 0)) {			rprintf(FERROR, "rsync: didn't get server startup line\n");			free(modname);			return -1;		} 		if (strncmp(line,"@RSYNCD: AUTHREQD ",18) == 0) {			auth_client(f_out, user, line+18);			continue;		} 		if (strcmp(line,"@RSYNCD: OK") == 0)			break; 		if (strcmp(line,"@RSYNCD: EXIT") == 0) {			/* This is sent by recent versions of the			 * server to terminate the listing of modules.			 * We don't want to go on and transfer			 * anything; just exit. */			exit(0);		} 		if (strncmp(line, "@ERROR", 6) == 0) {			rprintf(FERROR, "%s\n", line);			/* This is always fatal; the server will now			 * close the socket. */			free(modname);			return -1;		} 		/* This might be a MOTD line or a module listing, but there is		 * no way to differentiate it.  The manpage mentions this. */		if (output_motd)			rprintf(FINFO, "%s\n", line);	}	kluge_around_eof = 0; 	if (rl_nulls) {		for (i = 0; i < sargc; i++) {			if (!sargs[i]) /* stop at --secluded-args NULL */				break;			write_sbuf(f_out, sargs[i]);			write_byte(f_out, 0);		}		write_byte(f_out, 0);	} else {		for (i = 0; i < sargc; i++)			io_printf(f_out, "%s\n", sargs[i]);		write_sbuf(f_out, "\n");	} 	if (protect_args)		send_protected_args(f_out, sargs); 	if (protocol_version < 23) {		if (protocol_version == 22 || !am_sender)			io_start_multiplex_in(f_in);	} 	free(modname); 	return 0;} #if defined HAVE_SETENV || defined HAVE_PUTENVstatic int read_arg_from_pipe(int fd, char *buf, int limit){	char *bp = buf, *eob = buf + limit - 1; 	while (1) {		int got = read(fd, bp, 1);		if (got != 1) {			if (got < 0 && errno == EINTR)				continue;			return -1;		}		if (*bp == '\0')			break;		if (bp < eob)			bp++;	}	*bp = '\0'; 	return bp - buf;}#endif void set_env_str(const char *var, const char *str){#ifdef HAVE_SETENV	if (setenv(var, str, 1) < 0)		out_of_memory("set_env_str");#else#ifdef HAVE_PUTENV	char *mem;	if (asprintf(&mem, "%s=%s", var, str) < 0)		out_of_memory("set_env_str");	putenv(mem);#else

Lines 1–500 of 1799.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t