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(); |