rsync/backport-CVE-2024-12086-part2.patch

265 lines
7.6 KiB
Diff

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(+)
Index: rsync-3.1.3/syscall.c
===================================================================
--- rsync-3.1.3.orig/syscall.c
+++ rsync-3.1.3/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;
@@ -578,3 +580,75 @@ int do_open_nofollow(const char *pathnam
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
+}
Index: rsync-3.1.3/ifuncs.h
===================================================================
--- rsync-3.1.3.orig/ifuncs.h
+++ rsync-3.1.3/ifuncs.h
@@ -104,3 +104,11 @@ free_stat_x(stat_x *sx_p)
}
#endif
}
+
+static inline char *my_strdup(const char *str, const char *file, int line)
+{
+ int len = strlen(str)+1;
+ char *buf = my_alloc(NULL, len, 1, file, line);
+ memcpy(buf, str, len);
+ return buf;
+}
\ No newline at end of file
Index: rsync-3.1.3/util2.c
===================================================================
--- rsync-3.1.3.orig/util2.c
+++ rsync-3.1.3/util2.c
@@ -25,6 +25,9 @@
#include "itypes.h"
#include "inums.h"
+extern size_t max_alloc;
+
+char *do_calloc = "42";
/**
* Sleep for a specified number of milliseconds.
*
@@ -77,6 +80,26 @@ void *_realloc_array(void *ptr, unsigned
return realloc(ptr, size * num);
}
+void *my_alloc(void *ptr, size_t num, size_t size, const char *file, int line)
+{
+ if (max_alloc && num >= max_alloc/size) {
+ if (!file)
+ return NULL;
+ rprintf(FERROR, "[%s] exceeded --max-alloc=%s setting (file=%s, line=%d)\n",
+ who_am_i(), do_big_num(max_alloc, 0, NULL), src_file(file), line);
+ exit_cleanup(RERR_MALLOC);
+ }
+ if (!ptr)
+ ptr = malloc(num * size);
+ else if (ptr == do_calloc)
+ ptr = calloc(num, size);
+ else
+ ptr = realloc(ptr, num * size);
+ if (!ptr && file)
+ _out_of_memory("my_alloc caller", file, line);
+ return ptr;
+}
+
const char *sum_as_hex(int csum_type, const char *sum, int flist_csum)
{
static char buf[MAX_DIGEST_LEN*2+1];
@@ -99,6 +122,27 @@ const char *sum_as_hex(int csum_type, co
return buf;
}
+NORETURN void _out_of_memory(const char *msg, const char *file, int line)
+{
+ rprintf(FERROR, "[%s] out of memory: %s (file=%s, line=%d)\n", who_am_i(), msg, src_file(file), line);
+ exit_cleanup(RERR_MALLOC);
+}
+
+const char *src_file(const char *file)
+{
+ static const char *util2 = __FILE__;
+ static int prefix = -1;
+
+ if (prefix < 0) {
+ const char *cp = strrchr(util2, '/');
+ prefix = cp ? cp - util2 + 1 : 0;
+ }
+
+ if (prefix && strncmp(file, util2, prefix) == 0)
+ return file + prefix;
+ return file;
+}
+
NORETURN void out_of_memory(const char *str)
{
rprintf(FERROR, "ERROR: out of memory in %s [%s]\n", str, who_am_i());
Index: rsync-3.1.3/options.c
===================================================================
--- rsync-3.1.3.orig/options.c
+++ rsync-3.1.3/options.c
@@ -185,6 +185,9 @@ int link_dest = 0;
int basis_dir_cnt = 0;
char *dest_option = NULL;
+#define DEFAULT_MAX_ALLOC (1024L * 1024 * 1024)
+size_t max_alloc = DEFAULT_MAX_ALLOC;
+
static int remote_option_alloc = 0;
int remote_option_cnt = 0;
const char **remote_options = NULL;
Index: rsync-3.1.3/Makefile.in
===================================================================
--- rsync-3.1.3.orig/Makefile.in
+++ rsync-3.1.3/Makefile.in
@@ -46,7 +46,7 @@ popt_OBJS=popt/findme.o popt/popt.o po
popt/popthelp.o popt/poptparse.o
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
-TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
+TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
# Programs we must have to run the test cases
CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
@@ -128,7 +128,7 @@ getgroups$(EXEEXT): getgroups.o
getfsdev$(EXEEXT): getfsdev.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
-TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o
+TRIMSLASH_OBJ = trimslash.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o
trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
Index: rsync-3.1.3/t_stub.c
===================================================================
--- rsync-3.1.3.orig/t_stub.c
+++ rsync-3.1.3/t_stub.c
@@ -31,6 +31,7 @@ int module_dirlen = 0;
int preserve_acls = 0;
int preserve_times = 0;
int preserve_xattrs = 0;
+size_t max_alloc = 0; /* max_alloc is needed when combined with util2.o */
char *partial_dir;
char *module_dir;
filter_rule_list daemon_filter_list;
Index: rsync-3.1.3/tls.c
===================================================================
--- rsync-3.1.3.orig/tls.c
+++ rsync-3.1.3/tls.c
@@ -51,8 +51,8 @@ int link_owner = 0;
int nsec_times = 0;
int preserve_perms = 0;
int preserve_executability = 0;
-int preallocate_files = 0;
-int inplace = 0;
+// int preallocate_files = 0;
+// int inplace = 0;
int noatime = 0;
#ifdef SUPPORT_XATTRS
Index: rsync-3.1.3/trimslash.c
===================================================================
--- rsync-3.1.3.orig/trimslash.c
+++ rsync-3.1.3/trimslash.c
@@ -28,8 +28,8 @@ int read_only = 1;
int list_only = 0;
int preserve_perms = 0;
int preserve_executability = 0;
-int preallocate_files = 0;
-int inplace = 0;
+// int preallocate_files = 0;
+// int inplace = 0;
int noatime = 0;
int