*
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2001-2002 Martin Pool <mbp@samba.org>
- * Copyright (C) 2002-2009 Wayne Davison
+ * Copyright (C) 2002-2018 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
extern char *iconv_opt;
extern iconv_t ic_send, ic_recv;
#endif
-
-#define MAX_GID_LIST 32
+extern uid_t our_uid;
+extern gid_t our_gid;
char *auth_user;
int read_only = 0;
static struct sigaction sigact;
#endif
-static gid_t gid_list[MAX_GID_LIST];
-static int gid_count = 0;
+static item_list gid_list = EMPTY_ITEM_LIST;
/* Used when "reverse lookup" is off. */
const char undetermined_hostname[] = "UNDETERMINED";
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)
{
- int j = 0, status = -1;
+ char buf[BIGPATHBUFLEN], *bp;
+ int j = 0, status = -1, msglen = sizeof buf - 1;
if (!request)
request = "(NONE)";
- write_buf(fd, request, strlen(request)+1);
+ write_buf(write_fd, request, strlen(request)+1);
if (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. */
}
- for ( ; argv[j]; j++) {
- write_buf(fd, argv[j], strlen(argv[j])+1);
- if (argv[j][0] == '.' && argv[j][1] == '\0')
- break;
+ for ( ; argv[j]; j++)
+ write_buf(write_fd, argv[j], strlen(argv[j])+1);
+ write_byte(write_fd, 0);
+
+ 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
|| !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
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 < 0 ? strerror(errno) : "") < 0)
- out_of_memory("finish_pre_exec");
+ status < 0 ? strerror(errno) : "",
+ *buf ? ":" : "", buf) < 0)
+ return "out_of_memory in finish_pre_exec\n";
return e;
}
return NULL;
static int path_failure(int f_out, const char *dir, BOOL was_chdir)
{
if (was_chdir)
- rsyserr(FLOG, errno, "chdir %s failed\n", dir);
+ rsyserr(FLOG, errno, "chdir %s failed", dir);
else
rprintf(FLOG, "normalize_path(%s) failed\n", dir);
io_printf(f_out, "@ERROR: chdir failed\n");
static int add_a_group(int f_out, const char *gname)
{
- gid_t gid;
+ 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;
}
- if (gid_count == MAX_GID_LIST) {
- rprintf(FLOG, "Too many groups specified via gid parameter.\n");
- io_printf(f_out, "@ERROR: too many groups\n");
- return -1;
- }
- gid_list[gid_count++] = gid;
+ gid_p = EXPAND_ITEM_LIST(&gid_list, gid_t, -32);
+ *gid_p = gid;
return 0;
}
static int want_all_groups(int f_out, uid_t uid)
{
const char *err;
- gid_count = MAX_GID_LIST;
- if ((err = getallgroups(uid, gid_list, &gid_count)) != NULL) {
+ if ((err = getallgroups(uid, &gid_list)) != NULL) {
rsyserr(FLOG, errno, "%s", err);
io_printf(f_out, "@ERROR: %s\n", err);
return -1;
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 reset. */
- gid_count = 1;
- gid_list[0] = pw->pw_gid;
+ /* 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
char *p, *err_msg = NULL;
char *name = lp_name(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;
char *request = NULL;
} else
set_uid = 0;
- p = *lp_gid(i) ? strtok(lp_gid(i), ", ") : NULL;
+ p = *lp_gid(i) ? conf_strtok(lp_gid(i)) : NULL;
if (p) {
/* The "*" gid must be the first item in the list. */
if (strcmp(p, "*") == 0) {
#endif
} else if (add_a_group(f_out, p) < 0)
return -1;
- while ((p = strtok(NULL, ", ")) != NULL) {
+ while ((p = conf_strtok(NULL)) != NULL) {
#if defined HAVE_INITGROUPS && !defined HAVE_GETGROUPLIST
if (pw) {
rprintf(FLOG, "This rsync cannot add groups after \"*\".\n");
log_init(1);
#ifdef HAVE_PUTENV
- if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
+ if ((*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) && !getenv("RSYNC_NO_XFER_EXEC")) {
int status;
/* For post-xfer exec, fork a new process to run the rsync
else
status = -1;
set_env_num("RSYNC_EXIT_STATUS", status);
- if (system(lp_postxfer_exec(i)) < 0)
+ if (shell_exec(lp_postxfer_exec(i)) < 0)
status = -1;
_exit(status);
}
* command, though it first waits for the parent process to
* send us the user's request via a pipe. */
if (*lp_prexfer_exec(i)) {
- int fds[2];
+ int arg_fds[2], error_fds[2];
set_env_num("RSYNC_PID", (long)getpid());
- if (pipe(fds) < 0 || (pre_exec_pid = fork()) < 0) {
+ if (pipe(arg_fds) < 0 || pipe(error_fds) < 0 || (pre_exec_pid = fork()) < 0) {
rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
return -1;
if (pre_exec_pid == 0) {
char buf[BIGPATHBUFLEN];
int j, len;
- close(fds[1]);
- set_blocking(fds[0]);
- len = read_arg_from_pipe(fds[0], buf, BIGPATHBUFLEN);
+ close(arg_fds[1]);
+ close(error_fds[0]);
+ 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)
_exit(1);
set_env_str("RSYNC_REQUEST", buf);
for (j = 0; ; j++) {
- len = read_arg_from_pipe(fds[0], buf,
+ len = read_arg_from_pipe(pre_exec_arg_fd, buf,
BIGPATHBUFLEN);
if (len <= 0) {
if (!len)
break;
_exit(1);
}
- if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) > 0)
+ if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0)
putenv(p);
}
- close(fds[0]);
+ close(pre_exec_arg_fd);
close(STDIN_FILENO);
- close(STDOUT_FILENO);
- status = system(lp_prexfer_exec(i));
+ dup2(pre_exec_error_fd, STDOUT_FILENO);
+ close(pre_exec_error_fd);
+ status = shell_exec(lp_prexfer_exec(i));
if (!WIFEXITED(status))
_exit(1);
_exit(WEXITSTATUS(status));
}
- close(fds[0]);
- set_blocking(fds[1]);
- pre_exec_fd = fds[1];
+ close(arg_fds[0]);
+ close(error_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);
}
}
#endif
if (!change_dir(module_chdir, CD_NORMAL))
return path_failure(f_out, module_chdir, True);
- if (module_dirlen || !use_chroot)
+ if (module_dirlen || (!use_chroot && !*lp_daemon_chroot()))
sanitize_paths = 1;
if ((munge_symlinks = lp_munge_symlinks(i)) < 0)
}
}
- if (gid_count) {
- if (setgid(gid_list[0])) {
- rsyserr(FLOG, errno, "setgid %ld failed", (long)gid_list[0]);
+ if (gid_list.count) {
+ gid_t *gid_array = gid_list.items;
+ if (setgid(gid_array[0])) {
+ rsyserr(FLOG, errno, "setgid %ld failed", (long)gid_array[0]);
io_printf(f_out, "@ERROR: setgid failed\n");
return -1;
}
#ifdef HAVE_SETGROUPS
/* Set the group(s) we want to be active. */
- if (setgroups(gid_count, gid_list)) {
+ if (setgroups(gid_list.count, gid_array)) {
rsyserr(FLOG, errno, "setgroups failed");
io_printf(f_out, "@ERROR: setgroups failed\n");
return -1;
return -1;
}
#endif
+ our_gid = MY_GID();
}
if (set_uid) {
return -1;
}
- am_root = (MY_UID() == 0);
+ our_uid = MY_UID();
+ am_root = (our_uid == 0);
}
if (lp_temp_dir(i) && *lp_temp_dir(i)) {
munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */
if (pre_exec_pid) {
- err_msg = finish_pre_exec(pre_exec_pid, pre_exec_fd, request,
- orig_early_argv, orig_argv);
+ err_msg = finish_pre_exec(pre_exec_pid, pre_exec_arg_fd, pre_exec_error_fd,
+ request, orig_early_argv, orig_argv);
}
if (orig_early_argv)
}
if (!ret || err_msg) {
- if (err_msg)
- rwrite(FERROR, err_msg, strlen(err_msg), 0);
- else
+ if (err_msg) {
+ while ((p = strchr(err_msg, '\n')) != NULL) {
+ 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();
msleep(400);
exit_cleanup(RERR_UNSUPPORTED);
{
char line[1024];
const char *addr, *host;
+ char *p;
int i;
io_set_sock_fds(f_in, f_out);
if (!load_config(0))
exit_cleanup(RERR_SYNTAX);
+ 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 == 0);
+ }
+
addr = client_addr(f_in);
host = lp_reverse_lookup(-1) ? client_name(f_in) : undetermined_hostname;
rprintf(FLOG, "connect from %s (%s)\n", host, addr);
rsyserr(FLOG, errno, "failed to create pid file %s", pid_file);
exit_cleanup(RERR_FILEIO);
}
- snprintf(pidbuf, sizeof pidbuf, "%ld\n", (long)pid);
+ snprintf(pidbuf, sizeof pidbuf, "%d\n", (int)pid);
len = strlen(pidbuf);
if (write(fd, pidbuf, len) != len)
goto failure;
rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n",
RSYNC_VERSION, rsync_port);
/* TODO: If listening on a particular address, then show that
- * address too. In fact, why not just do inet_ntop on the
+ * address too. In fact, why not just do getnameinfo on the
* local address??? */
start_accept_loop(rsync_port, start_daemon);