rsync server over SSH [includes code patches]

JD Paul jdpaul at interstel.net
Tue Nov 20 14:54:44 EST 2001


Hi Folks --

I've recently had the need to add a piece of functionality to rsync
that allows one to run 'rsync --daemon' (rsync in "rsync server" mode)
over SSH.  My main goal was to be able to use some of the features
from rsyncd.conf(5) (notably modules) while still using SSH for
authentication and network transport.

Background:   I wanted to have the capability of setting up a
locked-down host (allowing only SSH access, with single-use keys) that
could be used as a staging server for file copying between domains
that need strict separation for security reasons. 

With my code changes (diffs included below), you can use "rsync
server" mode over ssh by using ':::' instead of ':' or '::' -- e.g. by
using one of the following invocations:

rsync [options] -e ssh source [...] [user@]host:::module[/path]
rsync [options] -e ssh [user@]host:::module[/path] dest

(Note that unless <user> is root, you won't be able to use many of the
features of /etc/rsyncd.conf, such as chroot or anything that requires
setuid().)

This is more powerful (security-wise) if you use single-use SSH keys.
For instance, if you create a user with a ~/.ssh/authorized_keys file
that contains

command="rsync --server --daemon --config-file=<file> ." <key> <comment>

then any connection using this SSH public key will cause sshd to
ignore the ssh client's requested command; it will instead run rsync
with the command shown above (which uses <file> instead of rsyncd.conf
for its configuration).  You can include multiple keys, each with its
own <file> for rsyncd.conf-style configuration; this provides access
control based on SSH authentication, including the possibility of
separate modules and separate logs for each key. 

The client-side invocation would be exactly the same as above:

rsync [options] -e ssh source [...] [user@]host:::module[/path]
rsync [options] -e ssh [user@]host:::module[/path] dest

The existing transfer modes ('rsync [options] source [user@]host:[dest]' 
and 'rsync [options] source [user@]host::module[/dest]') are
unaffected by the addition of the ':::' transfer mode.

Context diffs (against 2.4.6) for the code and documentation changes
are provided below, after some notes and gory details.  

Share and enjoy --

					JD Paul
					jdpaul at interstel.net

P.S.  Does one have to explicitly petition to get changes merged into
the main development branch, or is submitting code changes considered
that petition?

--------------------------------------------------------------------------

NOTES:

This functionality was inquired about back in June of 2001 on
the rsync FAQ-o-matic pages:
http://rsync.samba.org/rsync/fom-serve/cache/88.html

Some of the conversations today (2001-11-19) on the list are apropos
as well; at least one person was asking if there is any way to get
modules with ssh.


GORY DETAILS:

The code changes are about 150 lines, plus 70 lines of man page
changes.  Most of the code changes were to several functions (e.g.
auth_server(), start_accept_loop(), rsync_module(), start_daemon()) to
make them handle two file descriptors (f_in and f_out) instead of one
bidirectional socket file descriptor (fd).  

Other changes include:

- main() will call start_daemon() instead of start_server() if the
  program is called with both '--server' and '--daemon'.  

- start_daemon() is modified to no longer be static (it needs to be
  visible in main.c) and to take two file descriptors.

- start_socket_client() had most of its code removed and turned into a
  new function, start_inband_exchange(), which is used by both
  start_socket_client() and start_client().  

- start_client() is modified to handle the ':::' syntax:  it calls
  start_inband_exchange() after making the rsh/ssh connection to the
  remote host. 

- A couple of global variables were added:  'run_inband' and
  'remote_user'.  'run_inband' is set if the ':::' syntax is used, and
  is used by server_options() when building the remote command (which
  will include both '--server' and '--daemon').  'remote_user' is set
  by using '--remote-user=USER' and is useful for distinguishing
  between the rsh/ssh-level user in user at host:::module/path and the
  user in an rsyncd.conf file on the remote server


-----------------------------<cut here>-----------------------------------

Index: authenticate.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/authenticate.c,v
retrieving revision 1.1.1.4
retrieving revision 1.3
diff -c -b -r1.1.1.4 -r1.3
*** authenticate.c	2001/10/19 04:10:00	1.1.1.4
--- authenticate.c	2001/10/24 22:00:38	1.3
***************
*** 202,208 ****
  
     otherwise return username
  */
! char *auth_server(int fd, int module, char *addr, char *leader)
  {
  	char *users = lp_auth_users(module);
  	char challenge[16];
--- 202,208 ----
  
     otherwise return username
  */
! char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
  {
  	char *users = lp_auth_users(module);
  	char challenge[16];
***************
*** 221,229 ****
  	
  	base64_encode(challenge, 16, b64_challenge);
  
! 	io_printf(fd,"%s%s\n", leader, b64_challenge);
  
! 	if (!read_line(fd, line, sizeof(line)-1)) {
  		return NULL;
  	}
  
--- 221,229 ----
  	
  	base64_encode(challenge, 16, b64_challenge);
  
! 	io_printf(f_out,"%s%s\n", leader, b64_challenge);
  
! 	if (!read_line(f_in, line, sizeof(line)-1)) {
  		return NULL;
  	}
  
Index: clientserver.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/clientserver.c,v
retrieving revision 1.1.1.4
retrieving revision 1.4
diff -c -b -r1.1.1.4 -r1.4
*** clientserver.c	2001/10/19 04:09:27	1.1.1.4
--- clientserver.c	2001/11/19 20:31:47	1.4
***************
*** 29,48 ****
  
  int start_socket_client(char *host, char *path, int argc, char *argv[])
  {
! 	int fd, i;
! 	char *sargs[MAX_ARGS];
! 	int sargc=0;
! 	char line[MAXPATHLEN];
  	char *p, *user=NULL;
- 	extern int remote_version;
- 	extern int am_sender;
  	extern struct in_addr socket_address;
- 
- 	if (argc == 0 && !am_sender) {
- 		extern int list_only;
- 		list_only = 1;
- 	}
  
  	if (*path == '/') {
  		rprintf(FERROR,"ERROR: The remote path must start with a module name\n");
  		return -1;
--- 29,42 ----
  
  int start_socket_client(char *host, char *path, int argc, char *argv[])
  {
! 	int fd;
! 	int ret;
  	char *p, *user=NULL;
  	extern struct in_addr socket_address;
  
+ 	/* this is redundant with code in start_inband_exchange(), but
+ 	   this short-circuits a problem before we open a socket, and 
+ 	   the extra check won't hurt */
  	if (*path == '/') {
  		rprintf(FERROR,"ERROR: The remote path must start with a module name\n");
  		return -1;
***************
*** 55,68 ****
  		*p = 0;
  	}
  
- 	if (!user) user = getenv("USER");
- 	if (!user) user = getenv("LOGNAME");
- 
  	fd = open_socket_out(host, rsync_port, &socket_address);
  	if (fd == -1) {
  		exit_cleanup(RERR_SOCKETIO);
  	}
  	
  	server_options(sargs,&sargc);
  
  	sargs[sargc++] = ".";
--- 49,95 ----
  		*p = 0;
  	}
  
  	fd = open_socket_out(host, rsync_port, &socket_address);
  	if (fd == -1) {
  		exit_cleanup(RERR_SOCKETIO);
  	}
  	
+ 	ret = start_inband_exchange(user, path, fd, fd, argc, argv);
+ 
+ 	return (ret < 0 ) ? ret : client_run(fd, fd, -1, argc, argv);
+ }
+ 
+ int start_inband_exchange(char *user, char *path, int f_in, int f_out, int argc, char *argv[])
+ {
+ 	int i;
+ 	char *sargs[MAX_ARGS];
+ 	int sargc = 0;
+ 	char line[MAXPATHLEN];
+ 	char *p;
+ 	extern int remote_version;
+ 	extern int am_sender;
+ 	extern int run_inband;
+ 	extern char *remote_user;
+ 
+ 	if (argc == 0 && !am_sender) {
+ 		extern int list_only;
+ 		list_only = 1;
+ 	}
+ 
+ 	if (*path == '/') {
+ 		rprintf(FERROR,"ERROR: The remote path must start with a module name\n");
+ 		return -1;
+ 	}
+ 
+ 	if (!user) user = getenv("USER");
+ 	if (!user) user = getenv("LOGNAME");
+ 	if (!remote_user) {
+ 		remote_user = user;
+ 	}
+ 
+ 	/* set run_inband to false since we need to build the true set of args;
+ 	   this is a no-op for socket_client mode */
+ 	run_inband = 0;
  	server_options(sargs,&sargc);
  
  	sargs[sargc++] = ".";
***************
*** 72,80 ****
  
  	sargs[sargc] = NULL;
  
! 	io_printf(fd,"@RSYNCD: %d\n", PROTOCOL_VERSION);
  
! 	if (!read_line(fd, line, sizeof(line)-1)) {
  		return -1;
  	}
  
--- 99,107 ----
  
  	sargs[sargc] = NULL;
  
! 	io_printf(f_out,"@RSYNCD: %d\n", PROTOCOL_VERSION);
  
! 	if (!read_line(f_in, line, sizeof(line)-1)) {
  		return -1;
  	}
  
***************
*** 84,99 ****
  
  	p = strchr(path,'/');
  	if (p) *p = 0;
! 	io_printf(fd,"%s\n",path);
  	if (p) *p = '/';
  
  	while (1) {
! 		if (!read_line(fd, line, sizeof(line)-1)) {
  			return -1;
  		}
  
  		if (strncmp(line,"@RSYNCD: AUTHREQD ",18) == 0) {
! 			auth_client(fd, user, line+18);
  			continue;
  		}
  
--- 111,126 ----
  
  	p = strchr(path,'/');
  	if (p) *p = 0;
! 	io_printf(f_out,"%s\n",path);
  	if (p) *p = '/';
  
  	while (1) {
! 		if (!read_line(f_in, line, sizeof(line)-1)) {
  			return -1;
  		}
  
  		if (strncmp(line,"@RSYNCD: AUTHREQD ",18) == 0) {
! 			auth_client(f_out, remote_user, line+18);
  			continue;
  		}
  
***************
*** 102,122 ****
  	}
  
  	for (i=0;i<sargc;i++) {
! 		io_printf(fd,"%s\n", sargs[i]);
  	}
! 	io_printf(fd,"\n");
  
  	if (remote_version < 23) {
  		if (remote_version == 22 || (remote_version > 17 && !am_sender))
! 			io_start_multiplex_in(fd);
  	}
  
! 	return client_run(fd, fd, -1, argc, argv);
! }
  
  
  
! static int rsync_module(int fd, int i)
  {
  	int argc=0;
  	char *argv[MAX_ARGS];
--- 129,149 ----
  	}
  
  	for (i=0;i<sargc;i++) {
! 		io_printf(f_out,"%s\n", sargs[i]);
  	}
! 	io_printf(f_out,"\n");
  
  	if (remote_version < 23) {
  		if (remote_version == 22 || (remote_version > 17 && !am_sender))
! 			io_start_multiplex_in(f_in);
  	}
  
! 	return 0;
  
+ }
  
  
! static int rsync_module(int f_in, int f_out, int i)
  {
  	int argc=0;
  	char *argv[MAX_ARGS];
***************
*** 125,170 ****
  	uid_t uid = (uid_t)-2;
  	gid_t gid = (gid_t)-2;
  	char *p;
! 	char *addr = client_addr(fd);
! 	char *host = client_name(fd);
  	char *name = lp_name(i);
  	int use_chroot = lp_use_chroot(i);
  	int start_glob=0;
  	int ret;
  	char *request=NULL;
  	extern int am_sender;
  	extern int remote_version;
  	extern int am_root;
  
  	if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
  		rprintf(FERROR,"rsync denied on module %s from %s (%s)\n",
! 			name, client_name(fd), client_addr(fd));
! 		io_printf(fd,"@ERROR: access denied to %s from %s (%s)\n",
! 			  name, client_name(fd), client_addr(fd));
  		return -1;
  	}
  
  	if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) {
  		if (errno) {
  			rprintf(FERROR,"failed to open lock file %s : %s\n",
  				lp_lock_file(i), strerror(errno));
! 			io_printf(fd,"@ERROR: failed to open lock file %s : %s\n",
  				  lp_lock_file(i), strerror(errno));
  		} else {
  			rprintf(FERROR,"max connections (%d) reached\n",
  				lp_max_connections(i));
! 			io_printf(fd,"@ERROR: max connections (%d) reached - try again later\n", lp_max_connections(i));
  		}
  		return -1;
  	}
  
  	
! 	auth_user = auth_server(fd, i, addr, "@RSYNCD: AUTHREQD ");
  
  	if (!auth_user) {
  		rprintf(FERROR,"auth failed on module %s from %s (%s)\n",
! 			name, client_name(fd), client_addr(fd));
! 		io_printf(fd,"@ERROR: auth failed on module %s\n",name);
  		return -1;		
  	}
  
--- 152,213 ----
  	uid_t uid = (uid_t)-2;
  	gid_t gid = (gid_t)-2;
  	char *p;
! 	char *addr;
! 	char *host;
  	char *name = lp_name(i);
  	int use_chroot = lp_use_chroot(i);
  	int start_glob=0;
  	int ret;
  	char *request=NULL;
  	extern int am_sender;
+ 	extern int am_server;
+ 	extern int am_daemon;
  	extern int remote_version;
  	extern int am_root;
  
+ 	if (is_a_socket(f_in)) {
+ 		addr = client_addr(f_in);
+ 		host = client_name(f_in);
+ 	} else {
+ 		char *ssh_client = getenv("SSH_CLIENT");
+ 		addr = ssh_client ? ssh_client : "n/a";
+ 		host = "remote shell connection";
+ 	}
+ 
  	if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
  		rprintf(FERROR,"rsync denied on module %s from %s (%s)\n",
! 			name, host, addr);
! 		io_printf(f_out,"@ERROR: access denied to %s from %s (%s)\n",
! 			  name, host, addr);
  		return -1;
  	}
  
+ 	if (am_daemon && am_server) {
+ 		rprintf(FINFO, "rsync allowed access on module %s from %s (%s)\n",
+ 			name, host, addr);
+ 	}
+ 
  	if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) {
  		if (errno) {
  			rprintf(FERROR,"failed to open lock file %s : %s\n",
  				lp_lock_file(i), strerror(errno));
! 			io_printf(f_out,"@ERROR: failed to open lock file %s : %s\n",
  				  lp_lock_file(i), strerror(errno));
  		} else {
  			rprintf(FERROR,"max connections (%d) reached\n",
  				lp_max_connections(i));
! 			io_printf(f_out,"@ERROR: max connections (%d) reached - try again later\n", lp_max_connections(i));
  		}
  		return -1;
  	}
  
  	
! 	auth_user = auth_server(f_in, f_out, i, addr, "@RSYNCD: AUTHREQD ");
  
  	if (!auth_user) {
  		rprintf(FERROR,"auth failed on module %s from %s (%s)\n",
! 			name, host, addr);
! 		io_printf(f_out,"@ERROR: auth failed on module %s\n",name);
  		return -1;		
  	}
  
***************
*** 177,183 ****
  		if (!name_to_uid(p, &uid)) {
  			if (!isdigit(*p)) {
  				rprintf(FERROR,"Invalid uid %s\n", p);
! 				io_printf(fd,"@ERROR: invalid uid\n");
  				return -1;
  			} 
  			uid = atoi(p);
--- 220,226 ----
  		if (!name_to_uid(p, &uid)) {
  			if (!isdigit(*p)) {
  				rprintf(FERROR,"Invalid uid %s\n", p);
! 				io_printf(f_out,"@ERROR: invalid uid\n");
  				return -1;
  			} 
  			uid = atoi(p);
***************
*** 187,193 ****
  		if (!name_to_gid(p, &gid)) {
  			if (!isdigit(*p)) {
  				rprintf(FERROR,"Invalid gid %s\n", p);
! 				io_printf(fd,"@ERROR: invalid gid\n");
  				return -1;
  			} 
  			gid = atoi(p);
--- 230,236 ----
  		if (!name_to_gid(p, &gid)) {
  			if (!isdigit(*p)) {
  				rprintf(FERROR,"Invalid gid %s\n", p);
! 				io_printf(f_out,"@ERROR: invalid gid\n");
  				return -1;
  			} 
  			gid = atoi(p);
***************
*** 211,230 ****
  	if (use_chroot) {
  		if (chroot(lp_path(i))) {
  			rprintf(FERROR,"chroot %s failed\n", lp_path(i));
! 			io_printf(fd,"@ERROR: chroot failed\n");
  			return -1;
  		}
  
  		if (!push_dir("/", 0)) {
  			rprintf(FERROR,"chdir %s failed\n", lp_path(i));
! 			io_printf(fd,"@ERROR: chdir failed\n");
  			return -1;
  		}
  
  	} else {
  		if (!push_dir(lp_path(i), 0)) {
  			rprintf(FERROR,"chdir %s failed\n", lp_path(i));
! 			io_printf(fd,"@ERROR: chdir failed\n");
  			return -1;
  		}
  		sanitize_paths = 1;
--- 254,273 ----
  	if (use_chroot) {
  		if (chroot(lp_path(i))) {
  			rprintf(FERROR,"chroot %s failed\n", lp_path(i));
! 			io_printf(f_out,"@ERROR: chroot failed\n");
  			return -1;
  		}
  
  		if (!push_dir("/", 0)) {
  			rprintf(FERROR,"chdir %s failed\n", lp_path(i));
! 			io_printf(f_out,"@ERROR: chdir failed\n");
  			return -1;
  		}
  
  	} else {
  		if (!push_dir(lp_path(i), 0)) {
  			rprintf(FERROR,"chdir %s failed\n", lp_path(i));
! 			io_printf(f_out,"@ERROR: chdir failed\n");
  			return -1;
  		}
  		sanitize_paths = 1;
***************
*** 233,257 ****
  	if (am_root) {
  		if (setgid(gid)) {
  			rprintf(FERROR,"setgid %d failed\n", gid);
! 			io_printf(fd,"@ERROR: setgid failed\n");
  			return -1;
  		}
  
  		if (setuid(uid)) {
  			rprintf(FERROR,"setuid %d failed\n", uid);
! 			io_printf(fd,"@ERROR: setuid failed\n");
  			return -1;
  		}
  
  		am_root = (getuid() == 0);
  	}
  
! 	io_printf(fd,"@RSYNCD: OK\n");
  
  	argv[argc++] = "rsyncd";
  
  	while (1) {
! 		if (!read_line(fd, line, sizeof(line)-1)) {
  			return -1;
  		}
  
--- 276,300 ----
  	if (am_root) {
  		if (setgid(gid)) {
  			rprintf(FERROR,"setgid %d failed\n", gid);
! 			io_printf(f_out,"@ERROR: setgid failed\n");
  			return -1;
  		}
  
  		if (setuid(uid)) {
  			rprintf(FERROR,"setuid %d failed\n", uid);
! 			io_printf(f_out,"@ERROR: setuid failed\n");
  			return -1;
  		}
  
  		am_root = (getuid() == 0);
  	}
  
! 	io_printf(f_out,"@RSYNCD: OK\n");
  
  	argv[argc++] = "rsyncd";
  
  	while (1) {
! 		if (!read_line(f_in, line, sizeof(line)-1)) {
  			return -1;
  		}
  
***************
*** 322,328 ****
  
  	if (remote_version < 23) {
  		if (remote_version == 22 || (remote_version > 17 && am_sender))
! 			io_start_multiplex_out(fd);
  	}
  
  	if (!ret) {
--- 365,371 ----
  
  	if (remote_version < 23) {
  		if (remote_version == 22 || (remote_version > 17 && am_sender))
! 			io_start_multiplex_out(f_out);
  	}
  
  	if (!ret) {
***************
*** 334,340 ****
  		io_timeout = lp_timeout(i);
  	}
  
! 	start_server(fd, fd, argc, argp);
  
  	return 0;
  }
--- 377,383 ----
  		io_timeout = lp_timeout(i);
  	}
  
! 	start_server(f_in, f_out, argc, argp);
  
  	return 0;
  }
***************
*** 351,376 ****
  		    io_printf(fd, "%-15s\t%s\n", lp_name(i), lp_comment(i));
  }
  
! /* this is called when a socket connection is established to a client
     and we want to start talking. The setup of the system is done from
     here */
! static int start_daemon(int fd)
  {
  	char line[200];
  	char *motd;
  	int i = -1;
  	extern char *config_file;
  	extern int remote_version;
  
  	if (!lp_load(config_file, 0)) {
  		exit_cleanup(RERR_SYNTAX);
  	}
  
! 	set_socket_options(fd,"SO_KEEPALIVE");
! 	set_socket_options(fd,lp_socket_options());
! 	set_nonblocking(fd);
  
! 	io_printf(fd,"@RSYNCD: %d\n", PROTOCOL_VERSION);
  
  	motd = lp_motd_file();
  	if (motd && *motd) {
--- 394,422 ----
  		    io_printf(fd, "%-15s\t%s\n", lp_name(i), lp_comment(i));
  }
  
! /* this is called when a connection is established to a client
     and we want to start talking. The setup of the system is done from
     here */
! int start_daemon(int f_in, int f_out)
  {
  	char line[200];
  	char *motd;
  	int i = -1;
  	extern char *config_file;
  	extern int remote_version;
+ 	extern int am_server;
  
  	if (!lp_load(config_file, 0)) {
  		exit_cleanup(RERR_SYNTAX);
  	}
  
! 	if ( !am_server ) {
! 		set_socket_options(f_in,"SO_KEEPALIVE");
! 		set_socket_options(f_in,lp_socket_options());
! 		set_nonblocking(f_in);
! 	}
  
! 	io_printf(f_out,"@RSYNCD: %d\n", PROTOCOL_VERSION);
  
  	motd = lp_motd_file();
  	if (motd && *motd) {
***************
*** 379,428 ****
  			int len = fread(line, 1, sizeof(line)-1, f);
  			if (len > 0) {
  				line[len] = 0;
! 				io_printf(fd,"%s", line);
  			}
  		}
  		if (f) fclose(f);
! 		io_printf(fd,"\n");
  	}
  
! 	if (!read_line(fd, line, sizeof(line)-1)) {
  		return -1;
  	}
  
  	if (sscanf(line,"@RSYNCD: %d", &remote_version) != 1) {
! 		io_printf(fd,"@ERROR: protocol startup error\n");
  		return -1;
  	}	
  
  	while (i == -1) {
  		line[0] = 0;
! 		if (!read_line(fd, line, sizeof(line)-1)) {
  			return -1;
  		}
  
  		if (!*line || strcmp(line,"#list")==0) {
! 			send_listing(fd);
  			return -1;
  		} 
  
  		if (*line == '#') {
  			/* it's some sort of command that I don't understand */
! 			io_printf(fd,"@ERROR: Unknown command '%s'\n", line);
  			return -1;
  		}
  
  		i = lp_number(line);
  		if (i == -1) {
! 			io_printf(fd,"@ERROR: Unknown module '%s'\n", line);
  			return -1;
  		}
  	}
  
! 	return rsync_module(fd, i);
  }
  
- 
  int daemon_main(void)
  {
  	extern char *config_file;
--- 425,473 ----
  			int len = fread(line, 1, sizeof(line)-1, f);
  			if (len > 0) {
  				line[len] = 0;
! 				io_printf(f_out,"%s", line);
  			}
  		}
  		if (f) fclose(f);
! 		io_printf(f_out,"\n");
  	}
  
! 	if (!read_line(f_in, line, sizeof(line)-1)) {
  		return -1;
  	}
  
  	if (sscanf(line,"@RSYNCD: %d", &remote_version) != 1) {
! 		io_printf(f_out,"@ERROR: protocol startup error\n");
  		return -1;
  	}	
  
  	while (i == -1) {
  		line[0] = 0;
! 		if (!read_line(f_in, line, sizeof(line)-1)) {
  			return -1;
  		}
  
  		if (!*line || strcmp(line,"#list")==0) {
! 			send_listing(f_out);
  			return -1;
  		} 
  
  		if (*line == '#') {
  			/* it's some sort of command that I don't understand */
! 			io_printf(f_out,"@ERROR: Unknown command '%s'\n", line);
  			return -1;
  		}
  
  		i = lp_number(line);
  		if (i == -1) {
! 			io_printf(f_out,"@ERROR: Unknown module '%s'\n", line);
  			return -1;
  		}
  	}
  
! 	return rsync_module(f_in, f_out, i);
  }
  
  int daemon_main(void)
  {
  	extern char *config_file;
***************
*** 440,446 ****
  			open("/dev/null", O_RDWR);
  		}
  
! 		return start_daemon(STDIN_FILENO);
  	}
  
  	become_daemon();
--- 485,491 ----
  			open("/dev/null", O_RDWR);
  		}
  
! 		return start_daemon(STDIN_FILENO, STDIN_FILENO);
  	}
  
  	become_daemon();
Index: main.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/main.c,v
retrieving revision 1.1.1.6
retrieving revision 1.4
diff -c -b -r1.1.1.6 -r1.4
*** main.c	2001/10/19 04:09:33	1.1.1.6
--- main.c	2001/11/19 20:31:47	1.4
***************
*** 126,131 ****
--- 126,132 ----
      extern int local_server;
      extern char *rsync_path;
      extern int blocking_io;
+     extern int run_inband;
  
      if (!local_server) {
          if (!cmd)
***************
*** 165,172 ****
--- 166,175 ----
  
      args[argc++] = ".";
  
+     if (!run_inband) {
          if (path && *path)
              args[argc++] = path;
+     }
  
      args[argc] = NULL;
  
***************
*** 555,560 ****
--- 558,564 ----
      extern int am_sender;
      extern char *shell_cmd;
      extern int rsync_port;
+     extern int run_inband;
  
      if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) {
          char *host, *path;
***************
*** 578,584 ****
  	p = find_colon(argv[0]);
  
  	if (p) {
! 		if (p[1] == ':') {
  			*p = 0;
  			return start_socket_client(argv[0], p+2, argc-1, argv+1);
  		}
--- 582,591 ----
      p = find_colon(argv[0]);
  
      if (p) {
!         if ((p[1] == ':') && (p[2] == ':')) {
!             *p++ = 0; *p++ = 0; /* leave it pointing to last ':' */
!             run_inband = 1;
!         } else if (p[1] == ':') {
              *p = 0;
              return start_socket_client(argv[0], p+2, argc-1, argv+1);
          }
***************
*** 600,605 ****
--- 607,615 ----
          p = find_colon(argv[argc-1]);
          if (!p) {
              local_server = 1;
+         } else if ((p[1] == ':') && (p[2] == ':')) {
+             *p++ = 0; *p++ = 0; /* leave it pointing to last ':' */
+             run_inband = 1;
          } else if (p[1] == ':') {
              *p = 0;
              return start_socket_client(argv[argc-1], p+2, argc-1, argv);
***************
*** 650,655 ****
--- 660,675 ----
  
      pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,&f_in,&f_out);
  
+     /* if we're running in-band, we need to do the RSYNCD stuff first */
+     if (run_inband) {
+         int tmpret;
+         tmpret = start_inband_exchange(shell_user, shell_path,
+                                        f_in, f_out, argc, argv);
+         if ( tmpret < 0 ) {
+             return tmpret;
+         }
+     }
+ 
      ret = client_run(f_in, f_out, pid, argc, argv);
  
      fflush(stdout);
***************
*** 712,718 ****
  	   that implement getcwd that way "pwd" can't be found after chroot. */
  	push_dir(NULL,0);
  
! 	if (am_daemon) {
  		return daemon_main();
  	}
  
--- 732,738 ----
         that implement getcwd that way "pwd" can't be found after chroot. */
      push_dir(NULL,0);
  
!     if (am_daemon && !am_server) {
          return daemon_main();
      }
  
***************
*** 734,741 ****
--- 754,765 ----
      if (am_server) {
          set_nonblocking(STDIN_FILENO);
          set_nonblocking(STDOUT_FILENO);
+         if (am_daemon) {
+             return start_daemon(STDIN_FILENO, STDOUT_FILENO);
+         } else {
              start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
          }
+     }
  
      return start_client(argc, argv);
  }
Index: options.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/options.c,v
retrieving revision 1.1.1.4
retrieving revision 1.4
diff -c -b -r1.1.1.4 -r1.4
*** options.c	2001/10/19 04:09:46	1.1.1.4
--- options.c	2001/11/20 02:55:39	1.4
***************
*** 53,58 ****
--- 53,59 ----
  int module_id = -1;
  int am_server = 0;
  int am_sender=0;
+ int run_inband = 0;
  int recurse = 0;
  int am_daemon=0;
  int do_stats=0;
***************
*** 84,89 ****
--- 85,91 ----
  char *rsync_path = RSYNC_NAME;
  char *backup_dir = NULL;
  int rsync_port = RSYNC_PORT;
+ char *remote_user = NULL;
  
  int verbose = 0;
  int quiet = 0;
***************
*** 104,109 ****
--- 106,113 ----
  
    rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n");
    rprintf(F,"  or   rsync [OPTION]... [USER@]HOST:SRC DEST\n");
+   rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST:::DEST\n");
+   rprintf(F,"  or   rsync [OPTION]... [USER@]HOST:::SRC DEST\n");
    rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... DEST\n");
    rprintf(F,"  or   rsync [OPTION]... [USER@]HOST::SRC [DEST]\n");
    rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n");
***************
*** 165,170 ****
--- 169,175 ----
    rprintf(F,"     --daemon                run as a rsync daemon\n");  
    rprintf(F,"     --address               bind to the specified address\n");  
    rprintf(F,"     --config=FILE           specify alternate rsyncd.conf file\n");  
+   rprintf(F,"     --remote-user=USER      remote username for ::: mode\n");
    rprintf(F,"     --port=PORT             specify alternate rsyncd port number\n");
    rprintf(F,"     --blocking-io           use blocking IO for the remote shell\n");  
    rprintf(F,"     --stats                 give some file transfer stats\n");  
***************
*** 188,194 ****
        OPT_LOG_FORMAT, OPT_PASSWORD_FILE, OPT_SIZE_ONLY, OPT_ADDRESS,
        OPT_DELETE_AFTER, OPT_EXISTING, OPT_MAX_DELETE, OPT_BACKUP_DIR, 
        OPT_IGNORE_ERRORS, OPT_BWLIMIT, OPT_BLOCKING_IO,
!       OPT_MODIFY_WINDOW};
  
  static char *short_options = "oblLWHpguDCtcahvqrRIxnSe:B:T:zP";
  
--- 193,199 ----
        OPT_LOG_FORMAT, OPT_PASSWORD_FILE, OPT_SIZE_ONLY, OPT_ADDRESS,
        OPT_DELETE_AFTER, OPT_EXISTING, OPT_MAX_DELETE, OPT_BACKUP_DIR, 
        OPT_IGNORE_ERRORS, OPT_BWLIMIT, OPT_BLOCKING_IO,
!       OPT_MODIFY_WINDOW, OPT_REMOTE_USER};
  
  static char *short_options = "oblLWHpguDCtcahvqrRIxnSe:B:T:zP";
  
***************
*** 249,254 ****
--- 254,260 ----
    {"ignore-errors",0,     0,   OPT_IGNORE_ERRORS},
    {"blocking-io" ,0,     0,    OPT_BLOCKING_IO},
    {"config",      1,     0,    OPT_CONFIG},
+   {"remote-user", 1,     0,    OPT_REMOTE_USER},
    {"port",        1,     0,    OPT_PORT},
    {"log-format",  1,     0,    OPT_LOG_FORMAT},
    {"bwlimit",	  1,	 0,    OPT_BWLIMIT},
***************
*** 571,576 ****
--- 577,586 ----
  			config_file = optarg;
  			break;
  
+ 		case OPT_REMOTE_USER:
+ 			remote_user = optarg;
+ 			break;
+ 
  		case OPT_PORT:
  			rsync_port = atoi(optarg);
  			break;
***************
*** 620,625 ****
--- 630,642 ----
  	int i, x;
  
  	args[ac++] = "--server";
+ 
+ 	if (run_inband) {
+ 		args[ac++] = "--daemon";
+ 		*argc = ac;
+ 		/* if we're passing --daemon, we're done */
+ 		return;
+ 	}
  
  	if (!am_sender)
  		args[ac++] = "--sender";
Index: proto.h
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/proto.h,v
retrieving revision 1.1.1.6
retrieving revision 1.4
diff -c -b -r1.1.1.6 -r1.4
*** proto.h	2001/10/19 04:09:57	1.1.1.6
--- proto.h	2001/11/19 20:31:47	1.4
***************
*** 1,7 ****
  /* This file is automatically generated with "make proto". DO NOT EDIT */
  
  int allow_access(char *addr, char *host, char *allow_list, char *deny_list);
! char *auth_server(int fd, int module, char *addr, char *leader);
  void auth_client(int fd, char *user, char *challenge);
  int make_backup(char *fname);
  uint32 get_checksum1(char *buf1,int len);
--- 1,7 ----
  /* This file is automatically generated with "make proto". DO NOT EDIT */
  
  int allow_access(char *addr, char *host, char *allow_list, char *deny_list);
! char *auth_server(int f_in, int f_out, int module, char *addr, char *leader);
  void auth_client(int fd, char *user, char *challenge);
  int make_backup(char *fname);
  uint32 get_checksum1(char *buf1,int len);
***************
*** 17,22 ****
--- 17,24 ----
  		 struct map_struct *buf, int fd1, int fd2);
  void cleanup_set_pid(int pid);
  int start_socket_client(char *host, char *path, int argc, char *argv[]);
+ int start_inband_exchange(char *user, char *path, int f_in, int f_out, int argc, char *argv[]);
+ int start_daemon(int f_in, int f_out);
  int daemon_main(void);
  void setup_protocol(int f_out,int f_in);
  int claim_connection(char *fname,int max_connections);
***************
*** 145,151 ****
  void send_files(struct file_list *flist,int f_out,int f_in);
  int open_socket_out(char *host, int port, struct in_addr *address);
  int is_a_socket(int fd);
! void start_accept_loop(int port, int (*fn)(int ));
  void set_socket_options(int fd, char *options);
  void become_daemon(void);
  char *client_addr(int fd);
--- 147,153 ----
  void send_files(struct file_list *flist,int f_out,int f_in);
  int open_socket_out(char *host, int port, struct in_addr *address);
  int is_a_socket(int fd);
! void start_accept_loop(int port, int (*fn)(int, int));
  void set_socket_options(int fd, char *options);
  void become_daemon(void);
  char *client_addr(int fd);
Index: rsync.1
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/rsync.1,v
retrieving revision 1.1.1.6
retrieving revision 1.4
diff -c -b -r1.1.1.6 -r1.4
*** rsync.1	2001/10/19 04:09:42	1.1.1.6
--- rsync.1	2001/11/20 03:31:11	1.4
***************
*** 1,4 ****
! .TH "rsync" "1" "1 Mar 1999" "" "" 
  .SH "NAME" 
  rsync \- faster, flexible replacement for rcp
  .SH "SYNOPSIS" 
--- 1,4 ----
! .TH "rsync" "1" "31 Oct 2001" "" "" 
  .SH "NAME" 
  rsync \- faster, flexible replacement for rcp
  .SH "SYNOPSIS" 
***************
*** 15,20 ****
--- 15,24 ----
  .PP 
  rsync [OPTION]\&.\&.\&. rsync://[USER@]HOST[:PORT]/SRC [DEST]
  .PP 
+ rsync [OPTION]\&.\&.\&. [USER@]HOST:::SRC [DEST]
+ .PP 
+ rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. [USER@]HOST:::DEST
+ .PP 
  .SH "DESCRIPTION" 
  .PP 
  rsync is a program that behaves in much the same way that rcp does,
***************
*** 47,53 ****
  .PP 
  .SH "GENERAL" 
  .PP 
! There are six different ways of using rsync\&. They are:
  .PP 
  .IP o 
  for copying local files\&. This is invoked when neither
--- 51,57 ----
  .PP 
  .SH "GENERAL" 
  .PP 
! There are eight different ways of using rsync\&. They are:
  .PP 
  .IP o 
  for copying local files\&. This is invoked when neither
***************
*** 75,80 ****
--- 79,95 ----
  separator\&. 
  .IP 
  .IP o 
+ for copying from a remote machine using a remote shell program as the
+ transport, using rsync server on the remote machine\&.  This is
+ invoked when the source path contains a ::: separator.
+ .IP 
+ .IP o 
+ for copying from the local machine to a remote machine using a remote
+ shell program as the transport, using rsync server on the remote
+ machine\&.  This is invoked when the destination path contains a :::
+ separator\&.
+ .IP 
+ .IP o 
  for listing files on a remote machine\&. This is done the
  same way as rsync transfers except that you leave off the
  local destination\&.  
***************
*** 155,161 ****
  transport\&. In this case you will connect to a remote rsync server
  running on TCP port 873\&. 
  .PP 
! You may establish the connetcion via a web proxy by setting the
  environment variable RSYNC_PROXY to a hostname:port pair pointing to
  your web proxy\&. Note that your web proxy must allow proxying to port
  873, this must be configured in your proxy servers ruleset\&.
--- 170,176 ----
  transport\&. In this case you will connect to a remote rsync server
  running on TCP port 873\&. 
  .PP 
! You may establish the connection via a web proxy by setting the
  environment variable RSYNC_PROXY to a hostname:port pair pointing to
  your web proxy\&. Note that your web proxy must allow proxying to port
  873, this must be configured in your proxy servers ruleset\&.
***************
*** 188,199 ****
--- 203,257 ----
  WARNING: On some systems environment variables are visible to all
  users\&. On those systems using --password-file is recommended\&.
  .PP 
+ .SH "CONNECTING TO AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM" 
+ .PP
+ It is sometimes useful to be able to set up file transfers using rsync
+ server capabilities on the remote machine, while still using rsh or
+ ssh for transport.  This is especially useful when you want to connect
+ to a remote machine via SSH (for encryption or to get through a
+ firewall), but you still want to have access to the rsync server
+ features (see \fBRUNNING AN RSYNC SERVER OVER A REMOTE SHELL
+ PROGRAM\fR, below).  
+ .PP
+ From the user's perspective, using rsync in this way is the same as
+ using it with rsh or ssh except that
+ .PP
+ .IP o
+ you use a triple colon ::: instead of a single colon to separate the
+ hostname from the path\&.
+ .IP
+ .IP o
+ all the other items in the \fBCONNECTING TO AN RSYNC SERVER\fR section
+ above apply\&.
+ .PP
  .SH "RUNNING AN RSYNC SERVER" 
  .PP 
  An rsync server is configured using a config file which by default is
  called /etc/rsyncd\&.conf\&. Please see the rsyncd\&.conf(5) man page for more
  information\&. 
  .PP 
+ .SH "RUNNING AN RSYNC SERVER OVER A REMOTE SHELL PROGRAM" 
+ .PP 
+ See the rsyncd\&.conf(5) man page for information on the rsync server
+ configuration file.  Several configuration options will not be
+ available unless the remote user is root (e.g. chroot, uid/gid, etc.).
+ There is no need to configure inetd or the services map to include the
+ rsync server port if you run an rsync server via a remote shell
+ program.
+ .PP
+ To run an rsync server out of a single-use SSH key, use the
+ "command=\fIcommand\fR" syntax in the remote user's
+ \fIauthorized_keys\fR entry where \fIcommand\fR would be
+ .PP
+ rsync --server --daemon .
+ .PP
+ (the trailing "." is quite important to rsync's argument parsing, so
+ make sure it's in there).  If you want to use a rsyncd\&.conf(5)-style
+ configuration file other than /etc/rsyncd.conf, you can added a
+ --config-file option:
+ .PP 
+ rsync --server --daemon --config-file=<file> .
+ .PP
  .SH "EXAMPLES" 
  .PP 
  Here are some examples of how I use rsync\&.
***************
*** 298,303 ****
--- 356,362 ----
       --daemon                run as a rsync daemon
       --address               bind to the specified address
       --config=FILE           specify alternate rsyncd\&.conf file
+      --remote-user=USER      remote username for ::: mode
       --port=PORT             specify alternate rsyncd port number
       --blocking-io           use blocking IO for the remote shell
       --stats                 give some file transfer stats
***************
*** 713,718 ****
--- 772,786 ----
  This specifies an alternate config file than
  the default /etc/rsyncd\&.conf\&. This is only relevant when --daemon is
  specified\&. 
+ .IP 
+ .IP "\fB--remote-user=USER\fP" 
+ This option allows you to specify the \fIrsyncd.conf\fR-level username
+ separately from the rsh/ssh-level username, e.g. when using
+ \fBrsh-user at host:::module/path\fR.  The \fBrsh-user\fR is used for
+ logging into the remote \fBhost\fR at the rsh/ssh level -- and is not
+ necessarily the \fBUSER\fR you want to use at the \fIrsyncd.conf\fR
+ level.  Use of this option will override \fBuser\fR in
+ \fBuser at host::module/path\fR.
  .IP 
  .IP "\fB--port=PORT\fP" 
  This specifies an alternate TCP port number to use
Index: socket.c
===================================================================
RCS file: /juno/repository/usr/local/pkg/rsync/socket.c,v
retrieving revision 1.1.1.4
retrieving revision 1.2
diff -c -b -r1.1.1.4 -r1.2
*** socket.c	2001/10/19 04:09:53	1.1.1.4
--- socket.c	2001/11/19 20:31:48	1.2
***************
*** 222,228 ****
  }
  
  
! void start_accept_loop(int port, int (*fn)(int ))
  {
  	int s;
  	extern struct in_addr socket_address;
--- 222,228 ----
  }
  
  
! void start_accept_loop(int port, int (*fn)(int, int))
  {
  	int s;
  	extern struct in_addr socket_address;
***************
*** 272,278 ****
  		if (fork()==0) {
  			close(s);
  
! 			_exit(fn(fd));
  		}
  
  		close(fd);
--- 272,278 ----
  		if (fork()==0) {
  			close(s);
  
! 			_exit(fn(fd, fd));
  		}
  
  		close(fd);







More information about the rsync mailing list