4062 lines · 121.6 KB · C
RawDownload
/* * Syscall wrappers to ensure that nothing gets done in dry_run mode * and to handle system peculiarities. * * Copyright (C) 1998 Andrew Tridgell * Copyright (C) 2002 Martin Pool * Copyright (C) 2003-2022 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h" /* Exercise the pre-*at() portability tier on modern build hosts. */#ifdef RSYNC_TEST_NO_AT_FDCWD#undef AT_FDCWD#undef AT_SYMLINK_NOFOLLOW#undef HAVE_LINKAT#undef HAVE_OPENAT2#undef HAVE_UTIMENSAT#undef O_RESOLVE_BENEATH#endif #if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H#include <sys/un.h>	/* for the socket+bind() fallback in do_mknod() */#endif#ifdef HAVE_SYS_ATTR_H#include <sys/attr.h>#endif #if defined HAVE_SYS_FALLOCATE && !defined HAVE_FALLOCATE#include <sys/syscall.h>#endif #ifdef __linux__#include <sys/syscall.h>	/* SYS_fchmodat2 / SYS_fallocate raw-syscall wrappers */#endif #include "ifuncs.h" extern int dry_run;extern int am_root;extern int am_sender;extern int read_only;extern int list_only;extern int inplace;extern int preallocate_files;extern int sparse_files;extern int preserve_perms;extern int preserve_executability;extern int open_noatime;extern int copy_links;extern int copy_unsafe_links;extern int am_daemon;extern int am_chrooted;extern int insecure_links;extern int module_id;extern unsigned int module_dirlen;extern char *module_dir;extern int module_dirfd;	/* daemon: served module root pinned by identity, or -1 */extern char *confine_root;	/* --confine-root, or NULL; see confinement_root() */extern unsigned int confine_rootlen;extern char curr_dir[MAXPATHLEN];	/* defined below; fwd-declared for the seed */extern int operator_path_resolve;	/* defined below; fwd-declared for the exclude check */ /* A directory fd used only for pathname traversal, fchdir(), or as *at() * authority does not need read permission on Linux.  Keep the portable * O_RDONLY fallback for systems without O_PATH. */static int directory_traverse_flags(void){#if defined O_PATH && defined O_DIRECTORY	return O_PATH | O_DIRECTORY;#elif defined O_DIRECTORY	return O_RDONLY | O_DIRECTORY;#else	return O_RDONLY;#endif} #if defined AT_FDCWD && defined O_NOFOLLOW && defined O_DIRECTORY/* Open a trusted absolute anchor directory as an owned dirfd.  When the anchor is * the served module root and the daemon pinned it by identity (module_dirfd), dup * that fd rather than re-resolving the absolute path with openat(AT_FDCWD, ...) -- * which re-traverses the module's ancestors as the dropped-privilege module uid * and EACCESes when the module sits under a non-traversable parent (a 0700 home). * Functionally identical (same inode), just privilege-drop-safe.  Gated like its * callers (the secure resolver and dpc_dir_fd both require these three). */static int open_anchor_dirfd(const char *path){	if (module_dirfd >= 0 && am_daemon && module_dir && strcmp(path, module_dir) == 0)		return dup(module_dirfd);	return openat(AT_FDCWD, path, directory_traverse_flags());}#endif /* Single gate for whether path resolution must be hardened against * parent-component symlink races (TOCTOU).  Used by the do_*_at()/do_*_atfd() * wrappers and the receiver's secure-open/secure-mkstemp choices.  Hardens * every non-chrooted receiver (a chroot is its own confinement); the sender is * excluded so it still follows -L/--copy-links symlinks.  A daemon chroot with * an inner-module /./ boundary still needs these checks because the kernel * chroot confines the outer path, not the inner module. */int secure_relpath_active(void){	/* The "insecure links" / --insecure-links opt-out restores the legacy	 * follow-any-symlink behaviour uniformly, so it disables the secure	 * resolver on the RECEIVER side too (not just the sender enumeration that	 * already checks symlink_optout_allowed()).  Without this an opted-out	 * module still confined receiver writes/stats through a pre-existing	 * in-module symlink -- failing to match the pre-3.4.3 behaviour the opt-out	 * promises (documented in rsyncd.conf(5) "munge symlinks"/"insecure links"). */	if (symlink_optout_allowed())		return 0;	if (am_daemon && am_chrooted && module_dirlen)		return 1;	return !am_chrooted && (am_daemon || !am_sender);} /* Whether the operator-supplied-path symlink confinement is opted out.  For a * non-daemon transfer this is the local --insecure-links flag.  For a daemon it * is governed ONLY by the module's "insecure links" config (lp_insecure_links) * -- never by a peer-supplied --insecure-links (a client cannot disable a * daemon's confinement; the daemon also drops a connection that sends it).  So a * forwarded flag is structurally inert here. */int symlink_optout_allowed(void){	if (am_daemon)		return module_id >= 0 && lp_insecure_links(module_id);	return insecure_links;} /* The root an operator/peer-supplied path must stay under, or NULL when nothing * is confined.  A daemon has the served module; a server launched by a wrapper * with its own restricted directory (rrsync) gets one from --confine-root. * * A daemon never honours --confine-root: module_dir is the boundary there, and * the option arrives in a peer-supplied argv, so obeying it could only loosen * the module. */static const char *confinement_root(unsigned int *lenp){	if (am_daemon) {		*lenp = module_dirlen;		return module_dir;	}	*lenp = confine_rootlen;	return confine_root;} /* Split a recognised fd-pin prefix off `p`, returning the tail -- "" for the * pin directory itself, otherwise a string starting with '/'.  NULL when `p` * is not in an fd-pin namespace. */static const char *fd_pin_tail(const char *p){	const char *s; 	if (strncmp(p, "/dev/fd", 7) == 0) {		s = p + 7;		return (*s == '\0' || *s == '/') ? s : NULL;	} 	if (strncmp(p, "/proc/", 6) != 0)		return NULL;	s = p + 6;	if (strncmp(s, "self/", 5) == 0)	/* "/proc/self/..." */		s += 4;	else {					/* "/proc/<pid>/..." */		const char *d = s;		while (*s >= '0' && *s <= '9')			s++;		if (s == d || *s != '/')			return NULL;	}	if (strncmp(s, "/fd", 3) != 0)		return NULL;	s += 3;	return (*s == '\0' || *s == '/') ? s : NULL;} /* An EXACT pin entry, such as "/proc/self/fd/7" or "/dev/fd/7", whose target is * what confinement must judge.  rrsync also writes a pinned parent as * ".../fd/7/<leaf>", but the walk resolves the magic link itself and checks the * components past it, so only the bare entry is resolved here.  Requiring all * digits keeps a planted name like ".../fd/outside-secret" out. */static int is_exact_fd_pin(const char *p){	const char *tail = fd_pin_tail(p); 	if (!tail || *tail != '/')		return 0;	for (++tail; *tail >= '0' && *tail <= '9'; tail++) {}	return *tail == '\0' && tail[-1] != '/';} /* Refuse (return 1) when the ABSOLUTE resolved path `abspath` lands OUTSIDE the * confinement root, for an operator/peer-supplied path that must stay inside it * (--partial-dir/--backup-dir/alt-basis/merge files: operator_path_resolve).  An * in-tree symlink owned by uid 0 / the euid is followed by design, so it can * redirect the resolved target outside the root; this catches that escape. * * This is ROOT confinement only.  The daemon exclude/filter list is a name-based * visibility filter, NOT a physical-path boundary: a symlink whose own name is * not excluded may still resolve into an excluded IN-tree subtree, exactly as in * stock rsync.  The defense for a writable module is `munge symlinks` (see * rsyncd.conf(5)), not this walk. */static int abspath_outside_confinement(const char *abspath){	unsigned int rootlen;	const char *root = confinement_root(&rootlen);	char pinned[MAXPATHLEN]; 	if (!root || !abspath)		return 0;	if (rootlen <= 1)			/* root is "/": nothing is outside */		return 0;	/* An fd pin (rrsync rewrites a validated option path to /proc/self/fd/N so	 * no later symlink can redirect it) is spelled outside the root by	 * construction.  Judge it by what it points AT rather than by its spelling,	 * so a pin is neither wrongly refused nor blindly trusted.  A pin we cannot	 * resolve to an absolute path is refused, not waved through: an unreadable	 * pin is exactly the case where we cannot say where the open would land. */	if (!am_daemon) {		const char *tail = fd_pin_tail(abspath);		if (tail && !*tail)			return 0;		/* the pin directory: transit, opens nothing */		if (is_exact_fd_pin(abspath)) {			ssize_t n = readlink(abspath, pinned, sizeof pinned - 1);			if (n <= 0 || pinned[0] != '/')				return operator_path_resolve ? 1 : 0;			pinned[n] = '\0';			abspath = pinned;		}	}	if (strncmp(abspath, root, rootlen) == 0	 && (abspath[rootlen] == '\0' || abspath[rootlen] == '/'))		return 0;			/* inside: name-based exclude is not a boundary */	/* Not under the root.  An ABSOLUTE walk passes through the root's ancestors	 * ("/", "/home", ...) on the way down -- those are not "outside", just	 * not-yet-arrived, so allow them.  A path that has truly DIVERGED is	 * outside: refuse it for an operator/peer path that must stay in the tree	 * (operator_path_resolve); other opens (--log-file, --*-from, lock/motd)	 * may legitimately live elsewhere.  The --insecure-links / "insecure links	 * = yes" opt-out short-circuits before we get here. */	size_t alen = strlen(abspath);	if (alen == 0	 || (strncmp(abspath, root, alen) == 0 && root[alen] == '/'))		return 0;			/* ancestor of the root: still descending */	return operator_path_resolve ? 1 : 0;} /* Advance the tracked absolute path `abspath` by one resolved component, * normalizing "." and ".." exactly as openat() does so the module-confinement * check (abspath_outside_confinement) sees the REAL resolved target.  -1/ * ENAMETOOLONG on overflow. */static int abspath_step(char *abspath, size_t cap, const char *comp, size_t comp_len){	if (comp_len == 1 && comp[0] == '.')		return 0;				/* "." -- no movement */	if (comp_len == 2 && comp[0] == '.' && comp[1] == '.') {		char *s = strrchr(abspath, '/');	/* ".." -- pop a component */		if (s)			*s = '\0';		else			abspath[0] = '\0';		return 0;	}	size_t al = strlen(abspath);	size_t off = (al > 0 && abspath[al-1] == '/') ? al : al + 1;	/* no "//" */	if (off + comp_len >= cap) {		errno = ENAMETOOLONG;		return -1;	}	if (off != al)		abspath[al] = '/';	memcpy(abspath + off, comp, comp_len + 1);	return 0;} /* Open an operator-supplied path, refusing to traverse any symlink (parent or * leaf) not owned by uid 0 or our euid.  A trusted-owned symlink (e.g. root's * /var/log -> /data/log) is still followed; an untrusted one fails ELOOP. * Unlike plain O_NOFOLLOW this also defends a planted parent component * (--log-file=$plant/log), not just a planted leaf.  Used for opens that may * transit attacker-writable parents: --log-file, --password-file, --*-from, * --read/write-batch, daemon motd/lock/early-input/--config. * * Walks component-by-component with fstatat(AT_SYMLINK_NOFOLLOW) + * openat(O_NOFOLLOW), splicing a trusted symlink's target back into the path. * Returns the fd, or -1 (errno ELOOP on the security refusal so callers can * tell it apart).  Falls back to plain open() where openat/O_NOFOLLOW are * unavailable. *//* Core walk.  When out_abs is non-NULL and the path resolves to a directory * (O_DIRECTORY), the resolved absolute path is copied there -- owner_walk_parent * uses it to filter-check the (otherwise unchecked) leaf basename. */static int ona_open(const char *path, int flags, mode_t mode, char *out_abs, size_t out_cap){#if defined AT_FDCWD && defined O_NOFOLLOW && defined O_DIRECTORY	/* O_CLOEXEC predates some still-supported targets; mirror rand_bytes()'s	 * fallback in syscall.c so a build without it still compiles. */#ifndef O_CLOEXEC#define O_CLOEXEC 0#endif	const int dir_traverse_flags = directory_traverse_flags() | O_CLOEXEC;	if (!path || !*path) {		errno = EINVAL;		return -1;	} 	/* Opted out (local --insecure-links, or a daemon module with "insecure	 * links = yes"): restore the legacy symlink-following open. */	if (symlink_optout_allowed())		return open(path, flags, mode); 	const uid_t trusted_uid = geteuid();	int dfd = AT_FDCWD;	int dfd_owns = 0; 	/* Absolute path of the current dir, for the confinement refusal	 * (abspath_outside_confinement).  A relative operator path starts at the	 * daemon's cwd == the module root; an absolute one (or a followed absolute	 * symlink target) restarts at "/". */	char abspath[MAXPATHLEN] = {0};	if (am_daemon && module_dir && module_dir[0] == '/')		strlcpy(abspath, module_dir, sizeof abspath);	/* "/" for a path=/ module */	else if (confine_root) {		/* Unlike a daemon's, this cwd is not pinned to the root -- the receiver		 * chdir's into the destination -- so it has to be read, not assumed.		 * It must be the PHYSICAL cwd: curr_dir is the lexical name change_dir()		 * was given, so after descending a trusted symlink the tracker sits at a		 * different depth than the kernel, and a ".." that really escapes looks		 * like it landed inside.		 *		 * Without it there is nothing to measure against, and an empty tracker		 * does NOT deny by itself -- a leading ".." pops nothing and an empty		 * path reads as an ancestor of the root -- so refuse the open instead. */		if (!getcwd(abspath, sizeof abspath))			return -1;	} 	/* An fd pin (rrsync rewrites an option path to /proc/self/fd/N so no	 * later symlink can redirect it) is spelled outside the root by	 * construction, so the walk has to be allowed through /proc/self/fd to	 * reach the magic link.  This only suspends the check for that prefix:	 * following the link restarts the walk at its absolute target, and every	 * component of THAT is checked, so a pin aimed outside is still refused. */	const char *ptail = fd_pin_tail(path);	int pin_transit = !am_daemon && confine_root && ptail != NULL; 	/* Path-walk state. `remaining` is the unconsumed tail; we splice	 * symlink targets back into it as we go. Sized 2x MAXPATHLEN so a	 * one-level expansion can't immediately overflow; deeper chains	 * fail with ENAMETOOLONG below. */	char remaining[MAXPATHLEN * 2];	if (strlcpy(remaining, path, sizeof remaining) >= sizeof remaining) {		errno = ENAMETOOLONG;		return -1;	} 	/* Absolute path: pin "/" as the starting dfd. */	if (remaining[0] == '/') {		dfd = open("/", dir_traverse_flags);		if (dfd < 0)			return -1;		dfd_owns = 1;		abspath[0] = '\0';			/* now resolving from "/" */		char *p = remaining;		while (*p == '/') p++;		memmove(remaining, p, strlen(p) + 1);	} 	int loops = 40;	/* SYMLOOP_MAX-ish; breaks symlink cycles. Counts symlink			 * expansions only (below), NOT path depth -- a deep but			 * symlink-free path must resolve, not ELOOP. */	int retfd = -1;	int saved_errno = 0; 	while (*remaining) {		/* Peel one component off the front of `remaining`. */		char *slash = strchr(remaining, '/');		size_t comp_len = slash ? (size_t)(slash - remaining) : strlen(remaining);		char comp[MAXPATHLEN];		if (comp_len == 0 || comp_len >= sizeof comp) {			saved_errno = comp_len == 0 ? EINVAL : ENAMETOOLONG;			goto out;		}		memcpy(comp, remaining, comp_len);		comp[comp_len] = '\0';		int is_last = (slash == NULL); 		/* Inspect this component without following symlinks. */		STRUCT_STAT lst;		if (fstatat(dfd, comp, &lst, AT_SYMLINK_NOFOLLOW) < 0) {			/* The leaf may not exist yet (O_CREAT case). Allow it			 * and openat with O_NOFOLLOW so a race-planted leaf			 * symlink at this instant is still refused. */			if (is_last && errno == ENOENT && (flags & O_CREAT)) {				if (abspath_step(abspath, sizeof abspath, comp, comp_len) < 0) {					saved_errno = errno;					goto out;				}				if (!pin_transit && abspath_outside_confinement(abspath)) {					saved_errno = ELOOP;					goto out;				}				retfd = openat(dfd, comp, flags | O_NOFOLLOW, mode);				saved_errno = errno;				goto out;			}			saved_errno = errno;			goto out;		} 		if (S_ISLNK(lst.st_mode)) {			/* Symlink: untrusted owner is refused; trusted owner is followed			 * via readlinkat + splice.  In a user namespace the /proc/self and			 * /dev/fd symlinks may report the overflow uid, so			 * allow those exact components while traversing a recognised pin. */			int namespace_pin = pin_transit				&& ((strcmp(abspath, "/proc") == 0 && strcmp(comp, "self") == 0)				 || (strcmp(abspath, "/dev") == 0 && strcmp(comp, "fd") == 0));			if (!namespace_pin && lst.st_uid != 0 && lst.st_uid != trusted_uid) {				rprintf(FERROR,					"refusing to follow a symlink owned by an untrusted user; "					"use --insecure-links locally or \"insecure links = yes\" in a "					"daemon module ONLY if every path component is trusted\n");				saved_errno = ELOOP;				goto out;			}			if (--loops < 0) {	/* cap symlink-follow chains */				saved_errno = ELOOP;				goto out;			}			char target[MAXPATHLEN];			ssize_t n = readlinkat(dfd, comp, target, sizeof target - 1);			if (n < 0) {				saved_errno = errno;				goto out;			}			target[n] = '\0'; 			/* Detect Linux kernel pseudo-paths (pipes, sockets, anon_inodes).			 * These are not real paths on disk and never contain slashes. */			const char *abstail = fd_pin_tail(abspath);			int is_fd_dir = (abstail != NULL && *abstail == '\0' && ptail != NULL);			if (is_fd_dir && (strncmp(target, "pipe:[", 6) == 0			    || strncmp(target, "socket:[", 8) == 0			    || strncmp(target, "anon_inode:", 11) == 0)) {				if (!is_last) {					saved_errno = ENOTDIR;					goto out;				}				if (confine_root) {					/* Anonymous objects cannot be proven to reside beneath					 * the confinement root. */					saved_errno = ENOENT;					goto out;				}				/* Process substitution exposes /dev/fd/X as a symlink to a				 * kernel object. Reopen the validated leaf without O_NOFOLLOW				 * so the kernel applies the caller's requested open flags. */				retfd = openat(dfd, comp, (flags & ~O_NOFOLLOW) | O_CLOEXEC, mode);				/* Refuse a descriptor that changed to a filesystem object				 * between validation and openat(). */				if (retfd >= 0) {					STRUCT_STAT pst;					if (fstat(retfd, &pst) < 0 || S_ISREG(pst.st_mode) || S_ISDIR(pst.st_mode)) {						close(retfd);						retfd = -1;						errno = ELOOP;					}				}				saved_errno = retfd < 0 ? errno : 0;				goto out;			} 			/* Splice: new `remaining` = <target> + <tail-after-comp>.			 * Absolute target restarts the walk from "/". */			char tail[MAXPATHLEN];			tail[0] = '\0';			if (slash)				strlcpy(tail, slash, sizeof tail); 			char rebuilt[MAXPATHLEN * 2];			if (snprintf(rebuilt, sizeof rebuilt, "%s%s",				     target, tail) >= (int)sizeof rebuilt) {				saved_errno = ENAMETOOLONG;				goto out;			} 			if (target[0] == '/') {

Lines 1–500 of 4062.