mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-09-08 19:30:38 -04:00
rsync: confine the daemon files-from open to the module root
A daemon serving a writable, non-chrooted module reads a client-requested --files-from=:LIST through open_no_attacker_symlinks(), which follows a symlink owned by uid 0 or the euid. The module-root confinement in that resolver (abspath_excluded_by_module) only fires when operator_path_resolve is set, and this open left it clear -- so a trusted-owned symlink whose target escapes the module was followed. An attacker can obtain such a symlink without owning it: a --backup-dir push makes the daemon back up the old destination symlink with the daemon's own (root) ownership, and a parent-swap race can leave that root-owned backup symlink pointing outside the module. A later --files-from=:backup/... then reads out-of-module file content as the file list, bypassing the same-uid ownership constraint that normally protects files-from. Set operator_path_resolve around the files-from open so the ownership walk also refuses a trusted-owned symlink that redirects the list outside the module root. A daemon has no rsyncd.conf "files from" of its own, so this path is always client-requested and confining it is unconditional. No-op off a daemon (the module-root check only fires when am_daemon). The same ownership-walk opener backs the daemon merge/--exclude-from reads in exclude.c, but those also load the module's own "include from"/"exclude from" admin files, which on a non-chrooted module may legitimately live outside the module; confining them there needs a client-vs-admin distinction and is left to a separate change.
This commit is contained in:
1 parent
3b826d6683
commit
3fe1ed512c
1 file changed
+11
-1
@@ -27,6 +27,7 @@
|
||||
extern int module_id;
|
||||
extern int local_server;
|
||||
extern int sanitize_paths;
|
||||
extern int operator_path_resolve;
|
||||
extern int trust_sender_args;
|
||||
extern int trust_sender_filter;
|
||||
extern unsigned int module_dirlen;
|
||||
@@ -2550,8 +2551,17 @@ int parse_arguments(int *argc_p, const char ***argv_p)
|
||||
}
|
||||
/* Operator-supplied path that may transit attacker-writable
|
||||
* parents; refuse symlinks not owned by uid 0 or our euid,
|
||||
* as for --exclude-from/--include-from/--filter in exclude.c. */
|
||||
* as for --exclude-from/--include-from/--filter in exclude.c.
|
||||
* A daemon reads this list from a CLIENT-requested path
|
||||
* (--files-from=:LIST) and it must stay inside the module:
|
||||
* operator_path_resolve makes the ownership walk also refuse a
|
||||
* (trusted-owned) symlink that redirects the list outside the
|
||||
* module root -- e.g. a root-owned backup symlink. No-op off a
|
||||
* daemon (the module-root check only fires when am_daemon). */
|
||||
int save_opr = operator_path_resolve;
|
||||
operator_path_resolve = 1;
|
||||
filesfrom_fd = open_no_attacker_symlinks(files_from, O_RDONLY|O_BINARY, 0);
|
||||
operator_path_resolve = save_opr;
|
||||
if (filesfrom_fd < 0) {
|
||||
snprintf(err_buf, sizeof err_buf,
|
||||
"failed to open files-from file %s: %s\n",
|
||||
|
||||
Reference in new issue
Block a user