diff --git a/SOURCES/rsync-3.1.3-cve-2024-12087.patch b/SOURCES/rsync-3.1.3-cve-2024-12087.patch new file mode 100644 index 0000000..187f580 --- /dev/null +++ b/SOURCES/rsync-3.1.3-cve-2024-12087.patch @@ -0,0 +1,36 @@ +diff --git a/flist.c b/flist.c +index 464d556..087f9da 100644 +--- a/flist.c ++++ b/flist.c +@@ -2584,6 +2584,19 @@ struct file_list *recv_file_list(int f, int dir_ndx) + init_hard_links(); + #endif + ++ if (inc_recurse && dir_ndx >= 0) { ++ if (dir_ndx >= dir_flist->used) { ++ rprintf(FERROR_XFER, "rsync: refusing invalid dir_ndx %u >= %u\n", dir_ndx, dir_flist->used); ++ exit_cleanup(RERR_PROTOCOL); ++ } ++ struct file_struct *file = dir_flist->files[dir_ndx]; ++ if (file->flags & FLAG_GOT_DIR_FLIST) { ++ rprintf(FERROR_XFER, "rsync: refusing malicious duplicate flist for dir %d\n", dir_ndx); ++ exit_cleanup(RERR_PROTOCOL); ++ } ++ file->flags |= FLAG_GOT_DIR_FLIST; ++ } ++ + flist = flist_new(0, "recv_file_list"); + + if (inc_recurse) { +diff --git a/rsync.h b/rsync.h +index b357dad..bc9abac 100644 +--- a/rsync.h ++++ b/rsync.h +@@ -83,6 +83,7 @@ + #define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */ + #define FLAG_TIME_FAILED (1<<11)/* generator */ + #define FLAG_MOD_NSEC (1<<12) /* sender/receiver/generator */ ++#define FLAG_GOT_DIR_FLIST (1<<13)/* sender/receiver/generator - dir_flist only */ + + /* These flags are passed to functions but not stored. */ + diff --git a/SOURCES/rsync-3.1.3-cve-2024-12088.patch b/SOURCES/rsync-3.1.3-cve-2024-12088.patch new file mode 100644 index 0000000..412242e --- /dev/null +++ b/SOURCES/rsync-3.1.3-cve-2024-12088.patch @@ -0,0 +1,57 @@ +diff --git a/testsuite/unsafe-byname.test b/testsuite/unsafe-byname.test +index 75e7201..d2e318e 100644 +--- a/testsuite/unsafe-byname.test ++++ b/testsuite/unsafe-byname.test +@@ -40,7 +40,7 @@ test_unsafe ..//../dest from/dir unsafe + test_unsafe .. from/file safe + test_unsafe ../.. from/file unsafe + test_unsafe ..//.. from//file unsafe +-test_unsafe dir/.. from safe ++test_unsafe dir/.. from unsafe + test_unsafe dir/../.. from unsafe + test_unsafe dir/..//.. from unsafe + +diff --git a/util.c b/util.c +index da50ff1..f260d39 100644 +--- a/util.c ++++ b/util.c +@@ -1318,7 +1318,14 @@ int handle_partial_dir(const char *fname, int create) + * + * "src" is the top source directory currently applicable at the level + * of the referenced symlink. This is usually the symlink's full path +- * (including its name), as referenced from the root of the transfer. */ ++ * (including its name), as referenced from the root of the transfer. ++ * ++ * NOTE: this also rejects dest names with a .. component in other ++ * than the first component of the name ie. it rejects names such as ++ * a/b/../x/y. This needs to be done as the leading subpaths 'a' or ++ * 'b' could later be replaced with symlinks such as a link to '.' ++ * resulting in the link being transferred now becoming unsafe ++ */ + int unsafe_symlink(const char *dest, const char *src) + { + const char *name, *slash; +@@ -1328,6 +1335,23 @@ int unsafe_symlink(const char *dest, const char *src) + if (!dest || !*dest || *dest == '/') + return 1; + ++ // reject destinations with /../ in the name other than at the start of the name ++ const char *dest2 = dest; ++ while (strncmp(dest2, "../", 3) == 0) { ++ dest2 += 3; ++ while (*dest2 == '/') { ++ // allow for ..//..///../foo ++ dest2++; ++ } ++ } ++ if (strstr(dest2, "/../")) ++ return 1; ++ ++ // reject if the destination ends in /.. ++ const size_t dlen = strlen(dest); ++ if (dlen > 3 && strcmp(&dest[dlen-3], "/..") == 0) ++ return 1; ++ + /* find out what our safety margin is */ + for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) { + /* ".." segment starts the count over. "." segment is ignored. */ diff --git a/SOURCES/rsync-3.1.3-cve-2024-12747.patch b/SOURCES/rsync-3.1.3-cve-2024-12747.patch new file mode 100644 index 0000000..85bd696 --- /dev/null +++ b/SOURCES/rsync-3.1.3-cve-2024-12747.patch @@ -0,0 +1,141 @@ +diff --git a/checksum.c b/checksum.c +index cb21882..66e8089 100644 +--- a/checksum.c ++++ b/checksum.c +@@ -406,7 +406,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) + + memset(sum, 0, MAX_DIGEST_LEN); + +- fd = do_open(fname, O_RDONLY, 0); ++ fd = do_open_checklinks(fname); + if (fd == -1) + return; + +diff --git a/generator.c b/generator.c +index 110db28..3f13bb9 100644 +--- a/generator.c ++++ b/generator.c +@@ -1867,7 +1867,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, + } + + /* 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: +diff --git a/receiver.c b/receiver.c +index 8031b8f..edfbb21 100644 +--- a/receiver.c ++++ b/receiver.c +@@ -775,7 +775,7 @@ int recv_files(int f_in, int f_out, char *local_name) + 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]) { +diff --git a/sender.c b/sender.c +index 2bbff2f..a4d46c3 100644 +--- a/sender.c ++++ b/sender.c +@@ -350,7 +350,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 +diff --git a/syscall.c b/syscall.c +index 47c5ea5..c55ae5f 100644 +--- a/syscall.c ++++ b/syscall.c +@@ -45,6 +45,8 @@ extern int preallocate_files; + extern int preallocate_files; + extern int preserve_perms; + extern int preserve_executability; ++extern int copy_links; ++extern int copy_unsafe_links; + + #ifndef S_BLKSIZE + # if defined hpux || defined __hpux__ || defined __hpux +@@ -575,3 +575,21 @@ int do_open_nofollow(const char *pathname, int flags) + + return fd; + } ++ ++/* ++ 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); ++} +diff --git a/t_unsafe.c b/t_unsafe.c +index 010cac5..e10619a 100644 +--- a/t_unsafe.c ++++ b/t_unsafe.c +@@ -28,6 +28,9 @@ int am_root = 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]; + + int +diff --git a/tls.c b/tls.c +index e6b0708..858f8f1 100644 +--- a/tls.c ++++ b/tls.c +@@ -49,6 +49,9 @@ int list_only = 0; + int preserve_executability = 0; + int preallocate_files = 0; + int inplace = 0; ++int safe_symlinks = 0; ++int copy_links = 0; ++int copy_unsafe_links = 0; + + #ifdef SUPPORT_XATTRS + +diff --git a/trimslash.c b/trimslash.c +index 1ec928c..f2774cd 100644 +--- a/trimslash.c ++++ b/trimslash.c +@@ -26,6 +26,8 @@ int am_root = 0; + int preserve_executability = 0; + int preallocate_files = 0; + int inplace = 0; ++int copy_links = 0; ++int copy_unsafe_links = 0; + + int + main(int argc, char **argv) +diff --git a/util.c b/util.c +index f260d39..d84bc41 100644 +--- a/util.c ++++ b/util.c +@@ -365,7 +365,7 @@ int copy_file(const char *source, const char *dest, int tmpfilefd, mode_t mode) + 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; diff --git a/SPECS/rsync.spec b/SPECS/rsync.spec index 848041c..d493056 100644 --- a/SPECS/rsync.spec +++ b/SPECS/rsync.spec @@ -9,7 +9,7 @@ Summary: A program for synchronizing files over a network Name: rsync Version: 3.1.3 -Release: 20%{?dist} +Release: 21%{?dist} Group: Applications/Internet URL: http://rsync.samba.org/ @@ -43,6 +43,9 @@ Patch12: rsync-3.1.3-cve-2022-37434.patch Patch13: rsync-3.1.3-filtering-rules.patch Patch14: rsync-3.1.3-missing-xattr-filter.patch Patch15: rsync-3.1.3-cve-2024-12085.patch +Patch16: rsync-3.1.3-cve-2024-12087.patch +Patch17: rsync-3.1.3-cve-2024-12088.patch +Patch18: rsync-3.1.3-cve-2024-12747.patch %description Rsync uses a reliable algorithm to bring remote and host files into @@ -96,6 +99,9 @@ patch -p1 -i patches/copy-devices.diff %patch13 -p1 -b .filtering-rules %patch14 -p1 -b .xattr-filter %patch15 -p1 -b .cve-2024-12085 +%patch16 -p1 -b .cve-2024-12087 +%patch17 -p1 -b .cve-2024-12088 +%patch18 -p1 -b .cve-2024-12747 %build %configure @@ -142,7 +148,13 @@ chmod -x support/* %systemd_postun_with_restart rsyncd.service %changelog -* Fri Jan 03 2025 Michal Ruprich - 3.1.3-20 +* Tue Feb 04 2025 Michal Ruprich - 3.1.3-21 +- Resolves: RHEL-70207 - Path traversal vulnerability in rsync + +* Mon Feb 03 2025 Michal Ruprich - 3.1.3-20 +- Resolves: RHEL-70207 - Path traversal vulnerability in rsync +- Resolves: RHEL-70209 - --safe-links option bypass leads to path traversal +- Resolves: RHEL-72502 - Race Condition in rsync Handling Symbolic Links - Resolves: RHEL-70157 - Info Leak via Uninitialized Stack Contents * Wed Nov 02 2022 Michal Ruprich - 3.1.3-19.1