Skip to content

Commit 0590b09

Browse files
committed
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
1 parent 407c71c commit 0590b09

10 files changed

+35
-7
lines changed

checksum.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
406406
int32 remainder;
407407
int fd;
408408

409-
fd = do_open(fname, O_RDONLY, 0);
409+
fd = do_open_checklinks(fname);
410410
if (fd == -1) {
411411
memset(sum, 0, file_sum_len);
412412
return;

flist.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1390,7 +1390,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
13901390

13911391
if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
13921392
if (st.st_size == 0) {
1393-
int fd = do_open(fname, O_RDONLY, 0);
1393+
int fd = do_open_checklinks(fname);
13941394
if (fd >= 0) {
13951395
st.st_size = get_device_size(fd, fname);
13961396
close(fd);

generator.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1798,7 +1798,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
17981798

17991799
if (write_devices && IS_DEVICE(sx.st.st_mode) && sx.st.st_size == 0) {
18001800
/* This early open into fd skips the regular open below. */
1801-
if ((fd = do_open(fnamecmp, O_RDONLY, 0)) >= 0)
1801+
if ((fd = do_open_nofollow(fnamecmp, O_RDONLY)) >= 0)
18021802
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
18031803
}
18041804

@@ -1867,7 +1867,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
18671867
}
18681868

18691869
/* open the file */
1870-
if (fd < 0 && (fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
1870+
if (fd < 0 && (fd = do_open_checklinks(fnamecmp)) < 0) {
18711871
rsyserr(FERROR, errno, "failed to open %s, continuing",
18721872
full_fname(fnamecmp));
18731873
pretend_missing:

receiver.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ int recv_files(int f_in, int f_out, char *local_name)
775775
if (fnamecmp != fname) {
776776
fnamecmp = fname;
777777
fnamecmp_type = FNAMECMP_FNAME;
778-
fd1 = do_open(fnamecmp, O_RDONLY, 0);
778+
fd1 = do_open_nofollow(fnamecmp, O_RDONLY);
779779
}
780780

781781
if (fd1 == -1 && basis_dir[0]) {

sender.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ void send_files(int f_in, int f_out)
350350
exit_cleanup(RERR_PROTOCOL);
351351
}
352352

353-
fd = do_open(fname, O_RDONLY, 0);
353+
fd = do_open_checklinks(fname);
354354
if (fd == -1) {
355355
if (errno == ENOENT) {
356356
enum logcode c = am_daemon && protocol_version < 28 ? FERROR : FWARNING;

syscall.c

+20
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ extern int preallocate_files;
4545
extern int preserve_perms;
4646
extern int preserve_executability;
4747
extern int open_noatime;
48+
extern int copy_links;
49+
extern int copy_unsafe_links;
4850

4951
#ifndef S_BLKSIZE
5052
# if defined hpux || defined __hpux__ || defined __hpux
@@ -788,3 +790,21 @@ int secure_relative_open(const char *basedir, const char *relpath, int flags, mo
788790
return retfd;
789791
#endif // O_NOFOLLOW, O_DIRECTORY
790792
}
793+
794+
/*
795+
varient of do_open/do_open_nofollow which does do_open() if the
796+
copy_links or copy_unsafe_links options are set and does
797+
do_open_nofollow() otherwise
798+
799+
This is used to prevent a race condition where an attacker could be
800+
switching a file between being a symlink and being a normal file
801+
802+
The open is always done with O_RDONLY flags
803+
*/
804+
int do_open_checklinks(const char *pathname)
805+
{
806+
if (copy_links || copy_unsafe_links) {
807+
return do_open(pathname, O_RDONLY, 0);
808+
}
809+
return do_open_nofollow(pathname, O_RDONLY);
810+
}

t_unsafe.c

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ int am_root = 0;
2828
int am_sender = 1;
2929
int read_only = 0;
3030
int list_only = 0;
31+
int copy_links = 0;
32+
int copy_unsafe_links = 0;
33+
3134
short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
3235

3336
int

tls.c

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ int list_only = 0;
4949
int link_times = 0;
5050
int link_owner = 0;
5151
int nsec_times = 0;
52+
int safe_symlinks = 0;
53+
int copy_links = 0;
54+
int copy_unsafe_links = 0;
5255

5356
#ifdef SUPPORT_XATTRS
5457

trimslash.c

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ int am_root = 0;
2626
int am_sender = 1;
2727
int read_only = 1;
2828
int list_only = 0;
29+
int copy_links = 0;
30+
int copy_unsafe_links = 0;
2931

3032
int
3133
main(int argc, char **argv)

util1.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ int copy_file(const char *source, const char *dest, int tmpfilefd, mode_t mode)
365365
int len; /* Number of bytes read into `buf'. */
366366
OFF_T prealloc_len = 0, offset = 0;
367367

368-
if ((ifd = do_open(source, O_RDONLY, 0)) < 0) {
368+
if ((ifd = do_open_nofollow(source, O_RDONLY)) < 0) {
369369
int save_errno = errno;
370370
rsyserr(FERROR_XFER, errno, "open %s", full_fname(source));
371371
errno = save_errno;

0 commit comments

Comments
 (0)