The patches for 3.0.0pre9.
[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
26@@ -59,6 +59,7 @@ char *auth_user;
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
34@@ -67,6 +68,7 @@ char *module_dir = NULL;
35 unsigned int module_dirlen = 0;
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;
42@@ -508,7 +510,7 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
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;
50 if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0
51@@ -595,6 +597,44 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
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
a5e6228a 96@@ -793,6 +833,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
144@@ -139,6 +139,7 @@ typedef struct
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;
152@@ -188,6 +189,7 @@ static service sDefault =
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,
160@@ -323,6 +325,7 @@ static struct parm_struct parm_table[] =
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},
165 {"outgoing chmod", P_STRING, P_LOCAL, &sDefault.outgoing_chmod, NULL,0},
166 {"path", P_PATH, P_LOCAL, &sDefault.path, NULL,0},
167 #ifdef HAVE_PUTENV
168@@ -411,6 +414,7 @@ FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
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)
175 FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
176diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
177--- a/rsyncd.conf.yo
178+++ b/rsyncd.conf.yo
179@@ -144,7 +144,10 @@ args if rsync believes they would escape the chroot.
180 The default for "use chroot" is true, and is the safer choice (especially
181 if the module is not read-only).
182
183-In order to preserve usernames and groupnames, rsync needs to be able to
184+In order to preserve usernames and groupnames, you can use the
185+bf(name converter) option to specify a name-converting program that the
186+rsync daemon will start prior to enabling chroot (see the option for more
187+details). If that option is not specified, the daemon needs to be able to
188 use the standard library functions for looking up names and IDs (i.e.
189 code(getpwuid()), code(getgrgid()), code(getpwname()), and code(getgrnam())). This means a
190 process in the chroot namespace will need to have access to the resources
191@@ -200,6 +203,27 @@ path elements that rsync believes will allow a symlink to escape the module's
192 hierarchy. There are tricky ways to work around this, though, so you had
193 better trust your users if you choose this combination of options.
194
195+dit(bf(name converter)) The "name converter" option lets you specify a
196+program that will be run by the rsync daemon (prior to bf(use chroot), if
197+that option is enabled) to convert user/group names into numbers or visa
198+versa. There is a sample perl script in the support directory named
199+"nameconvert" that you can use to enable the use of the normal passwd/group
200+lookup calls in a chroot daemon (which does not require any extra files
201+be placed in the chroot area). This use is configured as follows:
202+
203+verb( name converter = /path/nameconvert)
204+
205+You could alternately specify a program that responds to each request using
206+a lookup table to find the names and numbers, this allows you to configure
207+per-module name conversion. See the support/nameconvert script for the
208+details of what requests can be sent to the program.
209+
210+The program will have access to some of the environment variables that are
211+described in the section on bf(pre-xfer exec): bf(RSYNC_MODULE_NAME),
212+bf(RSYNC_MODULE_PATH), bf(RSYNC_HOST_ADDR), bf(RSYNC_HOST_NAME), and
213+bf(RSYNC_USER_NAME). This is useful if you want to customize the
214+conversion using a single program invocation.
215+
216 dit(bf(max connections)) The "max connections" option allows you to
217 specify the maximum number of simultaneous connections you will allow.
218 Any clients connecting when the maximum has been reached will receive a
219diff --git a/support/nameconvert b/support/nameconvert
220new file mode 100755
221--- /dev/null
222+++ b/support/nameconvert
223@@ -0,0 +1,42 @@
224+#!/usr/bin/perl -w
225+# This implements a simple protocol to do {user,group}-{name,id}
226+# conversions. All input and output consists of simple strings
227+# with a terminating null char (or newline for debugging). If
228+# the conversion fails, an empty string is returned.
229+#
230+# The requests can be:
231+#
232+# uid ID_NUM\0 -> NAME\0
233+# gid ID_NUM\0 -> NAME\0
234+# usr NAME\0 -> ID_NUM\0
235+# grp NAME\0 -> ID_NUM\0
236+#
237+# An unknown ID_NUM or NAME results in an empty return value.
238+#
239+# This is used by an rsync daemon when configured with the
240+# "name converter" setting.
241+
242+use strict;
243+
244+my $eol = grep(/^--debug$/, @ARGV) ? "\n" : "\0";
245+$/ = $eol;
246+
247+$| = 1;
248+
249+while (<STDIN>) {
250+ chomp;
251+ my $ans;
252+ if (/^uid (\d+)$/) {
253+ $ans = getpwuid($1);
254+ } elsif (/^gid (\d+)$/) {
255+ $ans = getgrgid($1);
256+ } elsif (/^usr (\S+)$/) {
257+ $ans = getpwnam($1);
258+ } elsif (/^grp (\S+)$/) {
259+ $ans = getgrnam($1);
260+ } else {
261+ die "Invalid request: $_";
262+ }
263+ $ans = '' unless defined $ans;
264+ print $ans, $eol;
265+}
266diff --git a/t_stub.c b/t_stub.c
267--- a/t_stub.c
268+++ b/t_stub.c
269@@ -29,6 +29,7 @@ int module_dirlen = 0;
270 mode_t orig_umask = 002;
271 char *partial_dir;
272 char *module_dir;
273+pid_t namecvt_pid;
274 struct filter_list_struct server_filter_list;
275
276 void rprintf(UNUSED(enum logcode code), const char *format, ...)
277@@ -69,6 +70,11 @@ struct filter_list_struct server_filter_list;
278 return -1;
279 }
280
281+ int namecvt_name(UNUSED(const char *cmd), UNUSED(const char *name))
282+{
283+ return 0;
284+}
285+
286 char *lp_name(UNUSED(int mod))
287 {
288 return NULL;
289diff --git a/uidlist.c b/uidlist.c
290--- a/uidlist.c
291+++ b/uidlist.c
292@@ -32,6 +32,7 @@ extern int preserve_uid;
293 extern int preserve_gid;
294 extern int preserve_acls;
295 extern int numeric_ids;
296+extern pid_t namecvt_pid;
297
298 #ifdef HAVE_GETGROUPS
299 # ifndef GETGROUPS_T
300@@ -69,8 +70,12 @@ static struct idlist *add_to_list(struct idlist **root, id_t id, const char *nam
301 /* turn a uid into a user name */
302 static const char *uid_to_name(uid_t uid)
303 {
304- struct passwd *pass = getpwuid(uid);
305- if (pass)
306+ struct passwd *pass;
307+
308+ if (namecvt_pid)
309+ return namecvt_id("uid", (int)uid);
310+
311+ if ((pass = getpwuid(uid)) != NULL)
312 return strdup(pass->pw_name);
313 return NULL;
314 }
315@@ -78,8 +83,12 @@ static const char *uid_to_name(uid_t uid)
316 /* turn a gid into a group name */
317 static const char *gid_to_name(gid_t gid)
318 {
319- struct group *grp = getgrgid(gid);
320- if (grp)
321+ struct group *grp;
322+
323+ if (namecvt_pid)
324+ return namecvt_id("gid", (int)gid);
325+
326+ if ((grp = getgrgid(gid)) != NULL)
327 return strdup(grp->gr_name);
328 return NULL;
329 }
330diff --git a/util.c b/util.c
331--- a/util.c
332+++ b/util.c
333@@ -30,9 +30,10 @@ extern int modify_window;
334 extern int relative_paths;
335 extern int human_readable;
336 extern char *module_dir;
337-extern unsigned int module_dirlen;
338 extern mode_t orig_umask;
339 extern char *partial_dir;
340+extern pid_t namecvt_pid;
341+extern unsigned int module_dirlen;
342 extern struct filter_list_struct server_filter_list;
343
344 int sanitize_paths = 0;
345@@ -468,24 +469,44 @@ void kill_all(int sig)
346 /** Turn a user name into a uid */
347 int name_to_uid(const char *name, uid_t *uid_p)
348 {
349- struct passwd *pass;
350+ uid_t uid;
351+
352 if (!name || !*name)
353 return 0;
354- if (!(pass = getpwnam(name)))
355- return 0;
356- *uid_p = pass->pw_uid;
357+
358+ if (namecvt_pid) {
359+ if (!(uid = namecvt_name("usr", name)))
360+ return 0;
361+ } else {
362+ struct passwd *pass;
363+ if (!(pass = getpwnam(name)))
364+ return 0;
365+ uid = pass->pw_uid;
366+ }
367+
368+ *uid_p = uid;
369 return 1;
370 }
371
372 /** Turn a group name into a gid */
373 int name_to_gid(const char *name, gid_t *gid_p)
374 {
375- struct group *grp;
376+ gid_t gid;
377+
378 if (!name || !*name)
379 return 0;
380- if (!(grp = getgrnam(name)))
381- return 0;
382- *gid_p = grp->gr_gid;
383+
384+ if (namecvt_pid) {
385+ if (!(gid = namecvt_name("grp", name)))
386+ return 0;
387+ } else {
388+ struct group *grp;
389+ if (!(grp = getgrnam(name)))
390+ return 0;
391+ gid = grp->gr_gid;
392+ }
393+
394+ *gid_p = gid;
395 return 1;
396 }
397