logit() wrote the message to the log file with a raw fprintf, so an
attacker-controlled filename could inject terminal escapes that an admin later
executes when cat'ing the log (CWE-117). Route it through filtered_fwrite(),
keeping the trailing newline raw. Also escape C1 controls (0x80-0x9f, incl CSI)
on filtered_fwrite's use_isprint=0 path, which previously caught only C0.
Reported-by: Leonid Bugaev
The C1 escaping is gated by an escape_c1 flag set only for the log path, so
--8-bit-output / iconv terminal output still passes 8-bit bytes (incl. UTF-8)
through unchanged.
(cherry picked from commit ad64e99590)
log_formatted() renders %% as a literal '%', but log_format_has() still
rescanned the literal '%' as the start of a new escape, so a format such
as --out-format='%%i' misdetected the 'i' and turned on itemizing (and
'%%b'/'%%c'/'%%C' likewise perturbed log_before_transfer and checksum
retention). Skip the literal so both parsers agree.
Extends the ki58 test: an attribute-only change must not be logged under
--out-format='%%i %n', while a transferred file still renders the
literal '%i'.
The log_formatted() switch had no case for '%', so %% did not
produce a literal percent character. Add case '%' to output
a single '%' character, matching the printf convention.
Test: testsuite/ki58-log-format-percent_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).
A daemon receiver (am_server=1, am_generator=0, send_msgs_to_gen=1) that
receives MSG_INFO/MSG_ERROR/MSG_ERROR_XFER/MSG_WARNING from the wire calls
rwrite() with is_utf8=!am_generator=1 (io.c:read_a_msg). The
send_msgs_to_gen branch in rwrite() then asserted !is_utf8, giving a
remotely-reachable abort of the per-connection receiver child.
Legitimate senders never put these tags on the wire (they write to their
own terminal), so this never fired on real traffic. Under NDEBUG the
assert compiles out and the bytes are forwarded raw to the generator,
which logs them with is_utf8=0 through filtered_fwrite -- safe. Match
that behavior in debug builds: drop the assert and document why is_utf8
may legitimately be set here.
(cherry picked from commit e5a89f3372716ee42fcb145d7a65f9037391e074)
log_delete() builds a placeholder file_struct with only
(file_extra_cnt + 2) extras — enough for the REQ_EXTRA fields and the
first two OPT_EXTRA slots, but not for the SUM_EXTRA_CNT slots that
F_SUM() reaches into. When the active out-format / log-format contains
%C and always_checksum is on, log_formatted() called sum_as_hex() on
F_SUM(file) for the placeholder, reading flist_csum_len bytes that begin
before the new_array0() allocation and hex-encoding them into the log
line / FCLIENT message.
A daemon receiver can be driven onto this path entirely from the wire:
the client args line can include --out-format='%o%C' and -c, and
read_a_msg() accepts a multiplexed MSG_DELETED from the peer; a payload
of MAXPATHLEN or more bytes skips the send_msg() forward and lands in
the FCLIENT log_formatted() call with the attacker-supplied format.
A deleted entry has no checksum to print, so fall through to the
existing blank-fill branch when ITEM_DELETED is set. This also covers
the legitimate generator-side log_delete() callers when the daemon's
configured "log format" contains %C.
(cherry picked from commit 84728d81ef92268fb549c24f393c18a8e8eadfd9)
rwrite()'s daemon/logfile branch did strlcpy(msg, buf, MIN(sizeof msg, len+1)),
but strlcpy() scans the whole source with strlen(); buf is the data buffer from
read_a_msg() (io.c) holding exactly len bytes of a forwarded MSG_* payload with
no NUL terminator, so strlen() reads past the message into uninitialised stack.
Copy exactly len (bounded) bytes with memcpy() and NUL-terminate, matching the
(buf, len) contract the rest of rwrite() already honours. Behaviour is
unchanged for the NUL-terminated callers; the over-read is gone.
- log: open the --log-file / daemon log file through
safe_open_no_attacker_symlinks() so a planted symlink can't redirect the
privileged log write;
- batch: open the --write-batch/--read-batch files the same way (+ an
S_ISREG check), and restore O_BINARY / an O_CLOEXEC fallback on the batch
opens.
Tests: log-file-symlink, batch-file-symlink.
Two cumulative-snprintf patterns in log.c (rsyserr) and main.c
(output_itemized_counts) had the shape
len = snprintf(buf, sizeof buf, ...);
len += snprintf(buf+len, sizeof buf - len, ...);
with no guard between calls. snprintf returns the would-have-been
length on truncation, so a truncated first call leaves
"sizeof buf - len" as a negative-then-promoted-to-size_t value,
underflowing into a huge size_t and writing past buf.
Realistic exposure is small in both cases (log header well under
buffer, only ~5 itemized iterations writing ~25 chars each into a
1024-byte buffer) but the defect class matches bb0a8118 and the
fix is cheap. Guard before each subsequent call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Size flist checksum data to hold the active size, not the max.
- Add a negotiated hash method to the daemon auth code.
- Use EVP for all openssl digests. This makes it easy to add more
openssl digest methods and avoids deprecation warnings.
- Support a way to re-enable deprecated digests via openssl conf
file and allow a default file to be configured.
- Supply a simple openssl-rsync.cnf file to enable legacy digests.
- Use -pedantic-errors with gcc to make an array-init fatal.
- Fix all the extra warnings that gcc outputs due to this option.
- Also add -Wno-pedantic to gcc if we're using the internal popt
code (since it has lots of pedantic issues).
- Use strdup(do_big_num(...)) to replace num_to_byte_string(...).
- Allow a ',' for a decimal point in a SIZE option in some locales.
- Get rid of old (now unused) strdup() compatibility function.
- Try harder to include the newline in a single error message write.
- Set am_daemon to -1 (from 1) when the daemon is run via rsh.
- Only disable --msgs2stderr for a normal (socket) daemon.
- Forward a -q to the server if --msgs2stderr was also specified.
- Added --no-msgs2stderr option to allow it to be overridden.
Avoid a newline issue during the output of --DEBUG=CSUM info from
both the server and the client -- we need to output the full message
with its newline as much as possible.
I replaced git-set-file-times with an improved version that I wrote
recently (in python3). A new script uses it to figure out the
last-modified year for each *.[ch] file and updates its copyright.
It also puts the latest year into the latest-year.h file for the
output of --version.
The %b and %c escapes were outputting cumulative values when logged via
--log-file only (the bug didn't affect daemon transfer logging or the
output of the client's --out-format info). Also unified the %b & %c
switch case to make it easier to maintain. Fixes bug 11496.
If the client is the sender and it is wanting to log deletes, the
current generator code neglects to send MSG_DELETED to the client side
unless some delete verbosity is enabled. With this new version on the
generator side, the logfile will now mention deletes, even if the
sending (client) side is an older rsync. Fixes bug 10182.