Use "use warnings" rather than -w on the #! line.
[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 --- a/clientserver.c
25 +++ b/clientserver.c
26 @@ -67,6 +67,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 @@ -78,6 +79,7 @@ unsigned int module_dirlen = 0;
35  char *full_module_path;
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 @@ -565,7 +567,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  
51 @@ -654,6 +656,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
96 @@ -877,6 +917,44 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
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)
141 diff --git a/loadparm.c b/loadparm.c
142 --- a/loadparm.c
143 +++ b/loadparm.c
144 @@ -140,6 +140,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 @@ -191,6 +192,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 @@ -328,6 +330,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   {"numeric ids",       P_BOOL,   P_LOCAL, &sDefault.numeric_ids,       NULL,0},
166   {"outgoing chmod",    P_STRING, P_LOCAL, &sDefault.outgoing_chmod,    NULL,0},
167   {"path",              P_PATH,   P_LOCAL, &sDefault.path,              NULL,0},
168 @@ -418,6 +421,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_STRING(lp_temp_dir, temp_dir)
176 diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
177 --- a/rsyncd.conf.yo
178 +++ b/rsyncd.conf.yo
179 @@ -159,10 +159,11 @@ if the module is not read-only).
180  
181  When this parameter is enabled, rsync will not attempt to map users and groups
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
186  use the standard library functions for looking up names and IDs (i.e.
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
194  hierarchy.  There are tricky ways to work around this, though, so you had
195  better trust your users if you choose this combination of parameters.
196  
197 +dit(bf(name converter))  This parameter lets you specify a
198 +program that will be run by the rsync daemon (prior to bf(use chroot), if
199 +that parameter is enabled) to convert user/group names into numbers or visa
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 +
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
221 diff --git a/support/nameconvert b/support/nameconvert
222 new file mode 100755
223 --- /dev/null
224 +++ b/support/nameconvert
225 @@ -0,0 +1,43 @@
226 +#!/usr/bin/perl
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;
245 +use warnings;
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 +}
269 diff --git a/t_stub.c b/t_stub.c
270 --- a/t_stub.c
271 +++ b/t_stub.c
272 @@ -30,6 +30,7 @@ int preserve_xattrs = 0;
273  mode_t orig_umask = 002;
274  char *partial_dir;
275  char *module_dir;
276 +pid_t namecvt_pid;
277  struct filter_list_struct daemon_filter_list;
278  
279   void rprintf(UNUSED(enum logcode code), const char *format, ...)
280 @@ -75,6 +76,11 @@ struct filter_list_struct daemon_filter_list;
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;
292 diff --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  }
333 diff --git a/util.c b/util.c
334 --- a/util.c
335 +++ b/util.c
336 @@ -31,9 +31,10 @@ extern int relative_paths;
337  extern int human_readable;
338  extern int preserve_xattrs;
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;
345  extern struct filter_list_struct daemon_filter_list;
346  
347  int sanitize_paths = 0;
348 @@ -497,24 +498,44 @@ void kill_all(int sig)
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