Some daemon security improvements, including the new parameters
[rsync/rsync.git] / options.c
index c868cfe..d12d28a 100644 (file)
--- a/options.c
+++ b/options.c
  */
 
 #include "rsync.h"
+#include "ifuncs.h"
 #include <popt.h>
 #include "zlib/zlib.h"
 
 extern int module_id;
 extern int sanitize_paths;
 extern int daemon_over_rsh;
+extern unsigned int module_dirlen;
 extern struct filter_list_struct filter_list;
 extern struct filter_list_struct server_filter_list;
 
@@ -98,6 +100,7 @@ int xfer_dirs = -1;
 int am_daemon = 0;
 int do_stats = 0;
 int do_progress = 0;
+int connect_timeout = 0;
 int keep_partial = 0;
 int safe_symlinks = 0;
 int copy_unsafe_links = 0;
@@ -184,8 +187,8 @@ int list_only = 0;
 #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
 char *batch_name = NULL;
 
-#ifdef ICONV_OPTION
 int need_unsorted_flist = 0;
+#ifdef ICONV_OPTION
 char *iconv_opt = ICONV_OPTION;
 #endif
 
@@ -199,7 +202,7 @@ static int itemize_changes = 0;
 static int refused_delete, refused_archive_part, refused_compress;
 static int refused_partial, refused_progress, refused_delete_before;
 static int refused_delete_during;
-static int refused_inplace;
+static int refused_inplace, refused_no_iconv;
 static char *max_size_arg, *min_size_arg;
 static char tmp_partialdir[] = ".~tmp~";
 
@@ -350,8 +353,8 @@ void usage(enum logcode F)
   rprintf(F,"     --fake-super            store/recover privileged attrs using xattrs\n");
 #endif
   rprintf(F," -S, --sparse                handle sparse files efficiently\n");
-  rprintf(F," -n, --dry-run               show what would have been transferred\n");
-  rprintf(F," -W, --whole-file            copy files whole (without rsync algorithm)\n");
+  rprintf(F," -n, --dry-run               perform a trial run with no changes made\n");
+  rprintf(F," -W, --whole-file            copy files whole (without delta-xfer algorithm)\n");
   rprintf(F," -x, --one-file-system       don't cross filesystem boundaries\n");
   rprintf(F," -B, --block-size=SIZE       force a fixed checksum block-size\n");
   rprintf(F," -e, --rsh=COMMAND           specify the remote shell to use\n");
@@ -376,7 +379,8 @@ void usage(enum logcode F)
   rprintf(F,"     --delay-updates         put all updated files into place at transfer's end\n");
   rprintf(F," -m, --prune-empty-dirs      prune empty directory chains from the file-list\n");
   rprintf(F,"     --numeric-ids           don't map uid/gid values by user/group name\n");
-  rprintf(F,"     --timeout=TIME          set I/O timeout in seconds\n");
+  rprintf(F,"     --timeout=SECONDS       set I/O timeout in seconds\n");
+  rprintf(F,"     --contimeout=SECONDS    set daemon connection timeout in seconds\n");
   rprintf(F," -I, --ignore-times          don't skip files that match in size and mod-time\n");
   rprintf(F,"     --size-only             skip files that match in size\n");
   rprintf(F,"     --modify-window=NUM     compare mod-times with reduced accuracy\n");
@@ -420,7 +424,7 @@ void usage(enum logcode F)
   rprintf(F,"     --read-batch=FILE       read a batched update from FILE\n");
   rprintf(F,"     --protocol=NUM          force an older protocol version to be used\n");
 #ifdef ICONV_OPTION
-  rprintf(F,"     --iconv=CONVERT_SPEC    request charset conversion of filesnames\n");
+  rprintf(F,"     --iconv=CONVERT_SPEC    request charset conversion of filenames\n");
 #endif
   rprintf(F," -4, --ipv4                  prefer IPv4\n");
   rprintf(F," -6, --ipv6                  prefer IPv6\n");
@@ -437,7 +441,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
       OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP,
       OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
       OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
-      OPT_NO_D, OPT_APPEND,
+      OPT_NO_D, OPT_APPEND, OPT_NO_ICONV,
       OPT_SERVER, OPT_REFUSED_BASE = 9000};
 
 static struct poptOption long_options[] = {
@@ -463,7 +467,6 @@ static struct poptOption long_options[] = {
   {"no-inc-recursive", 0,  POPT_ARG_VAL,    &allow_inc_recurse, 0, 0, 0 },
   {"i-r",              0,  POPT_ARG_VAL,    &allow_inc_recurse, 1, 0, 0 },
   {"no-i-r",           0,  POPT_ARG_VAL,    &allow_inc_recurse, 0, 0, 0 },
-  {"no-ir",            0,  POPT_ARG_VAL,    &allow_inc_recurse, 0, 0, 0 }, /* XXX remove soon */
   {"dirs",            'd', POPT_ARG_VAL,    &xfer_dirs, 2, 0, 0 },
   {"no-dirs",          0,  POPT_ARG_VAL,    &xfer_dirs, 0, 0, 0 },
   {"no-d",             0,  POPT_ARG_VAL,    &xfer_dirs, 0, 0, 0 },
@@ -603,11 +606,13 @@ static struct poptOption long_options[] = {
   {"no-numeric-ids",   0,  POPT_ARG_VAL,    &numeric_ids, 0, 0, 0 },
   {"timeout",          0,  POPT_ARG_INT,    &io_timeout, 0, 0, 0 },
   {"no-timeout",       0,  POPT_ARG_VAL,    &io_timeout, 0, 0, 0 },
+  {"contimeout",       0,  POPT_ARG_INT,    &connect_timeout, 0, 0, 0 },
   {"rsh",             'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
   {"rsync-path",       0,  POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
   {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
 #ifdef ICONV_OPTION
   {"iconv",            0,  POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
+  {"no-iconv",         0,  POPT_ARG_NONE,   0, OPT_NO_ICONV, 0, 0 },
 #endif
   {"ipv4",            '4', POPT_ARG_VAL,    &default_af_hint, AF_INET, 0, 0 },
   {"ipv6",            '6', POPT_ARG_VAL,    &default_af_hint, AF_INET6, 0, 0 },
@@ -754,6 +759,8 @@ static void set_refuse_options(char *bp)
                                                refused_progress = op->val;
                                        else if (wildmatch("inplace", op->longName))
                                                refused_inplace = op->val;
+                                       else if (wildmatch("no-iconv", op->longName))
+                                               refused_no_iconv = op->val;
                                        break;
                                }
                                if (!is_wild)
@@ -874,8 +881,11 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
 
        if (ref && *ref)
                set_refuse_options(ref);
-       if (am_daemon)
+       if (am_daemon) {
                set_refuse_options("log-file*");
+               if (!*lp_charset(module_id))
+                       set_refuse_options("iconv");
+       }
 
 #ifdef ICONV_OPTION
        if (!am_daemon && !protect_args && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
@@ -1001,17 +1011,20 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                case OPT_INCLUDE_FROM:
                        arg = poptGetOptArg(pc);
                        if (sanitize_paths)
-                               arg = sanitize_path(NULL, arg, NULL, 0, NULL);
+                               arg = sanitize_path(NULL, arg, NULL, 0);
                        if (server_filter_list.head) {
-                               char *cp = strdup(arg);
+                               int rej;
+                               char *dir, *cp = strdup(arg);
                                if (!cp)
                                        out_of_memory("parse_arguments");
                                if (!*cp)
                                        goto options_rejected;
-                               clean_fname(cp, 1);
-                               if (check_filter(&server_filter_list, cp, 0) < 0)
-                                       goto options_rejected;
+                               dir = cp + (*cp == '/' ? module_dirlen : 0);
+                               clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+                               rej = check_filter(&server_filter_list, dir, 0) < 0;
                                free(cp);
+                               if (rej)
+                                       goto options_rejected;
                        }
                        parse_filter_file(&filter_list, arg,
                                opt == OPT_INCLUDE_FROM ? MATCHFLG_INCLUDE : 0,
@@ -1120,6 +1133,10 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                        read_batch = 1;
                        break;
 
+               case OPT_NO_ICONV:
+                       iconv_opt = NULL;
+                       break;
+
                case OPT_MAX_SIZE:
                        if ((max_size = parse_size_arg(&max_size_arg, 'b')) <= 0) {
                                snprintf(err_buf, sizeof err_buf,
@@ -1198,9 +1215,9 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                        break;
 #else
                        /* FIXME: this should probably be ignored with a
-                        * warning and then countermeasures taken to
-                        * restrict group and other access in the presence
-                        * of any more restrictive ACLs, but this is safe
+                        * warning and then countermeasures taken to
+                        * restrict group and other access in the presence
+                        * of any more restrictive ACLs, but this is safe
                         * for now */
                        snprintf(err_buf,sizeof(err_buf),
                                  "ACLs are not supported on this %s\n",
@@ -1247,7 +1264,10 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                else
                        need_unsorted_flist = 1;
        }
-       setup_iconv();
+       if (refused_no_iconv && !iconv_opt) {
+               create_refuse_error(refused_no_iconv);
+               return 0;
+       }
 #endif
 
        if (protect_args == 1) {
@@ -1300,7 +1320,8 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                        batch_name = NULL;
                } else if (dry_run)
                        write_batch = 0;
-       }
+       } else if (write_batch < 0 && dry_run)
+               write_batch = 0;
        if (read_batch && files_from) {
                snprintf(err_buf, sizeof err_buf,
                        "--read-batch cannot be used with --files-from\n");
@@ -1393,26 +1414,30 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
        if (sanitize_paths) {
                int i;
                for (i = argc; i-- > 0; )
-                       argv[i] = sanitize_path(NULL, argv[i], "", 0, NULL);
+                       argv[i] = sanitize_path(NULL, argv[i], "", 0);
                if (tmpdir)
-                       tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, NULL);
+                       tmpdir = sanitize_path(NULL, tmpdir, NULL, 0);
                if (backup_dir)
-                       backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, NULL);
+                       backup_dir = sanitize_path(NULL, backup_dir, NULL, 0);
        }
        if (server_filter_list.head && !am_sender) {
                struct filter_list_struct *elp = &server_filter_list;
                if (tmpdir) {
+                       char *dir;
                        if (!*tmpdir)
                                goto options_rejected;
-                       clean_fname(tmpdir, 1);
-                       if (check_filter(elp, tmpdir, 1) < 0)
+                       dir = tmpdir + (*tmpdir == '/' ? module_dirlen : 0);
+                       clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+                       if (check_filter(elp, dir, 1) < 0)
                                goto options_rejected;
                }
                if (backup_dir) {
+                       char *dir;
                        if (!*backup_dir)
                                goto options_rejected;
-                       clean_fname(backup_dir, 1);
-                       if (check_filter(elp, backup_dir, 1) < 0)
+                       dir = backup_dir + (*backup_dir == '/' ? module_dirlen : 0);
+                       clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+                       if (check_filter(elp, dir, 1) < 0)
                                goto options_rejected;
                }
        }
@@ -1444,10 +1469,10 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                snprintf(err_buf, sizeof err_buf,
                        "--suffix cannot be a null string without --backup-dir\n");
                return 0;
-       } else if (make_backups && delete_mode && !delete_excluded && !am_server) {
-               snprintf(backup_dir_buf, sizeof backup_dir_buf,
+       } else if (make_backups && delete_mode && !delete_excluded && !am_server) {
+               snprintf(backup_dir_buf, sizeof backup_dir_buf,
                        "P *%s", backup_suffix);
-               parse_rule(&filter_list, backup_dir_buf, 0, 0);
+               parse_rule(&filter_list, backup_dir_buf, 0, 0);
        }
 
        if (make_backups && !backup_dir) {
@@ -1564,7 +1589,7 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                }
                if (partial_dir) {
                        if (*partial_dir)
-                               clean_fname(partial_dir, 1);
+                               clean_fname(partial_dir, CFN_COLLAPSE_DOT_DOT_DIRS);
                        if (!*partial_dir || strcmp(partial_dir, ".") == 0)
                                partial_dir = NULL;
                        if (!partial_dir && refused_partial) {
@@ -1601,12 +1626,14 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain)
                        }
                } else {
                        if (sanitize_paths)
-                               files_from = sanitize_path(NULL, files_from, NULL, 0, NULL);
+                               files_from = sanitize_path(NULL, files_from, NULL, 0);
                        if (server_filter_list.head) {
+                               char *dir;
                                if (!*files_from)
                                        goto options_rejected;
-                               clean_fname(files_from, 1);
-                               if (check_filter(&server_filter_list, files_from, 0) < 0)
+                               dir = files_from + (*files_from == '/' ? module_dirlen : 0);
+                               clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
+                               if (check_filter(&server_filter_list, dir, 0) < 0)
                                        goto options_rejected;
                        }
                        filesfrom_fd = open(files_from, O_RDONLY|O_BINARY);
@@ -1643,12 +1670,8 @@ void server_options(char **args, int *argc_p)
        static char argstr[64];
        int ac = *argc_p;
        char *arg;
-
        int i, x;
 
-       if (blocking_io == -1)
-               blocking_io = 0;
-
        /* This should always remain first on the server's command-line. */
        args[ac++] = "--server";
 
@@ -1680,7 +1703,8 @@ void server_options(char **args, int *argc_p)
                argstr[x++] = 'n';
        if (preserve_links)
                argstr[x++] = 'l';
-       if (xfer_dirs > (recurse || !delete_mode || !am_sender ? 1 : 0))
+       if ((list_only && !recurse) || xfer_dirs > 1
+        || (xfer_dirs && !recurse && delete_mode && am_sender))
                argstr[x++] = 'd';
        if (am_sender) {
                if (keep_dirlinks)
@@ -1750,20 +1774,20 @@ void server_options(char **args, int *argc_p)
        if (do_compression)
                argstr[x++] = 'z';
 
-       /* This is a complete hack - blame Rusty.  FIXME!
-        * This hack is only needed for older rsync versions that
-        * don't understand the --list-only option. */
-       if (list_only == 1 && !recurse)
-               argstr[x++] = 'r';
-
+       /* We make use of the -e option to let the server know about any
+        * pre-release protocol version && our allow_inc_recurse status. */
+       set_allow_inc_recurse();
 #if SUBPROTOCOL_VERSION != 0
-       /* If we're speaking a pre-release version of a protocol, we tell
-        * the server about this by (ab)using the -e option. */
        if (protocol_version == PROTOCOL_VERSION) {
                x += snprintf(argstr+x, sizeof argstr - x,
-                             "e%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
-       }
+                             "e%d.%d%s", PROTOCOL_VERSION, SUBPROTOCOL_VERSION,
+                             allow_inc_recurse ? "i" : "");
+       } else
 #endif
+       if (allow_inc_recurse) {
+               argstr[x++] = 'e';
+               argstr[x++] = 'i';
+       }
 
        argstr[x] = '\0';
 
@@ -1929,8 +1953,8 @@ void server_options(char **args, int *argc_p)
        if (numeric_ids)
                args[ac++] = "--numeric-ids";
 
-       if (!allow_inc_recurse)
-               args[ac++] = "--no-ir"; /* XXX change to --no-i-r soon */
+       if (use_qsort)
+               args[ac++] = "--use-qsort";
 
        if (am_sender) {
                if (ignore_existing)
@@ -1978,7 +2002,8 @@ void server_options(char **args, int *argc_p)
                if (!relative_paths)
                        args[ac++] = "--no-relative";
        }
-       if (relative_paths && !implied_dirs && !am_sender)
+       /* It's OK that this checks the upper-bound of the protocol_version. */
+       if (relative_paths && !implied_dirs && (!am_sender || protocol_version >= 30))
                args[ac++] = "--no-implied-dirs";
 
        if (fuzzy_basis && am_sender)