Updated patches to work with the current trunk.
[rsync/rsync-patches.git] / nameconverter.diff
CommitLineData
4c15e800
WD
1This patch adds a "name converter" daemon option that allows you
2to specify a user-/group- name converter program that converts
3between ID numbers and names. This only works in daemon mode,
4and is useful for both chroot use (since the converter runs
5outside the chroot) or to specify a converter that doesn't use
6the normal passwd/group setup.
7
8The converter must use a null char ('\0') as the line terminator
9for input/output on stdin/stdout. A sample converter written in
10perl is supplied in the support dir: nameconvert. To use it,
11specify this daemon option:
12
13 name converter = /path/nameconvert
14
15If /path/ is omitted, the script will be found on the $PATH.
16
17To use this patch, run these commands for a successful build:
18
19 patch -p1 <patches/nameconverter.diff
20 ./configure (optional if already run)
21 make
22
5214a41b 23based-on: 24079e988fc31af4eba56cd2701fdc5a4154980d
4c15e800
WD
24diff --git a/clientserver.c b/clientserver.c
25--- a/clientserver.c
26+++ b/clientserver.c
5214a41b 27@@ -66,6 +66,7 @@ extern iconv_t ic_send, ic_recv;
fc557362 28 char *auth_user;
4c15e800
WD
29 int read_only = 0;
30 int module_id = -1;
4c15e800
WD
31+pid_t namecvt_pid = 0;
32 struct chmod_mode_struct *daemon_chmod_modes;
33
34 /* module_dirlen is the length of the module_dir string when in daemon
5214a41b 35@@ -77,6 +78,7 @@ unsigned int module_dirlen = 0;
963ca808 36 char *full_module_path;
4c15e800
WD
37
38 static int rl_nulls = 0;
39+static int namecvt_fd_req = -1, namecvt_fd_ans = -1;
40
41 #ifdef HAVE_SIGACTION
42 static struct sigaction sigact;
5214a41b 43@@ -668,7 +670,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
4c15e800
WD
44 log_init(1);
45
46 #ifdef HAVE_PUTENV
47- if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
48+ if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i) || *lp_name_converter(i)) {
4c15e800 49 int status;
85096e5e 50
72e5645e 51 umask(orig_umask);
5214a41b 52@@ -740,6 +742,44 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
4c15e800
WD
53 set_blocking(fds[1]);
54 pre_exec_fd = fds[1];
55 }
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");
62+ return -1;
63+ }
64+ if (namecvt_pid == 0) {
65+ char *args[100], *run = lp_name_converter(i);
66+ int cnt = 0;
67+ close(fds_to[1]);
68+ close(fds_from[0]);
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, ' ');
77+ args[cnt++] = run;
78+ if (!space)
79+ break;
80+ *space = '\0';
81+ run = space + 1;
82+ }
83+ args[cnt] = NULL;
84+ execvp(args[0], args);
85+ _exit(1);
86+ }
87+ close(fds_to[0]);
88+ close(fds_from[1]);
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];
93+ }
94 umask(0);
95 }
96 #endif
5214a41b 97@@ -971,6 +1011,44 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
4c15e800
WD
98 return 0;
99 }
100
101+int namecvt_name(const char *cmd, const char *name)
102+{
103+ char buf[1024];
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);
108+ }
109+ while ((got = write(namecvt_fd_req, buf, len + 1)) != len + 1) {
110+ if (got < 0 && errno == EINTR)
111+ continue;
112+ rprintf(FERROR, "Connection to name-converter failed.\n");
113+ exit_cleanup(RERR_SOCKETIO);
114+ }
115+ if (!(len = read_arg_from_pipe(namecvt_fd_ans, buf, sizeof buf)))
116+ return 0;
117+ return atoi(buf);
118+}
119+
120+const char *namecvt_id(const char *cmd, int id)
121+{
122+ char buf[1024];
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);
127+ }
128+ while ((got = write(namecvt_fd_req, buf, len + 1)) != len + 1) {
129+ if (got < 0 && errno == EINTR)
130+ continue;
131+ rprintf(FERROR, "Connection to name-converter failed.\n");
132+ exit_cleanup(RERR_SOCKETIO);
133+ }
134+ if (!(len = read_arg_from_pipe(namecvt_fd_ans, buf, sizeof buf)))
135+ return NULL;
136+ return strdup(buf);
137+}
138+
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)
142diff --git a/loadparm.c b/loadparm.c
143--- a/loadparm.c
144+++ b/loadparm.c
72e5645e 145@@ -122,6 +122,7 @@ typedef struct {
4c15e800
WD
146 char *log_file;
147 char *log_format;
148 char *name;
149+ char *name_converter;
150 char *outgoing_chmod;
151 char *path;
152 char *postxfer_exec;
72e5645e 153@@ -196,6 +197,7 @@ static const all_vars Defaults = {
4c15e800
WD
154 /* log_file; */ NULL,
155 /* log_format; */ "%o %h [%a] %m (%u) %f %l",
156 /* name; */ NULL,
157+ /* name_converter; */ NULL,
158 /* outgoing_chmod; */ NULL,
159 /* path; */ NULL,
160 /* postxfer_exec; */ NULL,
72e5645e 161@@ -338,6 +340,7 @@ static struct parm_struct parm_table[] =
fc557362
WD
162 {"max verbosity", P_INTEGER,P_LOCAL, &Vars.l.max_verbosity, NULL,0},
163 {"munge symlinks", P_BOOL, P_LOCAL, &Vars.l.munge_symlinks, NULL,0},
164 {"name", P_STRING, P_LOCAL, &Vars.l.name, NULL,0},
165+ {"name converter", P_STRING, P_LOCAL, &Vars.l.name_converter, NULL,0},
166 {"numeric ids", P_BOOL, P_LOCAL, &Vars.l.numeric_ids, NULL,0},
167 {"outgoing chmod", P_STRING, P_LOCAL, &Vars.l.outgoing_chmod, NULL,0},
168 {"path", P_PATH, P_LOCAL, &Vars.l.path, NULL,0},
72e5645e 169@@ -465,6 +468,7 @@ FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
4c15e800
WD
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)
85096e5e 176 FN_LOCAL_STRING(lp_temp_dir, temp_dir)
4c15e800
WD
177diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
178--- a/rsyncd.conf.yo
179+++ b/rsyncd.conf.yo
72e5645e 180@@ -183,10 +183,11 @@ if the module is not read-only).
4c15e800 181
e66d6d51 182 When this parameter is enabled, rsync will not attempt to map users and groups
85096e5e
WD
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
4c15e800 187 use the standard library functions for looking up names and IDs (i.e.
85096e5e
WD
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).
72e5645e 194@@ -252,6 +253,27 @@ path elements that rsync believes will allow a symlink to escape the module's
4c15e800 195 hierarchy. There are tricky ways to work around this, though, so you had
e66d6d51 196 better trust your users if you choose this combination of parameters.
4c15e800 197
e66d6d51 198+dit(bf(name converter)) This parameter lets you specify a
4c15e800 199+program that will be run by the rsync daemon (prior to bf(use chroot), if
e66d6d51 200+that parameter is enabled) to convert user/group names into numbers or visa
4c15e800
WD
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:
205+
206+verb( name converter = /path/nameconvert)
207+
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.
212+
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.
218+
85096e5e
WD
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
4c15e800
WD
222diff --git a/support/nameconvert b/support/nameconvert
223new file mode 100755
224--- /dev/null
225+++ b/support/nameconvert
fc557362
WD
226@@ -0,0 +1,42 @@
227+#!/usr/bin/perl -w
4c15e800
WD
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.
232+#
233+# The requests can be:
234+#
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
239+#
240+# An unknown ID_NUM or NAME results in an empty return value.
241+#
242+# This is used by an rsync daemon when configured with the
243+# "name converter" setting.
244+
245+use strict;
246+
247+my $eol = grep(/^--debug$/, @ARGV) ? "\n" : "\0";
248+$/ = $eol;
249+
250+$| = 1;
251+
252+while (<STDIN>) {
253+ chomp;
254+ my $ans;
255+ if (/^uid (\d+)$/) {
256+ $ans = getpwuid($1);
257+ } elsif (/^gid (\d+)$/) {
258+ $ans = getgrgid($1);
259+ } elsif (/^usr (\S+)$/) {
260+ $ans = getpwnam($1);
261+ } elsif (/^grp (\S+)$/) {
262+ $ans = getgrnam($1);
263+ } else {
264+ die "Invalid request: $_";
265+ }
266+ $ans = '' unless defined $ans;
267+ print $ans, $eol;
268+}
269diff --git a/t_stub.c b/t_stub.c
270--- a/t_stub.c
271+++ b/t_stub.c
fc557362
WD
272@@ -30,6 +30,7 @@ mode_t orig_umask = 002;
273 char number_separator = ',';
4c15e800
WD
274 char *partial_dir;
275 char *module_dir;
276+pid_t namecvt_pid;
7170ca8d 277 filter_rule_list daemon_filter_list;
4c15e800
WD
278
279 void rprintf(UNUSED(enum logcode code), const char *format, ...)
7170ca8d 280@@ -70,6 +71,11 @@ filter_rule_list daemon_filter_list;
4c15e800
WD
281 return -1;
282 }
283
284+ int namecvt_name(UNUSED(const char *cmd), UNUSED(const char *name))
285+{
286+ return 0;
287+}
288+
289 char *lp_name(UNUSED(int mod))
290 {
291 return NULL;
292diff --git a/uidlist.c b/uidlist.c
293--- a/uidlist.c
294+++ b/uidlist.c
fc557362 295@@ -33,6 +33,7 @@ extern int preserve_uid;
4c15e800
WD
296 extern int preserve_gid;
297 extern int preserve_acls;
298 extern int numeric_ids;
299+extern pid_t namecvt_pid;
72e5645e 300 extern gid_t our_gid;
fc557362
WD
301 extern char *usermap;
302 extern char *groupmap;
72e5645e 303@@ -76,8 +77,12 @@ static struct idlist *add_to_list(struct idlist **root, id_t id, const char *nam
4c15e800 304 /* turn a uid into a user name */
72e5645e 305 char *uid_to_user(uid_t uid)
4c15e800
WD
306 {
307- struct passwd *pass = getpwuid(uid);
308- if (pass)
309+ struct passwd *pass;
310+
311+ if (namecvt_pid)
312+ return namecvt_id("uid", (int)uid);
313+
314+ if ((pass = getpwuid(uid)) != NULL)
315 return strdup(pass->pw_name);
316 return NULL;
317 }
72e5645e 318@@ -85,8 +90,12 @@ char *uid_to_user(uid_t uid)
4c15e800 319 /* turn a gid into a group name */
72e5645e 320 char *gid_to_group(gid_t gid)
4c15e800
WD
321 {
322- struct group *grp = getgrgid(gid);
323- if (grp)
324+ struct group *grp;
325+
326+ if (namecvt_pid)
327+ return namecvt_id("gid", (int)gid);
328+
329+ if ((grp = getgrgid(gid)) != NULL)
330 return strdup(grp->gr_name);
331 return NULL;
332 }
72e5645e 333@@ -94,32 +103,54 @@ char *gid_to_group(gid_t gid)
7170ca8d
WD
334 /* Parse a user name or (optionally) a number into a uid */
335 int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok)
4c15e800
WD
336 {
337- struct passwd *pass;
338+ uid_t uid;
339+
340 if (!name || !*name)
341 return 0;
7170ca8d
WD
342+
343 if (num_ok && name[strspn(name, "0123456789")] == '\0') {
344 *uid_p = atol(name);
345 return 1;
346 }
4c15e800
WD
347- if (!(pass = getpwnam(name)))
348- return 0;
349- *uid_p = pass->pw_uid;
350+
351+ if (namecvt_pid) {
352+ if (!(uid = namecvt_name("usr", name)))
353+ return 0;
354+ } else {
355+ struct passwd *pass;
356+ if (!(pass = getpwnam(name)))
357+ return 0;
358+ uid = pass->pw_uid;
359+ }
360+
361+ *uid_p = uid;
362 return 1;
363 }
364
7170ca8d
WD
365 /* Parse a group name or (optionally) a number into a gid */
366 int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok)
4c15e800
WD
367 {
368- struct group *grp;
369+ gid_t gid;
370+
371 if (!name || !*name)
372 return 0;
7170ca8d
WD
373+
374 if (num_ok && name[strspn(name, "0123456789")] == '\0') {
375 *gid_p = atol(name);
376 return 1;
377 }
4c15e800
WD
378- if (!(grp = getgrnam(name)))
379- return 0;
380- *gid_p = grp->gr_gid;
381+
382+ if (namecvt_pid) {
383+ if (!(gid = namecvt_name("grp", name)))
384+ return 0;
385+ } else {
386+ struct group *grp;
387+ if (!(grp = getgrnam(name)))
388+ return 0;
389+ gid = grp->gr_gid;
390+ }
391+
392+ *gid_p = gid;
393 return 1;
394 }
395
72e5645e
WD
396diff --git a/util.c b/util.c
397--- a/util.c
398+++ b/util.c
399@@ -31,9 +31,10 @@ extern int modify_window;
400 extern int relative_paths;
401 extern int preserve_xattrs;
402 extern char *module_dir;
403-extern unsigned int module_dirlen;
404 extern mode_t orig_umask;
405 extern char *partial_dir;
406+extern pid_t namecvt_pid;
407+extern unsigned int module_dirlen;
408 extern filter_rule_list daemon_filter_list;
409
410 int sanitize_paths = 0;