X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/e844a4a8a8500e6e1cbb53c2a932c2c57bc0ca9f..cbbd8e2e8bf72aa46c84c7de43e19da40f040fa7:/clientserver.c diff --git a/clientserver.c b/clientserver.c index 8cbde16b..6bcbc81e 100644 --- a/clientserver.c +++ b/clientserver.c @@ -3,7 +3,7 @@ * * Copyright (C) 1998-2001 Andrew Tridgell * Copyright (C) 2001-2002 Martin Pool - * Copyright (C) 2002-2007 Wayne Davison + * Copyright (C) 2002-2008 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 @@ -23,6 +23,7 @@ #include "ifuncs.h" extern int verbose; +extern int dry_run; extern int output_motd; extern int list_only; extern int am_sender; @@ -31,14 +32,17 @@ extern int am_daemon; extern int am_root; extern int rsync_port; extern int ignore_errors; +extern int preserve_xattrs; extern int kluge_around_eof; extern int daemon_over_rsh; extern int sanitize_paths; +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 default_af_hint; extern int logfile_format_has_i; extern int logfile_format_has_o_or_i; @@ -52,14 +56,20 @@ extern char *tmpdir; extern struct chmod_mode_struct *chmod_modes; extern struct filter_list_struct server_filter_list; extern char curr_dir[]; +#ifdef ICONV_OPTION +extern char *iconv_opt; +extern iconv_t ic_send, ic_recv; +#endif char *auth_user; int read_only = 0; int module_id = -1; +int munge_symlinks = 0; struct chmod_mode_struct *daemon_chmod_modes; /* module_dirlen is the length of the module_dir string when in daemon - * mode, not chrooted, and the path is not "/"; otherwise 0. */ + * 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; @@ -111,6 +121,10 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[], set_socket_options(fd, sockopts); +#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); @@ -242,7 +256,7 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n"); exit_cleanup(RERR_SYNTAX); } - if (list_only && strncmp(*argv, modname, modlen) == 0 + if (strncmp(*argv, modname, modlen) == 0 && argv[0][modlen] == '\0') sargs[sargc++] = modname; /* we send "modname/" */ else @@ -352,8 +366,12 @@ static int read_arg_from_pipe(int fd, char *buf, int limit) char *bp = buf, *eob = buf + limit - 1; while (1) { - if (read(fd, bp, 1) != 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) @@ -367,7 +385,7 @@ static int read_arg_from_pipe(int fd, char *buf, int limit) static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) { int argc, opt_cnt; - char **argv; + char **argv, *chroot_path = NULL; char line[BIGPATHBUFLEN]; uid_t uid = (uid_t)-2; /* canonically "nobody" */ gid_t gid = (gid_t)-2; @@ -378,6 +396,13 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) pid_t pre_exec_pid = 0; char *request = NULL; +#ifdef ICONV_OPTION + iconv_opt = lp_charset(i); + if (*iconv_opt) + setup_iconv(); + iconv_opt = NULL; +#endif + if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", name, host, addr); @@ -460,18 +485,32 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) /* TODO: Perhaps take a list of gids, and make them into the * supplementary groups. */ - /* We do a push_dir() without actually calling chdir() in order - * to make sure that the module's path is absolute. After this - * check, module_dir will be set to an absolute path. */ module_dir = lp_path(i); + if (use_chroot) { + if ((p = strstr(module_dir, "/./")) != NULL) { + *p = '\0'; + p += 2; + } else if ((p = strdup("/")) == NULL) + out_of_memory("rsync_module"); + } + + /* We do a push_dir() that doesn't actually call chdir() + * just to make a relative path absolute. */ strlcpy(line, curr_dir, sizeof line); if (!push_dir(module_dir, 1)) goto chdir_failed; - if (strcmp(curr_dir, module_dir) != 0) - module_dir = strdup(curr_dir); + if (strcmp(curr_dir, module_dir) != 0 + && (module_dir = strdup(curr_dir)) == NULL) + out_of_memory("rsync_module"); push_dir(line, 1); /* Restore curr_dir. */ - if (use_chroot || (module_dirlen = strlen(module_dir)) == 1) { + if (use_chroot) { + chroot_path = module_dir; + module_dir = p; /* p is "/" or our inside-chroot path */ + } + module_dirlen = clean_fname(module_dir, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR); + + if (module_dirlen == 1) { module_dirlen = 0; set_filter_dir("/", 1); } else @@ -504,8 +543,17 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { char *modname, *modpath, *hostaddr, *hostname, *username; int status; + + if (!use_chroot) + p = module_dir; + else if (module_dirlen) { + pathjoin(line, sizeof line, chroot_path, module_dir+1); + p = line; + } else + p = chroot_path; + if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0 - || asprintf(&modpath, "RSYNC_MODULE_PATH=%s", module_dir) < 0 + || asprintf(&modpath, "RSYNC_MODULE_PATH=%s", p) < 0 || asprintf(&hostaddr, "RSYNC_HOST_ADDR=%s", addr) < 0 || asprintf(&hostname, "RSYNC_HOST_NAME=%s", host) < 0 || asprintf(&username, "RSYNC_USER_NAME=%s", auth_user) < 0) @@ -605,13 +653,15 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) * a warning, unless a "require chroot" flag is set, * in which case we fail. */ - if (chroot(module_dir)) { - rsyserr(FLOG, errno, "chroot %s failed", module_dir); + if (chroot(chroot_path)) { + rsyserr(FLOG, errno, "chroot %s failed", chroot_path); io_printf(f_out, "@ERROR: chroot failed\n"); return -1; } - if (!push_dir("/", 0)) + if (!push_dir(module_dir, 0)) goto chdir_failed; + if (module_dirlen) + sanitize_paths = 1; } else { if (!push_dir(module_dir, 0)) { chdir_failed: @@ -622,6 +672,18 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) sanitize_paths = 1; } + if ((munge_symlinks = lp_munge_symlinks(i)) < 0) + munge_symlinks = !use_chroot || module_dirlen; + if (munge_symlinks) { + STRUCT_STAT st; + if (stat(SYMLINK_PREFIX, &st) == 0 && S_ISDIR(st.st_mode)) { + rprintf(FLOG, "Symlink munging is unsupported when a %s directory exists.\n", + SYMLINK_PREFIX); + io_printf(f_out, "@ERROR: daemon security issue -- contact admin\n", name); + exit_cleanup(RERR_UNSUPPORTED); + } + } + if (am_root) { /* XXXX: You could argue that if the daemon is started * by a non-root user and they explicitly specify a @@ -681,10 +743,14 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) am_server = 1; /* Don't let someone try to be tricky. */ if (lp_ignore_errors(module_id)) ignore_errors = 1; + if (write_batch < 0) + dry_run = 1; - if (lp_fake_super(i)) + if (lp_fake_super(i)) { + if (preserve_xattrs > 1) + preserve_xattrs = 1; am_root = -1; - else if (am_root < 0) /* Treat --fake-super from client as --super. */ + } else if (am_root < 0) /* Treat --fake-super from client as --super. */ am_root = 2; if (filesfrom_fd == 0) @@ -747,6 +813,23 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) exit_cleanup(RERR_UNSUPPORTED); } +#ifdef ICONV_OPTION + if (!iconv_opt) { + if (ic_send != (iconv_t)-1) { + iconv_close(ic_send); + ic_send = (iconv_t)-1; + } + if (ic_recv != (iconv_t)-1) { + iconv_close(ic_recv); + ic_recv = (iconv_t)-1; + } + } +#endif + + if (!numeric_ids + && (use_chroot ? lp_numeric_ids(i) != False : lp_numeric_ids(i) == True)) + numeric_ids = -1; /* Set --numeric-ids w/o breaking protocol. */ + if (lp_timeout(i) && lp_timeout(i) > io_timeout) set_io_timeout(lp_timeout(i)); @@ -784,6 +867,17 @@ static void send_listing(int fd) io_printf(fd,"@RSYNCD: EXIT\n"); } +static int load_config(int globals_only) +{ + if (!config_file) { + if (am_server && am_root <= 0) + config_file = RSYNCD_USERCONF; + else + config_file = RSYNCD_SYSCONF; + } + return lp_load(config_file, globals_only); +} + /* 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 */ @@ -799,7 +893,7 @@ int start_daemon(int f_in, int f_out) * might cause log-file output to occur. This ensures that the * "log file" param gets honored for the 2 non-forked use-cases * (when rsync is run by init and run by a remote shell). */ - if (!lp_load(config_file, 0)) + if (!load_config(0)) exit_cleanup(RERR_SYNTAX); addr = client_addr(f_in); @@ -908,13 +1002,6 @@ static void become_daemon(void) int daemon_main(void) { - if (!config_file) { - if (am_server && am_root <= 0) - config_file = RSYNCD_USERCONF; - else - config_file = RSYNCD_SYSCONF; - } - if (is_a_socket(STDIN_FILENO)) { int i; @@ -929,7 +1016,7 @@ int daemon_main(void) return start_daemon(STDIN_FILENO, STDIN_FILENO); } - if (!lp_load(config_file, 1)) { + if (!load_config(1)) { fprintf(stderr, "Failed to parse config file: %s\n", config_file); exit_cleanup(RERR_SYNTAX); }