1 This patch adds a "name converter" daemon option that allows you
2 to specify a user-/group- name converter program that converts
3 between ID numbers and names. This only works in daemon mode,
4 and is useful for both chroot use (since the converter runs
5 outside the chroot) or to specify a converter that doesn't use
6 the normal passwd/group setup.
8 The converter must use a null char ('\0') as the line terminator
9 for input/output on stdin/stdout. A sample converter written in
10 perl is supplied in the support dir: nameconvert. To use it,
11 specify this daemon option:
13 name converter = /path/nameconvert
15 If /path/ is omitted, the script will be found on the $PATH.
17 To use this patch, run these commands for a successful build:
19 patch -p1 <patches/nameconverter.diff
20 ./configure (optional if already run)
23 based-on: 40afd365cc8ca968fd16e161d24df5b8a8a520cc
24 diff --git a/clientserver.c b/clientserver.c
27 @@ -65,6 +65,7 @@ char *auth_user;
30 int munge_symlinks = 0;
31 +pid_t namecvt_pid = 0;
32 struct chmod_mode_struct *daemon_chmod_modes;
34 /* module_dirlen is the length of the module_dir string when in daemon
35 @@ -76,6 +77,7 @@ unsigned int module_dirlen = 0;
36 char *full_module_path;
38 static int rl_nulls = 0;
39 +static int namecvt_fd_req = -1, namecvt_fd_ans = -1;
42 static struct sigaction sigact;
43 @@ -571,7 +573,7 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
47 - if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
48 + if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i) || *lp_name_converter(i)) {
49 char *modname, *modpath, *hostaddr, *hostname, *username;
52 @@ -660,6 +662,44 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
56 + if (*lp_name_converter(i)) {
57 + int fds_to[2], fds_from[2];
58 + if (pipe(fds_to) < 0 || pipe(fds_from) < 0
59 + || (namecvt_pid = fork()) < 0) {
60 + rsyserr(FLOG, errno, "name-converter exec preparation failed");
61 + io_printf(f_out, "@ERROR: name-converter exec preparation failed\n");
64 + if (namecvt_pid == 0) {
65 + char *args[100], *run = lp_name_converter(i);
69 + set_blocking(fds_to[0]);
70 + set_blocking(fds_from[1]);
71 + close(STDIN_FILENO);
72 + close(STDOUT_FILENO);
73 + dup2(fds_to[0], STDIN_FILENO);
74 + dup2(fds_from[1], STDOUT_FILENO);
75 + while (cnt+1 < (int)(sizeof args / sizeof (char *))) {
76 + char *space = strchr(run, ' ');
84 + execvp(args[0], args);
89 + set_blocking(fds_to[1]);
90 + set_blocking(fds_from[0]);
91 + namecvt_fd_req = fds_to[1];
92 + namecvt_fd_ans = fds_from[0];
97 @@ -887,6 +927,44 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
101 +int namecvt_name(const char *cmd, const char *name)
104 + int got, len = snprintf(buf, sizeof buf, "%s %s", cmd, name);
105 + if (len >= (int)sizeof buf) {
106 + rprintf(FERROR, "namecvt_name() request was too large.\n");
107 + exit_cleanup(RERR_UNSUPPORTED);
109 + while ((got = write(namecvt_fd_req, buf, len + 1)) != len + 1) {
110 + if (got < 0 && errno == EINTR)
112 + rprintf(FERROR, "Connection to name-converter failed.\n");
113 + exit_cleanup(RERR_SOCKETIO);
115 + if (!(len = read_arg_from_pipe(namecvt_fd_ans, buf, sizeof buf)))
120 +const char *namecvt_id(const char *cmd, int id)
123 + int got, len = snprintf(buf, sizeof buf, "%s %d", cmd, id);
124 + if (len >= (int)sizeof buf) {
125 + rprintf(FERROR, "namecvt_id() request was too large.\n");
126 + exit_cleanup(RERR_UNSUPPORTED);
128 + while ((got = write(namecvt_fd_req, buf, len + 1)) != len + 1) {
129 + if (got < 0 && errno == EINTR)
131 + rprintf(FERROR, "Connection to name-converter failed.\n");
132 + exit_cleanup(RERR_SOCKETIO);
134 + if (!(len = read_arg_from_pipe(namecvt_fd_ans, buf, sizeof buf)))
136 + return strdup(buf);
139 /* send a list of available modules to the client. Don't list those
140 with "list = False". */
141 static void send_listing(int fd)
142 diff --git a/loadparm.c b/loadparm.c
145 @@ -138,6 +138,7 @@ typedef struct
149 + char *name_converter;
150 char *outgoing_chmod;
153 @@ -189,6 +190,7 @@ static service sDefault =
154 /* log_file; */ NULL,
155 /* log_format; */ "%o %h [%a] %m (%u) %f %l",
157 + /* name_converter; */ NULL,
158 /* outgoing_chmod; */ NULL,
160 /* postxfer_exec; */ NULL,
161 @@ -326,6 +328,7 @@ static struct parm_struct parm_table[] =
162 {"max verbosity", P_INTEGER,P_LOCAL, &sDefault.max_verbosity, NULL,0},
163 {"munge symlinks", P_BOOL, P_LOCAL, &sDefault.munge_symlinks, NULL,0},
164 {"name", P_STRING, P_LOCAL, &sDefault.name, NULL,0},
165 + {"name converter", P_STRING, P_LOCAL, &sDefault.name_converter, NULL,0},
166 {"numeric ids", P_BOOL, P_LOCAL, &sDefault.numeric_ids, NULL,0},
167 {"outgoing chmod", P_STRING, P_LOCAL, &sDefault.outgoing_chmod, NULL,0},
168 {"path", P_PATH, P_LOCAL, &sDefault.path, NULL,0},
169 @@ -416,6 +419,7 @@ FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
170 FN_LOCAL_STRING(lp_path, path)
171 FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec)
172 FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
173 +FN_LOCAL_STRING(lp_name_converter, name_converter)
174 FN_LOCAL_STRING(lp_refuse_options, refuse_options)
175 FN_LOCAL_STRING(lp_secrets_file, secrets_file)
176 FN_LOCAL_STRING(lp_temp_dir, temp_dir)
177 diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
180 @@ -165,10 +165,11 @@ if the module is not read-only).
182 When this parameter is enabled, rsync will not attempt to map users and groups
183 by name (by default), but instead copy IDs as though bf(--numeric-ids) had
184 -been specified. In order to enable name-mapping, rsync needs to be able to
185 +been specified. In order to enable name-mapping, rsync needs either the
186 +bf(name converter) parameter to specify a conversion program, or it needs to
187 use the standard library functions for looking up names and IDs (i.e.
188 code(getpwuid()), code(getgrgid()), code(getpwname()), and code(getgrnam())).
189 -This means the rsync
190 +The latter choice means the rsync
191 process in the chroot hierarchy will need to have access to the resources
192 used by these library functions (traditionally /etc/passwd and
193 /etc/group, but perhaps additional dynamic libraries as well).
194 @@ -233,6 +234,27 @@ path elements that rsync believes will allow a symlink to escape the module's
195 hierarchy. There are tricky ways to work around this, though, so you had
196 better trust your users if you choose this combination of parameters.
198 +dit(bf(name converter)) This parameter lets you specify a
199 +program that will be run by the rsync daemon (prior to bf(use chroot), if
200 +that parameter is enabled) to convert user/group names into numbers or visa
201 +versa. There is a sample perl script in the support directory named
202 +"nameconvert" that you can use to enable the use of the normal passwd/group
203 +lookup calls in a chroot daemon (which does not require any extra files
204 +be placed in the chroot area). This use is configured as follows:
206 +verb( name converter = /path/nameconvert)
208 +You could alternately specify a program that responds to each request using
209 +a lookup table to find the names and numbers, this allows you to configure
210 +per-module name conversion. See the support/nameconvert script for the
211 +details of what requests can be sent to the program.
213 +The program will have access to some of the environment variables that are
214 +described in the section on bf(pre-xfer exec): bf(RSYNC_MODULE_NAME),
215 +bf(RSYNC_MODULE_PATH), bf(RSYNC_HOST_ADDR), bf(RSYNC_HOST_NAME), and
216 +bf(RSYNC_USER_NAME). This is useful if you want to customize the
217 +conversion using a single program invocation.
219 dit(bf(charset)) This specifies the name of the character set in which the
220 module's filenames are stored. If the client uses an bf(--iconv) option,
221 the daemon will use the value of the "charset" parameter regardless of the
222 diff --git a/support/nameconvert b/support/nameconvert
225 +++ b/support/nameconvert
228 +# This implements a simple protocol to do {user,group}-{name,id}
229 +# conversions. All input and output consists of simple strings
230 +# with a terminating null char (or newline for debugging). If
231 +# the conversion fails, an empty string is returned.
233 +# The requests can be:
235 +# uid ID_NUM\0 -> NAME\0
236 +# gid ID_NUM\0 -> NAME\0
237 +# usr NAME\0 -> ID_NUM\0
238 +# grp NAME\0 -> ID_NUM\0
240 +# An unknown ID_NUM or NAME results in an empty return value.
242 +# This is used by an rsync daemon when configured with the
243 +# "name converter" setting.
248 +my $eol = grep(/^--debug$/, @ARGV) ? "\n" : "\0";
256 + if (/^uid (\d+)$/) {
257 + $ans = getpwuid($1);
258 + } elsif (/^gid (\d+)$/) {
259 + $ans = getgrgid($1);
260 + } elsif (/^usr (\S+)$/) {
261 + $ans = getpwnam($1);
262 + } elsif (/^grp (\S+)$/) {
263 + $ans = getgrnam($1);
265 + die "Invalid request: $_";
267 + $ans = '' unless defined $ans;
270 diff --git a/t_stub.c b/t_stub.c
273 @@ -31,6 +31,7 @@ int preserve_xattrs = 0;
274 mode_t orig_umask = 002;
278 struct filter_list_struct daemon_filter_list;
280 void rprintf(UNUSED(enum logcode code), const char *format, ...)
281 @@ -76,6 +77,11 @@ struct filter_list_struct daemon_filter_list;
285 + int namecvt_name(UNUSED(const char *cmd), UNUSED(const char *name))
290 char *lp_name(UNUSED(int mod))
293 diff --git a/uidlist.c b/uidlist.c
296 @@ -32,6 +32,7 @@ extern int preserve_uid;
297 extern int preserve_gid;
298 extern int preserve_acls;
299 extern int numeric_ids;
300 +extern pid_t namecvt_pid;
302 #ifdef HAVE_GETGROUPS
304 @@ -67,8 +68,12 @@ static struct idlist *add_to_list(struct idlist **root, id_t id, const char *nam
305 /* turn a uid into a user name */
306 static const char *uid_to_name(uid_t uid)
308 - struct passwd *pass = getpwuid(uid);
310 + struct passwd *pass;
313 + return namecvt_id("uid", (int)uid);
315 + if ((pass = getpwuid(uid)) != NULL)
316 return strdup(pass->pw_name);
319 @@ -76,8 +81,12 @@ static const char *uid_to_name(uid_t uid)
320 /* turn a gid into a group name */
321 static const char *gid_to_name(gid_t gid)
323 - struct group *grp = getgrgid(gid);
328 + return namecvt_id("gid", (int)gid);
330 + if ((grp = getgrgid(gid)) != NULL)
331 return strdup(grp->gr_name);
334 diff --git a/util.c b/util.c
337 @@ -31,9 +31,10 @@ extern int preserve_times;
338 extern int human_readable;
339 extern int preserve_xattrs;
340 extern char *module_dir;
341 -extern unsigned int module_dirlen;
342 extern mode_t orig_umask;
343 extern char *partial_dir;
344 +extern pid_t namecvt_pid;
345 +extern unsigned int module_dirlen;
346 extern struct filter_list_struct daemon_filter_list;
348 int sanitize_paths = 0;
349 @@ -507,24 +508,44 @@ void kill_all(int sig)
350 /** Turn a user name into a uid */
351 int name_to_uid(const char *name, uid_t *uid_p)
353 - struct passwd *pass;
358 - if (!(pass = getpwnam(name)))
360 - *uid_p = pass->pw_uid;
363 + if (!(uid = namecvt_name("usr", name)))
366 + struct passwd *pass;
367 + if (!(pass = getpwnam(name)))
369 + uid = pass->pw_uid;
376 /** Turn a group name into a gid */
377 int name_to_gid(const char *name, gid_t *gid_p)
384 - if (!(grp = getgrnam(name)))
386 - *gid_p = grp->gr_gid;
389 + if (!(gid = namecvt_name("grp", name)))
393 + if (!(grp = getgrnam(name)))