- Allow `--max-alloc=0` to specify no limit to the alloc sanity check.
- - Allow `--block-size=SIZE` to specify the size using units such as "100K".
+ - Allow `--block-size=SIZE` to specify the size using units (e.g. "100K").
- - The name of the id-0 user & group is now sent to the receiver along with the
- other user/group names in the transfer (instead of assuming that both sides
- have the same id-0 names).
+ - The name of the id-0 user & group are now sent to the receiver along with
+ the other user/group names in the transfer (instead of assuming that both
+ sides have the same id-0 names).
- Added the `--stop-after=MINS` and `--stop-at=DATE_TIME` options (with the
`--time-limit=MINS` option accepted as an alias for `--stop-after`). This
is an enhanced version of the time-limit patch from the patches repo.
+ - Added the `name converter` daemon parameter to make it easier to convert
+ user & group names inside a chrooted daemon module. This is based on the
+ nameconverter patch with some improvements, including a tweak to the request
+ protocol (so if you used this patch in the past, be sure to update your
+ converter script).
+
- Added the ability to specify "@netgroup" names to the `hosts allow` and
`hosts deny` daemon parameters. This is a finalized version of the
netgroup-auth patch from the patches repo.
#include "rsync.h"
#include "itypes.h"
+#include "ifuncs.h"
extern int quiet;
extern int dry_run;
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;
#define EARLY_INPUT_CMD "#early_input="
char *full_module_path;
static int rl_nulls = 0;
+static int namecvt_fd_req = -1, namecvt_fd_ans = -1;
#ifdef HAVE_SIGACTION
static struct sigaction sigact;
}
#endif
-/* Used for both early exec & pre-xfer exec */
+/* 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;
return pid;
}
-static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int am_early)
+static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int exec_type)
{
int j = 0;
}
write_byte(write_fd, 0);
- if (am_early && early_input_len)
+ if (exec_type == 1 && early_input_len)
write_buf(write_fd, early_input, early_input_len);
- close(write_fd);
+ 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)
log_init(1);
#ifdef HAVE_PUTENV
- if ((*lp_early_exec(module_id) || *lp_prexfer_exec(module_id) || *lp_postxfer_exec(module_id))
+ if ((*lp_early_exec(module_id) || *lp_prexfer_exec(module_id)
+ || *lp_postxfer_exec(module_id) || *lp_name_converter(module_id))
&& !getenv("RSYNC_NO_XFER_EXEC")) {
set_env_num("RSYNC_PID", (long)getpid());
return -1;
}
}
+
+ if (*lp_name_converter(module_id)) {
+ namecvt_pid = start_pre_exec(lp_name_converter(module_id), &namecvt_fd_req, &namecvt_fd_ans);
+ if (namecvt_pid == (pid_t)-1) {
+ rsyserr(FLOG, errno, "name-converter exec preparation failed");
+ io_printf(f_out, "@ERROR: name-converter exec preparation failed\n");
+ return -1;
+ }
+ }
}
#endif
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)
free(orig_early_argv);
#endif
if (!numeric_ids
- && (use_chroot ? lp_numeric_ids(module_id) != False : lp_numeric_ids(module_id) == 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. */
if (lp_timeout(module_id) && (!io_timeout || lp_timeout(module_id) < io_timeout))
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
with "list = False". */
static void send_listing(int fd)
STRING log_file NULL
STRING log_format "%o %h [%a] %m (%u) %f %l"
STRING name NULL
+STRING name_converter NULL
STRING outgoing_chmod NULL
STRING post-xfer_exec NULL
STRING pre-xfer_exec NULL
they would escape the module hierarchy. The default for "use chroot" is
true, and is the safer choice (especially if the module is not read-only).
- When this parameter is enabled, the "numeric-ids" option will also default
- to being enabled (disabling name lookups). See below for what a chroot
- needs in order for name lookups to succeed.
+ When this parameter is enabled *and* the "name converter" parameter is
+ *not* set, the "numeric ids" parameter will default to being enabled
+ (disabling name lookups). This means that if you manually setup
+ name-lookup libraries in your chroot (instead of using a name converter)
+ that you need to explicitly set `numeric ids = false` for rsync to do name
+ lookups.
If you copy library resources into the module's chroot area, you should
protect them through your OS's normal user/group or ACL settings (to
prevent the rsync module's user from being able to change them), and then
hide them from the user's view via "exclude" (see how in the discussion of
- that parameter). At that point it will be safe to enable the mapping of
- users and groups by name using the "numeric ids" daemon parameter (see
- below).
-
- Note also that you are free to setup custom user/group information in the
- chroot area that is different from your normal system. For example, you
- could abbreviate the list of users and groups.
+ that parameter). However, it's easier and safer to setup a name converter.
0. `daemon chroot`
others, then you will need to setup multiple rsync daemon processes on
different ports.
+0. `name converter`
+
+ This parameter lets you specify a program that will be run by the rsync
+ daemon to do user & group conversions between names & ids. This script
+ is started prior to any chroot being setup, and runs as the daemon user
+ (not the transfer user). You can specify a fully qualified pathname or
+ a program name that is on the $PATH.
+
+ The program can be used to do normal user & group lookups without having to
+ put any extra files into the chroot area of the module *or* you can do
+ customized conversions.
+
+ The nameconvert program has access to all of the environment variables that
+ are described in the section on `pre-xfer exec`. This is useful if you
+ want to customize the conversion using information about the module and/or
+ the copy request.
+
+ There is a sample python script in the support dir named "nameconvert" that
+ implements the normal user & group lookups. Feel free to customize it or
+ just use it as documentation to implement your own.
+
0. `numeric ids`
Enabling this parameter disables the mapping of users and groups by name
uid/gid preservation requires the module to be running as root (see "uid")
or for "fake super" to be configured.
- A chroot-enabled module should not have this parameter enabled unless
- you've taken steps to ensure that the module has the necessary resources it
- needs to translate names, and that it is not possible for a user to change
- those resources. That includes being the code being able to call functions
- like **getpwuid()**, **getgrgid()**, **getpwname()**, and **getgrnam()**.
- You should test what libraries and config files are required for your OS
- and get those setup before starting to test name mapping in rsync.
+ A chroot-enabled module should not have this parameter set to false unless
+ you're using a "name converter" program *or* you've taken steps to ensure
+ that the module has the necessary resources it needs to translate names and
+ that it is not possible for a user to change those resources.
0. `munge symlinks`
--- /dev/null
+#!/usr/bin/env python3
+
+# This implements a simple protocol to do user & group conversions between
+# names & ids. All input and output consists of simple strings with a
+# terminating newline.
+#
+# The requests can be:
+#
+# uid ID_NUM\n -> NAME\n
+# gid ID_NUM\n -> NAME\n
+# usr NAME\n -> ID_NUM\n
+# grp NAME\n -> ID_NUM\n
+#
+# An unknown ID_NUM or NAME results in an empty return value.
+#
+# This is used by an rsync daemon when configured with the "name converter" and
+# "use chroot = true". While this converter uses real user & group lookups you
+# could change it to use any mapping idiom you'd like.
+
+import sys, argparse, pwd, grp
+
+def main():
+ for line in sys.stdin:
+ try:
+ req, arg = line.rstrip().split(' ', 1)
+ except:
+ req = None
+ try:
+ if req == 'uid':
+ ans = pwd.getpwuid(int(arg)).pw_name
+ elif req == 'gid':
+ ans = grp.getgrgid(int(arg)).gr_name
+ elif req == 'usr':
+ ans = pwd.getpwnam(arg).pw_uid
+ elif req == 'grp':
+ ans = grp.getgrnam(arg).gr_gid
+ else:
+ print("Invalid request", file=sys.stderr)
+ sys.exit(1)
+ except KeyError:
+ ans = ''
+ print(ans, flush=True)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Convert users & groups between names & numbers for an rsync daemon.")
+ args = parser.parse_args()
+ main()
+
+# vim: sw=4 et
extern int preserve_acls;
extern int numeric_ids;
extern int xmit_id0_names;
+extern pid_t namecvt_pid;
extern gid_t our_gid;
extern char *usermap;
extern char *groupmap;
/* turn a uid into a user name */
const char *uid_to_user(uid_t uid)
{
- struct passwd *pass = getpwuid(uid);
- if (pass)
- return strdup(pass->pw_name);
- return NULL;
+ const char *name = NULL;
+
+ if (namecvt_pid) {
+ id_t id = uid;
+ namecvt_call("uid", &name, &id);
+ } else {
+ struct passwd *pass = getpwuid(uid);
+ if (pass)
+ name = strdup(pass->pw_name);
+ }
+
+ return name;
}
/* turn a gid into a group name */
const char *gid_to_group(gid_t gid)
{
- struct group *grp = getgrgid(gid);
- if (grp)
- return strdup(grp->gr_name);
- return NULL;
+ const char *name = NULL;
+
+ if (namecvt_pid) {
+ id_t id = gid;
+ namecvt_call("gid", &name, &id);
+ } else {
+ struct group *grp = getgrgid(gid);
+ if (grp)
+ name = strdup(grp->gr_name);
+ }
+
+ return name;
}
/* Parse a user name or (optionally) a number into a uid */
int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok)
{
- struct passwd *pass;
if (!name || !*name)
return 0;
+
if (num_ok && name[strspn(name, "0123456789")] == '\0') {
*uid_p = id_parse(name);
return 1;
}
- if (!(pass = getpwnam(name)))
- return 0;
- *uid_p = pass->pw_uid;
+
+ if (namecvt_pid) {
+ id_t id;
+ if (!namecvt_call("usr", &name, &id))
+ return 0;
+ *uid_p = id;
+ } else {
+ struct passwd *pass = getpwnam(name);
+ if (!pass)
+ return 0;
+ *uid_p = pass->pw_uid;
+ }
+
return 1;
}
/* Parse a group name or (optionally) a number into a gid */
int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok)
{
- struct group *grp;
if (!name || !*name)
return 0;
+
if (num_ok && name[strspn(name, "0123456789")] == '\0') {
*gid_p = id_parse(name);
return 1;
}
- if (!(grp = getgrnam(name)))
- return 0;
- *gid_p = grp->gr_gid;
+
+ if (namecvt_pid) {
+ id_t id;
+ if (!namecvt_call("grp", &name, &id))
+ return 0;
+ *gid_p = id;
+ } else {
+ struct group *grp = getgrnam(name);
+ if (!grp)
+ return 0;
+ *gid_p = grp->gr_gid;
+ }
+
return 1;
}