|
version 1.1, 2012/02/17 15:09:30
|
version 1.1.1.3, 2016/11/01 09:54:32
|
|
Line 3
|
Line 3
|
| * |
* |
| * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org> |
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org> |
| * Copyright (C) 2001-2002 Martin Pool <mbp@samba.org> |
* Copyright (C) 2001-2002 Martin Pool <mbp@samba.org> |
| * Copyright (C) 2002-2009 Wayne Davison | * Copyright (C) 2002-2015 Wayne Davison |
| * |
* |
| * This program is free software; you can redistribute it and/or modify |
* 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 |
* it under the terms of the GNU General Public License as published by |
|
Line 20
|
Line 20
|
| */ |
*/ |
| |
|
| #include "rsync.h" |
#include "rsync.h" |
| #include "ifuncs.h" | #include "itypes.h" |
| |
|
| extern int quiet; |
extern int quiet; |
| extern int verbose; |
|
| extern int dry_run; |
extern int dry_run; |
| extern int output_motd; |
extern int output_motd; |
| extern int list_only; |
extern int list_only; |
|
Line 37 extern int ignore_errors;
|
Line 36 extern int ignore_errors;
|
| extern int preserve_xattrs; |
extern int preserve_xattrs; |
| extern int kluge_around_eof; |
extern int kluge_around_eof; |
| extern int daemon_over_rsh; |
extern int daemon_over_rsh; |
| |
extern int munge_symlinks; |
| extern int sanitize_paths; |
extern int sanitize_paths; |
| extern int numeric_ids; |
extern int numeric_ids; |
| extern int filesfrom_fd; |
extern int filesfrom_fd; |
|
Line 48 extern int write_batch;
|
Line 48 extern int write_batch;
|
| extern int default_af_hint; |
extern int default_af_hint; |
| extern int logfile_format_has_i; |
extern int logfile_format_has_i; |
| extern int logfile_format_has_o_or_i; |
extern int logfile_format_has_o_or_i; |
| extern mode_t orig_umask; |
|
| extern char *bind_address; |
extern char *bind_address; |
| extern char *config_file; |
extern char *config_file; |
| extern char *logfile_format; |
extern char *logfile_format; |
| extern char *files_from; |
extern char *files_from; |
| extern char *tmpdir; |
extern char *tmpdir; |
| extern struct chmod_mode_struct *chmod_modes; |
extern struct chmod_mode_struct *chmod_modes; |
| extern struct filter_list_struct daemon_filter_list; | extern filter_rule_list daemon_filter_list; |
| #ifdef ICONV_OPTION |
#ifdef ICONV_OPTION |
| extern char *iconv_opt; |
extern char *iconv_opt; |
| extern iconv_t ic_send, ic_recv; |
extern iconv_t ic_send, ic_recv; |
|
Line 64 extern iconv_t ic_send, ic_recv;
|
Line 63 extern iconv_t ic_send, ic_recv;
|
| char *auth_user; |
char *auth_user; |
| int read_only = 0; |
int read_only = 0; |
| int module_id = -1; |
int module_id = -1; |
| int munge_symlinks = 0; |
|
| struct chmod_mode_struct *daemon_chmod_modes; |
struct chmod_mode_struct *daemon_chmod_modes; |
| |
|
| /* module_dirlen is the length of the module_dir string when in daemon |
/* module_dirlen is the length of the module_dir string when in daemon |
|
Line 81 static int rl_nulls = 0;
|
Line 79 static int rl_nulls = 0;
|
| static struct sigaction sigact; |
static struct sigaction sigact; |
| #endif |
#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 |
* Run a client connected to an rsyncd. The alternative to this |
| * function for remote-shell connections is do_cmd(). |
* function for remote-shell connections is do_cmd(). |
|
Line 158 static int exchange_protocols(int f_in, int f_out, cha
|
Line 161 static int exchange_protocols(int f_in, int f_out, cha
|
| } |
} |
| |
|
| /* This strips the \n. */ |
/* This strips the \n. */ |
| if (!read_line_old(f_in, buf, bufsiz)) { | if (!read_line_old(f_in, buf, bufsiz, 0)) { |
| if (am_client) |
if (am_client) |
| rprintf(FERROR, "rsync: did not see server greeting\n"); |
rprintf(FERROR, "rsync: did not see server greeting\n"); |
| return -1; |
return -1; |
|
Line 270 int start_inband_exchange(int f_in, int f_out, const c
|
Line 273 int start_inband_exchange(int f_in, int f_out, const c
|
| |
|
| sargs[sargc] = NULL; |
sargs[sargc] = NULL; |
| |
|
| if (verbose > 1) | if (DEBUG_GTE(CMD, 1)) |
| print_child_argv("sending daemon args:", sargs); |
print_child_argv("sending daemon args:", sargs); |
| |
|
| io_printf(f_out, "%.*s\n", modlen, modname); |
io_printf(f_out, "%.*s\n", modlen, modname); |
|
Line 280 int start_inband_exchange(int f_in, int f_out, const c
|
Line 283 int start_inband_exchange(int f_in, int f_out, const c
|
| kluge_around_eof = list_only && protocol_version < 25 ? 1 : 0; |
kluge_around_eof = list_only && protocol_version < 25 ? 1 : 0; |
| |
|
| while (1) { |
while (1) { |
| if (!read_line_old(f_in, line, sizeof line)) { | if (!read_line_old(f_in, line, sizeof line, 0)) { |
| rprintf(FERROR, "rsync: didn't get server startup line\n"); |
rprintf(FERROR, "rsync: didn't get server startup line\n"); |
| return -1; |
return -1; |
| } |
} |
|
Line 334 int start_inband_exchange(int f_in, int f_out, const c
|
Line 337 int start_inband_exchange(int f_in, int f_out, const c
|
| |
|
| if (protocol_version < 23) { |
if (protocol_version < 23) { |
| if (protocol_version == 22 || !am_sender) |
if (protocol_version == 22 || !am_sender) |
| io_start_multiplex_in(); | io_start_multiplex_in(f_in); |
| } |
} |
| |
|
| free(modname); |
free(modname); |
|
Line 342 int start_inband_exchange(int f_in, int f_out, const c
|
Line 345 int start_inband_exchange(int f_in, int f_out, const c
|
| return 0; |
return 0; |
| } |
} |
| |
|
| static char *finish_pre_exec(pid_t pid, int fd, char *request, | static char *finish_pre_exec(pid_t pid, int write_fd, int read_fd, char *request, |
| char **early_argv, char **argv) |
char **early_argv, char **argv) |
| { |
{ |
| int j = 0, status = -1; | char buf[BIGPATHBUFLEN], *bp; |
| | int j = 0, status = -1, msglen = sizeof buf - 1; |
| |
|
| if (!request) |
if (!request) |
| request = "(NONE)"; |
request = "(NONE)"; |
| |
|
| write_buf(fd, request, strlen(request)+1); | write_buf(write_fd, request, strlen(request)+1); |
| if (early_argv) { |
if (early_argv) { |
| for ( ; *early_argv; early_argv++) |
for ( ; *early_argv; early_argv++) |
| write_buf(fd, *early_argv, strlen(*early_argv)+1); | write_buf(write_fd, *early_argv, strlen(*early_argv)+1); |
| j = 1; /* Skip arg0 name in argv. */ |
j = 1; /* Skip arg0 name in argv. */ |
| } |
} |
| for ( ; argv[j]; j++) { | for ( ; argv[j]; j++) |
| write_buf(fd, argv[j], strlen(argv[j])+1); | write_buf(write_fd, argv[j], strlen(argv[j])+1); |
| if (argv[j][0] == '.' && argv[j][1] == '\0') | write_byte(write_fd, 0); |
| break; | |
| | close(write_fd); |
| | |
| | /* Read the stdout from the pre-xfer exec program. This it is only |
| | * displayed to the user if the script also returns an error status. */ |
| | for (bp = buf; msglen > 0; msglen -= j) { |
| | if ((j = read(read_fd, bp, msglen)) <= 0) { |
| | if (j == 0) |
| | break; |
| | if (errno == EINTR) |
| | continue; |
| | break; /* Just ignore the read error for now... */ |
| | } |
| | bp += j; |
| | if (j > 1 && bp[-1] == '\n' && bp[-2] == '\r') { |
| | bp--; |
| | j--; |
| | bp[-1] = '\n'; |
| | } |
| } |
} |
| write_byte(fd, 0); | *bp = '\0'; |
| |
|
| close(fd); | close(read_fd); |
| |
|
| if (wait_process(pid, &status, 0) < 0 |
if (wait_process(pid, &status, 0) < 0 |
| || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
|| !WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
| char *e; |
char *e; |
| if (asprintf(&e, "pre-xfer exec returned failure (%d)%s%s\n", | if (asprintf(&e, "pre-xfer exec returned failure (%d)%s%s%s\n%s", |
| status, status < 0 ? ": " : "", |
status, status < 0 ? ": " : "", |
| status < 0 ? strerror(errno) : "") < 0) | status < 0 ? strerror(errno) : "", |
| out_of_memory("finish_pre_exec"); | *buf ? ":" : "", buf) < 0) |
| | return "out_of_memory in finish_pre_exec\n"; |
| return e; |
return e; |
| } |
} |
| return NULL; |
return NULL; |
| } |
} |
| |
|
| |
#ifdef HAVE_PUTENV |
| static int read_arg_from_pipe(int fd, char *buf, int limit) |
static int read_arg_from_pipe(int fd, char *buf, int limit) |
| { |
{ |
| char *bp = buf, *eob = buf + limit - 1; |
char *bp = buf, *eob = buf + limit - 1; |
| |
|
| while (1) { |
while (1) { |
| int got = read(fd, bp, 1); | int got = read(fd, bp, 1); |
| if (got != 1) { | if (got != 1) { |
| if (got < 0 && errno == EINTR) | if (got < 0 && errno == EINTR) |
| continue; | continue; |
| return -1; | return -1; |
| } | } |
| if (*bp == '\0') | if (*bp == '\0') |
| break; | break; |
| if (bp < eob) | if (bp < eob) |
| bp++; | bp++; |
| } |
} |
| *bp = '\0'; |
*bp = '\0'; |
| |
|
| return bp - buf; |
return bp - buf; |
| } |
} |
| |
#endif |
| |
|
| static int path_failure(int f_out, const char *dir, BOOL was_chdir) |
static int path_failure(int f_out, const char *dir, BOOL was_chdir) |
| { |
{ |
|
Line 408 static int path_failure(int f_out, const char *dir, BO
|
Line 433 static int path_failure(int f_out, const char *dir, BO
|
| return -1; |
return -1; |
| } |
} |
| |
|
| static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) | static int add_a_group(int f_out, const char *gname) |
| { |
{ |
| |
gid_t gid, *gid_p; |
| |
if (!group_to_gid(gname, &gid, True)) { |
| |
rprintf(FLOG, "Invalid gid %s\n", gname); |
| |
io_printf(f_out, "@ERROR: invalid gid %s\n", gname); |
| |
return -1; |
| |
} |
| |
gid_p = EXPAND_ITEM_LIST(&gid_list, gid_t, -32); |
| |
*gid_p = gid; |
| |
return 0; |
| |
} |
| |
|
| |
#ifdef HAVE_GETGROUPLIST |
| |
static int want_all_groups(int f_out, uid_t uid) |
| |
{ |
| |
const char *err; |
| |
if ((err = getallgroups(uid, &gid_list)) != NULL) { |
| |
rsyserr(FLOG, errno, "%s", err); |
| |
io_printf(f_out, "@ERROR: %s\n", err); |
| |
return -1; |
| |
} |
| |
return 0; |
| |
} |
| |
#elif defined HAVE_INITGROUPS |
| |
static struct passwd *want_all_groups(int f_out, uid_t uid) |
| |
{ |
| |
struct passwd *pw; |
| |
gid_t *gid_p; |
| |
if ((pw = getpwuid(uid)) == NULL) { |
| |
rsyserr(FLOG, errno, "getpwuid failed"); |
| |
io_printf(f_out, "@ERROR: getpwuid failed\n"); |
| |
return NULL; |
| |
} |
| |
/* Start with the default group and initgroups() will add the rest. */ |
| |
gid_p = EXPAND_ITEM_LIST(&gid_list, gid_t, -32); |
| |
*gid_p = pw->pw_gid; |
| |
return pw; |
| |
} |
| |
#endif |
| |
|
| |
static void set_env_str(const char *var, const char *str) |
| |
{ |
| |
#ifdef HAVE_PUTENV |
| |
char *mem; |
| |
if (asprintf(&mem, "%s=%s", var, str) < 0) |
| |
out_of_memory("set_env_str"); |
| |
putenv(mem); |
| |
#endif |
| |
} |
| |
|
| |
#ifdef HAVE_PUTENV |
| |
static void set_env_num(const char *var, long num) |
| |
{ |
| |
char *mem; |
| |
if (asprintf(&mem, "%s=%ld", var, num) < 0) |
| |
out_of_memory("set_env_num"); |
| |
putenv(mem); |
| |
} |
| |
#endif |
| |
|
| |
static int rsync_module(int f_in, int f_out, int i, const char *addr, const char *host) |
| |
{ |
| int argc; |
int argc; |
| char **argv, **orig_argv, **orig_early_argv, *module_chdir; |
char **argv, **orig_argv, **orig_early_argv, *module_chdir; |
| char line[BIGPATHBUFLEN]; |
char line[BIGPATHBUFLEN]; |
| uid_t uid = (uid_t)-2; /* canonically "nobody" */ | #if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST |
| gid_t gid = (gid_t)-2; | struct passwd *pw = NULL; |
| | #endif |
| | uid_t uid; |
| | int set_uid; |
| char *p, *err_msg = NULL; |
char *p, *err_msg = NULL; |
| char *name = lp_name(i); |
char *name = lp_name(i); |
| int use_chroot = lp_use_chroot(i); |
int use_chroot = lp_use_chroot(i); |
| int ret, pre_exec_fd = -1; | int ret, pre_exec_arg_fd = -1, pre_exec_error_fd = -1; |
| | int save_munge_symlinks; |
| pid_t pre_exec_pid = 0; |
pid_t pre_exec_pid = 0; |
| char *request = NULL; |
char *request = NULL; |
| |
|
| |
set_env_str("RSYNC_MODULE_NAME", name); |
| |
|
| #ifdef ICONV_OPTION |
#ifdef ICONV_OPTION |
| iconv_opt = lp_charset(i); |
iconv_opt = lp_charset(i); |
| if (*iconv_opt) |
if (*iconv_opt) |
|
Line 429 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 521 static int rsync_module(int f_in, int f_out, int i, ch
|
| iconv_opt = NULL; |
iconv_opt = NULL; |
| #endif |
#endif |
| |
|
| if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { | /* If reverse lookup is disabled globally but enabled for this module, |
| | * we need to do it now before the access check. */ |
| | if (host == undetermined_hostname && lp_reverse_lookup(i)) |
| | host = client_name(f_in); |
| | set_env_str("RSYNC_HOST_NAME", host); |
| | set_env_str("RSYNC_HOST_ADDR", addr); |
| | |
| | if (!allow_access(addr, &host, i)) { |
| rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", |
rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", |
| name, host, addr); |
name, host, addr); |
| if (!lp_list(i)) |
if (!lp_list(i)) |
|
Line 461 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 560 static int rsync_module(int f_in, int f_out, int i, ch
|
| return -1; |
return -1; |
| } |
} |
| |
|
| |
read_only = lp_read_only(i); /* may also be overridden by auth_server() */ |
| auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD "); |
auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD "); |
| |
|
| if (!auth_user) { |
if (!auth_user) { |
| io_printf(f_out, "@ERROR: auth failed on module %s\n", name); |
io_printf(f_out, "@ERROR: auth failed on module %s\n", name); |
| return -1; |
return -1; |
| } |
} |
| |
set_env_str("RSYNC_USER_NAME", auth_user); |
| |
|
| module_id = i; |
module_id = i; |
| |
|
| if (lp_read_only(i)) |
|
| read_only = 1; |
|
| |
|
| if (lp_transfer_logging(i) && !logfile_format) |
if (lp_transfer_logging(i) && !logfile_format) |
| logfile_format = lp_log_format(i); |
logfile_format = lp_log_format(i); |
| if (log_format_has(logfile_format, 'i')) |
if (log_format_has(logfile_format, 'i')) |
|
Line 480 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 578 static int rsync_module(int f_in, int f_out, int i, ch
|
| if (logfile_format_has_i || log_format_has(logfile_format, 'o')) |
if (logfile_format_has_i || log_format_has(logfile_format, 'o')) |
| logfile_format_has_o_or_i = 1; |
logfile_format_has_o_or_i = 1; |
| |
|
| am_root = (MY_UID() == 0); | uid = MY_UID(); |
| | am_root = (uid == 0); |
| |
|
| if (am_root) { | p = *lp_uid(i) ? lp_uid(i) : am_root ? NOBODY_USER : NULL; |
| p = lp_uid(i); | if (p) { |
| if (!name_to_uid(p, &uid)) { | if (!user_to_uid(p, &uid, True)) { |
| if (!isDigit(p)) { | rprintf(FLOG, "Invalid uid %s\n", p); |
| rprintf(FLOG, "Invalid uid %s\n", p); | io_printf(f_out, "@ERROR: invalid uid %s\n", p); |
| io_printf(f_out, "@ERROR: invalid uid %s\n", p); | return -1; |
| return -1; | |
| } | |
| uid = atoi(p); | |
| } |
} |
| |
set_uid = 1; |
| |
} else |
| |
set_uid = 0; |
| |
|
| p = lp_gid(i); | p = *lp_gid(i) ? strtok(lp_gid(i), ", ") : NULL; |
| if (!name_to_gid(p, &gid)) { | if (p) { |
| if (!isDigit(p)) { | /* The "*" gid must be the first item in the list. */ |
| rprintf(FLOG, "Invalid gid %s\n", p); | if (strcmp(p, "*") == 0) { |
| io_printf(f_out, "@ERROR: invalid gid %s\n", p); | #ifdef HAVE_GETGROUPLIST |
| | if (want_all_groups(f_out, uid) < 0) |
| return -1; |
return -1; |
| |
#elif defined HAVE_INITGROUPS |
| |
if ((pw = want_all_groups(f_out, uid)) == NULL) |
| |
return -1; |
| |
#else |
| |
rprintf(FLOG, "This rsync does not support a gid of \"*\"\n"); |
| |
io_printf(f_out, "@ERROR: invalid gid setting.\n"); |
| |
return -1; |
| |
#endif |
| |
} else if (add_a_group(f_out, p) < 0) |
| |
return -1; |
| |
while ((p = strtok(NULL, ", ")) != NULL) { |
| |
#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST |
| |
if (pw) { |
| |
rprintf(FLOG, "This rsync cannot add groups after \"*\".\n"); |
| |
io_printf(f_out, "@ERROR: invalid gid setting.\n"); |
| |
return -1; |
| } |
} |
| gid = atoi(p); | #endif |
| | if (add_a_group(f_out, p) < 0) |
| | return -1; |
| } |
} |
| |
} else if (am_root) { |
| |
if (add_a_group(f_out, NOBODY_GROUP) < 0) |
| |
return -1; |
| } |
} |
| |
|
| /* TODO: If we're not root, but the configuration requests |
|
| * that we change to some uid other than the current one, then |
|
| * log a warning. */ |
|
| |
|
| /* TODO: Perhaps take a list of gids, and make them into the |
|
| * supplementary groups. */ |
|
| |
|
| module_dir = lp_path(i); |
module_dir = lp_path(i); |
| if (*module_dir == '\0') { |
if (*module_dir == '\0') { |
| rprintf(FLOG, "No path specified for module %s\n", name); |
rprintf(FLOG, "No path specified for module %s\n", name); |
|
Line 540 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 654 static int rsync_module(int f_in, int f_out, int i, ch
|
| return path_failure(f_out, module_dir, False); |
return path_failure(f_out, module_dir, False); |
| full_module_path = module_dir = module_chdir; |
full_module_path = module_dir = module_chdir; |
| } |
} |
| |
set_env_str("RSYNC_MODULE_PATH", full_module_path); |
| |
|
| if (module_dirlen == 1) { |
if (module_dirlen == 1) { |
| module_dirlen = 0; |
module_dirlen = 0; |
|
Line 548 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 663 static int rsync_module(int f_in, int f_out, int i, ch
|
| set_filter_dir(module_dir, module_dirlen); |
set_filter_dir(module_dir, module_dirlen); |
| |
|
| p = lp_filter(i); |
p = lp_filter(i); |
| parse_rule(&daemon_filter_list, p, MATCHFLG_WORD_SPLIT, | parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT), |
| XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3); |
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3); |
| |
|
| p = lp_include_from(i); |
p = lp_include_from(i); |
| parse_filter_file(&daemon_filter_list, p, MATCHFLG_INCLUDE, | parse_filter_file(&daemon_filter_list, p, rule_template(FILTRULE_INCLUDE), |
| XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); |
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); |
| |
|
| p = lp_include(i); |
p = lp_include(i); |
| parse_rule(&daemon_filter_list, p, | parse_filter_str(&daemon_filter_list, p, |
| MATCHFLG_INCLUDE | MATCHFLG_WORD_SPLIT, | rule_template(FILTRULE_INCLUDE | FILTRULE_WORD_SPLIT), |
| XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES); |
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES); |
| |
|
| p = lp_exclude_from(i); |
p = lp_exclude_from(i); |
| parse_filter_file(&daemon_filter_list, p, 0, | parse_filter_file(&daemon_filter_list, p, rule_template(0), |
| XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); |
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); |
| |
|
| p = lp_exclude(i); |
p = lp_exclude(i); |
| parse_rule(&daemon_filter_list, p, MATCHFLG_WORD_SPLIT, | parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT), |
| XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES); |
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES); |
| |
|
| log_init(1); |
log_init(1); |
| |
|
| #ifdef HAVE_PUTENV |
#ifdef HAVE_PUTENV |
| if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { |
if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { |
| char *modname, *modpath, *hostaddr, *hostname, *username; |
|
| int status; |
int status; |
| |
|
| if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0 |
|
| || asprintf(&modpath, "RSYNC_MODULE_PATH=%s", full_module_path) < 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) |
|
| out_of_memory("rsync_module"); |
|
| putenv(modname); |
|
| putenv(modpath); |
|
| putenv(hostaddr); |
|
| putenv(hostname); |
|
| putenv(username); |
|
| umask(orig_umask); |
|
| /* For post-xfer exec, fork a new process to run the rsync |
/* For post-xfer exec, fork a new process to run the rsync |
| * daemon while this process waits for the exit status and |
* daemon while this process waits for the exit status and |
| * runs the indicated command at that point. */ |
* runs the indicated command at that point. */ |
|
Line 598 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 700 static int rsync_module(int f_in, int f_out, int i, ch
|
| return -1; |
return -1; |
| } |
} |
| if (pid) { |
if (pid) { |
| if (asprintf(&p, "RSYNC_PID=%ld", (long)pid) > 0) | close(f_in); |
| putenv(p); | if (f_out != f_in) |
| | close(f_out); |
| | set_env_num("RSYNC_PID", (long)pid); |
| if (wait_process(pid, &status, 0) < 0) |
if (wait_process(pid, &status, 0) < 0) |
| status = -1; |
status = -1; |
| if (asprintf(&p, "RSYNC_RAW_STATUS=%d", status) > 0) | set_env_num("RSYNC_RAW_STATUS", status); |
| putenv(p); | |
| if (WIFEXITED(status)) |
if (WIFEXITED(status)) |
| status = WEXITSTATUS(status); |
status = WEXITSTATUS(status); |
| else |
else |
| status = -1; |
status = -1; |
| if (asprintf(&p, "RSYNC_EXIT_STATUS=%d", status) > 0) | set_env_num("RSYNC_EXIT_STATUS", status); |
| putenv(p); | |
| if (system(lp_postxfer_exec(i)) < 0) |
if (system(lp_postxfer_exec(i)) < 0) |
| status = -1; |
status = -1; |
| _exit(status); |
_exit(status); |
|
Line 619 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 721 static int rsync_module(int f_in, int f_out, int i, ch
|
| * command, though it first waits for the parent process to |
* command, though it first waits for the parent process to |
| * send us the user's request via a pipe. */ |
* send us the user's request via a pipe. */ |
| if (*lp_prexfer_exec(i)) { |
if (*lp_prexfer_exec(i)) { |
| int fds[2]; | int arg_fds[2], error_fds[2]; |
| if (asprintf(&p, "RSYNC_PID=%ld", (long)getpid()) > 0) | set_env_num("RSYNC_PID", (long)getpid()); |
| putenv(p); | if (pipe(arg_fds) < 0 || pipe(error_fds) < 0 || (pre_exec_pid = fork()) < 0) { |
| if (pipe(fds) < 0 || (pre_exec_pid = fork()) < 0) { | |
| rsyserr(FLOG, errno, "pre-xfer exec preparation failed"); |
rsyserr(FLOG, errno, "pre-xfer exec preparation failed"); |
| io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n"); |
io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n"); |
| return -1; |
return -1; |
|
Line 630 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 731 static int rsync_module(int f_in, int f_out, int i, ch
|
| if (pre_exec_pid == 0) { |
if (pre_exec_pid == 0) { |
| char buf[BIGPATHBUFLEN]; |
char buf[BIGPATHBUFLEN]; |
| int j, len; |
int j, len; |
| close(fds[1]); | close(arg_fds[1]); |
| set_blocking(fds[0]); | close(error_fds[0]); |
| len = read_arg_from_pipe(fds[0], buf, BIGPATHBUFLEN); | pre_exec_arg_fd = arg_fds[0]; |
| | pre_exec_error_fd = error_fds[1]; |
| | set_blocking(pre_exec_arg_fd); |
| | set_blocking(pre_exec_error_fd); |
| | len = read_arg_from_pipe(pre_exec_arg_fd, buf, BIGPATHBUFLEN); |
| if (len <= 0) |
if (len <= 0) |
| _exit(1); |
_exit(1); |
| if (asprintf(&p, "RSYNC_REQUEST=%s", buf) > 0) | set_env_str("RSYNC_REQUEST", buf); |
| putenv(p); | |
| for (j = 0; ; j++) { |
for (j = 0; ; j++) { |
| len = read_arg_from_pipe(fds[0], buf, | len = read_arg_from_pipe(pre_exec_arg_fd, buf, |
| BIGPATHBUFLEN); |
BIGPATHBUFLEN); |
| if (len <= 0) { |
if (len <= 0) { |
| if (!len) |
if (!len) |
| break; |
break; |
| _exit(1); |
_exit(1); |
| } |
} |
| if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) > 0) | if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0) |
| putenv(p); |
putenv(p); |
| } |
} |
| close(fds[0]); | close(pre_exec_arg_fd); |
| close(STDIN_FILENO); |
close(STDIN_FILENO); |
| close(STDOUT_FILENO); | dup2(pre_exec_error_fd, STDOUT_FILENO); |
| | close(pre_exec_error_fd); |
| status = system(lp_prexfer_exec(i)); |
status = system(lp_prexfer_exec(i)); |
| if (!WIFEXITED(status)) |
if (!WIFEXITED(status)) |
| _exit(1); |
_exit(1); |
| _exit(WEXITSTATUS(status)); |
_exit(WEXITSTATUS(status)); |
| } |
} |
| close(fds[0]); | close(arg_fds[0]); |
| set_blocking(fds[1]); | close(error_fds[1]); |
| pre_exec_fd = fds[1]; | pre_exec_arg_fd = arg_fds[1]; |
| | pre_exec_error_fd = error_fds[0]; |
| | set_blocking(pre_exec_arg_fd); |
| | set_blocking(pre_exec_error_fd); |
| } |
} |
| umask(0); |
|
| } |
} |
| #endif |
#endif |
| |
|
|
Line 694 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 801 static int rsync_module(int f_in, int f_out, int i, ch
|
| munge_symlinks = !use_chroot || module_dirlen; |
munge_symlinks = !use_chroot || module_dirlen; |
| if (munge_symlinks) { |
if (munge_symlinks) { |
| STRUCT_STAT st; |
STRUCT_STAT st; |
| if (do_stat(SYMLINK_PREFIX, &st) == 0 && S_ISDIR(st.st_mode)) { | char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */ |
| rprintf(FLOG, "Symlink munging is unsupported when a %s directory exists.\n", | strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */ |
| SYMLINK_PREFIX); | if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) { |
| | rprintf(FLOG, "Symlink munging is unsafe when a %s directory exists.\n", |
| | prefix); |
| io_printf(f_out, "@ERROR: daemon security issue -- contact admin\n", name); |
io_printf(f_out, "@ERROR: daemon security issue -- contact admin\n", name); |
| exit_cleanup(RERR_UNSUPPORTED); |
exit_cleanup(RERR_UNSUPPORTED); |
| } |
} |
| } |
} |
| |
|
| if (am_root) { | if (gid_list.count) { |
| /* XXXX: You could argue that if the daemon is started | gid_t *gid_array = gid_list.items; |
| * by a non-root user and they explicitly specify a | if (setgid(gid_array[0])) { |
| * gid, then we should try to change to that gid -- | rsyserr(FLOG, errno, "setgid %ld failed", (long)gid_array[0]); |
| * this could be possible if it's already in their | |
| * supplementary groups. */ | |
| |
| /* TODO: Perhaps we need to document that if rsyncd is | |
| * started by somebody other than root it will inherit | |
| * all their supplementary groups. */ | |
| |
| if (setgid(gid)) { | |
| rsyserr(FLOG, errno, "setgid %d failed", (int)gid); | |
| io_printf(f_out, "@ERROR: setgid failed\n"); |
io_printf(f_out, "@ERROR: setgid failed\n"); |
| return -1; |
return -1; |
| } |
} |
| #ifdef HAVE_SETGROUPS |
#ifdef HAVE_SETGROUPS |
| /* Get rid of any supplementary groups this process | /* Set the group(s) we want to be active. */ |
| * might have inheristed. */ | if (setgroups(gid_list.count, gid_array)) { |
| if (setgroups(1, &gid)) { | |
| rsyserr(FLOG, errno, "setgroups failed"); |
rsyserr(FLOG, errno, "setgroups failed"); |
| io_printf(f_out, "@ERROR: setgroups failed\n"); |
io_printf(f_out, "@ERROR: setgroups failed\n"); |
| return -1; |
return -1; |
| } |
} |
| #endif |
#endif |
| |
#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST |
| |
/* pw is set if the user wants all the user's groups. */ |
| |
if (pw && initgroups(pw->pw_name, pw->pw_gid) < 0) { |
| |
rsyserr(FLOG, errno, "initgroups failed"); |
| |
io_printf(f_out, "@ERROR: initgroups failed\n"); |
| |
return -1; |
| |
} |
| |
#endif |
| |
} |
| |
|
| |
if (set_uid) { |
| if (setuid(uid) < 0 |
if (setuid(uid) < 0 |
| #ifdef HAVE_SETEUID |
#ifdef HAVE_SETEUID |
| || seteuid(uid) < 0 |
|| seteuid(uid) < 0 |
| #endif |
#endif |
| ) { |
) { |
| rsyserr(FLOG, errno, "setuid %d failed", (int)uid); | rsyserr(FLOG, errno, "setuid %ld failed", (long)uid); |
| io_printf(f_out, "@ERROR: setuid failed\n"); |
io_printf(f_out, "@ERROR: setuid failed\n"); |
| return -1; |
return -1; |
| } |
} |
|
Line 756 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 865 static int rsync_module(int f_in, int f_out, int i, ch
|
| read_args(f_in, name, line, sizeof line, rl_nulls, &argv, &argc, &request); |
read_args(f_in, name, line, sizeof line, rl_nulls, &argv, &argc, &request); |
| orig_argv = argv; |
orig_argv = argv; |
| |
|
| verbose = 0; /* future verbosity is controlled by client options */ | save_munge_symlinks = munge_symlinks; |
| | |
| | reset_output_levels(); /* future verbosity is controlled by client options */ |
| ret = parse_arguments(&argc, (const char ***) &argv); |
ret = parse_arguments(&argc, (const char ***) &argv); |
| if (protect_args && ret) { |
if (protect_args && ret) { |
| orig_early_argv = orig_argv; |
orig_early_argv = orig_argv; |
|
Line 767 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 878 static int rsync_module(int f_in, int f_out, int i, ch
|
| } else |
} else |
| orig_early_argv = NULL; |
orig_early_argv = NULL; |
| |
|
| |
munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */ |
| |
|
| if (pre_exec_pid) { |
if (pre_exec_pid) { |
| err_msg = finish_pre_exec(pre_exec_pid, pre_exec_fd, request, | err_msg = finish_pre_exec(pre_exec_pid, pre_exec_arg_fd, pre_exec_error_fd, |
| orig_early_argv, orig_argv); | request, orig_early_argv, orig_argv); |
| } |
} |
| |
|
| if (orig_early_argv) |
if (orig_early_argv) |
|
Line 807 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 920 static int rsync_module(int f_in, int f_out, int i, ch
|
| |
|
| #ifndef DEBUG |
#ifndef DEBUG |
| /* don't allow the logs to be flooded too fast */ |
/* don't allow the logs to be flooded too fast */ |
| if (verbose > lp_max_verbosity(i)) | limit_output_verbosity(lp_max_verbosity(i)); |
| verbose = lp_max_verbosity(i); | |
| #endif |
#endif |
| |
|
| if (protocol_version < 23 |
if (protocol_version < 23 |
| && (protocol_version == 22 || am_sender)) |
&& (protocol_version == 22 || am_sender)) |
| io_start_multiplex_out(); | io_start_multiplex_out(f_out); |
| else if (!ret || err_msg) { |
else if (!ret || err_msg) { |
| /* We have to get I/O multiplexing started so that we can |
/* We have to get I/O multiplexing started so that we can |
| * get the error back to the client. This means getting |
* get the error back to the client. This means getting |
|
Line 837 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 949 static int rsync_module(int f_in, int f_out, int i, ch
|
| if (files_from) |
if (files_from) |
| write_byte(f_out, 0); |
write_byte(f_out, 0); |
| } |
} |
| io_start_multiplex_out(); | io_start_multiplex_out(f_out); |
| } |
} |
| |
|
| if (!ret || err_msg) { |
if (!ret || err_msg) { |
| if (err_msg) | if (err_msg) { |
| rwrite(FERROR, err_msg, strlen(err_msg), 0); | while ((p = strchr(err_msg, '\n')) != NULL) { |
| else | int len = p - err_msg + 1; |
| | rwrite(FERROR, err_msg, len, 0); |
| | err_msg += len; |
| | } |
| | if (*err_msg) |
| | rprintf(FERROR, "%s\n", err_msg); |
| | } else |
| option_error(); |
option_error(); |
| msleep(400); |
msleep(400); |
| exit_cleanup(RERR_UNSUPPORTED); |
exit_cleanup(RERR_UNSUPPORTED); |
|
Line 891 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 1009 static int rsync_module(int f_in, int f_out, int i, ch
|
| with "list = False". */ |
with "list = False". */ |
| static void send_listing(int fd) |
static void send_listing(int fd) |
| { |
{ |
| int n = lp_numservices(); | int n = lp_num_modules(); |
| int i; |
int i; |
| |
|
| for (i = 0; i < n; i++) { |
for (i = 0; i < n; i++) { |
|
Line 920 static int load_config(int globals_only)
|
Line 1038 static int load_config(int globals_only)
|
| int start_daemon(int f_in, int f_out) |
int start_daemon(int f_in, int f_out) |
| { |
{ |
| char line[1024]; |
char line[1024]; |
| char *addr, *host; | const char *addr, *host; |
| int i; |
int i; |
| |
|
| io_set_sock_fds(f_in, f_out); |
io_set_sock_fds(f_in, f_out); |
|
Line 933 int start_daemon(int f_in, int f_out)
|
Line 1051 int start_daemon(int f_in, int f_out)
|
| exit_cleanup(RERR_SYNTAX); |
exit_cleanup(RERR_SYNTAX); |
| |
|
| addr = client_addr(f_in); |
addr = client_addr(f_in); |
| host = client_name(f_in); | host = lp_reverse_lookup(-1) ? client_name(f_in) : undetermined_hostname; |
| rprintf(FLOG, "connect from %s (%s)\n", host, addr); |
rprintf(FLOG, "connect from %s (%s)\n", host, addr); |
| |
|
| if (!am_server) { |
if (!am_server) { |
|
Line 945 int start_daemon(int f_in, int f_out)
|
Line 1063 int start_daemon(int f_in, int f_out)
|
| return -1; |
return -1; |
| |
|
| line[0] = 0; |
line[0] = 0; |
| if (!read_line_old(f_in, line, sizeof line)) | if (!read_line_old(f_in, line, sizeof line, 0)) |
| return -1; |
return -1; |
| |
|
| if (!*line || strcmp(line, "#list") == 0) { |
if (!*line || strcmp(line, "#list") == 0) { |
|
Line 987 static void create_pid_file(void)
|
Line 1105 static void create_pid_file(void)
|
| return; |
return; |
| |
|
| cleanup_set_pid(pid); |
cleanup_set_pid(pid); |
| if ((fd = do_open(pid_file, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~orig_umask)) == -1) { | if ((fd = do_open(pid_file, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) { |
| failure: |
failure: |
| cleanup_set_pid(0); |
cleanup_set_pid(0); |
| fprintf(stderr, "failed to create pid file %s: %s\n", pid_file, strerror(errno)); |
fprintf(stderr, "failed to create pid file %s: %s\n", pid_file, strerror(errno)); |
| rsyserr(FLOG, errno, "failed to create pid file %s", pid_file); |
rsyserr(FLOG, errno, "failed to create pid file %s", pid_file); |
| exit_cleanup(RERR_FILEIO); |
exit_cleanup(RERR_FILEIO); |
| } |
} |
| snprintf(pidbuf, sizeof pidbuf, "%ld\n", (long)pid); | snprintf(pidbuf, sizeof pidbuf, "%d\n", (int)pid); |
| len = strlen(pidbuf); |
len = strlen(pidbuf); |
| if (write(fd, pidbuf, len) != len) |
if (write(fd, pidbuf, len) != len) |
| goto failure; |
goto failure; |
|
Line 1055 int daemon_main(void)
|
Line 1173 int daemon_main(void)
|
| fprintf(stderr, "Failed to parse config file: %s\n", config_file); |
fprintf(stderr, "Failed to parse config file: %s\n", config_file); |
| exit_cleanup(RERR_SYNTAX); |
exit_cleanup(RERR_SYNTAX); |
| } |
} |
| |
set_dparams(0); |
| |
|
| if (no_detach) |
if (no_detach) |
| create_pid_file(); |
create_pid_file(); |