Allow %VAR% environment references in daemon-config parameter values.
authorWayne Davison <wayned@samba.org>
Sat, 17 Oct 2009 22:53:25 +0000 (15:53 -0700)
committerWayne Davison <wayned@samba.org>
Sat, 17 Oct 2009 23:30:23 +0000 (16:30 -0700)
clientserver.c
loadparm.c
rsyncd.conf.yo

index eeaf8e1..d3bcb2d 100644 (file)
@@ -382,6 +382,7 @@ static char *finish_pre_exec(pid_t pid, int fd, char *request,
        return NULL;
 }
 
        return NULL;
 }
 
+#ifdef HAVE_PUTENV
 static int read_arg_from_pipe(int fd, char *buf, int limit)
 {
        char *bp = buf, *eob = buf + limit - 1;
 static int read_arg_from_pipe(int fd, char *buf, int limit)
 {
        char *bp = buf, *eob = buf + limit - 1;
@@ -402,6 +403,7 @@ static int read_arg_from_pipe(int fd, char *buf, int limit)
 
        return bp - buf;
 }
 
        return bp - buf;
 }
+#endif
 
 static int path_failure(int f_out, const char *dir, BOOL was_chdir)
 {
 
 static int path_failure(int f_out, const char *dir, BOOL was_chdir)
 {
@@ -471,6 +473,26 @@ static struct passwd *want_all_groups(int f_out, uid_t uid)
 #endif
 }
 
 #endif
 }
 
+static void set_env_str(const char *var, const char *str)
+{
+#ifdef HAVE_PUTENV
+       char *mem;
+       if (asprintf(&mem, "%s=%s", var, str) < 0)
+               out_of_memory("set_env_str");
+       putenv(mem);
+#endif
+}
+
+#ifdef HAVE_PUTENV
+static void set_env_num(const char *var, long num)
+{
+       char *mem;
+       if (asprintf(&mem, "%s=%ld", var, num) < 0)
+               out_of_memory("set_env_num");
+       putenv(mem);
+}
+#endif
+
 static int rsync_module(int f_in, int f_out, int i, const char *addr, const char *host)
 {
        int argc;
 static int rsync_module(int f_in, int f_out, int i, const char *addr, const char *host)
 {
        int argc;
@@ -487,6 +509,8 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
        pid_t pre_exec_pid = 0;
        char *request = NULL;
 
        pid_t pre_exec_pid = 0;
        char *request = NULL;
 
+       set_env_str("RSYNC_MODULE_NAME", name);
+
 #ifdef ICONV_OPTION
        iconv_opt = lp_charset(i);
        if (*iconv_opt)
 #ifdef ICONV_OPTION
        iconv_opt = lp_charset(i);
        if (*iconv_opt)
@@ -498,6 +522,8 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
         * we need to do it now before the access check. */
        if (host == undetermined_hostname && lp_reverse_lookup(i))
                host = client_name(f_in);
         * we need to do it now before the access check. */
        if (host == undetermined_hostname && lp_reverse_lookup(i))
                host = client_name(f_in);
+       set_env_str("RSYNC_HOST_NAME", host);
+       set_env_str("RSYNC_HOST_ADDR", addr);
 
        if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
                rprintf(FLOG, "rsync denied on module %s from %s (%s)\n",
 
        if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
                rprintf(FLOG, "rsync denied on module %s from %s (%s)\n",
@@ -537,6 +563,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
                io_printf(f_out, "@ERROR: auth failed on module %s\n", name);
                return -1;
        }
                io_printf(f_out, "@ERROR: auth failed on module %s\n", name);
                return -1;
        }
+       set_env_str("RSYNC_USER_NAME", auth_user);
 
        module_id = i;
 
 
        module_id = i;
 
@@ -612,6 +639,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
                        return path_failure(f_out, module_dir, False);
                full_module_path = module_dir = module_chdir;
        }
                        return path_failure(f_out, module_dir, False);
                full_module_path = module_dir = module_chdir;
        }
+       set_env_str("RSYNC_MODULE_PATH", full_module_path);
 
        if (module_dirlen == 1) {
                module_dirlen = 0;
 
        if (module_dirlen == 1) {
                module_dirlen = 0;
@@ -644,20 +672,8 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
 
 #ifdef HAVE_PUTENV
        if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
 
 #ifdef HAVE_PUTENV
        if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
-               char *modname, *modpath, *hostaddr, *hostname, *username;
                int status;
 
                int status;
 
-               if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0
-                || asprintf(&modpath, "RSYNC_MODULE_PATH=%s", full_module_path) < 0
-                || asprintf(&hostaddr, "RSYNC_HOST_ADDR=%s", addr) < 0
-                || asprintf(&hostname, "RSYNC_HOST_NAME=%s", host) < 0
-                || asprintf(&username, "RSYNC_USER_NAME=%s", auth_user) < 0)
-                       out_of_memory("rsync_module");
-               putenv(modname);
-               putenv(modpath);
-               putenv(hostaddr);
-               putenv(hostname);
-               putenv(username);
                umask(orig_umask);
                /* For post-xfer exec, fork a new process to run the rsync
                 * daemon while this process waits for the exit status and
                umask(orig_umask);
                /* For post-xfer exec, fork a new process to run the rsync
                 * daemon while this process waits for the exit status and
@@ -670,18 +686,15 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
                                return -1;
                        }
                        if (pid) {
                                return -1;
                        }
                        if (pid) {
-                               if (asprintf(&p, "RSYNC_PID=%ld", (long)pid) > 0)
-                                       putenv(p);
+                               set_env_num("RSYNC_PID", (long)pid);
                                if (wait_process(pid, &status, 0) < 0)
                                        status = -1;
                                if (wait_process(pid, &status, 0) < 0)
                                        status = -1;
-                               if (asprintf(&p, "RSYNC_RAW_STATUS=%d", status) > 0)
-                                       putenv(p);
+                               set_env_num("RSYNC_RAW_STATUS", status);
                                if (WIFEXITED(status))
                                        status = WEXITSTATUS(status);
                                else
                                        status = -1;
                                if (WIFEXITED(status))
                                        status = WEXITSTATUS(status);
                                else
                                        status = -1;
-                               if (asprintf(&p, "RSYNC_EXIT_STATUS=%d", status) > 0)
-                                       putenv(p);
+                               set_env_num("RSYNC_EXIT_STATUS", status);
                                if (system(lp_postxfer_exec(i)) < 0)
                                        status = -1;
                                _exit(status);
                                if (system(lp_postxfer_exec(i)) < 0)
                                        status = -1;
                                _exit(status);
@@ -692,8 +705,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
                 * send us the user's request via a pipe. */
                if (*lp_prexfer_exec(i)) {
                        int fds[2];
                 * send us the user's request via a pipe. */
                if (*lp_prexfer_exec(i)) {
                        int fds[2];
-                       if (asprintf(&p, "RSYNC_PID=%ld", (long)getpid()) > 0)
-                               putenv(p);
+                       set_env_num("RSYNC_PID", (long)getpid());
                        if (pipe(fds) < 0 || (pre_exec_pid = fork()) < 0) {
                                rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
                                io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
                        if (pipe(fds) < 0 || (pre_exec_pid = fork()) < 0) {
                                rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
                                io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
@@ -707,8 +719,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
                                len = read_arg_from_pipe(fds[0], buf, BIGPATHBUFLEN);
                                if (len <= 0)
                                        _exit(1);
                                len = read_arg_from_pipe(fds[0], buf, BIGPATHBUFLEN);
                                if (len <= 0)
                                        _exit(1);
-                               if (asprintf(&p, "RSYNC_REQUEST=%s", buf) > 0)
-                                       putenv(p);
+                               set_env_str("RSYNC_REQUEST", buf);
                                for (j = 0; ; j++) {
                                        len = read_arg_from_pipe(fds[0], buf,
                                                                 BIGPATHBUFLEN);
                                for (j = 0; ; j++) {
                                        len = read_arg_from_pipe(fds[0], buf,
                                                                 BIGPATHBUFLEN);
index fc978dc..4ea2c71 100644 (file)
@@ -366,11 +366,61 @@ static void reset_all_vars(void)
        memcpy(&Vars, &Defaults, sizeof Vars);
 }
 
        memcpy(&Vars, &Defaults, sizeof Vars);
 }
 
+/* Expand %VAR% references.  Any unknown vars or unrecognized
+ * syntax leaves the raw chars unchanged. */
+static char *expand_vars(char *str)
+{
+       char *buf, *t, *f;
+       int bufsize;
+
+       if (strchr(str, '%') == NULL)
+               return str;
+
+       bufsize = strlen(str) + 2048;
+       if ((buf = new_array(char, bufsize+1)) == NULL) /* +1 for trailing '\0' */
+               out_of_memory("expand_vars");
+
+       for (t = buf, f = str; bufsize && *f; ) {
+               if (*f == '%' && *++f != '%') {
+                       char *percent = strchr(f, '%');
+                       if (percent) {
+                               char *val;
+                               *percent = '\0';
+                               val = getenv(f);
+                               *percent = '%';
+                               if (val) {
+                                       int len = strlcpy(t, val, bufsize+1);
+                                       if (len > bufsize)
+                                               break;
+                                       bufsize -= len;
+                                       t += len;
+                                       f = percent + 1;
+                                       continue;
+                               }
+                       }
+                       f--;
+               }
+               *t++ = *f++;
+               bufsize--;
+       }
+       *t = '\0';
+
+       if (*f) {
+               rprintf(FLOG, "Overflowed buf in expand_vars() trying to expand: %s\n", str);
+               exit_cleanup(RERR_MALLOC);
+       }
+
+       if (bufsize && (buf = realloc(buf, t - buf + 1)) == NULL)
+               out_of_memory("expand_vars");
+
+       return buf;
+}
+
 /* In this section all the functions that are used to access the
  * parameters from the rest of the program are defined. */
 
 #define FN_GLOBAL_STRING(fn_name, ptr) \
 /* In this section all the functions that are used to access the
  * parameters from the rest of the program are defined. */
 
 #define FN_GLOBAL_STRING(fn_name, ptr) \
- char *fn_name(void) {return *(char **)(ptr) ? *(char **)(ptr) : "";}
+ char *fn_name(void) {return expand_vars(*(char **)(ptr) ? *(char **)(ptr) : "");}
 #define FN_GLOBAL_BOOL(fn_name, ptr) \
  BOOL fn_name(void) {return *(BOOL *)(ptr);}
 #define FN_GLOBAL_CHAR(fn_name, ptr) \
 #define FN_GLOBAL_BOOL(fn_name, ptr) \
  BOOL fn_name(void) {return *(BOOL *)(ptr);}
 #define FN_GLOBAL_CHAR(fn_name, ptr) \
@@ -379,7 +429,7 @@ static void reset_all_vars(void)
  int fn_name(void) {return *(int *)(ptr);}
 
 #define FN_LOCAL_STRING(fn_name, val) \
  int fn_name(void) {return *(int *)(ptr);}
 
 #define FN_LOCAL_STRING(fn_name, val) \
- char *fn_name(int i) {return LP_SNUM_OK(i) && iSECTION(i).val? iSECTION(i).val : (Vars.l.val? Vars.l.val : "");}
+ char *fn_name(int i) {return expand_vars(LP_SNUM_OK(i) && iSECTION(i).val ? iSECTION(i).val : Vars.l.val ? Vars.l.val : "");}
 #define FN_LOCAL_BOOL(fn_name, val) \
  BOOL fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
 #define FN_LOCAL_CHAR(fn_name, val) \
 #define FN_LOCAL_BOOL(fn_name, val) \
  BOOL fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
 #define FN_LOCAL_CHAR(fn_name, val) \
@@ -603,6 +653,17 @@ static BOOL do_parameter(char *parmname, char *parmvalue)
        }
 
        /* now switch on the type of variable it is */
        }
 
        /* now switch on the type of variable it is */
+       switch (parm_table[parmnum].type) {
+       case P_PATH:
+       case P_STRING:
+               /* delay expansion of vars */
+               break;
+       default:
+               /* expand any %VARS% now */
+               parmvalue = expand_vars(parmvalue);
+               break;
+       }
+
        switch (parm_table[parmnum].type) {
        case P_BOOL:
                set_boolean(parm_ptr, parmvalue);
        switch (parm_table[parmnum].type) {
        case P_BOOL:
                set_boolean(parm_ptr, parmvalue);
index 31ef95f..d2d8f45 100644 (file)
@@ -78,6 +78,18 @@ You may also include any module parameters in the global part of the
 config file in which case the supplied value will override the
 default for that parameter.
 
 config file in which case the supplied value will override the
 default for that parameter.
 
+You may use references to environment variables in the values of parameters.
+String parameters will have %VAR% references expanded as late as possible (when
+the string is used in the program), allowing for the use of variables that
+rsync sets at connection time, such as RSYNC_USER_NAME.  Non-string parameters
+(such as true/false settings) are expanded when read from the config file.  If
+a variable does not exist in the environment, or if a sequence of characters is
+not a valid reference (such as an un-paired percent sign), the raw characters
+are passed through unchanged.  This helps with backward compatibility and
+safety (e.g. expanding a non-existent %VAR% to an empty string in a path could
+result in a very unsafe path).  Anyone that needs to insert a literal % string
+into a value should use %%.
+
 startdit()
 dit(bf(motd file)) This parameter allows you to specify a
 "message of the day" to display to clients on each connect. This
 startdit()
 dit(bf(motd file)) This parameter allows you to specify a
 "message of the day" to display to clients on each connect. This
@@ -121,6 +133,9 @@ name contains whitespace, each internal sequence of whitespace will be
 changed into a single space, while leading or trailing whitespace will be
 discarded.
 
 changed into a single space, while leading or trailing whitespace will be
 discarded.
 
+As with GLOBAL PARAMETERS, you may use references to environment variables in
+the values of parameters.  See the GLOBAL PARAMETERS section for more details.
+
 startdit()
 
 dit(bf(comment)) This parameter specifies a description string
 startdit()
 
 dit(bf(comment)) This parameter specifies a description string
@@ -129,7 +144,12 @@ of available modules. The default is no comment.
 
 dit(bf(path)) This parameter specifies the directory in the daemon's
 filesystem to make available in this module.  You must specify this parameter
 
 dit(bf(path)) This parameter specifies the directory in the daemon's
 filesystem to make available in this module.  You must specify this parameter
-for each module in tt(rsyncd.conf).
+for each module in tt(rsyncd.conf).  You may base the path's value off
+of an environment variable, even one that is set by rsync when the user
+connects.  For example, this would use the authorizing user's name in the
+path:
+
+verb(    path = /home/%RSYNC_USER_NAME% )
 
 dit(bf(use chroot)) If "use chroot" is true, the rsync daemon will chroot
 to the "path" before starting the file transfer with the client.  This has
 
 dit(bf(use chroot)) If "use chroot" is true, the rsync daemon will chroot
 to the "path" before starting the file transfer with the client.  This has
@@ -320,6 +340,13 @@ file permissions are available. The default when run by a super-user is to
 switch to the system's "nobody" user.  The default for a non-super-user is to
 not try to change the user.  See also the "gid" parameter.
 
 switch to the system's "nobody" user.  The default for a non-super-user is to
 not try to change the user.  See also the "gid" parameter.
 
+The RSYNC_USER_NAME environment variable may be used to request that rsync run
+as the authorizing user.  For example, if you want a rsync to run as the same
+user that was received for the rsync authentication, this setup is useful:
+
+verb(    uid = %RSYNC_USER_NAME%
+    gid = * )
+
 dit(bf(gid)) This parameter specifies one or more group names/IDs that will be
 used when accessing the module.  The first one will be the default group, and
 any extra ones be set as supplemental groups.  You may also specify a "*" as
 dit(bf(gid)) This parameter specifies one or more group names/IDs that will be
 used when accessing the module.  The first one will be the default group, and
 any extra ones be set as supplemental groups.  You may also specify a "*" as