Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
d787ebe76e
!60 [sync] PR-55: fix CVE-2024-12085, CVE-2024-12086, CVE-2024-12087, CVE-2024-12088, CVE-2024-12747
From: @openeuler-sync-bot 
Reviewed-by: @boluo56, @shenyangyang01 
Signed-off-by: @shenyangyang01
2025-01-16 11:57:22 +00:00
Funda Wang
5a96da060a fix CVE-2024-12085, CVE-2024-12086, CVE-2024-12087, CVE-2024-12088, CVE-2024-12747
(cherry picked from commit 286c0eafe4a805bb9d20a06591e15ece7256600b)
2025-01-16 17:29:05 +08:00
openeuler-ci-bot
12299c42ec
!50 [sync] PR-49: backport patch from upstream
From: @openeuler-sync-bot 
Reviewed-by: @gaoruoshu 
Signed-off-by: @gaoruoshu
2024-10-09 09:24:23 +00:00
zhoupengcheng
e00fad3924 backport patch from upstream
(cherry picked from commit 7697bd2dcfb8a12e2c57c8bfde40084d23d8622b)
2024-10-09 16:54:08 +08:00
openeuler-ci-bot
aee743c41c
!35 [sync] PR-34: Restrict the doc permission in rsync to 644.
From: @openeuler-sync-bot 
Reviewed-by: @lvying6 
Signed-off-by: @lvying6
2023-06-15 07:49:55 +00:00
ZhouPengcheng
06662961ab Restrict the doc permission in rsync to 644.
(cherry picked from commit af229ea1022a04cc285e7a0620f5de113546cff5)
2023-06-15 14:56:24 +08:00
openeuler-ci-bot
1c1b0eadff
!30 Update version to 3.2.5
From: @zhoupengcheng11 
Reviewed-by: @xiezhipeng1 
Signed-off-by: @xiezhipeng1
2022-11-07 13:30:10 +00:00
ZhouPengcheng
79d4257526 Update version to 3.2.5 2022-11-07 18:50:34 +08:00
openeuler-ci-bot
e4eff5d75a
!28 [sync] PR-26: fix CVE-2022-29154
From: @openeuler-sync-bot 
Reviewed-by: @lvying6 
Signed-off-by: @lvying6
2022-08-26 08:24:25 +00:00
panxiaohe
8fc6e5d84d fix CVE-2022-29154
(cherry picked from commit 8cf43bd648a493aca07baefdd0c3738f8041b111)
2022-08-26 16:02:31 +08:00
20 changed files with 1449 additions and 170 deletions

View File

@ -1,26 +0,0 @@
From 9e2921fce8c518e370c324407d35bc83ba12f2d5 Mon Sep 17 00:00:00 2001
From: Wayne Davison <wayne@opencoder.net>
Date: Mon, 8 Aug 2022 20:05:10 -0700
Subject: [PATCH] A fix for the zlib fix.
---
zlib/inflate.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/zlib/inflate.c b/zlib/inflate.c
index d15132ea..e9840b67 100644
--- a/zlib/inflate.c
+++ b/zlib/inflate.c
@@ -739,10 +739,10 @@ int flush;
copy = state->length;
if (copy > have) copy = have;
if (copy) {
- len = state->head->extra_len - state->length;
if (state->head != Z_NULL &&
state->head->extra != Z_NULL &&
- len < state->head->extra_max) {
+ (len = state->head->extra_len - state->length) <
+ state->head->extra_max) {
zmemcpy(state->head->extra + len, next,
len + copy > state->head->extra_max ?
state->head->extra_max - len : copy);

View File

@ -1,23 +0,0 @@
From c3f7414c450faaf6a8281cc4a4403529aeb7d859 Mon Sep 17 00:00:00 2001
From: Matt McCutchen <matt@mattmccutchen.net>
Date: Wed, 26 Aug 2020 12:16:08 -0400
Subject: [PATCH] rsync-ssl: Verify the hostname in the certificate when using
openssl.
---
rsync-ssl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rsync-ssl b/rsync-ssl
index 8101975a..46701af1 100755
--- a/rsync-ssl
+++ b/rsync-ssl
@@ -129,7 +129,7 @@ function rsync_ssl_helper {
fi
if [[ $RSYNC_SSL_TYPE == openssl ]]; then
- exec $RSYNC_SSL_OPENSSL s_client $caopt $certopt -quiet -verify_quiet -servername $hostname -connect $hostname:$port
+ exec $RSYNC_SSL_OPENSSL s_client $caopt $certopt -quiet -verify_quiet -servername $hostname -verify_hostname $hostname -connect $hostname:$port
elif [[ $RSYNC_SSL_TYPE == gnutls ]]; then
exec $RSYNC_SSL_GNUTLS --logfile=/dev/null $gnutls_cert_opt $gnutls_opts $hostname:$port
else

View File

@ -1,28 +0,0 @@
From 788f11ea6afeb96f0d84f140192165a1ca12ade4 Mon Sep 17 00:00:00 2001
From: Wayne Davison <wayne@opencoder.net>
Date: Sun, 7 Aug 2022 09:27:57 -0700
Subject: [PATCH] Fix zlib bug with a large gzip header extra field
From zlib commit eff308af425b67093bab25f80f1ae950166bece1.
Fixes CVE-2022-37434.
---
zlib/inflate.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/zlib/inflate.c b/zlib/inflate.c
index e43abd9e0..d15132eaf 100644
--- a/zlib/inflate.c
+++ b/zlib/inflate.c
@@ -739,9 +739,10 @@ int flush;
copy = state->length;
if (copy > have) copy = have;
if (copy) {
+ len = state->head->extra_len - state->length;
if (state->head != Z_NULL &&
- state->head->extra != Z_NULL) {
- len = state->head->extra_len - state->length;
+ state->head->extra != Z_NULL &&
+ len < state->head->extra_max) {
zmemcpy(state->head->extra + len, next,
len + copy > state->head->extra_max ?
state->head->extra_max - len : copy);

View File

@ -0,0 +1,27 @@
From cf620065502f065d4ea44f5df4f81295a738aa21 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Thu, 14 Nov 2024 09:57:08 +1100
Subject: [PATCH] prevent information leak off the stack
prevent leak of uninitialised stack data in hash_search
---
match.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/match.c b/match.c
index 36e78ed2..dfd6af2c 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;
--
2.34.1

View File

@ -0,0 +1,37 @@
From 3feb8669d875d03c9ceb82e208ef40ddda8eb908 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Sat, 23 Nov 2024 11:08:03 +1100
Subject: [PATCH 1/4] refuse fuzzy options when fuzzy not selected
this prevents a malicious server providing a file to compare to when
the user has not given the fuzzy option
---
receiver.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/receiver.c b/receiver.c
index 6b4b369e..2d7f6033 100644
--- a/receiver.c
+++ b/receiver.c
@@ -67,6 +67,7 @@
extern struct file_list *cur_flist, *first_flist, *dir_flist;
extern filter_rule_list daemon_filter_list;
extern OFF_T preallocated_len;
+extern int fuzzy_basis;
static struct bitbag *delayed_bits = NULL;
static int phase = 0, redoing = 0;
@@ -716,6 +717,10 @@
fnamecmp = get_backup_name(fname);
break;
case FNAMECMP_FUZZY:
+ if (fuzzy_basis == 0) {
+ rprintf(FERROR_XFER, "rsync: refusing malicious fuzzy operation for %s\n", xname);
+ exit_cleanup(RERR_PROTOCOL);
+ }
if (file->dirname) {
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
fnamecmp = fnamecmpbuf;
--
2.34.1

View File

@ -0,0 +1,103 @@
From 33385aefe4773e7a3982d41995681eb079c92d12 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Sat, 23 Nov 2024 12:26:10 +1100
Subject: [PATCH 2/4] added secure_relative_open()
this is an open that enforces no symlink following for all path
components in a relative path
---
syscall.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/syscall.c b/syscall.c
index d92074aa..a4b7f542 100644
--- a/syscall.c
+++ b/syscall.c
@@ -33,6 +33,8 @@
#include <sys/syscall.h>
#endif
+#include "ifuncs.h"
+
extern int dry_run;
extern int am_root;
extern int am_sender;
@@ -712,3 +714,75 @@ int do_open_nofollow(const char *pathname, int flags)
return fd;
}
+
+/*
+ open a file relative to a base directory. The basedir can be NULL,
+ in which case the current working directory is used. The relpath
+ must be a relative path, and the relpath must not contain any
+ elements in the path which follow symlinks (ie. like O_NOFOLLOW, but
+ applies to all path components, not just the last component)
+*/
+int secure_relative_open(const char *basedir, const char *relpath, int flags, mode_t mode)
+{
+ if (!relpath || relpath[0] == '/') {
+ // must be a relative path
+ errno = EINVAL;
+ return -1;
+ }
+
+#if !defined(O_NOFOLLOW) || !defined(O_DIRECTORY)
+ // really old system, all we can do is live with the risks
+ if (!basedir) {
+ return open(relpath, flags, mode);
+ }
+ char fullpath[MAXPATHLEN];
+ pathjoin(fullpath, sizeof fullpath, basedir, relpath);
+ return open(fullpath, flags, mode);
+#else
+ int dirfd = AT_FDCWD;
+ if (basedir != NULL) {
+ dirfd = openat(AT_FDCWD, basedir, O_RDONLY | O_DIRECTORY);
+ if (dirfd == -1) {
+ return -1;
+ }
+ }
+ int retfd = -1;
+
+ char *path_copy = my_strdup(relpath, __FILE__, __LINE__);
+ if (!path_copy) {
+ return -1;
+ }
+
+ for (const char *part = strtok(path_copy, "/");
+ part != NULL;
+ part = strtok(NULL, "/"))
+ {
+ int next_fd = openat(dirfd, part, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ if (next_fd == -1 && errno == ENOTDIR) {
+ if (strtok(NULL, "/") != NULL) {
+ // this is not the last component of the path
+ errno = ELOOP;
+ goto cleanup;
+ }
+ // this could be the last component of the path, try as a file
+ retfd = openat(dirfd, part, flags | O_NOFOLLOW, mode);
+ goto cleanup;
+ }
+ if (next_fd == -1) {
+ goto cleanup;
+ }
+ if (dirfd != AT_FDCWD) close(dirfd);
+ dirfd = next_fd;
+ }
+
+ // the path must be a directory
+ errno = EINVAL;
+
+cleanup:
+ free(path_copy);
+ if (dirfd != AT_FDCWD) {
+ close(dirfd);
+ }
+ return retfd;
+#endif // O_NOFOLLOW, O_DIRECTORY
+}
--
2.34.1

View File

@ -0,0 +1,103 @@
From e59ef9939d3f0ccc8f9bab51442989a81be0c914 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Sat, 23 Nov 2024 12:28:13 +1100
Subject: [PATCH 3/4] receiver: use secure_relative_open() for basis file
this prevents attacks where the basis file is manipulated by a
malicious sender to gain information about files outside the
destination tree
---
receiver.c | 42 ++++++++++++++++++++++++++----------------
1 file changed, 26 insertions(+), 16 deletions(-)
diff --git a/receiver.c b/receiver.c
index 2d7f6033..8031b8f4 100644
--- a/receiver.c
+++ b/receiver.c
@@ -552,6 +552,8 @@ int recv_files(int f_in, int f_out, char *local_name)
progress_init();
while (1) {
+ const char *basedir = NULL;
+
cleanup_disable();
/* This call also sets cur_flist. */
@@ -722,27 +724,29 @@ int recv_files(int f_in, int f_out, char *local_name)
exit_cleanup(RERR_PROTOCOL);
}
if (file->dirname) {
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
- fnamecmp = fnamecmpbuf;
- } else
- fnamecmp = xname;
+ basedir = file->dirname;
+ }
+ fnamecmp = xname;
break;
default:
if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
fnamecmp_type -= FNAMECMP_FUZZY + 1;
if (file->dirname) {
- stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
- basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
- } else
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], file->dirname);
+ basedir = fnamecmpbuf;
+ } else {
+ basedir = basis_dir[fnamecmp_type];
+ }
+ fnamecmp = xname;
} else if (fnamecmp_type >= basis_dir_cnt) {
rprintf(FERROR,
"invalid basis_dir index: %d.\n",
fnamecmp_type);
exit_cleanup(RERR_PROTOCOL);
- } else
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
- fnamecmp = fnamecmpbuf;
+ } else {
+ basedir = basis_dir[fnamecmp_type];
+ fnamecmp = fname;
+ }
break;
}
if (!fnamecmp || (daemon_filter_list.head
@@ -765,7 +769,7 @@ int recv_files(int f_in, int f_out, char *local_name)
}
/* open the file */
- fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
if (fd1 == -1 && protocol_version < 29) {
if (fnamecmp != fname) {
@@ -776,14 +780,20 @@ int recv_files(int f_in, int f_out, char *local_name)
if (fd1 == -1 && basis_dir[0]) {
/* pre-29 allowed only one alternate basis */
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
- basis_dir[0], fname);
- fnamecmp = fnamecmpbuf;
+ basedir = basis_dir[0];
+ fnamecmp = fname;
fnamecmp_type = FNAMECMP_BASIS_DIR_LOW;
- fd1 = do_open(fnamecmp, O_RDONLY, 0);
+ fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
}
}
+ if (basedir) {
+ // for the following code we need the full
+ // path name as a single string
+ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basedir, fnamecmp);
+ fnamecmp = fnamecmpbuf;
+ }
+
one_inplace = inplace_partial && fnamecmp_type == FNAMECMP_PARTIAL_DIR;
updating_basis_or_equiv = one_inplace
|| (inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP));
--
2.34.1

View File

@ -0,0 +1,37 @@
From c78e53edb802d04f7e4e070fe8314f2544749e7a Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Tue, 26 Nov 2024 09:16:31 +1100
Subject: [PATCH 4/4] disallow ../ elements in relpath for secure_relative_open
---
syscall.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/syscall.c b/syscall.c
index a4b7f542..47c5ea57 100644
--- a/syscall.c
+++ b/syscall.c
@@ -721,6 +721,8 @@ int do_open_nofollow(const char *pathname, int flags)
must be a relative path, and the relpath must not contain any
elements in the path which follow symlinks (ie. like O_NOFOLLOW, but
applies to all path components, not just the last component)
+
+ The relpath must also not contain any ../ elements in the path
*/
int secure_relative_open(const char *basedir, const char *relpath, int flags, mode_t mode)
{
@@ -729,6 +731,11 @@ int secure_relative_open(const char *basedir, const char *relpath, int flags, mo
errno = EINVAL;
return -1;
}
+ if (strncmp(relpath, "../", 3) == 0 || strstr(relpath, "/../")) {
+ // no ../ elements allowed in the relpath
+ errno = EINVAL;
+ return -1;
+ }
#if !defined(O_NOFOLLOW) || !defined(O_DIRECTORY)
// really old system, all we can do is live with the risks
--
2.34.1

View File

@ -0,0 +1,45 @@
From 0ebc19ee486a8e928a68d8f98d07d40f176770aa Mon Sep 17 00:00:00 2001
From: Wayne Davison <wayne@opencoder.net>
Date: Thu, 14 Nov 2024 15:46:50 -0800
Subject: [PATCH 1/2] Refuse a duplicate dirlist.
---
flist.c | 9 +++++++++
rsync.h | 1 +
2 files changed, 10 insertions(+)
diff --git a/flist.c b/flist.c
index 464d556e..847b1054 100644
--- a/flist.c
+++ b/flist.c
@@ -2584,6 +2584,15 @@ struct file_list *recv_file_list(int f, int dir_ndx)
init_hard_links();
#endif
+ if (inc_recurse && dir_ndx >= 0) {
+ 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 0f9e277f..b9a7101a 100644
--- a/rsync.h
+++ b/rsync.h
@@ -84,6 +84,7 @@
#define FLAG_DUPLICATE (1<<4) /* sender */
#define FLAG_MISSING_DIR (1<<4) /* generator */
#define FLAG_HLINKED (1<<5) /* receiver/generator (checked on all types) */
+#define FLAG_GOT_DIR_FLIST (1<<5)/* sender/receiver/generator - dir_flist only */
#define FLAG_HLINK_FIRST (1<<6) /* receiver/generator (w/FLAG_HLINKED) */
#define FLAG_IMPLIED_DIR (1<<6) /* sender/receiver/generator (dirs only) */
#define FLAG_HLINK_LAST (1<<7) /* receiver/generator */
--
2.34.1

View File

@ -0,0 +1,27 @@
From b3e16be18d582dac1513c0a932d146b36e867b1b Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Tue, 26 Nov 2024 16:12:45 +1100
Subject: [PATCH 2/2] range check dir_ndx before use
---
flist.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/flist.c b/flist.c
index 847b1054..087f9da6 100644
--- a/flist.c
+++ b/flist.c
@@ -2585,6 +2585,10 @@ struct file_list *recv_file_list(int f, int dir_ndx)
#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);
--
2.34.1

View File

@ -0,0 +1,136 @@
From 535f8f816539ba681ef0f12015d2cb587ae61b6d Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Sat, 23 Nov 2024 15:15:53 +1100
Subject: [PATCH] make --safe-links stricter
when --safe-links is used also reject links where a '../' component is
included in the destination as other than the leading part of the
filename
---
testsuite/safe-links.test | 55 ++++++++++++++++++++++++++++++++++++
testsuite/unsafe-byname.test | 2 +-
util1.c | 26 ++++++++++++++++-
3 files changed, 81 insertions(+), 2 deletions(-)
create mode 100644 testsuite/safe-links.test
diff --git a/testsuite/safe-links.test b/testsuite/safe-links.test
new file mode 100644
index 00000000..6e95a4b9
--- /dev/null
+++ b/testsuite/safe-links.test
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+. "$suitedir/rsync.fns"
+
+test_symlink() {
+ is_a_link "$1" || test_fail "File $1 is not a symlink"
+}
+
+test_regular() {
+ if [ ! -f "$1" ]; then
+ test_fail "File $1 is not regular file or not exists"
+ fi
+}
+
+test_notexist() {
+ if [ -e "$1" ]; then
+ test_fail "File $1 exists"
+ fi
+ if [ -h "$1" ]; then
+ test_fail "File $1 exists as a symlink"
+ fi
+}
+
+cd "$tmpdir"
+
+mkdir from
+
+mkdir "from/safe"
+mkdir "from/unsafe"
+
+mkdir "from/safe/files"
+mkdir "from/safe/links"
+
+touch "from/safe/files/file1"
+touch "from/safe/files/file2"
+touch "from/unsafe/unsafefile"
+
+ln -s ../files/file1 "from/safe/links/"
+ln -s ../files/file2 "from/safe/links/"
+ln -s ../../unsafe/unsafefile "from/safe/links/"
+ln -s a/a/a/../../../unsafe2 "from/safe/links/"
+
+#echo "LISTING FROM"
+#ls -lR from
+
+echo "rsync with relative path and just -a"
+$RSYNC -avv --safe-links from/safe/ to
+
+#echo "LISTING TO"
+#ls -lR to
+
+test_symlink to/links/file1
+test_symlink to/links/file2
+test_notexist to/links/unsafefile
+test_notexist to/links/unsafe2
diff --git a/testsuite/unsafe-byname.test b/testsuite/unsafe-byname.test
index 75e72014..d2e318ef 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 da50ff1e..f260d398 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. */
--
2.34.1

View File

@ -0,0 +1,187 @@
From f45f48055e548851bc7230f454dfeba139be6c04 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
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(-)
diff --git a/checksum.c b/checksum.c
index cb21882c..66e80896 100644
--- a/checksum.c
+++ b/checksum.c
@@ -313,7 +313,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 087f9da6..17832533 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 110db28f..3f13bb95 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 8031b8f4..edfbb210 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 2bbff2fa..a4d46c39 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 081357bb..8cea2900 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
@@ -788,3 +790,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);
+}
diff --git a/t_unsafe.c b/t_unsafe.c
index 010cac50..e10619a2 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 e6b0708a..858f8f10 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 1ec928ca..f2774cd7 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 f260d398..d84bc414 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;
--
2.34.1

View File

@ -0,0 +1,630 @@
From a2bd5eb03e299de04e63eb469ce3d3d3f092942f Mon Sep 17 00:00:00 2001
From: Wayne Davison <wayne@opencoder.net>
Date: Tue, 22 Nov 2022 21:00:04 -0800
Subject: [PATCH] Duplicate argv data before poptFreeContext().
Reference:https://github.com/RsyncProject/rsync/commit/8990ad96de881f7332d16d32485f9d8b841a87d2
Conflict:NA
---
main.c | 13 -----
options.c | 160 ++++++++++++++++++++++++++++++------------------------
2 files changed, 90 insertions(+), 83 deletions(-)
diff --git a/main.c b/main.c
index 9ebfbea..5f9a7a9 100644
--- a/main.c
+++ b/main.c
@@ -1372,15 +1372,6 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
return MAX(exit_code, exit_code2);
}
-static void dup_argv(char *argv[])
-{
- int i;
-
- for (i = 0; argv[i]; i++)
- argv[i] = strdup(argv[i]);
-}
-
-
/* Start a client for either type of remote connection. Work out
* whether the arguments request a remote shell or rsyncd connection,
* and call the appropriate connection function, then run_client.
@@ -1396,10 +1387,6 @@ static int start_client(int argc, char *argv[])
int ret;
pid_t pid;
- /* Don't clobber argv[] so that ps(1) can still show the right
- * command line. */
- dup_argv(argv);
-
if (!read_batch) { /* for read_batch, NO source is specified */
char *path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
if (path) { /* source is remote */
diff --git a/options.c b/options.c
index 4feeb7e..62558ef 100644
--- a/options.c
+++ b/options.c
@@ -200,6 +200,7 @@ int remote_option_cnt = 0;
const char **remote_options = NULL;
const char *checksum_choice = NULL;
const char *compress_choice = NULL;
+static const char *empty_argv[1];
int quiet = 0;
int output_motd = 1;
@@ -1345,7 +1346,7 @@ char *alt_dest_opt(int type)
**/
int parse_arguments(int *argc_p, const char ***argv_p)
{
- static poptContext pc;
+ poptContext pc;
const char *arg, **argv = *argv_p;
int argc = *argc_p;
int opt, want_dest_type;
@@ -1365,10 +1366,6 @@ int parse_arguments(int *argc_p, const char ***argv_p)
/* TODO: Call poptReadDefaultConfig; handle errors. */
- /* The context leaks in case of an error, but if there's a
- * problem we always exit anyhow. */
- if (pc)
- poptFreeContext(pc);
pc = poptGetContext(RSYNC_NAME, argc, argv, long_options, 0);
if (!am_server) {
poptReadDefaultConfig(pc, 0);
@@ -1411,7 +1408,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
strlcpy(err_buf,
"Attempt to hack rsync thwarted!\n",
sizeof err_buf);
- return 0;
+ goto cleanup;
}
#ifdef ICONV_OPTION
iconv_opt = NULL;
@@ -1457,7 +1454,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
snprintf(err_buf, sizeof err_buf,
"the --temp-dir path is WAY too long.\n");
- return 0;
+ goto cleanup;
}
if (!daemon_opt) {
@@ -1467,8 +1464,16 @@ int parse_arguments(int *argc_p, const char ***argv_p)
exit_cleanup(RERR_SYNTAX);
}
- *argv_p = argv = poptGetArgs(pc);
- *argc_p = argc = count_args(argv);
+ argv = poptGetArgs(pc);
+ argc = count_args(argv);
+ if (!argc) {
+ *argv_p = empty_argv;
+ *argc_p = 0;
+ } else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
+ out_of_memory("parse_arguments");
+ argv = *argv_p;
+ poptFreeContext(pc);
+
am_starting_up = 0;
daemon_opt = 0;
am_daemon = 1;
@@ -1523,7 +1528,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case 'a':
if (refused_archive_part) {
create_refuse_error(refused_archive_part);
- return 0;
+ goto cleanup;
}
if (!recurse) /* preserve recurse == 2 */
recurse = 1;
@@ -1593,7 +1598,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case 'P':
if (refused_partial || refused_progress) {
create_refuse_error(refused_partial ? refused_partial : refused_progress);
- return 0;
+ goto cleanup;
}
do_progress = 1;
keep_partial = 1;
@@ -1628,7 +1633,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (*arg != '-') {
snprintf(err_buf, sizeof err_buf,
"Remote option must start with a dash: %s\n", arg);
- return 0;
+ goto cleanup;
}
if (remote_option_cnt+2 >= remote_option_alloc) {
remote_option_alloc += 16;
@@ -1670,27 +1675,27 @@ int parse_arguments(int *argc_p, const char ***argv_p)
ssize_t size;
arg = poptGetOptArg(pc);
if ((size = parse_size_arg(arg, 'b', "block-size", 0, max_blength, False)) < 0)
- return 0;
+ goto cleanup;
block_size = (int32)size;
break;
}
case OPT_MAX_SIZE:
if ((max_size = parse_size_arg(max_size_arg, 'b', "max-size", 0, -1, False)) < 0)
- return 0;
+ goto cleanup;
max_size_arg = strdup(do_big_num(max_size, 0, NULL));
break;
case OPT_MIN_SIZE:
if ((min_size = parse_size_arg(min_size_arg, 'b', "min-size", 0, -1, False)) < 0)
- return 0;
+ goto cleanup;
min_size_arg = strdup(do_big_num(min_size, 0, NULL));
break;
case OPT_BWLIMIT: {
ssize_t size = parse_size_arg(bwlimit_arg, 'K', "bwlimit", 512, -1, True);
if (size < 0)
- return 0;
+ goto cleanup;
bwlimit_arg = strdup(do_big_num(size, 0, NULL));
bwlimit = (size + 512) / 1024;
break;
@@ -1719,7 +1724,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"ERROR: the %s option conflicts with the %s option\n",
alt_dest_opt(want_dest_type), alt_dest_opt(0));
- return 0;
+ goto cleanup;
}
alt_dest_type = want_dest_type;
@@ -1727,7 +1732,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"ERROR: at most %d %s args may be specified\n",
MAX_BASIS_DIRS, alt_dest_opt(0));
- return 0;
+ goto cleanup;
}
/* We defer sanitizing this arg until we know what
* our destination directory is going to be. */
@@ -1740,7 +1745,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"Invalid argument passed to --chmod (%s)\n",
arg);
- return 0;
+ goto cleanup;
}
break;
@@ -1759,11 +1764,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (usermap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--usermap conflicts with prior --chown.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify --usermap once.\n");
- return 0;
+ goto cleanup;
}
usermap = (char *)poptGetOptArg(pc);
usermap_via_chown = False;
@@ -1775,11 +1780,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (groupmap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--groupmap conflicts with prior --chown.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify --groupmap once.\n");
- return 0;
+ goto cleanup;
}
groupmap = (char *)poptGetOptArg(pc);
groupmap_via_chown = False;
@@ -1798,11 +1803,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!usermap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--chown conflicts with prior --usermap.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify a user-affecting --chown once.\n");
- return 0;
+ goto cleanup;
}
if (asprintf(&usermap, "*:%.*s", len, chown) < 0)
out_of_memory("parse_arguments");
@@ -1814,11 +1819,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!groupmap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--chown conflicts with prior --groupmap.\n");
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify a group-affecting --chown once.\n");
- return 0;
+ goto cleanup;
}
if (asprintf(&groupmap, "*:%s", arg) < 0)
out_of_memory("parse_arguments");
@@ -1846,7 +1851,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf,sizeof(err_buf),
"ACLs are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
#endif
case 'X':
@@ -1857,7 +1862,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf,sizeof(err_buf),
"extended attributes are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
#endif
case OPT_STOP_AFTER: {
@@ -1866,7 +1871,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
stop_at_utime = time(NULL);
if ((val = atol(arg) * 60) <= 0 || LONG_MAX - val < stop_at_utime || (long)(time_t)val != val) {
snprintf(err_buf, sizeof err_buf, "invalid --stop-after value: %s\n", arg);
- return 0;
+ goto cleanup;
}
stop_at_utime += val;
break;
@@ -1877,11 +1882,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
arg = poptGetOptArg(pc);
if ((stop_at_utime = parse_time(arg)) == (time_t)-1) {
snprintf(err_buf, sizeof err_buf, "invalid --stop-at format: %s\n", arg);
- return 0;
+ goto cleanup;
}
if (stop_at_utime <= time(NULL)) {
snprintf(err_buf, sizeof err_buf, "--stop-at time is not in the future: %s\n", arg);
- return 0;
+ goto cleanup;
}
break;
#endif
@@ -1899,7 +1904,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
else {
snprintf(err_buf, sizeof err_buf,
"--stderr mode \"%s\" is not one of errors, all, or client\n", arg);
- return 0;
+ goto cleanup;
}
saw_stderr_opt = 1;
break;
@@ -1910,13 +1915,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
* turned this option off. */
if (opt >= OPT_REFUSED_BASE) {
create_refuse_error(opt);
- return 0;
+ goto cleanup;
}
snprintf(err_buf, sizeof err_buf, "%s%s: %s\n",
am_server ? "on remote machine: " : "",
poptBadOption(pc, POPT_BADOPTION_NOALIAS),
poptStrerror(opt));
- return 0;
+ goto cleanup;
}
}
@@ -1936,7 +1941,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (max_alloc_arg) {
ssize_t size = parse_size_arg(max_alloc_arg, 'B', "max-alloc", 1024*1024, -1, True);
if (size < 0)
- return 0;
+ goto cleanup;
max_alloc = size;
}
@@ -1950,7 +1955,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (protect_args > 0) {
snprintf(err_buf, sizeof err_buf,
"--protect-args conflicts with --old-args.\n");
- return 0;
+ goto cleanup;
}
protect_args = 0;
}
@@ -1995,7 +2000,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
do_compression = CPRES_AUTO;
if (do_compression && refused_compress) {
create_refuse_error(refused_compress);
- return 0;
+ goto cleanup;
}
}
@@ -2020,7 +2025,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
default:
snprintf(err_buf, sizeof err_buf,
"Invalid --outbuf setting -- specify N, L, or B.\n");
- return 0;
+ goto cleanup;
}
setvbuf(stdout, (char *)NULL, mode, 0);
}
@@ -2048,7 +2053,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
}
if (refused_no_iconv && !iconv_opt) {
create_refuse_error(refused_no_iconv);
- return 0;
+ goto cleanup;
}
#endif
@@ -2059,18 +2064,30 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (orig_protect_args == 2 && am_server)
protect_args = orig_protect_args;
- if (protect_args == 1 && am_server)
+ if (protect_args == 1 && am_server) {
+ poptFreeContext(pc);
return 1;
+ }
- *argv_p = argv = poptGetArgs(pc);
- *argc_p = argc = count_args(argv);
+ /* Because popt 1.19 has started to free the returned args data, we now
+ * make a copy of the array and then do an immediate cleanup. */
+ argv = poptGetArgs(pc);
+ argc = count_args(argv);
+ if (!argc) {
+ *argv_p = empty_argv;
+ *argc_p = 0;
+ } else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
+ out_of_memory("parse_arguments");
+ argv = *argv_p;
+ poptFreeContext(pc);
+ pc = NULL;
#ifndef SUPPORT_LINKS
if (preserve_links && !am_sender) {
snprintf(err_buf, sizeof err_buf,
"symlinks are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
}
#endif
@@ -2079,7 +2096,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"hard links are not supported on this %s\n",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
}
#endif
@@ -2087,20 +2104,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (am_root < 0 && preserve_xattrs > 1) {
snprintf(err_buf, sizeof err_buf,
"--fake-super conflicts with -XX\n");
- return 0;
+ goto cleanup;
}
#else
if (am_root < 0) {
snprintf(err_buf, sizeof err_buf,
"--fake-super requires an rsync with extended attributes enabled\n");
- return 0;
+ goto cleanup;
}
#endif
if (write_batch && read_batch) {
snprintf(err_buf, sizeof err_buf,
"--write-batch and --read-batch can not be used together\n");
- return 0;
+ goto cleanup;
}
if (write_batch > 0 || read_batch) {
if (am_server) {
@@ -2119,25 +2136,25 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (read_batch && files_from) {
snprintf(err_buf, sizeof err_buf,
"--read-batch cannot be used with --files-from\n");
- return 0;
+ goto cleanup;
}
if (read_batch && remove_source_files) {
snprintf(err_buf, sizeof err_buf,
"--read-batch cannot be used with --remove-%s-files\n",
remove_source_files == 1 ? "source" : "sent");
- return 0;
+ goto cleanup;
}
if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) {
snprintf(err_buf, sizeof err_buf,
"the batch-file name must be %d characters or less.\n",
MAX_BATCH_NAME_LEN);
- return 0;
+ goto cleanup;
}
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
snprintf(err_buf, sizeof err_buf,
"the --temp-dir path is WAY too long.\n");
- return 0;
+ goto cleanup;
}
if (max_delete < 0 && max_delete != INT_MIN) {
@@ -2171,7 +2188,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (delete_before + !!delete_during + delete_after > 1) {
snprintf(err_buf, sizeof err_buf,
"You may not combine multiple --delete-WHEN options.\n");
- return 0;
+ goto cleanup;
}
if (delete_before || delete_during || delete_after)
delete_mode = 1;
@@ -2182,7 +2199,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
delete_during = 1;
else {
create_refuse_error(refused_delete_before);
- return 0;
+ goto cleanup;
}
} else if (refused_delete_during)
delete_before = 1;
@@ -2191,14 +2208,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!xfer_dirs && delete_mode) {
snprintf(err_buf, sizeof err_buf,
"--delete does not work without --recursive (-r) or --dirs (-d).\n");
- return 0;
+ goto cleanup;
}
if (missing_args == 3) /* simplify if both options were specified */
missing_args = 2;
if (refused_delete && (delete_mode || missing_args == 2)) {
create_refuse_error(refused_delete);
- return 0;
+ goto cleanup;
}
if (remove_source_files) {
@@ -2207,7 +2224,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
* options. */
if (refused_delete && am_sender) {
create_refuse_error(refused_delete);
- return 0;
+ goto cleanup;
}
need_messages_from_generator = 1;
}
@@ -2261,7 +2278,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"--suffix cannot contain slashes: %s\n",
backup_suffix);
- return 0;
+ goto cleanup;
}
if (backup_dir) {
size_t len;
@@ -2274,7 +2291,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (len > sizeof backup_dir_buf - 128) {
snprintf(err_buf, sizeof err_buf,
"the --backup-dir path is WAY too long.\n");
- return 0;
+ goto cleanup;
}
backup_dir_len = (int)len;
if (!backup_dir_len) {
@@ -2293,7 +2310,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--suffix cannot be empty %s\n", backup_dir_len < 0
? "when --backup-dir is the same as the dest dir"
: "without a --backup-dir");
- return 0;
+ goto cleanup;
} else if (make_backups && delete_mode && !delete_excluded && !am_server) {
snprintf(backup_dir_buf, sizeof backup_dir_buf,
"P *%s", backup_suffix);
@@ -2362,11 +2379,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (whole_file > 0) {
snprintf(err_buf, sizeof err_buf,
"--append cannot be used with --whole-file\n");
- return 0;
+ goto cleanup;
}
if (refused_inplace) {
create_refuse_error(refused_inplace);
- return 0;
+ goto cleanup;
}
inplace = 1;
}
@@ -2374,7 +2391,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (write_devices) {
if (refused_inplace) {
create_refuse_error(refused_inplace);
- return 0;
+ goto cleanup;
}
inplace = 1;
}
@@ -2389,13 +2406,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--%s cannot be used with --%s\n",
append_mode ? "append" : "inplace",
delay_updates ? "delay-updates" : "partial-dir");
- return 0;
+ goto cleanup;
}
/* --inplace implies --partial for refusal purposes, but we
* clear the keep_partial flag for internal logic purposes. */
if (refused_partial) {
create_refuse_error(refused_partial);
- return 0;
+ goto cleanup;
}
keep_partial = 0;
#else
@@ -2403,7 +2420,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--%s is not supported on this %s\n",
append_mode ? "append" : "inplace",
am_server ? "server" : "client");
- return 0;
+ goto cleanup;
#endif
} else {
if (keep_partial && !partial_dir && !am_server) {
@@ -2417,7 +2434,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
partial_dir = NULL;
if (!partial_dir && refused_partial) {
create_refuse_error(refused_partial);
- return 0;
+ goto cleanup;
}
keep_partial = 1;
}
@@ -2438,14 +2455,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (am_server) {
snprintf(err_buf, sizeof err_buf,
"The --files-from sent to the server cannot specify a host.\n");
- return 0;
+ goto cleanup;
}
files_from = p;
filesfrom_host = h;
if (strcmp(files_from, "-") == 0) {
snprintf(err_buf, sizeof err_buf,
"Invalid --files-from remote filename\n");
- return 0;
+ goto cleanup;
}
} else {
if (sanitize_paths)
@@ -2464,7 +2481,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"failed to open files-from file %s: %s\n",
files_from, strerror(errno));
- return 0;
+ goto cleanup;
}
}
}
@@ -2481,6 +2498,9 @@ int parse_arguments(int *argc_p, const char ***argv_p)
options_rejected:
snprintf(err_buf, sizeof err_buf,
"Your options have been rejected by the server.\n");
+ cleanup:
+ if (pc)
+ poptFreeContext(pc);
return 0;
}
--
2.33.0

View File

@ -0,0 +1,40 @@
From: Natanael Copa <ncopa@alpinelinux.org>
Date: Wed, 15 Jan 2025 15:10:24 +0100
Subject: Fix FLAG_GOT_DIR_FLIST collission with FLAG_HLINKED
Origin: https://github.com/ncopa/rsync/commit/efb85fd8db9e8f74eb3ab91ebf44f6ed35e3da5b
Bug: https://github.com/RsyncProject/rsync/issues/697
Bug-Debian: https://bugs.debian.org/1093089
Bug-Debian: https://bugs.debian.org/1093052
Bug: https://github.com/RsyncProject/rsync/issues/702
fixes commit 688f5c379a43 (Refuse a duplicate dirlist.)
Fixes: https://github.com/RsyncProject/rsync/issues/702
Fixes: https://github.com/RsyncProject/rsync/issues/697
---
rsync.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rsync.h b/rsync.h
index 9be1297bdd29..479ac4848991 100644
--- a/rsync.h
+++ b/rsync.h
@@ -84,7 +84,6 @@
#define FLAG_DUPLICATE (1<<4) /* sender */
#define FLAG_MISSING_DIR (1<<4) /* generator */
#define FLAG_HLINKED (1<<5) /* receiver/generator (checked on all types) */
-#define FLAG_GOT_DIR_FLIST (1<<5)/* sender/receiver/generator - dir_flist only */
#define FLAG_HLINK_FIRST (1<<6) /* receiver/generator (w/FLAG_HLINKED) */
#define FLAG_IMPLIED_DIR (1<<6) /* sender/receiver/generator (dirs only) */
#define FLAG_HLINK_LAST (1<<7) /* receiver/generator */
@@ -93,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. */
--
2.47.1

View File

@ -0,0 +1,31 @@
From f923b19fd85039a2b0e908391074872334646d51 Mon Sep 17 00:00:00 2001
From: Natanael Copa <ncopa@alpinelinux.org>
Date: Wed, 15 Jan 2025 15:48:04 +0100
Subject: [PATCH] Fix use-after-free in generator
full_fname() will free the return value in the next call so we need to
duplicate it before passing it to rsyserr.
Fixes: https://github.com/RsyncProject/rsync/issues/704
---
generator.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/generator.c b/generator.c
index 3f13bb95..b56fa569 100644
--- a/generator.c
+++ b/generator.c
@@ -2041,8 +2041,12 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
if (!skip_atomic) {
if (do_rename(tmpname, fname) < 0) {
+ char *full_tmpname = strdup(full_fname(tmpname));
+ if (full_tmpname == NULL)
+ out_of_memory("atomic_create");
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
- full_fname(tmpname), full_fname(fname));
+ full_tmpname, full_fname(fname));
+ free(full_tmpname);
do_unlink(tmpname);
return 0;
}

View File

@ -1,65 +0,0 @@
From 9dd62525f3b98d692e031f22c02be8f775966503 Mon Sep 17 00:00:00 2001
From: Wayne Davison <wayne@opencoder.net>
Date: Sun, 29 Nov 2020 09:33:54 -0800
Subject: [PATCH] Work around glibc's lchmod() issue a better way.
---
syscall.c | 34 +++++++++++++++++++++-------------
1 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/syscall.c b/syscall.c
index b9c3b4e..11d10e4 100644
--- a/syscall.c
+++ b/syscall.c
@@ -227,27 +227,35 @@ int do_open(const char *pathname, int flags, mode_t mode)
#ifdef HAVE_CHMOD
int do_chmod(const char *path, mode_t mode)
{
+ static int switch_step = 0;
int code;
if (dry_run) return 0;
RETURN_ERROR_IF_RO_OR_LO;
+ switch (switch_step) {
#ifdef HAVE_LCHMOD
- code = lchmod(path, mode & CHMOD_BITS);
-#else
- if (S_ISLNK(mode)) {
+#include "case_N.h"
+ if ((code = lchmod(path, mode & CHMOD_BITS)) == 0 || errno != ENOTSUP)
+ break;
+ switch_step++;
+#endif
+
+#include "case_N.h"
+ if (S_ISLNK(mode)) {
# if defined HAVE_SETATTRLIST
- struct attrlist attrList;
- uint32_t m = mode & CHMOD_BITS; /* manpage is wrong: not mode_t! */
+ struct attrlist attrList;
+ uint32_t m = mode & CHMOD_BITS; /* manpage is wrong: not mode_t! */
- memset(&attrList, 0, sizeof attrList);
- attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
- attrList.commonattr = ATTR_CMN_ACCESSMASK;
- code = setattrlist(path, &attrList, &m, sizeof m, FSOPT_NOFOLLOW);
+ memset(&attrList, 0, sizeof attrList);
+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrList.commonattr = ATTR_CMN_ACCESSMASK;
+ code = setattrlist(path, &attrList, &m, sizeof m, FSOPT_NOFOLLOW);
# else
- code = 1;
+ code = 1;
# endif
- } else
- code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
-#endif /* !HAVE_LCHMOD */
+ } else
+ code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
+ break;
+ }
if (code != 0 && (preserve_perms || preserve_executability))
return code;
return 0;
--
1.8.3.1

Binary file not shown.

BIN
rsync-3.2.5.tar.gz Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,16 +1,28 @@
Name: rsync
Version: 3.2.3
Release: 3
Version: 3.2.5
Release: 4
Summary: Fast incremental file transfer utility
License: GPLv3+
URL: http://rsync.samba.org/
Source0: https://download.samba.org/pub/rsync/src/rsync-%{version}%{?prerelease}.tar.gz
Source1: https://download.samba.org/pub/rsync/src/rsync-patches-%{version}.tar.gz
Source2: rsyncd.socket
Source3: rsyncd.service
Source4: rsyncd.conf
Source5: rsyncd.sysconfig
Source6: rsyncd@.service
Source0: https://download.samba.org/pub/rsync/src/rsync-%{version}.tar.gz
Source1: rsyncd.socket
Source2: rsyncd.service
Source3: rsyncd.conf
Source4: rsyncd.sysconfig
Source5: rsyncd@.service
Patch6000: backport-Duplicate-argv-data-before-poptFreeContext.patch
Patch6001: backport-CVE-2024-12085.patch
Patch6002: backport-CVE-2024-12086-part1.patch
Patch6003: backport-CVE-2024-12086-part2.patch
Patch6004: backport-CVE-2024-12086-part3.patch
Patch6005: backport-CVE-2024-12086-part4.patch
Patch6006: backport-CVE-2024-12087-part1.patch
Patch6007: backport-CVE-2024-12087-part2.patch
Patch6008: backport-CVE-2024-12088.patch
Patch6009: backport-CVE-2024-12747.patch
Patch6010: backport-Fix-FLAG_GOT_DIR_FLIST-collission-with-FLAG_HLINKED.patch
Patch6011: backport-Fix_use-after-free_in_generator.patch
BuildRequires: git gcc systemd libacl-devel libattr-devel autoconf popt-devel
BuildRequires: lz4-devel openssl-devel libzstd-devel
@ -18,11 +30,6 @@ Provides: bundled(zlib) = 1.2.8 rsync-daemon
Obsoletes: rsync-daemon
%{?systemd_requires}
Patch1: backport-Work-around-glibc-lchmod-issue-a-better-way.patch
Patch2: backport-CVE-2020-14387-rsync-ssl-Verify-the-hostname-in-the-certificate-whe.patch
Patch3: backport-CVE-2022-37434.patch
Patch4: backport-A-fix-for-the-zlib-fix.patch
%description
Rsync is an open source utility that provides fast incremental file transfer.
It uses the "rsync algorithm" which provides a very fast method for bringing
@ -33,9 +40,7 @@ at one of the ends of the link beforehand.
%package_help
%prep
%autosetup -b 1 -n %{name}-%{version} -p1
patch -p1 -i patches/copy-devices.diff
%autosetup -n %{name}-%{version} -p1
%build
%configure --disable-xxhash
@ -43,16 +48,15 @@ patch -p1 -i patches/copy-devices.diff
%check
make check
chmod -x support/*
%install
%make_install
install -D -m644 %{SOURCE2} %{buildroot}/%{_unitdir}/rsyncd.socket
install -D -m644 %{SOURCE3} %{buildroot}/%{_unitdir}/rsyncd.service
install -D -m644 %{SOURCE4} %{buildroot}/%{_sysconfdir}/rsyncd.conf
install -D -m644 %{SOURCE5} %{buildroot}/%{_sysconfdir}/sysconfig/rsyncd
install -D -m644 %{SOURCE6} %{buildroot}/%{_unitdir}/rsyncd@.service
install -D -m644 %{SOURCE1} %{buildroot}/%{_unitdir}/rsyncd.socket
install -D -m644 %{SOURCE2} %{buildroot}/%{_unitdir}/rsyncd.service
install -D -m644 %{SOURCE3} %{buildroot}/%{_sysconfdir}/rsyncd.conf
install -D -m644 %{SOURCE4} %{buildroot}/%{_sysconfdir}/sysconfig/rsyncd
install -D -m644 %{SOURCE5} %{buildroot}/%{_unitdir}/rsyncd@.service
%pre
@ -68,7 +72,7 @@ install -D -m644 %{SOURCE6} %{buildroot}/%{_unitdir}/rsyncd@.service
%files
%defattr(-,root,root)
%doc tech_report.tex
%doc support/*
%attr(0644,root,root) %doc support/*
%license COPYING
%config(noreplace) %{_sysconfdir}/*.conf
%config(noreplace) %{_sysconfdir}/sysconfig/rsyncd
@ -82,12 +86,26 @@ install -D -m644 %{SOURCE6} %{buildroot}/%{_unitdir}/rsyncd@.service
%{_mandir}/man5/rsyncd.conf.5*
%changelog
* Thu Aug 18 2022 fuanan <fuanan3@h-partners.com> - 3.2.3-3
- Fix CVE-2022-37434
* Wed Jan 15 2025 Funda Wang <fundawang@yeah.net> - 3.2.5-4
- fix CVE-2024-12085, CVE-2024-12086, CVE-2024-12087, CVE-2024-12088,
CVE-2024-12747
* Wed Oct 9 zhoupengcheng <zhoupengcheng11@huawei.com> - 3.2.5-3
- backport patch from upstream
* Thu Jun 15 2023 zhoupengcheng <zhoupengcheng11@huawei.com> - 3.2.5-2
- Type:bugfix
- CVE:
- SUG:NA
- DESC:Restrict the doc permission in rsync to 644.
* Thu Aug 18 2022 fuanan <fuanan3@h-partners.com> - 3.2.5-1
- Update version to 3.2.5
- Fix CVE-2022-29154,CVE-2022-37434
* Fri Jun 18 2021 yangzhuangzhuang <yangzhuangzhuang1@huawei.com> - 3.2.3-2
- Type:CVE
- CVE:CVE-2020-14387
- Type:bugfix
- ID:NA
- SUG:NA
- DESC:Fix CVE-2020-14387