Use "use warnings" rather than -w on the #! line.
[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
24--- a/clientserver.c
25+++ b/clientserver.c
c0c7984e 26@@ -67,6 +67,7 @@ char *auth_user;
4c15e800
WD
27 int read_only = 0;
28 int module_id = -1;
29 int munge_symlinks = 0;
30+pid_t namecvt_pid = 0;
31 struct chmod_mode_struct *daemon_chmod_modes;
32
33 /* module_dirlen is the length of the module_dir string when in daemon
963ca808
WD
34@@ -78,6 +79,7 @@ unsigned int module_dirlen = 0;
35 char *full_module_path;
4c15e800
WD
36
37 static int rl_nulls = 0;
38+static int namecvt_fd_req = -1, namecvt_fd_ans = -1;
39
40 #ifdef HAVE_SIGACTION
41 static struct sigaction sigact;
963ca808 42@@ -565,7 +567,7 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
4c15e800
WD
43 log_init(1);
44
45 #ifdef HAVE_PUTENV
46- if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
47+ if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i) || *lp_name_converter(i)) {
48 char *modname, *modpath, *hostaddr, *hostname, *username;
49 int status;
85096e5e 50
abd3adb8 51@@ -654,6 +656,44 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
4c15e800
WD
52 set_blocking(fds[1]);
53 pre_exec_fd = fds[1];
54 }
55+ if (*lp_name_converter(i)) {
56+ int fds_to[2], fds_from[2];
57+ if (pipe(fds_to) < 0 || pipe(fds_from) < 0
58+ || (namecvt_pid = fork()) < 0) {
59+ rsyserr(FLOG, errno, "name-converter exec preparation failed");
60+ io_printf(f_out, "@ERROR: name-converter exec preparation failed\n");
61+ return -1;
62+ }
63+ if (namecvt_pid == 0) {
64+ char *args[100], *run = lp_name_converter(i);
65+ int cnt = 0;
66+ close(fds_to[1]);
67+ close(fds_from[0]);
68+ set_blocking(fds_to[0]);
69+ set_blocking(fds_from[1]);
70+ close(STDIN_FILENO);
71+ close(STDOUT_FILENO);
72+ dup2(fds_to[0], STDIN_FILENO);
73+ dup2(fds_from[1], STDOUT_FILENO);
74+ while (cnt+1 < (int)(sizeof args / sizeof (char *))) {
75+ char *space = strchr(run, ' ');
76+ args[cnt++] = run;
77+ if (!space)
78+ break;
79+ *space = '\0';
80+ run = space + 1;
81+ }
82+ args[cnt] = NULL;
83+ execvp(args[0], args);
84+ _exit(1);
85+ }
86+ close(fds_to[0]);
87+ close(fds_from[1]);
88+ set_blocking(fds_to[1]);
89+ set_blocking(fds_from[0]);
90+ namecvt_fd_req = fds_to[1];
91+ namecvt_fd_ans = fds_from[0];
92+ }
93 umask(0);
94 }
95 #endif
abd3adb8 96@@ -877,6 +917,44 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
4c15e800
WD
97 return 0;
98 }
99
100+int namecvt_name(const char *cmd, const char *name)
101+{
102+ char buf[1024];
103+ int got, len = snprintf(buf, sizeof buf, "%s %s", cmd, name);
104+ if (len >= (int)sizeof buf) {
105+ rprintf(FERROR, "namecvt_name() request was too large.\n");
106+ exit_cleanup(RERR_UNSUPPORTED);
107+ }
108+ while ((got = write(namecvt_fd_req, buf, len + 1)) != len + 1) {
109+ if (got < 0 && errno == EINTR)
110+ continue;
111+ rprintf(FERROR, "Connection to name-converter failed.\n");
112+ exit_cleanup(RERR_SOCKETIO);
113+ }
114+ if (!(len = read_arg_from_pipe(namecvt_fd_ans, buf, sizeof buf)))
115+ return 0;
116+ return atoi(buf);
117+}
118+
119+const char *namecvt_id(const char *cmd, int id)
120+{
121+ char buf[1024];
122+ int got, len = snprintf(buf, sizeof buf, "%s %d", cmd, id);
123+ if (len >= (int)sizeof buf) {
124+ rprintf(FERROR, "namecvt_id() request was too large.\n");
125+ exit_cleanup(RERR_UNSUPPORTED);
126+ }
127+ while ((got = write(namecvt_fd_req, buf, len + 1)) != len + 1) {
128+ if (got < 0 && errno == EINTR)
129+ continue;
130+ rprintf(FERROR, "Connection to name-converter failed.\n");
131+ exit_cleanup(RERR_SOCKETIO);
132+ }
133+ if (!(len = read_arg_from_pipe(namecvt_fd_ans, buf, sizeof buf)))
134+ return NULL;
135+ return strdup(buf);
136+}
137+
138 /* send a list of available modules to the client. Don't list those
139 with "list = False". */
140 static void send_listing(int fd)
141diff --git a/loadparm.c b/loadparm.c
142--- a/loadparm.c
143+++ b/loadparm.c
85096e5e 144@@ -140,6 +140,7 @@ typedef struct
4c15e800
WD
145 char *log_file;
146 char *log_format;
147 char *name;
148+ char *name_converter;
149 char *outgoing_chmod;
150 char *path;
151 char *postxfer_exec;
85096e5e 152@@ -191,6 +192,7 @@ static service sDefault =
4c15e800
WD
153 /* log_file; */ NULL,
154 /* log_format; */ "%o %h [%a] %m (%u) %f %l",
155 /* name; */ NULL,
156+ /* name_converter; */ NULL,
157 /* outgoing_chmod; */ NULL,
158 /* path; */ NULL,
159 /* postxfer_exec; */ NULL,
85096e5e 160@@ -328,6 +330,7 @@ static struct parm_struct parm_table[] =
4c15e800
WD
161 {"max verbosity", P_INTEGER,P_LOCAL, &sDefault.max_verbosity, NULL,0},
162 {"munge symlinks", P_BOOL, P_LOCAL, &sDefault.munge_symlinks, NULL,0},
163 {"name", P_STRING, P_LOCAL, &sDefault.name, NULL,0},
164+ {"name converter", P_STRING, P_LOCAL, &sDefault.name_converter, NULL,0},
85096e5e 165 {"numeric ids", P_BOOL, P_LOCAL, &sDefault.numeric_ids, NULL,0},
4c15e800
WD
166 {"outgoing chmod", P_STRING, P_LOCAL, &sDefault.outgoing_chmod, NULL,0},
167 {"path", P_PATH, P_LOCAL, &sDefault.path, NULL,0},
85096e5e 168@@ -418,6 +421,7 @@ FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
4c15e800
WD
169 FN_LOCAL_STRING(lp_path, path)
170 FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec)
171 FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
172+FN_LOCAL_STRING(lp_name_converter, name_converter)
173 FN_LOCAL_STRING(lp_refuse_options, refuse_options)
174 FN_LOCAL_STRING(lp_secrets_file, secrets_file)
85096e5e 175 FN_LOCAL_STRING(lp_temp_dir, temp_dir)
4c15e800
WD
176diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
177--- a/rsyncd.conf.yo
178+++ b/rsyncd.conf.yo
ae306a29 179@@ -159,10 +159,11 @@ if the module is not read-only).
4c15e800 180
e66d6d51 181 When this parameter is enabled, rsync will not attempt to map users and groups
85096e5e
WD
182 by name (by default), but instead copy IDs as though bf(--numeric-ids) had
183-been specified. In order to enable name-mapping, rsync needs to be able to
184+been specified. In order to enable name-mapping, rsync needs either the
185+bf(name converter) parameter to specify a conversion program, or it needs to
4c15e800 186 use the standard library functions for looking up names and IDs (i.e.
85096e5e
WD
187 code(getpwuid()), code(getgrgid()), code(getpwname()), and code(getgrnam())).
188-This means the rsync
189+The latter choice means the rsync
190 process in the chroot hierarchy will need to have access to the resources
191 used by these library functions (traditionally /etc/passwd and
192 /etc/group, but perhaps additional dynamic libraries as well).
193@@ -227,6 +228,27 @@ path elements that rsync believes will allow a symlink to escape the module's
4c15e800 194 hierarchy. There are tricky ways to work around this, though, so you had
e66d6d51 195 better trust your users if you choose this combination of parameters.
4c15e800 196
e66d6d51 197+dit(bf(name converter)) This parameter lets you specify a
4c15e800 198+program that will be run by the rsync daemon (prior to bf(use chroot), if
e66d6d51 199+that parameter is enabled) to convert user/group names into numbers or visa
4c15e800
WD
200+versa. There is a sample perl script in the support directory named
201+"nameconvert" that you can use to enable the use of the normal passwd/group
202+lookup calls in a chroot daemon (which does not require any extra files
203+be placed in the chroot area). This use is configured as follows:
204+
205+verb( name converter = /path/nameconvert)
206+
207+You could alternately specify a program that responds to each request using
208+a lookup table to find the names and numbers, this allows you to configure
209+per-module name conversion. See the support/nameconvert script for the
210+details of what requests can be sent to the program.
211+
212+The program will have access to some of the environment variables that are
213+described in the section on bf(pre-xfer exec): bf(RSYNC_MODULE_NAME),
214+bf(RSYNC_MODULE_PATH), bf(RSYNC_HOST_ADDR), bf(RSYNC_HOST_NAME), and
215+bf(RSYNC_USER_NAME). This is useful if you want to customize the
216+conversion using a single program invocation.
217+
85096e5e
WD
218 dit(bf(charset)) This specifies the name of the character set in which the
219 module's filenames are stored. If the client uses an bf(--iconv) option,
220 the daemon will use the value of the "charset" parameter regardless of the
4c15e800
WD
221diff --git a/support/nameconvert b/support/nameconvert
222new file mode 100755
223--- /dev/null
224+++ b/support/nameconvert
0ef5abcb
WD
225@@ -0,0 +1,43 @@
226+#!/usr/bin/perl
4c15e800
WD
227+# This implements a simple protocol to do {user,group}-{name,id}
228+# conversions. All input and output consists of simple strings
229+# with a terminating null char (or newline for debugging). If
230+# the conversion fails, an empty string is returned.
231+#
232+# The requests can be:
233+#
234+# uid ID_NUM\0 -> NAME\0
235+# gid ID_NUM\0 -> NAME\0
236+# usr NAME\0 -> ID_NUM\0
237+# grp NAME\0 -> ID_NUM\0
238+#
239+# An unknown ID_NUM or NAME results in an empty return value.
240+#
241+# This is used by an rsync daemon when configured with the
242+# "name converter" setting.
243+
244+use strict;
0ef5abcb 245+use warnings;
4c15e800
WD
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
91270139 272@@ -30,6 +30,7 @@ int preserve_xattrs = 0;
4c15e800
WD
273 mode_t orig_umask = 002;
274 char *partial_dir;
275 char *module_dir;
276+pid_t namecvt_pid;
c0c7984e 277 struct filter_list_struct daemon_filter_list;
4c15e800
WD
278
279 void rprintf(UNUSED(enum logcode code), const char *format, ...)
91270139 280@@ -75,6 +76,11 @@ struct filter_list_struct 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
295@@ -32,6 +32,7 @@ extern int preserve_uid;
296 extern int preserve_gid;
297 extern int preserve_acls;
298 extern int numeric_ids;
299+extern pid_t namecvt_pid;
300
301 #ifdef HAVE_GETGROUPS
302 # ifndef GETGROUPS_T
303@@ -69,8 +70,12 @@ static struct idlist *add_to_list(struct idlist **root, id_t id, const char *nam
304 /* turn a uid into a user name */
305 static const char *uid_to_name(uid_t uid)
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 }
318@@ -78,8 +83,12 @@ static const char *uid_to_name(uid_t uid)
319 /* turn a gid into a group name */
320 static const char *gid_to_name(gid_t gid)
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 }
333diff --git a/util.c b/util.c
334--- a/util.c
335+++ b/util.c
91270139 336@@ -31,9 +31,10 @@ extern int relative_paths;
4c15e800 337 extern int human_readable;
91270139 338 extern int preserve_xattrs;
4c15e800
WD
339 extern char *module_dir;
340-extern unsigned int module_dirlen;
341 extern mode_t orig_umask;
342 extern char *partial_dir;
343+extern pid_t namecvt_pid;
344+extern unsigned int module_dirlen;
c0c7984e 345 extern struct filter_list_struct daemon_filter_list;
4c15e800
WD
346
347 int sanitize_paths = 0;
91270139 348@@ -497,24 +498,44 @@ void kill_all(int sig)
4c15e800
WD
349 /** Turn a user name into a uid */
350 int name_to_uid(const char *name, uid_t *uid_p)
351 {
352- struct passwd *pass;
353+ uid_t uid;
354+
355 if (!name || !*name)
356 return 0;
357- if (!(pass = getpwnam(name)))
358- return 0;
359- *uid_p = pass->pw_uid;
360+
361+ if (namecvt_pid) {
362+ if (!(uid = namecvt_name("usr", name)))
363+ return 0;
364+ } else {
365+ struct passwd *pass;
366+ if (!(pass = getpwnam(name)))
367+ return 0;
368+ uid = pass->pw_uid;
369+ }
370+
371+ *uid_p = uid;
372 return 1;
373 }
374
375 /** Turn a group name into a gid */
376 int name_to_gid(const char *name, gid_t *gid_p)
377 {
378- struct group *grp;
379+ gid_t gid;
380+
381 if (!name || !*name)
382 return 0;
383- if (!(grp = getgrnam(name)))
384- return 0;
385- *gid_p = grp->gr_gid;
386+
387+ if (namecvt_pid) {
388+ if (!(gid = namecvt_name("grp", name)))
389+ return 0;
390+ } else {
391+ struct group *grp;
392+ if (!(grp = getgrnam(name)))
393+ return 0;
394+ gid = grp->gr_gid;
395+ }
396+
397+ *gid_p = gid;
398 return 1;
399 }
400