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
23diff --git a/clientserver.c b/clientserver.c
fc557362 24index b6afe00..568a121 100644
4c15e800
WD
25--- a/clientserver.c
26+++ b/clientserver.c
fc557362
WD
27@@ -67,6 +67,7 @@ extern iconv_t ic_send, ic_recv;
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
963ca808
WD
35@@ -78,6 +79,7 @@ unsigned int module_dirlen = 0;
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;
fc557362 43@@ -649,7 +651,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)) {
49 char *modname, *modpath, *hostaddr, *hostname, *username;
50 int status;
85096e5e 51
fc557362 52@@ -738,6 +740,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
fc557362 97@@ -965,6 +1005,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
fc557362 143index 8e48e6d..c623689 100644
4c15e800
WD
144--- a/loadparm.c
145+++ b/loadparm.c
fc557362 146@@ -121,6 +121,7 @@ typedef struct {
4c15e800
WD
147 char *log_file;
148 char *log_format;
149 char *name;
150+ char *name_converter;
151 char *outgoing_chmod;
152 char *path;
153 char *postxfer_exec;
fc557362 154@@ -195,6 +196,7 @@ static const all_vars Defaults = {
4c15e800
WD
155 /* log_file; */ NULL,
156 /* log_format; */ "%o %h [%a] %m (%u) %f %l",
157 /* name; */ NULL,
158+ /* name_converter; */ NULL,
159 /* outgoing_chmod; */ NULL,
160 /* path; */ NULL,
161 /* postxfer_exec; */ NULL,
fc557362
WD
162@@ -337,6 +339,7 @@ static struct parm_struct parm_table[] =
163 {"max verbosity", P_INTEGER,P_LOCAL, &Vars.l.max_verbosity, NULL,0},
164 {"munge symlinks", P_BOOL, P_LOCAL, &Vars.l.munge_symlinks, NULL,0},
165 {"name", P_STRING, P_LOCAL, &Vars.l.name, NULL,0},
166+ {"name converter", P_STRING, P_LOCAL, &Vars.l.name_converter, NULL,0},
167 {"numeric ids", P_BOOL, P_LOCAL, &Vars.l.numeric_ids, NULL,0},
168 {"outgoing chmod", P_STRING, P_LOCAL, &Vars.l.outgoing_chmod, NULL,0},
169 {"path", P_PATH, P_LOCAL, &Vars.l.path, NULL,0},
170@@ -414,6 +417,7 @@ FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
4c15e800
WD
171 FN_LOCAL_STRING(lp_path, path)
172 FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec)
173 FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
174+FN_LOCAL_STRING(lp_name_converter, name_converter)
175 FN_LOCAL_STRING(lp_refuse_options, refuse_options)
176 FN_LOCAL_STRING(lp_secrets_file, secrets_file)
85096e5e 177 FN_LOCAL_STRING(lp_temp_dir, temp_dir)
4c15e800 178diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
fc557362 179index d4978cd..fd0f7d9 100644
4c15e800
WD
180--- a/rsyncd.conf.yo
181+++ b/rsyncd.conf.yo
fc557362 182@@ -163,10 +163,11 @@ if the module is not read-only).
4c15e800 183
e66d6d51 184 When this parameter is enabled, rsync will not attempt to map users and groups
85096e5e
WD
185 by name (by default), but instead copy IDs as though bf(--numeric-ids) had
186-been specified. In order to enable name-mapping, rsync needs to be able to
187+been specified. In order to enable name-mapping, rsync needs either the
188+bf(name converter) parameter to specify a conversion program, or it needs to
4c15e800 189 use the standard library functions for looking up names and IDs (i.e.
85096e5e
WD
190 code(getpwuid()), code(getgrgid()), code(getpwname()), and code(getgrnam())).
191-This means the rsync
192+The latter choice means the rsync
193 process in the chroot hierarchy will need to have access to the resources
194 used by these library functions (traditionally /etc/passwd and
195 /etc/group, but perhaps additional dynamic libraries as well).
fc557362 196@@ -232,6 +233,27 @@ path elements that rsync believes will allow a symlink to escape the module's
4c15e800 197 hierarchy. There are tricky ways to work around this, though, so you had
e66d6d51 198 better trust your users if you choose this combination of parameters.
4c15e800 199
e66d6d51 200+dit(bf(name converter)) This parameter lets you specify a
4c15e800 201+program that will be run by the rsync daemon (prior to bf(use chroot), if
e66d6d51 202+that parameter is enabled) to convert user/group names into numbers or visa
4c15e800
WD
203+versa. There is a sample perl script in the support directory named
204+"nameconvert" that you can use to enable the use of the normal passwd/group
205+lookup calls in a chroot daemon (which does not require any extra files
206+be placed in the chroot area). This use is configured as follows:
207+
208+verb( name converter = /path/nameconvert)
209+
210+You could alternately specify a program that responds to each request using
211+a lookup table to find the names and numbers, this allows you to configure
212+per-module name conversion. See the support/nameconvert script for the
213+details of what requests can be sent to the program.
214+
215+The program will have access to some of the environment variables that are
216+described in the section on bf(pre-xfer exec): bf(RSYNC_MODULE_NAME),
217+bf(RSYNC_MODULE_PATH), bf(RSYNC_HOST_ADDR), bf(RSYNC_HOST_NAME), and
218+bf(RSYNC_USER_NAME). This is useful if you want to customize the
219+conversion using a single program invocation.
220+
85096e5e
WD
221 dit(bf(charset)) This specifies the name of the character set in which the
222 module's filenames are stored. If the client uses an bf(--iconv) option,
223 the daemon will use the value of the "charset" parameter regardless of the
4c15e800
WD
224diff --git a/support/nameconvert b/support/nameconvert
225new file mode 100755
fc557362 226index 0000000..d01f593
4c15e800
WD
227--- /dev/null
228+++ b/support/nameconvert
fc557362
WD
229@@ -0,0 +1,42 @@
230+#!/usr/bin/perl -w
4c15e800
WD
231+# This implements a simple protocol to do {user,group}-{name,id}
232+# conversions. All input and output consists of simple strings
233+# with a terminating null char (or newline for debugging). If
234+# the conversion fails, an empty string is returned.
235+#
236+# The requests can be:
237+#
238+# uid ID_NUM\0 -> NAME\0
239+# gid ID_NUM\0 -> NAME\0
240+# usr NAME\0 -> ID_NUM\0
241+# grp NAME\0 -> ID_NUM\0
242+#
243+# An unknown ID_NUM or NAME results in an empty return value.
244+#
245+# This is used by an rsync daemon when configured with the
246+# "name converter" setting.
247+
248+use strict;
249+
250+my $eol = grep(/^--debug$/, @ARGV) ? "\n" : "\0";
251+$/ = $eol;
252+
253+$| = 1;
254+
255+while (<STDIN>) {
256+ chomp;
257+ my $ans;
258+ if (/^uid (\d+)$/) {
259+ $ans = getpwuid($1);
260+ } elsif (/^gid (\d+)$/) {
261+ $ans = getgrgid($1);
262+ } elsif (/^usr (\S+)$/) {
263+ $ans = getpwnam($1);
264+ } elsif (/^grp (\S+)$/) {
265+ $ans = getgrnam($1);
266+ } else {
267+ die "Invalid request: $_";
268+ }
269+ $ans = '' unless defined $ans;
270+ print $ans, $eol;
271+}
272diff --git a/t_stub.c b/t_stub.c
fc557362 273index 02cfa69..eaaf8a9 100644
4c15e800
WD
274--- a/t_stub.c
275+++ b/t_stub.c
fc557362
WD
276@@ -30,6 +30,7 @@ mode_t orig_umask = 002;
277 char number_separator = ',';
4c15e800
WD
278 char *partial_dir;
279 char *module_dir;
280+pid_t namecvt_pid;
c0c7984e 281 struct filter_list_struct daemon_filter_list;
4c15e800
WD
282
283 void rprintf(UNUSED(enum logcode code), const char *format, ...)
91270139 284@@ -75,6 +76,11 @@ struct filter_list_struct daemon_filter_list;
4c15e800
WD
285 return -1;
286 }
287
288+ int namecvt_name(UNUSED(const char *cmd), UNUSED(const char *name))
289+{
290+ return 0;
291+}
292+
293 char *lp_name(UNUSED(int mod))
294 {
295 return NULL;
296diff --git a/uidlist.c b/uidlist.c
fc557362 297index 7e8cbd7..472aeb9 100644
4c15e800
WD
298--- a/uidlist.c
299+++ b/uidlist.c
fc557362 300@@ -33,6 +33,7 @@ extern int preserve_uid;
4c15e800
WD
301 extern int preserve_gid;
302 extern int preserve_acls;
303 extern int numeric_ids;
304+extern pid_t namecvt_pid;
fc557362
WD
305 extern char *usermap;
306 extern char *groupmap;
4c15e800 307
fc557362 308@@ -75,8 +76,12 @@ static struct idlist *add_to_list(struct idlist **root, id_t id, const char *nam
4c15e800
WD
309 /* turn a uid into a user name */
310 static const char *uid_to_name(uid_t uid)
311 {
312- struct passwd *pass = getpwuid(uid);
313- if (pass)
314+ struct passwd *pass;
315+
316+ if (namecvt_pid)
317+ return namecvt_id("uid", (int)uid);
318+
319+ if ((pass = getpwuid(uid)) != NULL)
320 return strdup(pass->pw_name);
321 return NULL;
322 }
fc557362 323@@ -84,8 +89,12 @@ static const char *uid_to_name(uid_t uid)
4c15e800
WD
324 /* turn a gid into a group name */
325 static const char *gid_to_name(gid_t gid)
326 {
327- struct group *grp = getgrgid(gid);
328- if (grp)
329+ struct group *grp;
330+
331+ if (namecvt_pid)
332+ return namecvt_id("gid", (int)gid);
333+
334+ if ((grp = getgrgid(gid)) != NULL)
335 return strdup(grp->gr_name);
336 return NULL;
337 }
338diff --git a/util.c b/util.c
fc557362 339index 0cafed6..dc1647b 100644
4c15e800
WD
340--- a/util.c
341+++ b/util.c
fc557362
WD
342@@ -31,9 +31,10 @@ extern int modify_window;
343 extern int relative_paths;
91270139 344 extern int preserve_xattrs;
4c15e800
WD
345 extern char *module_dir;
346-extern unsigned int module_dirlen;
347 extern mode_t orig_umask;
348 extern char *partial_dir;
349+extern pid_t namecvt_pid;
350+extern unsigned int module_dirlen;
c0c7984e 351 extern struct filter_list_struct daemon_filter_list;
4c15e800
WD
352
353 int sanitize_paths = 0;
91270139 354@@ -497,24 +498,44 @@ void kill_all(int sig)
4c15e800
WD
355 /** Turn a user name into a uid */
356 int name_to_uid(const char *name, uid_t *uid_p)
357 {
358- struct passwd *pass;
359+ uid_t uid;
360+
361 if (!name || !*name)
362 return 0;
363- if (!(pass = getpwnam(name)))
364- return 0;
365- *uid_p = pass->pw_uid;
366+
367+ if (namecvt_pid) {
368+ if (!(uid = namecvt_name("usr", name)))
369+ return 0;
370+ } else {
371+ struct passwd *pass;
372+ if (!(pass = getpwnam(name)))
373+ return 0;
374+ uid = pass->pw_uid;
375+ }
376+
377+ *uid_p = uid;
378 return 1;
379 }
380
381 /** Turn a group name into a gid */
382 int name_to_gid(const char *name, gid_t *gid_p)
383 {
384- struct group *grp;
385+ gid_t gid;
386+
387 if (!name || !*name)
388 return 0;
389- if (!(grp = getgrnam(name)))
390- return 0;
391- *gid_p = grp->gr_gid;
392+
393+ if (namecvt_pid) {
394+ if (!(gid = namecvt_name("grp", name)))
395+ return 0;
396+ } else {
397+ struct group *grp;
398+ if (!(grp = getgrnam(name)))
399+ return 0;
400+ gid = grp->gr_gid;
401+ }
402+
403+ *gid_p = gid;
404 return 1;
405 }
406