Confining every parse_filter_file() open to the module root also caught
"filter", "include from" and "exclude from" from rsyncd.conf. Those name
operator-configured paths and pointing them outside the module -- at
/etc/rsync/excludes, say -- is the ordinary way to write them; rsyncd.conf(5)
puts no constraint on where the file lives. The result was not a refused
rule but a refused connection:
failed to open exclude file /etc/rsync/excludes:
Too many levels of symbolic links (40)
rsync error: error in file IO (code 11) at exclude.c(1582)
with no symlink involved anywhere -- just a regular file outside the module.
Mark the window in which the daemon loads its own parameters and skip the
confinement there. Everything else, in particular the peer-driven dir-merge
the leak test exercises, is still confined. Also fix the trailing whitespace
in the original hunk.
(cherry picked from commit 5eb99bb6b2)
The CLEAR_LIST guard in parse_rule_tok checked rule->rflags for
FILTRULE_NO_PREFIXES, but NO_PREFIXES is a template-level flag
excluded from FILTRULES_FROM_CONTAINER inheritance. The guard
was always true for CVS rules, causing RERR_SYNTAX abort instead
of clearing the list.
Fix: check template->rflags instead of rule->rflags.
Regression test: testsuite/ki73-cvs-clear-list_test.py
Route operator-supplied paths (--partial-dir, --backup-dir, alt/copy/link-dest,
--temp-dir, --files-from, --log-file, config and secrets files) and the sender's
own enumeration through the race-safe resolver and an ownership walk that refuses a
target redirected outside the module root. open_tmpfile() now creates the temp via
the cached held dir fd (do_mkstemp_atfd) so an in-tree dir symlink is followed
safely instead of refused. Adds --insecure-links to opt back into legacy following;
the daemon hard-refuses the client flag (use the "insecure links" module param).
Phase 1 of add_implied_include() decides whether a '\' is escaping a wildcard
via !strchr("*[?", cp[1]). strchr() returns a pointer to the string literal's
NUL terminator when cp[1]=='\0', so a trailing backslash matches neither branch:
it is emitted into new_pat via the fall-through but backslash_cnt is not
incremented. The phase-2 recurse/xfer_dirs '/**' rule then allocates
arg_len + backslash_cnt + 3 + 1 and doubles every '\' in new_pat, including the
uncounted trailing one, so the closing '*p = '\0'' lands one byte past the heap
buffer.
Reachable on a standard 'rsync --daemon' deployment (am_server==0 at the
per-module parse_arguments(), so trust_sender_args stays 0) by a remote
unauthenticated client that sends '-r --files-from=-' and a files-from name with
both an interior and a trailing backslash (e.g. 'a\b\'). Count the trailing
backslash so the allocation reserves the doubled slot.
(cherry picked from commit e377e7dc4f3e752723e5fbf9623a8315034e49ab)
A '. file' (merge) directive inside a filter file makes
parse_filter_str() call parse_filter_file() on the named file, which
in turn feeds each line back through parse_filter_str(). Nothing
bounds the depth, so a file that merges itself recurses until the
stack guard page is hit.
A malicious client of a writable daemon module can trigger this in a
single connection: send '--filter=": .merge"' with --delete-after,
upload a '.merge' file whose only line is '. .merge', and the
generator's per-dir merge handling in push_local_filters() then opens
and re-parses that file forever, crashing the forked daemon child.
Cap the nesting at MAX_MERGE_DEPTH (32). At the cap, mirror the
existing failed-open handling: abort with RERR_FILEIO under a fatal
(operator-supplied) merge, otherwise log an FERROR and drop the
offending merge so the transfer continues rather than turning a
planted filter file into a guaranteed abort.
(cherry picked from commit c6c585afdf0e2ae88ae57fd46ff9e4a7f4f442fc)
parse_filter_file() opened operator- and (via per-directory merge files
like .cvsignore) sender-controlled paths via plain fopen() with no symlink
defense: an unprivileged source-tree owner could plant a dir-merge file as
a symlink to a root-readable file, leaking its content through the filter
parser, or redirect an --exclude-from/--files-from open via a planted
parent. Open via safe_open_no_attacker_symlinks(O_RDONLY)+fdopen(), which
refuses symlinks anywhere in the path unless owned by uid 0 or our euid.
Tests: excludefrom-symlink, filter-merge-symlink.
Backport of two 3.5.0 daemon-filter fixes:
- parse_filter_file(): strip module_dirlen off an absolute merge-file
path before the daemon_filter_list check (parse_merge_name() prepended
module_dir), so an anchored module-relative rule actually matches; a
hidden merge file is treated as non-existent rather than tripping the
fatal-vs-silent oracle (Mitchell Benjamin finding 1);
- downgrade implied-parent directories to non-content so a malicious
sender cannot expand --delete scope via a content-dir entry
(Mitchell Benjamin finding 3).
Tests: daemon-filter-merge-bypass, malicious-sender-delete-scope.
Reported-by: Mitchell Benjamin <mitchell@revampstudio.com.au>
Glibc 2.43 added C23 const-preserving overloads to various string functions,
which change the return type depending on the constness of the argument(s).
Currently this leads to warnings from calls to strtok() or strchr().
Fix this by properly declaring the respective variable types.
Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com>
Fortified (-D_FORTIFY_SOURCE=2 for gcc) builds make strlcpy() crash when
its third parameter (size) is larger than the buffer:
$ rsync -FFXHav '--filter=merge global-rsync-filter' Align-37-43/ xxx
sending incremental file list
*** buffer overflow detected ***: terminated
It's in the exclude code in setup_merge_file():
strlcpy(y, save, MAXPATHLEN);
Note the 'y' pointer was incremented, so it no longer points to memory
with MAXPATHLEN "owned" bytes.
Fix it by remembering the number of copied bytes into the 'save' buffer
and use that instead of MAXPATHLEN which is clearly incorrect.
Fixes#511.
- Avoid implied rules on generator and (with extra certainty) on server
- Add -R implied-directory path elements as directory includes
- Log about extra file-list checking using a new --debug=FILTER3 level
- All the memory-allocation macros now auto-check for failure and exit
with a failure message that incudes the caller's file and lineno
info. This includes strdup().
- Added the `--max-alloc=SIZE` option to be able to override the memory
allocator's sanity-check limit. It defaults to 1G (as before).
Fixes bugzilla bug 12769.
- Change default_cvsignore char[] into a define.
- Make the DEFAULT_DONT_COMPRESS and DEFAULT_CVSIGNORE defines get set
based on their info in rsync.1.md.
- Add a few more don't-compress suffixes from Simon Matter.