MCPcopy Index your code
hub / github.com/RsyncProject/rsync / secure_relative_open

Function secure_relative_open

syscall.c:1791–1992  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

1789unsigned int curr_dir_len;
1790
1791int secure_relative_open(const char *basedir, const char *relpath, int flags, mode_t mode)
1792{
1793 extern int am_daemon, am_chrooted;
1794 extern char *module_dir;
1795 extern unsigned int module_dirlen;
1796 char modrel_buf[MAXPATHLEN];
1797 int reanchored = 0;
1798
1799 if (!relpath || relpath[0] == '/') {
1800 // must be a relative path
1801 errno = EINVAL;
1802 return -1;
1803 }
1804
1805 /* Sanitizing daemon only (am_daemon && !am_chrooted). Here we have chdir'd
1806 * into a sub-dir of the module (the transfer destination), so a relative
1807 * alt-dest like "../01" may legitimately climb to a sibling that is still
1808 * inside the module (#915). Confining beneath the cwd would reject that
1809 * climb. Re-anchor at the module root -- the real trust boundary -- by
1810 * prefixing the cwd's module-relative path (from rsync's logical curr_dir[],
1811 * a guaranteed lexical prefix of module_dir, unlike getcwd()) and resolving
1812 * beneath module_dir; RESOLVE_BENEATH then allows in-module climbs and still
1813 * rejects escapes. Only for paths that contain "..". module_dirlen is 0 for
1814 * a `path = /` module (clientserver.c), so we gate on module_dir, not its
1815 * length, to cover that case too -- the prefix check below treats
1816 * module_dirlen 0 as "module root is /". */
1817 if (am_daemon && !am_chrooted
1818 && module_dir && module_dir[0] == '/'
1819 && (basedir == NULL || basedir[0] != '/')
1820 && (path_has_dotdot_component(relpath)
1821 || (basedir && path_has_dotdot_component(basedir)))) {
1822 const char *p;
1823 int n;
1824 if (curr_dir_len >= module_dirlen
1825 && strncmp(curr_dir, module_dir, module_dirlen) == 0
1826 && (curr_dir[module_dirlen] == '\0' || curr_dir[module_dirlen] == '/')) {
1827 for (p = curr_dir + module_dirlen; *p == '/'; p++) {}
1828 if (basedir)
1829 n = snprintf(modrel_buf, sizeof modrel_buf, "%s%s%s/%s",
1830 p, *p ? "/" : "", basedir, relpath);
1831 else
1832 n = snprintf(modrel_buf, sizeof modrel_buf, "%s%s%s",
1833 p, *p ? "/" : "", relpath);
1834 if (n < 0 || n >= (int)sizeof modrel_buf) {
1835 errno = ENAMETOOLONG;
1836 return -1;
1837 }
1838 basedir = module_dir; /* absolute, operator-trusted anchor */
1839 relpath = modrel_buf;
1840 reanchored = 1;
1841 }
1842 /* else: cwd not under module root as expected -- fall through to the
1843 * front-door rejection below (fail safe). */
1844 }
1845
1846 /* Reject any path with a literal ".." component (bare "..",
1847 * "../foo", "foo/..", "foo/../bar", "subdir/.."). The previous
1848 * substring-based check caught only "../" prefix and "/../"

Callers 15

send_filesFunction · 0.85
copy_fileFunction · 0.85
change_dirFunction · 0.85
check_relpathFunction · 0.85
check_basedirFunction · 0.85
do_unlink_atFunction · 0.85
do_symlink_atFunction · 0.85
do_link_atFunction · 0.85
do_lchown_atFunction · 0.85
do_mknod_atFunction · 0.85
do_rmdir_atFunction · 0.85
do_open_atFunction · 0.85

Calls 5

pathjoinFunction · 0.85
my_strdupFunction · 0.85

Tested by

no test coverage detected