X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/44c26bf2a18a84c0f492c92c934060fe385096f7..ecc7623e7faf75f6ba3dd7b5a416c52e2346ac7d:/main.c diff --git a/main.c b/main.c index 3acdfd31..e94dd3f8 100644 --- a/main.c +++ b/main.c @@ -1,23 +1,25 @@ -/* -*- c-file-style: "linux" -*- - - Copyright (C) 1996-2001 by Andrew Tridgell - Copyright (C) Paul Mackerras 1996 - Copyright (C) 2001, 2002 by Martin Pool - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ +/* + * The startup routines, including main(), for rsync. + * + * Copyright (C) 1996-2001 Andrew Tridgell + * Copyright (C) 1996 Paul Mackerras + * Copyright (C) 2001, 2002 Martin Pool + * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ #include "rsync.h" #if defined CONFIG_LOCALE && defined HAVE_LOCALE_H @@ -47,6 +49,9 @@ extern int preserve_hard_links; extern int protocol_version; extern int recurse; extern int relative_paths; +extern int sanitize_paths; +extern int curr_dir_depth; +extern int module_id; extern int rsync_port; extern int whole_file; extern int read_batch; @@ -57,9 +62,12 @@ extern int filesfrom_fd; extern pid_t cleanup_child_pid; extern struct stats stats; extern char *filesfrom_host; +extern char *partial_dir; +extern char *basis_dir[]; extern char *rsync_path; extern char *shell_cmd; extern char *batch_name; +extern struct filter_list_struct server_filter_list; int local_server = 0; mode_t orig_umask = 0; @@ -189,7 +197,7 @@ static void handle_stats(int f) /* this is the client */ if (f < 0 && !am_sender) /* e.g. when we got an empty file list. */ - ; + ; else if (!am_sender) { /* Read the first two in opposite order because the meaning of * read/write swaps when switching from sender to receiver. */ @@ -465,8 +473,10 @@ static char *get_local_name(struct file_list *flist, char *dest_path) * it and use mode 1. If there is something other than a directory * at the destination path, we must be transferring one file * (anything at the destination will be overwritten). */ - if (do_stat(dest_path, &st) == 0) { + if (safe_stat(dest_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { + if (sanitize_paths) + die_on_unsafe_path(dest_path, 0); if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#1 %s failed", full_fname(dest_path)); @@ -474,6 +484,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path) } return NULL; } + if (sanitize_paths && S_ISLNK(st.st_mode)) + die_on_unsafe_path(dest_path, 0); if (flist->count > 1) { rprintf(FERROR, "ERROR: destination must be a directory when" @@ -519,6 +531,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path) return NULL; } + if (sanitize_paths) + die_on_unsafe_path(dest_path, 0); if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#2 %s failed", full_fname(dest_path)); @@ -539,6 +553,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path) dest_path = "/"; *cp = '\0'; + if (sanitize_paths) + die_on_unsafe_path(dest_path, 0); if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#3 %s failed", full_fname(dest_path)); @@ -596,10 +612,14 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) return; } - if (!relative_paths && !push_dir(dir)) { - rsyserr(FERROR, errno, "push_dir#3 %s failed", - full_fname(dir)); - exit_cleanup(RERR_FILESELECT); + if (!relative_paths) { + if (sanitize_paths) + die_on_unsafe_path(dir, 0); + if (!push_dir(dir)) { + rsyserr(FERROR, errno, "push_dir#3 %s failed", + full_fname(dir)); + exit_cleanup(RERR_FILESELECT); + } } argc--; argv++; @@ -783,6 +803,38 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) if (argc > 0) local_name = get_local_name(flist,argv[0]); + /* Now that we know what our destination directory turned out to be, + * we can sanitize the --link-/copy-/compare-dest args correctly. */ + if (sanitize_paths) { + char **dir; + for (dir = basis_dir; *dir; dir++) { + *dir = sanitize_path(NULL, *dir, NULL, curr_dir_depth, NULL); + die_on_unsafe_path(*dir, 0); + } + if (partial_dir) { + partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth, NULL); + /* A relative path gets this checked at every dir change. */ + if (*partial_dir == '/') + die_on_unsafe_path(partial_dir, 0); + } + } + if (server_filter_list.head) { + char **dir; + struct filter_list_struct *elp = &server_filter_list; + + for (dir = basis_dir; *dir; dir++) { + if (check_filter(elp, *dir, 1) < 0) + goto options_rejected; + } + if (partial_dir && *partial_dir == '/' + && check_filter(elp, partial_dir, 1) < 0) { + options_rejected: + rprintf(FERROR, + "Your options have been rejected by the server.\n"); + exit_cleanup(RERR_SYNTAX); + } + } + exit_code = do_recv(f_in,f_out,flist,local_name); exit_cleanup(exit_code); }