MCPcopy Create free account
hub / github.com/RsyncProject/rsync / do_chmod_at

Function do_chmod_at

syscall.c:833–883  ·  view source on GitHub ↗

Symlink-race-safe variant of do_chmod() for receiver-side use. Threat model: on a daemon running with "use chroot = no" (the prerequisite for CVE-2026-29518), a local attacker can race a symlink swap of one of the parent directory components of a path the receiver is about to chmod. Because chmod() resolves symlinks at every component, the swap redirects the chmod outside the receiver's

Source from the content-addressed store, hash-verified

831 component, where there is nothing to protect against.
832*/
833int do_chmod_at(const char *fname, mode_t mode)
834{
835#ifdef AT_FDCWD
836 extern int am_daemon, am_chrooted;
837 char dirpath[MAXPATHLEN];
838 const char *bname;
839 const char *slash;
840 int dfd, ret, e;
841 size_t dlen;
842
843 if (dry_run) return 0;
844 RETURN_ERROR_IF_RO_OR_LO;
845
846 /* Only the daemon-without-chroot case is exposed to the symlink-
847 * race attack: a chroot already confines the receiver, and a
848 * non-daemon rsync runs with the user's own authority so a
849 * symlink they planted can only redirect to files they could
850 * already access. Everywhere else, fall through to plain
851 * do_chmod() to avoid the dirfd-open overhead on every call. */
852 if (!am_daemon || am_chrooted)
853 return do_chmod(fname, mode);
854
855 if (!fname || !*fname || *fname == '/' || S_ISLNK(mode))
856 return do_chmod(fname, mode);
857
858 slash = strrchr(fname, '/');
859 if (!slash)
860 return do_chmod(fname, mode);
861
862 dlen = slash - fname;
863 if (dlen >= sizeof dirpath) {
864 errno = ENAMETOOLONG;
865 return -1;
866 }
867 memcpy(dirpath, fname, dlen);
868 dirpath[dlen] = '\0';
869 bname = slash + 1;
870
871 dfd = secure_relative_open(NULL, dirpath, O_RDONLY | O_DIRECTORY, 0);
872 if (dfd < 0)
873 return -1;
874
875 ret = fchmodat(dfd, bname, mode, 0);
876 e = errno;
877 close(dfd);
878 errno = e;
879 return ret;
880#else
881 return do_chmod(fname, mode);
882#endif
883}
884#endif
885
886int do_rename(const char *old_path, const char *new_path)

Callers 8

mainFunction · 0.85
set_file_attrsFunction · 0.85
recv_generatorFunction · 0.85
touch_up_dirsFunction · 0.85
delete_dir_contentsFunction · 0.85
delete_itemFunction · 0.85
set_xattrFunction · 0.85
set_stat_xattrFunction · 0.85

Calls 2

do_chmodFunction · 0.85
secure_relative_openFunction · 0.85

Tested by

no test coverage detected