Updated patches to work with the current trunk.
[rsync/rsync-patches.git] / nameconverter.diff
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.
7
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:
12
13     name converter = /path/nameconvert
14
15 If /path/ is omitted, the script will be found on the $PATH.
16
17 To 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
23 diff --git a/clientserver.c b/clientserver.c
24 index b6afe00..568a121 100644
25 --- a/clientserver.c
26 +++ b/clientserver.c
27 @@ -67,6 +67,7 @@ extern iconv_t ic_send, ic_recv;
28  char *auth_user;
29  int read_only = 0;
30  int module_id = -1;
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
35 @@ -78,6 +79,7 @@ unsigned int module_dirlen = 0;
36  char *full_module_path;
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;
43 @@ -649,7 +651,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
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;
51  
52 @@ -738,6 +740,44 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
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
97 @@ -965,6 +1005,44 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
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)
142 diff --git a/loadparm.c b/loadparm.c
143 index 8e48e6d..c623689 100644
144 --- a/loadparm.c
145 +++ b/loadparm.c
146 @@ -121,6 +121,7 @@ typedef struct {
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;
154 @@ -195,6 +196,7 @@ static const all_vars Defaults = {
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,
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)
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)
177  FN_LOCAL_STRING(lp_temp_dir, temp_dir)
178 diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
179 index d4978cd..fd0f7d9 100644
180 --- a/rsyncd.conf.yo
181 +++ b/rsyncd.conf.yo
182 @@ -163,10 +163,11 @@ if the module is not read-only).
183  
184  When this parameter is enabled, rsync will not attempt to map users and groups
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
189  use the standard library functions for looking up names and IDs (i.e.
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).
196 @@ -232,6 +233,27 @@ path elements that rsync believes will allow a symlink to escape the module's
197  hierarchy.  There are tricky ways to work around this, though, so you had
198  better trust your users if you choose this combination of parameters.
199  
200 +dit(bf(name converter))  This parameter lets you specify a
201 +program that will be run by the rsync daemon (prior to bf(use chroot), if
202 +that parameter is enabled) to convert user/group names into numbers or visa
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 +
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
224 diff --git a/support/nameconvert b/support/nameconvert
225 new file mode 100755
226 index 0000000..d01f593
227 --- /dev/null
228 +++ b/support/nameconvert
229 @@ -0,0 +1,42 @@
230 +#!/usr/bin/perl -w
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 +}
272 diff --git a/t_stub.c b/t_stub.c
273 index 02cfa69..eaaf8a9 100644
274 --- a/t_stub.c
275 +++ b/t_stub.c
276 @@ -30,6 +30,7 @@ mode_t orig_umask = 002;
277  char number_separator = ',';
278  char *partial_dir;
279  char *module_dir;
280 +pid_t namecvt_pid;
281  struct filter_list_struct daemon_filter_list;
282  
283   void rprintf(UNUSED(enum logcode code), const char *format, ...)
284 @@ -75,6 +76,11 @@ struct filter_list_struct daemon_filter_list;
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;
296 diff --git a/uidlist.c b/uidlist.c
297 index 7e8cbd7..472aeb9 100644
298 --- a/uidlist.c
299 +++ b/uidlist.c
300 @@ -33,6 +33,7 @@ extern int preserve_uid;
301  extern int preserve_gid;
302  extern int preserve_acls;
303  extern int numeric_ids;
304 +extern pid_t namecvt_pid;
305  extern char *usermap;
306  extern char *groupmap;
307  
308 @@ -75,8 +76,12 @@ static struct idlist *add_to_list(struct idlist **root, id_t id, const char *nam
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  }
323 @@ -84,8 +89,12 @@ static const char *uid_to_name(uid_t uid)
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  }
338 diff --git a/util.c b/util.c
339 index 0cafed6..dc1647b 100644
340 --- a/util.c
341 +++ b/util.c
342 @@ -31,9 +31,10 @@ extern int modify_window;
343  extern int relative_paths;
344  extern int preserve_xattrs;
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;
351  extern struct filter_list_struct daemon_filter_list;
352  
353  int sanitize_paths = 0;
354 @@ -497,24 +498,44 @@ void kill_all(int sig)
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