| 1 | This patch adds the --munge-links option, which works like the daemon's |
| 2 | "munge symlinks" parameter. |
| 3 | |
| 4 | To use this patch, run these commands for a successful build: |
| 5 | |
| 6 | patch -p1 <patches/remote-option.diff |
| 7 | patch -p1 <patches/munge-links.diff |
| 8 | ./configure (optional if already run) |
| 9 | make |
| 10 | |
| 11 | diff --git a/clientserver.c b/clientserver.c |
| 12 | --- a/clientserver.c |
| 13 | +++ b/clientserver.c |
| 14 | @@ -37,6 +37,7 @@ extern int ignore_errors; |
| 15 | extern int preserve_xattrs; |
| 16 | extern int kluge_around_eof; |
| 17 | extern int daemon_over_rsh; |
| 18 | +extern int munge_symlinks; |
| 19 | extern int sanitize_paths; |
| 20 | extern int numeric_ids; |
| 21 | extern int filesfrom_fd; |
| 22 | @@ -66,7 +67,6 @@ extern iconv_t ic_send, ic_recv; |
| 23 | char *auth_user; |
| 24 | int read_only = 0; |
| 25 | int module_id = -1; |
| 26 | -int munge_symlinks = 0; |
| 27 | struct chmod_mode_struct *daemon_chmod_modes; |
| 28 | |
| 29 | /* module_dirlen is the length of the module_dir string when in daemon |
| 30 | @@ -418,6 +418,7 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) |
| 31 | char *name = lp_name(i); |
| 32 | int use_chroot = lp_use_chroot(i); |
| 33 | int ret, pre_exec_fd = -1; |
| 34 | + int save_munge_symlinks; |
| 35 | pid_t pre_exec_pid = 0; |
| 36 | char *request = NULL; |
| 37 | |
| 38 | @@ -688,9 +689,11 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) |
| 39 | munge_symlinks = !use_chroot || module_dirlen; |
| 40 | if (munge_symlinks) { |
| 41 | STRUCT_STAT st; |
| 42 | - if (do_stat(SYMLINK_PREFIX, &st) == 0 && S_ISDIR(st.st_mode)) { |
| 43 | - rprintf(FLOG, "Symlink munging is unsupported when a %s directory exists.\n", |
| 44 | - SYMLINK_PREFIX); |
| 45 | + char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */ |
| 46 | + strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */ |
| 47 | + if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) { |
| 48 | + rprintf(FLOG, "Symlink munging is unsafe when a %s directory exists.\n", |
| 49 | + prefix); |
| 50 | io_printf(f_out, "@ERROR: daemon security issue -- contact admin\n", name); |
| 51 | exit_cleanup(RERR_UNSUPPORTED); |
| 52 | } |
| 53 | @@ -746,6 +749,8 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) |
| 54 | read_args(f_in, name, line, sizeof line, rl_nulls, &argv, &argc, &request); |
| 55 | orig_argv = argv; |
| 56 | |
| 57 | + save_munge_symlinks = munge_symlinks; |
| 58 | + |
| 59 | verbose = 0; /* future verbosity is controlled by client options */ |
| 60 | ret = parse_arguments(&argc, (const char ***) &argv); |
| 61 | if (protect_args && ret) { |
| 62 | @@ -757,6 +762,8 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) |
| 63 | } else |
| 64 | orig_early_argv = NULL; |
| 65 | |
| 66 | + munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */ |
| 67 | + |
| 68 | if (pre_exec_pid) { |
| 69 | err_msg = finish_pre_exec(pre_exec_pid, pre_exec_fd, request, |
| 70 | orig_early_argv, orig_argv); |
| 71 | diff --git a/options.c b/options.c |
| 72 | --- a/options.c |
| 73 | +++ b/options.c |
| 74 | @@ -105,6 +105,7 @@ int connect_timeout = 0; |
| 75 | int keep_partial = 0; |
| 76 | int safe_symlinks = 0; |
| 77 | int copy_unsafe_links = 0; |
| 78 | +int munge_symlinks = 0; |
| 79 | int size_only = 0; |
| 80 | int daemon_bwlimit = 0; |
| 81 | int bwlimit = 0; |
| 82 | @@ -339,6 +340,7 @@ void usage(enum logcode F) |
| 83 | rprintf(F," -L, --copy-links transform symlink into referent file/dir\n"); |
| 84 | rprintf(F," --copy-unsafe-links only \"unsafe\" symlinks are transformed\n"); |
| 85 | rprintf(F," --safe-links ignore symlinks that point outside the source tree\n"); |
| 86 | + rprintf(F," --munge-links munge symlinks to make them safer (but unusable)\n"); |
| 87 | rprintf(F," -k, --copy-dirlinks transform symlink to a dir into referent dir\n"); |
| 88 | rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n"); |
| 89 | rprintf(F," -H, --hard-links preserve hard links\n"); |
| 90 | @@ -521,6 +523,8 @@ static struct poptOption long_options[] = { |
| 91 | {"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 }, |
| 92 | {"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 }, |
| 93 | {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 }, |
| 94 | + {"munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 1, 0, 0 }, |
| 95 | + {"no-munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 0, 0, 0 }, |
| 96 | {"copy-dirlinks", 'k', POPT_ARG_NONE, ©_dirlinks, 0, 0, 0 }, |
| 97 | {"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 }, |
| 98 | {"hard-links", 'H', POPT_ARG_NONE, 0, 'H', 0, 0 }, |
| 99 | @@ -1472,6 +1476,17 @@ int parse_arguments(int *argc_p, const char ***argv_p) |
| 100 | need_messages_from_generator = 1; |
| 101 | } |
| 102 | |
| 103 | + if (munge_symlinks && !am_daemon) { |
| 104 | + STRUCT_STAT st; |
| 105 | + char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */ |
| 106 | + strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */ |
| 107 | + if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) { |
| 108 | + rprintf(FERROR, "Symlink munging is unsafe when a %s directory exists.\n", |
| 109 | + prefix); |
| 110 | + exit_cleanup(RERR_UNSUPPORTED); |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | if (sanitize_paths) { |
| 115 | int i; |
| 116 | for (i = argc; i-- > 0; ) |
| 117 | diff --git a/pipe.c b/pipe.c |
| 118 | --- a/pipe.c |
| 119 | +++ b/pipe.c |
| 120 | @@ -26,6 +26,7 @@ extern int am_sender; |
| 121 | extern int am_server; |
| 122 | extern int blocking_io; |
| 123 | extern int filesfrom_fd; |
| 124 | +extern int munge_symlinks; |
| 125 | extern mode_t orig_umask; |
| 126 | extern char *logfile_name; |
| 127 | extern int remote_option_cnt; |
| 128 | @@ -133,6 +134,7 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out, |
| 129 | am_sender = 0; |
| 130 | am_server = 1; |
| 131 | filesfrom_fd = -1; |
| 132 | + munge_symlinks = 0; /* Each side needs its own option. */ |
| 133 | chmod_modes = NULL; /* Let the sending side handle this. */ |
| 134 | |
| 135 | /* Let the client side handle this. */ |
| 136 | diff --git a/rsync.h b/rsync.h |
| 137 | --- a/rsync.h |
| 138 | +++ b/rsync.h |
| 139 | @@ -32,7 +32,7 @@ |
| 140 | #define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock" |
| 141 | #define URL_PREFIX "rsync://" |
| 142 | |
| 143 | -#define SYMLINK_PREFIX "/rsyncd-munged/" |
| 144 | +#define SYMLINK_PREFIX "/rsyncd-munged/" /* This MUST have a trailing slash! */ |
| 145 | #define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1) |
| 146 | |
| 147 | #define BACKUP_SUFFIX "~" |
| 148 | diff --git a/rsync.yo b/rsync.yo |
| 149 | --- a/rsync.yo |
| 150 | +++ b/rsync.yo |
| 151 | @@ -338,6 +338,7 @@ to the detailed description below for a complete description. verb( |
| 152 | -L, --copy-links transform symlink into referent file/dir |
| 153 | --copy-unsafe-links only "unsafe" symlinks are transformed |
| 154 | --safe-links ignore symlinks that point outside the tree |
| 155 | + --munge-links munge symlinks to make them safer |
| 156 | -k, --copy-dirlinks transform symlink to dir into referent dir |
| 157 | -K, --keep-dirlinks treat symlinked dir on receiver as dir |
| 158 | -H, --hard-links preserve hard links |
| 159 | @@ -795,6 +796,25 @@ which point outside the copied tree. All absolute symlinks are |
| 160 | also ignored. Using this option in conjunction with bf(--relative) may |
| 161 | give unexpected results. |
| 162 | |
| 163 | +dit(bf(--munge-links)) This option tells rsync to (1) modify all symlinks on |
| 164 | +the receiving side in a way that makes them unusable but recoverable (see |
| 165 | +below), or (2) to unmunge symlinks on the sending side that had been stored in |
| 166 | +a munged state. This is useful if you don't quite trust the source of the data |
| 167 | +to not try to slip in a symlink to a unexpected place. |
| 168 | + |
| 169 | +The way rsync disables the use of symlinks is to prefix each one with the |
| 170 | +string "/rsyncd-munged/". This prevents the links from being used as long as |
| 171 | +that directory does not exist. When this option is enabled, rsync will refuse |
| 172 | +to run if that path is a directory or a symlink to a directory. |
| 173 | + |
| 174 | +The option only affects the client side of the transfer, so if you need it to |
| 175 | +affect the server, specify it via bf(--remote-option). (Note that in a local |
| 176 | +transfer, the client side is the sender.) |
| 177 | + |
| 178 | +This option has no affect on a daemon, since the daemon configures whether it |
| 179 | +wants munged symlinks via its "munge symlinks" parameter. See also the |
| 180 | +"munge-symlinks" perl script in the support directory of the source code. |
| 181 | + |
| 182 | dit(bf(-k, --copy-dirlinks)) This option causes the sending side to treat |
| 183 | a symlink to a directory as though it were a real directory. This is |
| 184 | useful if you don't want symlinks to non-directories to be affected, as |
| 185 | diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo |
| 186 | --- a/rsyncd.conf.yo |
| 187 | +++ b/rsyncd.conf.yo |
| 188 | @@ -191,8 +191,9 @@ to translate names, and that it is not possible for a user to change those |
| 189 | resources. |
| 190 | |
| 191 | dit(bf(munge symlinks)) This parameter tells rsync to modify |
| 192 | -all incoming symlinks in a way that makes them unusable but recoverable |
| 193 | -(see below). This should help protect your files from user trickery when |
| 194 | +all symlinks in the same way as the (non-daemon-affecting) |
| 195 | +bf(--munge-links) command-line option (using a method described below). |
| 196 | +This should help protect your files from user trickery when |
| 197 | your daemon module is writable. The default is disabled when "use chroot" |
| 198 | is on and the inside-chroot path is "/", otherwise it is enabled. |
| 199 | |