Resolves: RHEL-70158 - Info Leak via Uninitialized Stack Contents

Resolves: RHEL-70208 - Path traversal vulnerability in rsync
Resolves: RHEL-70210 - --safe-links option bypass leads to path traversal
Resolves: RHEL-71657 - Race Condition in rsync Handling Symbolic Links
This commit is contained in:
Michal Ruprich 2025-01-29 15:08:29 +01:00
parent cd54d1b96b
commit 803ba84686
5 changed files with 298 additions and 3 deletions

View File

@ -0,0 +1,14 @@
diff --git a/match.c b/match.c
index 36e78ed..dfd6af2 100644
--- a/match.c
+++ b/match.c
@@ -147,6 +147,9 @@ static void hash_search(int f,struct sum_struct *s,
int more;
schar *map;
+ // prevent possible memory leaks
+ memset(sum2, 0, sizeof sum2);
+
/* want_i is used to encourage adjacent matches, allowing the RLL
* coding of the output to work more efficiently. */
want_i = 0;

View File

@ -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");
flist_expand(flist, FLIST_START_LARGE);
diff --git a/rsync.h b/rsync.h
index f8bd024..fbaf312 100644
--- a/rsync.h
+++ b/rsync.h
@@ -92,6 +92,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. */

View File

@ -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/util1.c b/util1.c
index da50ff1..f260d39 100644
--- a/util1.c
+++ b/util1.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. */

View File

@ -0,0 +1,163 @@
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/flist.c b/flist.c
index 087f9da..1783253 100644
--- a/flist.c
+++ b/flist.c
@@ -1390,7 +1390,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
if (st.st_size == 0) {
- int fd = do_open(fname, O_RDONLY, 0);
+ int fd = do_open_checklinks(fname);
if (fd >= 0) {
st.st_size = get_device_size(fd, fname);
close(fd);
diff --git a/generator.c b/generator.c
index 110db28..3f13bb9 100644
--- a/generator.c
+++ b/generator.c
@@ -1798,7 +1798,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
if (write_devices && IS_DEVICE(sx.st.st_mode) && sx.st.st_size == 0) {
/* This early open into fd skips the regular open below. */
- if ((fd = do_open(fnamecmp, O_RDONLY, 0)) >= 0)
+ if ((fd = do_open_nofollow(fnamecmp, O_RDONLY)) >= 0)
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
}
@@ -1867,7 +1867,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
/* open the file */
- if (fd < 0 && (fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
+ if (fd < 0 && (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 (fnamecmp != fname) {
fnamecmp = fname;
fnamecmp_type = 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 && protocol_version < 28 ? FERROR : FWARNING;
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 preserve_perms;
extern int preserve_executability;
extern int open_noatime;
+extern int copy_links;
+extern int copy_unsafe_links;
#ifndef S_BLKSIZE
# if defined hpux || defined __hpux__ || defined __hpux
@@ -714,3 +714,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 am_sender = 1;
int read_only = 0;
int list_only = 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 link_times = 0;
int link_owner = 0;
int nsec_times = 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 am_sender = 1;
int read_only = 1;
int list_only = 0;
+int copy_links = 0;
+int copy_unsafe_links = 0;
int
main(int argc, char **argv)
diff --git a/util1.c b/util1.c
index f260d39..d84bc41 100644
--- a/util1.c
+++ b/util1.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;

View File

@ -10,7 +10,7 @@
Summary: A program for synchronizing files over a network Summary: A program for synchronizing files over a network
Name: rsync Name: rsync
Version: 3.2.5 Version: 3.2.5
Release: 1%{?dist} Release: 2%{?dist}
URL: https://rsync.samba.org/ URL: https://rsync.samba.org/
Source0: https://download.samba.org/pub/rsync/src/rsync-%{version}%{?prerelease}.tar.gz Source0: https://download.samba.org/pub/rsync/src/rsync-%{version}%{?prerelease}.tar.gz
@ -42,6 +42,10 @@ Patch1: rsync-3.2.2-runtests.patch
Patch2: rsync-3.2.5-rrsync-man.patch Patch2: rsync-3.2.5-rrsync-man.patch
#A couple of fixes for the new filtering code #A couple of fixes for the new filtering code
Patch3: rsync-3.2.3-filtering-rules.patch Patch3: rsync-3.2.3-filtering-rules.patch
Patch4: rsync-3.2.5-cve-2024-12085.patch
Patch5: rsync-3.2.5-cve-2024-12087.patch
Patch6: rsync-3.2.5-cve-2024-12088.patch
Patch7: rsync-3.2.5-cve-2024-12747.patch
%description %description
Rsync uses a reliable algorithm to bring remote and host files into Rsync uses a reliable algorithm to bring remote and host files into
@ -61,6 +65,15 @@ Requires: %{name} = %{version}-%{release}
Rsync can be used to offer read only access to anonymous clients. This Rsync can be used to offer read only access to anonymous clients. This
package provides the anonymous rsync service. package provides the anonymous rsync service.
%package rrsync
Summary: A script to setup restricted rsync users via ssh logins
BuildArch: noarch
Requires: %{name} = %{version}-%{release}
Requires: %{__python3}
%description rrsync
This subpackage provides rrsync script and its manpage. rrsync
may be used to setup a restricted rsync users via ssh logins.
%prep %prep
# TAG: for pre versions use # TAG: for pre versions use
@ -75,6 +88,10 @@ package provides the anonymous rsync service.
%patch1 -p1 -b .runtests %patch1 -p1 -b .runtests
%patch2 -p1 -b .rrsync-man %patch2 -p1 -b .rrsync-man
%patch3 -p1 -b .filtering-rules %patch3 -p1 -b .filtering-rules
%patch4 -p1 -b .cve-2024-12085
%patch5 -p1 -b .cve-2024-12087
%patch6 -p1 -b .cve-2024-12088
%patch7 -p1 -b .cve-2024-12747
%build %build
%configure --disable-xxhash --with-rrsync %configure --disable-xxhash --with-rrsync
@ -99,10 +116,8 @@ install -D -m644 %{SOURCE6} $RPM_BUILD_ROOT/%{_unitdir}/rsyncd@.service
%license COPYING %license COPYING
%doc support/ tech_report.tex %doc support/ tech_report.tex
%{_bindir}/%{name} %{_bindir}/%{name}
%{_bindir}/r%{name}
%{_bindir}/%{name}-ssl %{_bindir}/%{name}-ssl
%{_mandir}/man1/%{name}.1* %{_mandir}/man1/%{name}.1*
%{_mandir}/man1/r%{name}.1*
%{_mandir}/man1/%{name}-ssl.1* %{_mandir}/man1/%{name}-ssl.1*
%{_mandir}/man5/rsyncd.conf.5* %{_mandir}/man5/rsyncd.conf.5*
%config(noreplace) %{_sysconfdir}/rsyncd.conf %config(noreplace) %{_sysconfdir}/rsyncd.conf
@ -113,6 +128,10 @@ install -D -m644 %{SOURCE6} $RPM_BUILD_ROOT/%{_unitdir}/rsyncd@.service
%{_unitdir}/rsyncd.service %{_unitdir}/rsyncd.service
%{_unitdir}/rsyncd@.service %{_unitdir}/rsyncd@.service
%files rrsync
%{_bindir}/r%{name}
%{_mandir}/man1/r%{name}.1*
%post daemon %post daemon
%systemd_post rsyncd.service %systemd_post rsyncd.service
@ -123,6 +142,12 @@ install -D -m644 %{SOURCE6} $RPM_BUILD_ROOT/%{_unitdir}/rsyncd@.service
%systemd_postun_with_restart rsyncd.service %systemd_postun_with_restart rsyncd.service
%changelog %changelog
* Wed Jan 29 2025 Michal Ruprich <mruprich@redhat.com> - 3.2.5-2
- Resolves: RHEL-70158 - Info Leak via Uninitialized Stack Contents
- Resolves: RHEL-70208 - Path traversal vulnerability in rsync
- Resolves: RHEL-70210 - --safe-links option bypass leads to path traversal
- Resolves: RHEL-71657 - Race Condition in rsync Handling Symbolic Links
* Mon Dec 09 2024 Michal Ruprich <mruprich@redhat.com> - 3.2.3-21 * Mon Dec 09 2024 Michal Ruprich <mruprich@redhat.com> - 3.2.3-21
- Resolves: RHEL-70265 - Rebase rsync to 3.2.5 - Resolves: RHEL-70265 - Rebase rsync to 3.2.5
- Resolves: RHEL-67142 - Wrong progress reported by rsync when using copy-devices - Resolves: RHEL-67142 - Wrong progress reported by rsync when using copy-devices