version 1.1, 2012/02/17 15:09:30
|
version 1.1.1.4, 2021/03/17 00:32:36
|
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-2020 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 "itypes.h" |
#include "ifuncs.h" |
#include "ifuncs.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 31 extern int am_sender;
|
Line 31 extern int am_sender;
|
extern int am_server; |
extern int am_server; |
extern int am_daemon; |
extern int am_daemon; |
extern int am_root; |
extern int am_root; |
|
extern int msgs2stderr; |
extern int rsync_port; |
extern int rsync_port; |
extern int protect_args; |
extern int protect_args; |
extern int ignore_errors; |
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 munge_symlinks; |
| extern int open_noatime; |
extern int sanitize_paths; |
extern int sanitize_paths; |
extern int numeric_ids; |
extern int numeric_ids; |
extern int filesfrom_fd; |
extern int filesfrom_fd; |
extern int remote_protocol; |
extern int remote_protocol; |
extern int protocol_version; |
extern int protocol_version; |
|
extern int always_checksum; |
|
extern int checksum_files; |
extern int io_timeout; |
extern int io_timeout; |
extern int no_detach; |
extern int no_detach; |
extern int write_batch; |
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 *link_by_hash_dir; |
extern char *logfile_format; |
extern char *logfile_format; |
extern char *files_from; |
extern char *files_from; |
extern char *tmpdir; |
extern char *tmpdir; |
|
extern char *early_input_file; |
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; |
#endif |
#endif |
|
extern uid_t our_uid; |
|
extern gid_t our_gid; |
|
|
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; | int pid_file_fd = -1; |
| int early_input_len = 0; |
| char *early_input = NULL; |
| pid_t namecvt_pid = 0; |
struct chmod_mode_struct *daemon_chmod_modes; |
struct chmod_mode_struct *daemon_chmod_modes; |
|
|
|
#define EARLY_INPUT_CMD "#early_input=" |
|
#define EARLY_INPUT_CMDLEN (sizeof EARLY_INPUT_CMD - 1) |
|
|
/* 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 |
* mode and module_dir is not "/"; otherwise 0. (Note that a chroot- |
* mode and module_dir is not "/"; otherwise 0. (Note that a chroot- |
* enabled module can have a non-"/" module_dir these days.) */ |
* enabled module can have a non-"/" module_dir these days.) */ |
Line 76 unsigned int module_dirlen = 0;
|
Line 89 unsigned int module_dirlen = 0;
|
char *full_module_path; |
char *full_module_path; |
|
|
static int rl_nulls = 0; |
static int rl_nulls = 0; |
|
static int namecvt_fd_req = -1, namecvt_fd_ans = -1; |
|
|
#ifdef HAVE_SIGACTION |
#ifdef HAVE_SIGACTION |
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 116 int start_socket_client(char *host, int remote_argc, c
|
Line 135 int start_socket_client(char *host, int remote_argc, c
|
*p = '\0'; |
*p = '\0'; |
} |
} |
|
|
fd = open_socket_out_wrapped(host, rsync_port, bind_address, | fd = open_socket_out_wrapped(host, rsync_port, bind_address, default_af_hint); |
default_af_hint); | |
if (fd == -1) |
if (fd == -1) |
exit_cleanup(RERR_SOCKETIO); |
exit_cleanup(RERR_SOCKETIO); |
|
|
Line 125 int start_socket_client(char *host, int remote_argc, c
|
Line 143 int start_socket_client(char *host, int remote_argc, c
|
setup_iconv(); |
setup_iconv(); |
#endif |
#endif |
|
|
ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv); | ret = start_inband_exchange(fd, fd, user, host, remote_argc, remote_argv); |
|
|
return ret ? ret : client_run(fd, fd, -1, argc, argv); |
return ret ? ret : client_run(fd, fd, -1, argc, argv); |
} |
} |
Line 138 static int exchange_protocols(int f_in, int f_out, cha
|
Line 156 static int exchange_protocols(int f_in, int f_out, cha
|
#else |
#else |
int our_sub = 0; |
int our_sub = 0; |
#endif |
#endif |
char *motd; |
|
|
|
io_printf(f_out, "@RSYNCD: %d.%d\n", protocol_version, our_sub); |
io_printf(f_out, "@RSYNCD: %d.%d\n", protocol_version, our_sub); |
|
|
if (!am_client) { |
if (!am_client) { |
motd = lp_motd_file(); | char *motd = lp_motd_file(); |
if (motd && *motd) { |
if (motd && *motd) { |
FILE *f = fopen(motd,"r"); | FILE *f = fopen(motd, "r"); |
while (f && !feof(f)) { |
while (f && !feof(f)) { |
int len = fread(buf, 1, bufsiz - 1, f); |
int len = fread(buf, 1, bufsiz - 1, f); |
if (len > 0) |
if (len > 0) |
Line 158 static int exchange_protocols(int f_in, int f_out, cha
|
Line 174 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 204 static int exchange_protocols(int f_in, int f_out, cha
|
Line 220 static int exchange_protocols(int f_in, int f_out, cha
|
return 0; |
return 0; |
} |
} |
|
|
int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char *argv[]) | int start_inband_exchange(int f_in, int f_out, const char *user, const char *host, int argc, char *argv[]) |
{ |
{ |
int i, modlen; |
int i, modlen; |
char line[BIGPATHBUFLEN]; |
char line[BIGPATHBUFLEN]; |
Line 225 int start_inband_exchange(int f_in, int f_out, const c
|
Line 241 int start_inband_exchange(int f_in, int f_out, const c
|
else |
else |
modlen = p - *argv; |
modlen = p - *argv; |
|
|
if (!(modname = new_array(char, modlen+1+1))) /* room for '/' & '\0' */ | modname = new_array(char, modlen+1+1); /* room for '/' & '\0' */ |
out_of_memory("start_inband_exchange"); | |
strlcpy(modname, *argv, modlen + 1); |
strlcpy(modname, *argv, modlen + 1); |
modname[modlen] = '/'; |
modname[modlen] = '/'; |
modname[modlen+1] = '\0'; |
modname[modlen+1] = '\0'; |
Line 239 int start_inband_exchange(int f_in, int f_out, const c
|
Line 254 int start_inband_exchange(int f_in, int f_out, const c
|
if (exchange_protocols(f_in, f_out, line, sizeof line, 1) < 0) |
if (exchange_protocols(f_in, f_out, line, sizeof line, 1) < 0) |
return -1; |
return -1; |
|
|
/* set daemon_over_rsh to false since we need to build the | if (early_input_file) { |
* true set of args passed through the rsh/ssh connection; | STRUCT_STAT st; |
* this is a no-op for direct-socket-connection mode */ | FILE *f = fopen(early_input_file, "rb"); |
daemon_over_rsh = 0; | if (!f || do_fstat(fileno(f), &st) < 0) { |
| rsyserr(FERROR, errno, "failed to open %s", early_input_file); |
| return -1; |
| } |
| early_input_len = st.st_size; |
| if (early_input_len > (int)sizeof line) { |
| rprintf(FERROR, "%s is > %d bytes.\n", early_input_file, (int)sizeof line); |
| return -1; |
| } |
| if (early_input_len > 0) { |
| io_printf(f_out, EARLY_INPUT_CMD "%d\n", early_input_len); |
| while (early_input_len > 0) { |
| int len; |
| if (feof(f)) { |
| rprintf(FERROR, "Early EOF in %s\n", early_input_file); |
| return -1; |
| } |
| len = fread(line, 1, early_input_len, f); |
| if (len > 0) { |
| write_buf(f_out, line, len); |
| early_input_len -= len; |
| } |
| } |
| } |
| fclose(f); |
| } |
| |
server_options(sargs, &sargc); |
server_options(sargs, &sargc); |
|
|
if (sargc >= MAX_ARGS - 2) |
if (sargc >= MAX_ARGS - 2) |
Line 270 int start_inband_exchange(int f_in, int f_out, const c
|
Line 311 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 321 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 290 int start_inband_exchange(int f_in, int f_out, const c
|
Line 331 int start_inband_exchange(int f_in, int f_out, const c
|
continue; |
continue; |
} |
} |
|
|
|
if (strcmp(line, "@RSYNCD: GSS") == 0) { |
|
#ifdef GSSAPI_OPTION |
|
if (auth_gss_client(f_out, host) < 0) |
|
return -1; |
|
continue; |
|
#else |
|
rprintf(FERROR, "GSSAPI is not supported\n"); |
|
return -1; |
|
#endif |
|
} |
|
|
if (strcmp(line,"@RSYNCD: OK") == 0) |
if (strcmp(line,"@RSYNCD: OK") == 0) |
break; |
break; |
|
|
Line 334 int start_inband_exchange(int f_in, int f_out, const c
|
Line 386 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 394 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, | #ifdef HAVE_PUTENV |
char **early_argv, char **argv) | static int read_arg_from_pipe(int fd, char *buf, int limit) |
{ |
{ |
int j = 0, status = -1; | char *bp = buf, *eob = buf + limit - 1; |
|
|
|
while (1) { |
|
int got = read(fd, bp, 1); |
|
if (got != 1) { |
|
if (got < 0 && errno == EINTR) |
|
continue; |
|
return -1; |
|
} |
|
if (*bp == '\0') |
|
break; |
|
if (bp < eob) |
|
bp++; |
|
} |
|
*bp = '\0'; |
|
|
|
return bp - buf; |
|
} |
|
#endif |
|
|
|
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 |
|
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 |
|
|
|
/* Used for "early exec", "pre-xfer exec", and the "name converter" script. */ |
|
static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr) |
|
{ |
|
int arg_fds[2], error_fds[2], arg_fd; |
|
pid_t pid; |
|
|
|
if ((error_fd_ptr && pipe(error_fds) < 0) || pipe(arg_fds) < 0 || (pid = fork()) < 0) |
|
return (pid_t)-1; |
|
|
|
if (pid == 0) { |
|
char buf[BIGPATHBUFLEN]; |
|
int j, len, status; |
|
|
|
if (error_fd_ptr) { |
|
close(error_fds[0]); |
|
set_blocking(error_fds[1]); |
|
} |
|
|
|
close(arg_fds[1]); |
|
arg_fd = arg_fds[0]; |
|
set_blocking(arg_fd); |
|
|
|
len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN); |
|
if (len <= 0) |
|
_exit(1); |
|
set_env_str("RSYNC_REQUEST", buf); |
|
|
|
for (j = 0; ; j++) { |
|
char *p; |
|
len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN); |
|
if (len <= 0) { |
|
if (!len) |
|
break; |
|
_exit(1); |
|
} |
|
if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0) |
|
putenv(p); |
|
} |
|
|
|
dup2(arg_fd, STDIN_FILENO); |
|
close(arg_fd); |
|
|
|
if (error_fd_ptr) { |
|
dup2(error_fds[1], STDOUT_FILENO); |
|
close(error_fds[1]); |
|
} |
|
|
|
status = shell_exec(cmd); |
|
|
|
if (!WIFEXITED(status)) |
|
_exit(1); |
|
_exit(WEXITSTATUS(status)); |
|
} |
|
|
|
if (error_fd_ptr) { |
|
close(error_fds[1]); |
|
*error_fd_ptr = error_fds[0]; |
|
set_blocking(error_fds[0]); |
|
} |
|
|
|
close(arg_fds[0]); |
|
arg_fd = *arg_fd_ptr = arg_fds[1]; |
|
set_blocking(arg_fd); |
|
|
|
return pid; |
|
} |
|
|
|
static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int exec_type) |
|
{ |
|
int j = 0; |
|
|
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++) { | if (argv) { |
write_buf(fd, argv[j], strlen(argv[j])+1); | for ( ; argv[j]; j++) |
if (argv[j][0] == '.' && argv[j][1] == '\0') | write_buf(write_fd, argv[j], strlen(argv[j])+1); |
break; | |
} |
} |
write_byte(fd, 0); | write_byte(write_fd, 0); |
|
|
close(fd); | if (exec_type == 1 && early_input_len) |
| write_buf(write_fd, early_input, early_input_len); |
|
|
|
if (exec_type != 2) /* the name converter needs this left open */ |
|
close(write_fd); |
|
} |
|
|
|
static char *finish_pre_exec(const char *desc, pid_t pid, int read_fd) |
|
{ |
|
char buf[BIGPATHBUFLEN], *bp, *cr; |
|
int j, status = -1, msglen = sizeof buf - 1; |
|
|
|
if (read_fd >= 0) { |
|
/* Read the stdout from the program. This it is only displayed |
|
* to the user if the script also returns an error status. */ |
|
for (bp = buf, cr = 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] = '\0'; |
|
while (1) { |
|
if ((cr = strchr(cr, '\r')) == NULL) { |
|
cr = bp + j; |
|
break; |
|
} |
|
if (!cr[1]) |
|
break; /* wait for more data before we decide what to do */ |
|
if (cr[1] == '\n') { |
|
memmove(cr, cr+1, j - (cr - bp)); |
|
j--; |
|
} else |
|
cr++; |
|
} |
|
bp += j; |
|
} |
|
*bp = '\0'; |
|
|
|
close(read_fd); |
|
} else |
|
*buf = '\0'; |
|
|
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, "%s returned failure (%d)%s%s%s\n%s", |
status, status < 0 ? ": " : "", | desc, 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; |
} |
} |
|
|
static int read_arg_from_pipe(int fd, char *buf, int limit) |
|
{ |
|
char *bp = buf, *eob = buf + limit - 1; |
|
|
|
while (1) { |
|
int got = read(fd, bp, 1); |
|
if (got != 1) { |
|
if (got < 0 && errno == EINTR) |
|
continue; |
|
return -1; |
|
} |
|
if (*bp == '\0') |
|
break; |
|
if (bp < eob) |
|
bp++; |
|
} |
|
*bp = '\0'; |
|
|
|
return bp - buf; |
|
} |
|
|
|
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) |
{ |
{ |
if (was_chdir) |
if (was_chdir) |
rsyserr(FLOG, errno, "chdir %s failed\n", dir); | rsyserr(FLOG, errno, "chdir %s failed", dir); |
else |
else |
rprintf(FLOG, "normalize_path(%s) failed\n", dir); |
rprintf(FLOG, "normalize_path(%s) failed\n", dir); |
io_printf(f_out, "@ERROR: chdir failed\n"); |
io_printf(f_out, "@ERROR: chdir failed\n"); |
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 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 659 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(client_addr(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 442 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 679 static int rsync_module(int f_in, int f_out, int i, ch
|
return -1; |
return -1; |
} |
} |
|
|
if (am_daemon && am_server) { | if (*lp_link_by_hash_dir(i)) |
| link_by_hash_dir = lp_link_by_hash_dir(i); |
| |
| if (am_daemon > 0) { |
rprintf(FLOG, "rsync allowed access on module %s from %s (%s)\n", |
rprintf(FLOG, "rsync allowed access on module %s from %s (%s)\n", |
name, host, addr); |
name, host, addr); |
} |
} |
Line 461 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 701 static int rsync_module(int f_in, int f_out, int i, ch
|
return -1; |
return -1; |
} |
} |
|
|
auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD "); | read_only = lp_read_only(i); /* may also be overridden by auth_server() */ |
| #ifdef GSSAPI_OPTION |
| if (lp_use_gssapi(i)) |
| auth_user = auth_gss_server(f_in, f_out, i, host, addr, "@RSYNCD: GSS"); |
| else |
| #endif |
| 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)) | if (lp_transfer_logging(module_id) && !logfile_format) |
read_only = 1; | logfile_format = lp_log_format(module_id); |
| |
if (lp_transfer_logging(i) && !logfile_format) | |
logfile_format = lp_log_format(i); | |
if (log_format_has(logfile_format, 'i')) |
if (log_format_has(logfile_format, 'i')) |
logfile_format_has_i = 1; |
logfile_format_has_i = 1; |
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 == ROOT_UID); |
|
|
if (am_root) { | p = *lp_uid(module_id) ? lp_uid(module_id) : 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(module_id) ? conf_strtok(lp_gid(module_id)) : 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 = conf_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 | module_dir = lp_path(module_id); |
* 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); | |
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); |
io_printf(f_out, "@ERROR: no path setting.\n"); |
io_printf(f_out, "@ERROR: no path setting.\n"); |
Line 540 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 800 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 547 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 808 static int rsync_module(int f_in, int f_out, int i, ch
|
} else |
} else |
set_filter_dir(module_dir, module_dirlen); |
set_filter_dir(module_dir, module_dirlen); |
|
|
p = lp_filter(i); | p = lp_filter(module_id); |
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(module_id); |
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(module_id); |
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(module_id); |
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(module_id); |
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); |
|
|
|
if (*lp_db_config(i)) { |
|
db_read_config(FLOG, lp_db_config(i)); |
|
db_lax = lp_db_lax(i); |
|
} |
|
|
#ifdef HAVE_PUTENV |
#ifdef HAVE_PUTENV |
if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { | if ((*lp_early_exec(module_id) || *lp_prexfer_exec(module_id) |
char *modname, *modpath, *hostaddr, *hostname, *username; | || *lp_postxfer_exec(module_id) || *lp_name_converter(module_id)) |
int status; | && !getenv("RSYNC_NO_XFER_EXEC")) { |
| set_env_num("RSYNC_PID", (long)getpid()); |
|
|
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. */ |
if (*lp_postxfer_exec(i)) { | if (*lp_postxfer_exec(module_id)) { |
pid_t pid = fork(); |
pid_t pid = fork(); |
if (pid < 0) { |
if (pid < 0) { |
rsyserr(FLOG, errno, "fork failed"); |
rsyserr(FLOG, errno, "fork failed"); |
Line 598 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 853 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) | int status; |
putenv(p); | close(f_in); |
| if (f_out != f_in) |
| close(f_out); |
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 (shell_exec(lp_postxfer_exec(module_id)) < 0) |
if (system(lp_postxfer_exec(i)) < 0) | |
status = -1; |
status = -1; |
_exit(status); |
_exit(status); |
} |
} |
} |
} |
|
|
|
/* For early exec, fork a child process to run the indicated |
|
* command and wait for it to exit. */ |
|
if (*lp_early_exec(module_id)) { |
|
int arg_fd; |
|
pid_t pid = start_pre_exec(lp_early_exec(module_id), &arg_fd, NULL); |
|
if (pid == (pid_t)-1) { |
|
rsyserr(FLOG, errno, "early exec preparation failed"); |
|
io_printf(f_out, "@ERROR: early exec preparation failed\n"); |
|
return -1; |
|
} |
|
write_pre_exec_args(arg_fd, NULL, NULL, NULL, 1); |
|
if (finish_pre_exec("early exec", pid, -1) != NULL) { |
|
rsyserr(FLOG, errno, "early exec failed"); |
|
io_printf(f_out, "@ERROR: early exec failed\n"); |
|
return -1; |
|
} |
|
} |
|
|
/* For pre-xfer exec, fork a child process to run the indicated |
/* For pre-xfer exec, fork a child process to run the indicated |
* 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(module_id)) { |
int fds[2]; | pre_exec_pid = start_pre_exec(lp_prexfer_exec(module_id), &pre_exec_arg_fd, &pre_exec_error_fd); |
if (asprintf(&p, "RSYNC_PID=%ld", (long)getpid()) > 0) | if (pre_exec_pid == (pid_t)-1) { |
putenv(p); | |
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; |
} |
} |
if (pre_exec_pid == 0) { | } |
char buf[BIGPATHBUFLEN]; | |
int j, len; | if (*lp_name_converter(module_id)) { |
close(fds[1]); | namecvt_pid = start_pre_exec(lp_name_converter(module_id), &namecvt_fd_req, &namecvt_fd_ans); |
set_blocking(fds[0]); | if (namecvt_pid == (pid_t)-1) { |
len = read_arg_from_pipe(fds[0], buf, BIGPATHBUFLEN); | rsyserr(FLOG, errno, "name-converter exec preparation failed"); |
if (len <= 0) | io_printf(f_out, "@ERROR: name-converter exec preparation failed\n"); |
_exit(1); | return -1; |
if (asprintf(&p, "RSYNC_REQUEST=%s", buf) > 0) | |
putenv(p); | |
for (j = 0; ; j++) { | |
len = read_arg_from_pipe(fds[0], buf, | |
BIGPATHBUFLEN); | |
if (len <= 0) { | |
if (!len) | |
break; | |
_exit(1); | |
} | |
if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) > 0) | |
putenv(p); | |
} | |
close(fds[0]); | |
close(STDIN_FILENO); | |
close(STDOUT_FILENO); | |
status = system(lp_prexfer_exec(i)); | |
if (!WIFEXITED(status)) | |
_exit(1); | |
_exit(WEXITSTATUS(status)); | |
} |
} |
close(fds[0]); |
|
set_blocking(fds[1]); |
|
pre_exec_fd = fds[1]; |
|
} |
} |
umask(0); |
|
} |
} |
#endif |
#endif |
|
|
|
if (early_input) { |
|
free(early_input); |
|
early_input = NULL; |
|
} |
|
|
if (use_chroot) { |
if (use_chroot) { |
/* |
/* |
* XXX: The 'use chroot' flag is a fairly reliable |
* XXX: The 'use chroot' flag is a fairly reliable |
Line 687 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 940 static int rsync_module(int f_in, int f_out, int i, ch
|
|
|
if (!change_dir(module_chdir, CD_NORMAL)) |
if (!change_dir(module_chdir, CD_NORMAL)) |
return path_failure(f_out, module_chdir, True); |
return path_failure(f_out, module_chdir, True); |
if (module_dirlen || !use_chroot) | if (module_dirlen || (!use_chroot && !*lp_daemon_chroot())) |
sanitize_paths = 1; |
sanitize_paths = 1; |
|
|
if ((munge_symlinks = lp_munge_symlinks(i)) < 0) | if ((munge_symlinks = lp_munge_symlinks(module_id)) < 0) |
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 |
|
our_gid = MY_GID(); |
|
} |
|
|
|
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; |
} |
} |
|
|
am_root = (MY_UID() == 0); | our_uid = MY_UID(); |
| am_root = (our_uid == ROOT_UID); |
} |
} |
|
|
if (lp_temp_dir(i) && *lp_temp_dir(i)) { | if (lp_temp_dir(module_id) && *lp_temp_dir(module_id)) { |
tmpdir = lp_temp_dir(i); | tmpdir = lp_temp_dir(module_id); |
if (strlen(tmpdir) >= MAXPATHLEN - 10) { |
if (strlen(tmpdir) >= MAXPATHLEN - 10) { |
rprintf(FLOG, |
rprintf(FLOG, |
"the 'temp dir' value for %s is WAY too long -- ignoring.\n", |
"the 'temp dir' value for %s is WAY too long -- ignoring.\n", |
Line 756 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 1013 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 1026 static int rsync_module(int f_in, int f_out, int i, ch
|
} else |
} else |
orig_early_argv = NULL; |
orig_early_argv = NULL; |
|
|
|
/* The default is to use the user's setting unless the module sets True or False. */ |
|
if (lp_open_noatime(module_id) >= 0) |
|
open_noatime = lp_open_noatime(module_id); |
|
|
|
munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */ |
|
|
|
if (am_daemon > 0) |
|
msgs2stderr = 0; /* A non-rsh-run daemon doesn't have stderr for msgs. */ |
|
|
if (pre_exec_pid) { |
if (pre_exec_pid) { |
err_msg = finish_pre_exec(pre_exec_pid, pre_exec_fd, request, | write_pre_exec_args(pre_exec_arg_fd, request, orig_early_argv, orig_argv, 0); |
orig_early_argv, orig_argv); | err_msg = finish_pre_exec("pre-xfer exec", pre_exec_pid, pre_exec_error_fd); |
} |
} |
|
|
|
if (namecvt_pid) |
|
write_pre_exec_args(namecvt_fd_req, request, orig_early_argv, orig_argv, 2); |
|
|
if (orig_early_argv) |
if (orig_early_argv) |
free(orig_early_argv); |
free(orig_early_argv); |
|
|
am_server = 1; /* Don't let someone try to be tricky. */ |
am_server = 1; /* Don't let someone try to be tricky. */ |
quiet = 0; |
quiet = 0; |
|
db_config = NULL; |
|
|
if (lp_ignore_errors(module_id)) |
if (lp_ignore_errors(module_id)) |
ignore_errors = 1; |
ignore_errors = 1; |
if (write_batch < 0) |
if (write_batch < 0) |
dry_run = 1; |
dry_run = 1; |
|
|
if (lp_fake_super(i)) { | if (lp_fake_super(module_id)) { |
if (preserve_xattrs > 1) |
if (preserve_xattrs > 1) |
preserve_xattrs = 1; |
preserve_xattrs = 1; |
am_root = -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; |
am_root = 2; |
|
|
|
checksum_files = always_checksum ? lp_checksum_files(i) |
|
: CSF_IGNORE_FILES; |
|
|
if (filesfrom_fd == 0) |
if (filesfrom_fd == 0) |
filesfrom_fd = f_in; |
filesfrom_fd = f_in; |
|
|
Line 807 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 1083 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(module_id)); |
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(f_out); |
io_start_multiplex_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 1111 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); |
| io_flush(MSG_FLUSH); |
| } else |
option_error(); |
option_error(); |
msleep(400); |
msleep(400); |
exit_cleanup(RERR_UNSUPPORTED); |
exit_cleanup(RERR_UNSUPPORTED); |
Line 863 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 1144 static int rsync_module(int f_in, int f_out, int i, ch
|
#endif |
#endif |
|
|
if (!numeric_ids |
if (!numeric_ids |
&& (use_chroot ? lp_numeric_ids(i) != False : lp_numeric_ids(i) == True)) | && (use_chroot ? lp_numeric_ids(module_id) != False && !*lp_name_converter(module_id) |
| : lp_numeric_ids(module_id) == True)) |
numeric_ids = -1; /* Set --numeric-ids w/o breaking protocol. */ |
numeric_ids = -1; /* Set --numeric-ids w/o breaking protocol. */ |
|
|
if (lp_timeout(i) && (!io_timeout || lp_timeout(i) < io_timeout)) | if (lp_timeout(module_id) && (!io_timeout || lp_timeout(module_id) < io_timeout)) |
set_io_timeout(lp_timeout(i)); | set_io_timeout(lp_timeout(module_id)); |
|
|
/* If we have some incoming/outgoing chmod changes, append them to |
/* If we have some incoming/outgoing chmod changes, append them to |
* any user-specified changes (making our changes have priority). |
* any user-specified changes (making our changes have priority). |
* We also get a pointer to just our changes so that a receiver |
* We also get a pointer to just our changes so that a receiver |
* process can use them separately if --perms wasn't specified. */ |
* process can use them separately if --perms wasn't specified. */ |
if (am_sender) |
if (am_sender) |
p = lp_outgoing_chmod(i); | p = lp_outgoing_chmod(module_id); |
else |
else |
p = lp_incoming_chmod(i); | p = lp_incoming_chmod(module_id); |
if (*p && !(daemon_chmod_modes = parse_chmod(p, &chmod_modes))) { |
if (*p && !(daemon_chmod_modes = parse_chmod(p, &chmod_modes))) { |
rprintf(FLOG, "Invalid \"%sing chmod\" directive: %s\n", |
rprintf(FLOG, "Invalid \"%sing chmod\" directive: %s\n", |
am_sender ? "outgo" : "incom", p); |
am_sender ? "outgo" : "incom", p); |
Line 887 static int rsync_module(int f_in, int f_out, int i, ch
|
Line 1169 static int rsync_module(int f_in, int f_out, int i, ch
|
return 0; |
return 0; |
} |
} |
|
|
|
BOOL namecvt_call(const char *cmd, const char **name_p, id_t *id_p) |
|
{ |
|
char buf[1024]; |
|
int got, len; |
|
|
|
if (*name_p) |
|
len = snprintf(buf, sizeof buf, "%s %s\n", cmd, *name_p); |
|
else |
|
len = snprintf(buf, sizeof buf, "%s %ld\n", cmd, (long)*id_p); |
|
if (len >= (int)sizeof buf) { |
|
rprintf(FERROR, "namecvt_call() request was too large.\n"); |
|
exit_cleanup(RERR_UNSUPPORTED); |
|
} |
|
|
|
while ((got = write(namecvt_fd_req, buf, len)) != len) { |
|
if (got < 0 && errno == EINTR) |
|
continue; |
|
rprintf(FERROR, "Connection to name-converter failed.\n"); |
|
exit_cleanup(RERR_SOCKETIO); |
|
} |
|
|
|
if (!read_line_old(namecvt_fd_ans, buf, sizeof buf, 0)) |
|
return False; |
|
|
|
if (*name_p) |
|
*id_p = (id_t)atol(buf); |
|
else |
|
*name_p = strdup(buf); |
|
|
|
return True; |
|
} |
|
|
/* send a list of available modules to the client. Don't list those |
/* send a list of available modules to the client. Don't list those |
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 906 static void send_listing(int fd)
|
Line 1220 static void send_listing(int fd)
|
static int load_config(int globals_only) |
static int load_config(int globals_only) |
{ |
{ |
if (!config_file) { |
if (!config_file) { |
if (am_server && am_root <= 0) | if (am_daemon < 0 && am_root <= 0) |
config_file = RSYNCD_USERCONF; |
config_file = RSYNCD_USERCONF; |
else |
else |
config_file = RSYNCD_SYSCONF; |
config_file = RSYNCD_SYSCONF; |
Line 920 static int load_config(int globals_only)
|
Line 1234 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; |
| char *p; |
int i; |
int i; |
|
|
|
/* At this point, am_server is only set for a daemon started via rsh. |
|
* Because am_server gets forced on soon, we'll set am_daemon to -1 as |
|
* a flag that can be checked later on to distinguish a normal daemon |
|
* from an rsh-run daemon. */ |
|
if (am_server) |
|
am_daemon = -1; |
|
|
io_set_sock_fds(f_in, f_out); |
io_set_sock_fds(f_in, f_out); |
|
|
/* We must load the config file before calling any function that |
/* We must load the config file before calling any function that |
Line 932 int start_daemon(int f_in, int f_out)
|
Line 1254 int start_daemon(int f_in, int f_out)
|
if (!load_config(0)) |
if (!load_config(0)) |
exit_cleanup(RERR_SYNTAX); |
exit_cleanup(RERR_SYNTAX); |
|
|
|
if (lp_proxy_protocol() && !read_proxy_protocol_header(f_in)) |
|
return -1; |
|
|
|
p = lp_daemon_chroot(); |
|
if (*p) { |
|
log_init(0); /* Make use we've initialized syslog before chrooting. */ |
|
if (chroot(p) < 0 || chdir("/") < 0) { |
|
rsyserr(FLOG, errno, "daemon chroot %s failed", p); |
|
return -1; |
|
} |
|
} |
|
p = lp_daemon_gid(); |
|
if (*p) { |
|
gid_t gid; |
|
if (!group_to_gid(p, &gid, True)) { |
|
rprintf(FLOG, "Invalid daemon gid: %s\n", p); |
|
return -1; |
|
} |
|
if (setgid(gid) < 0) { |
|
rsyserr(FLOG, errno, "Unable to set group to daemon gid %ld", (long)gid); |
|
return -1; |
|
} |
|
our_gid = MY_GID(); |
|
} |
|
p = lp_daemon_uid(); |
|
if (*p) { |
|
uid_t uid; |
|
if (!user_to_uid(p, &uid, True)) { |
|
rprintf(FLOG, "Invalid daemon uid: %s\n", p); |
|
return -1; |
|
} |
|
if (setuid(uid) < 0) { |
|
rsyserr(FLOG, errno, "Unable to set user to daemon uid %ld", (long)uid); |
|
return -1; |
|
} |
|
our_uid = MY_UID(); |
|
am_root = (our_uid == ROOT_UID); |
|
} |
|
|
addr = client_addr(f_in); |
addr = client_addr(f_in); |
host = client_name(f_in); | host = lp_reverse_lookup(-1) ? client_name(addr) : 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_daemon > 0) { |
set_socket_options(f_in, "SO_KEEPALIVE"); |
set_socket_options(f_in, "SO_KEEPALIVE"); |
set_nonblocking(f_in); |
set_nonblocking(f_in); |
} |
} |
Line 945 int start_daemon(int f_in, int f_out)
|
Line 1306 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 (strncmp(line, EARLY_INPUT_CMD, EARLY_INPUT_CMDLEN) == 0) { |
|
early_input_len = strtol(line + EARLY_INPUT_CMDLEN, NULL, 10); |
|
if (early_input_len <= 0 || early_input_len > BIGPATHBUFLEN) { |
|
io_printf(f_out, "@ERROR: invalid early_input length\n"); |
|
return -1; |
|
} |
|
early_input = new_array(char, early_input_len); |
|
read_buf(f_in, early_input, early_input_len); |
|
|
|
if (!read_line_old(f_in, line, sizeof line, 0)) |
|
return -1; |
|
} |
|
|
if (!*line || strcmp(line, "#list") == 0) { |
if (!*line || strcmp(line, "#list") == 0) { |
rprintf(FLOG, "module-list request from %s (%s)\n", |
rprintf(FLOG, "module-list request from %s (%s)\n", |
host, addr); |
host, addr); |
Line 979 int start_daemon(int f_in, int f_out)
|
Line 1353 int start_daemon(int f_in, int f_out)
|
static void create_pid_file(void) |
static void create_pid_file(void) |
{ |
{ |
char *pid_file = lp_pid_file(); |
char *pid_file = lp_pid_file(); |
char pidbuf[16]; | char pidbuf[32]; |
pid_t pid = getpid(); | STRUCT_STAT st1, st2; |
int fd, len; | char *fail = NULL; |
|
|
if (!pid_file || !*pid_file) |
if (!pid_file || !*pid_file) |
return; |
return; |
|
|
cleanup_set_pid(pid); | #ifdef O_NOFOLLOW |
if ((fd = do_open(pid_file, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~orig_umask)) == -1) { | #define SAFE_OPEN_FLAGS (O_CREAT|O_NOFOLLOW) |
failure: | #else |
cleanup_set_pid(0); | #define SAFE_OPEN_FLAGS (O_CREAT) |
fprintf(stderr, "failed to create pid file %s: %s\n", pid_file, strerror(errno)); | #endif |
rsyserr(FLOG, errno, "failed to create pid file %s", pid_file); | |
| /* These tests make sure that a temp-style lock dir is handled safely. */ |
| st1.st_mode = 0; |
| if (do_lstat(pid_file, &st1) == 0 && !S_ISREG(st1.st_mode) && unlink(pid_file) < 0) |
| fail = "unlink"; |
| else if ((pid_file_fd = do_open(pid_file, O_RDWR|SAFE_OPEN_FLAGS, 0664)) < 0) |
| fail = S_ISREG(st1.st_mode) ? "open" : "create"; |
| else if (!lock_range(pid_file_fd, 0, 4)) |
| fail = "lock"; |
| else if (do_fstat(pid_file_fd, &st1) < 0) |
| fail = "fstat opened"; |
| else if (st1.st_size > (int)sizeof pidbuf) |
| fail = "find small"; |
| else if (do_lstat(pid_file, &st2) < 0) |
| fail = "lstat"; |
| else if (!S_ISREG(st1.st_mode)) |
| fail = "avoid file overwrite race for"; |
| else if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) |
| fail = "verify stat info for"; |
| #ifdef HAVE_FTRUNCATE |
| else if (do_ftruncate(pid_file_fd, 0) < 0) |
| fail = "truncate"; |
| #endif |
| else { |
| pid_t pid = getpid(); |
| int len = snprintf(pidbuf, sizeof pidbuf, "%d\n", (int)pid); |
| #ifndef HAVE_FTRUNCATE |
| /* What can we do with a too-long file and no truncate? I guess we'll add extra newlines. */ |
| while (len < st1.st_size) /* We already verified that st_size chars fits in the buffer. */ |
| pidbuf[len++] = '\n'; |
| /* We don't need the buffer to end in a '\0' (and we may not have room to add it). */ |
| #endif |
| if (write(pid_file_fd, pidbuf, len) != len) |
| fail = "write"; |
| cleanup_set_pid(pid); /* Mark the file for removal on exit, even if the write failed. */ |
| } |
| |
| if (fail) { |
| char msg[1024]; |
| snprintf(msg, sizeof msg, "failed to %s pid file %s: %s\n", |
| fail, pid_file, strerror(errno)); |
| fputs(msg, stderr); |
| rprintf(FLOG, "%s", msg); |
exit_cleanup(RERR_FILEIO); |
exit_cleanup(RERR_FILEIO); |
} |
} |
snprintf(pidbuf, sizeof pidbuf, "%ld\n", (long)pid); | |
len = strlen(pidbuf); | /* The file is left open so that the lock remains valid. It is closed in our forked child procs. */ |
if (write(fd, pidbuf, len) != len) | |
goto failure; | |
close(fd); | |
} |
} |
|
|
/* Become a daemon, discarding the controlling terminal. */ |
/* Become a daemon, discarding the controlling terminal. */ |
Line 1055 int daemon_main(void)
|
Line 1468 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(); |
Line 1069 int daemon_main(void)
|
Line 1483 int daemon_main(void)
|
log_init(0); |
log_init(0); |
|
|
rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n", |
rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n", |
RSYNC_VERSION, rsync_port); | rsync_version(), rsync_port); |
/* TODO: If listening on a particular address, then show that |
/* TODO: If listening on a particular address, then show that |
* address too. In fact, why not just do getnameinfo on the |
* address too. In fact, why not just do getnameinfo on the |
* local address??? */ |
* local address??? */ |
|
|
|
#ifdef HAVE_LIBSLP |
|
if (lp_use_slp() && register_services()) { |
|
rprintf(FINFO, |
|
"Couldn't register with service discovery protocol, continuing anyway\n"); |
|
} |
|
#endif |
|
|
start_accept_loop(rsync_port, start_daemon); |
start_accept_loop(rsync_port, start_daemon); |
return -1; |
return -1; |