From f45f48055e548851bc7230f454dfeba139be6c04 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 18 Dec 2024 08:59:42 +1100 Subject: [PATCH] fixed symlink race condition in sender when we open a file that we don't expect to be a symlink use O_NOFOLLOW to prevent a race condition where an attacker could change a file between being a normal file and a symlink --- checksum.c | 2 +- flist.c | 2 +- generator.c | 4 ++-- receiver.c | 2 +- sender.c | 2 +- syscall.c | 20 ++++++++++++++++++++ t_unsafe.c | 3 +++ tls.c | 3 +++ trimslash.c | 2 ++ util1.c | 2 +- 10 files changed, 35 insertions(+), 7 deletions(-) Index: rsync-3.1.3/checksum.c =================================================================== --- rsync-3.1.3.orig/checksum.c +++ rsync-3.1.3/checksum.c @@ -200,7 +200,7 @@ void file_checksum(const char *fname, co memset(sum, 0, MAX_DIGEST_LEN); - fd = do_open(fname, O_RDONLY, 0); + fd = do_open_checklinks(fname); if (fd == -1) return; Index: rsync-3.1.3/generator.c =================================================================== --- rsync-3.1.3.orig/generator.c +++ rsync-3.1.3/generator.c @@ -1828,7 +1828,7 @@ static void recv_generator(char *fname, } /* open the file */ - if ((fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) { + if ((fd = do_open_checklinks(fnamecmp)) < 0) { rsyserr(FERROR, errno, "failed to open %s, continuing", full_fname(fnamecmp)); pretend_missing: Index: rsync-3.1.3/receiver.c =================================================================== --- rsync-3.1.3.orig/receiver.c +++ rsync-3.1.3/receiver.c @@ -758,7 +758,7 @@ int recv_files(int f_in, int f_out, char if (fd1 == -1 && protocol_version < 29) { if (fnamecmp != fname) { fnamecmp = fname; - fd1 = do_open(fnamecmp, O_RDONLY, 0); + fd1 = do_open_nofollow(fnamecmp, O_RDONLY); } if (fd1 == -1 && basis_dir[0]) { Index: rsync-3.1.3/sender.c =================================================================== --- rsync-3.1.3.orig/sender.c +++ rsync-3.1.3/sender.c @@ -335,7 +335,7 @@ void send_files(int f_in, int f_out) exit_cleanup(RERR_PROTOCOL); } - fd = do_open(fname, O_RDONLY, 0); + fd = do_open_checklinks(fname); if (fd == -1) { if (errno == ENOENT) { enum logcode c = am_daemon Index: rsync-3.1.3/syscall.c =================================================================== --- rsync-3.1.3.orig/syscall.c +++ rsync-3.1.3/syscall.c @@ -45,6 +45,8 @@ extern int preallocate_files; extern int preserve_perms; extern int preserve_executability; extern int noatime; +extern int copy_links; +extern int copy_unsafe_links; #ifndef S_BLKSIZE # if defined hpux || defined __hpux__ || defined __hpux @@ -659,3 +661,21 @@ cleanup: return retfd; #endif // O_NOFOLLOW, O_DIRECTORY } + +/* + varient of do_open/do_open_nofollow which does do_open() if the + copy_links or copy_unsafe_links options are set and does + do_open_nofollow() otherwise + + This is used to prevent a race condition where an attacker could be + switching a file between being a symlink and being a normal file + + The open is always done with O_RDONLY flags + */ +int do_open_checklinks(const char *pathname) +{ + if (copy_links || copy_unsafe_links) { + return do_open(pathname, O_RDONLY, 0); + } + return do_open_nofollow(pathname, O_RDONLY); +} Index: rsync-3.1.3/t_unsafe.c =================================================================== --- rsync-3.1.3.orig/t_unsafe.c +++ rsync-3.1.3/t_unsafe.c @@ -31,6 +31,9 @@ int list_only = 0; int human_readable = 0; int preserve_perms = 0; int preserve_executability = 0; +int copy_links = 0; +int copy_unsafe_links = 0; + short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG]; /* This is to make syscall.o shut up. */ Index: rsync-3.1.3/tls.c =================================================================== --- rsync-3.1.3.orig/tls.c +++ rsync-3.1.3/tls.c @@ -54,6 +54,9 @@ int preserve_executability = 0; // int preallocate_files = 0; // int inplace = 0; int noatime = 0; +int safe_symlinks = 0; +int copy_links = 0; +int copy_unsafe_links = 0; #ifdef SUPPORT_XATTRS Index: rsync-3.1.3/trimslash.c =================================================================== --- rsync-3.1.3.orig/trimslash.c +++ rsync-3.1.3/trimslash.c @@ -31,6 +31,8 @@ int preserve_executability = 0; // int preallocate_files = 0; // int inplace = 0; int noatime = 0; +int copy_links = 0; +int copy_unsafe_links = 0; int main(int argc, char **argv) Index: rsync-3.1.3/util.c =================================================================== --- rsync-3.1.3.orig/util.c +++ rsync-3.1.3/util.c @@ -462,7 +462,7 @@ int copy_file(const char *source, const int len; /* Number of bytes read into `buf'. */ OFF_T prealloc_len = 0, offset = 0; - if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { + if ((ifd = do_open_nofollow(source, O_RDONLY)) < 0) { int save_errno = errno; rsyserr(FERROR_XFER, errno, "open %s", full_fname(source)); errno = save_errno;