]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
fixed symlink race condition in sender
authorAndrew Tridgell <andrew@tridgell.net>
Tue, 17 Dec 2024 21:59:42 +0000 (08:59 +1100)
committerAndrew Tridgell <andrew@tridgell.net>
Tue, 14 Jan 2025 18:30:32 +0000 (05:30 +1100)
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
flist.c
generator.c
receiver.c
sender.c
syscall.c
t_unsafe.c
tls.c
trimslash.c
util1.c

index cb21882c5695dff6c3447e1e6da89c4840722162..66e8089678d2e647351f2eb6b4297c41a09b8889 100644 (file)
@@ -406,7 +406,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
        int32 remainder;
        int fd;
 
-       fd = do_open(fname, O_RDONLY, 0);
+       fd = do_open_checklinks(fname);
        if (fd == -1) {
                memset(sum, 0, file_sum_len);
                return;
diff --git a/flist.c b/flist.c
index 087f9da6c5d390481af091c156fae05f09618b11..17832533ed2a9a7e83ca9c2432146e92cea5bf8f 100644 (file)
--- 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);
index 110db28fc76f097426249a454b9cf999aec56c07..3f13bb959b7b619e6ab6811f9c9d7b2c7f3befc5 100644 (file)
@@ -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:
index 8031b8f4b6a2d533bea72cf8f08ad84cac5fd2c8..edfbb210656ce45824ea6ceea8a498d6b2f277fe 100644 (file)
@@ -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]) {
index 2bbff2faae83ada289bd6f305e2d3cbea681bfaf..a4d46c39e4843df5e0e42dfce845c7e0b8bb855f 100644 (file)
--- 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;
index 081357bb96d0a6fa152498ebff2f5d4235f235c0..8cea2900d2838aa3da964b1f7243ba5d49ffe7ba 100644 (file)
--- 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);
+}
index 010cac50d85f14c6a260df62d34bc1f02a5e22b2..e10619a2ae4a3cf4f350c1a74332d0d03d374ccc 100644 (file)
@@ -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 e6b0708ad3f0538a9da01c6fcbd8cc985db7d227..858f8f10c83fdafbf96e2b67756fbbca436e18fe 100644 (file)
--- 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
 
index 1ec928cac46c064f57dc17dfacb0b8de18105009..f2774cd73cedea192bdc086736bf4ec0c5f9bb69 100644 (file)
@@ -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 f260d39801904d4aea9c4bb74efed9f525bf0956..d84bc414030e0e5405d55ed542792ea3b31c8290 100644 (file)
--- 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;