Commit Graph
86 Commits
Author SHA1 Message Date
Andrew Tridgell 8cbfd8a434 xattrs/backup: read source metadata through a held fd, not a path
The hardened receiver confined the destination side of an xattr/ACL copy (the
fsetxattr/acl_set_fd through a held O_NOFOLLOW fd) but still read the SOURCE side
by path: copy_xattrs() did get_xattr_names/get_xattr_data on the source path, and
make_backup() cached the backed-up file's ACL/xattr via get_acl()/get_xattr() by
path.  A local module writer could race the source/basis parent to a symlink
after the confined content/stat open and before that path-based metadata read,
so out-of-module xattrs/ACLs got copied onto an in-module destination or backup.

Thread a source fd through the read side, mirroring the existing dest-fd plumbing:
 - get_xattr_data(), get_xattr() and get_xattr_acl() gain an fd arg (get_xattr_names
   already had one) and use sys_fgetxattr/sys_flistxattr when fd >= 0; this also
   covers the --fake-super ACL-as-xattr read in get_rsync_acl().
 - copy_xattrs() gains a source_fd; copy_file() passes its held source fd (ifd)
   and keeps it open across the xattr copy (closing it on the fsync error path
   too); gen_entry_copy_xattrs() O_NOFOLLOW-opens the basis leaf under the
   confined resolver (with O_DIRECTORY for a directory basis) and passes it.
 - make_backup() pins the source leaf with a confined O_NOFOLLOW fd
   (backup_source_fd, like set_file_attrs's op_leaf_fd) and reads its ACL via
   get_acl_fdat() and its xattrs via get_xattr(fd); the in-place delta-backup in
   the generator pins fname the same way.  On a hardened receiver a raced/absent
   leaf skips the cache rather than reading through a flippable path.

Non-hardened receivers (fd < 0) keep the path-based behaviour unchanged.  The
basis COMPARE reads (the generator deciding a match) stay path-based: they never
copy out-of-module metadata onto a file, so they are not part of this sink.
2026-06-27 18:22:33 +10:00
Andrew Tridgell cf0c361988 flist/xattrs/generator/uidlist/clientserver: plug audit-reported leaks
Five error-path/cleanup memory leaks found by an external audit:

- flist.c send_file_name: free the ACL loaded by get_acl() when a later
  get_xattr() fails (and on the get_acl error path).
- xattrs.c copy_xattrs: free the xattr datum buffer when the setxattr fails.
- generator.c recv_generator: free real_sx at the cleanup label (the
  directory branch loaded its ACL via set_file_attrs but only the
  regular-file path freed it); zero-init real_sx so the early gotos are safe.
- uidlist.c send_one_list: free the strdup'd id-0 name after send_one_name.
- clientserver.c start_inband_exchange: free modname on the early error
  returns (it was freed only on the success path).

ASan/LSan regression tests cover the generator, uidlist and clientserver
leaks; the flist and xattrs leaks need a forced syscall failure and are
covered by the audit's standalone harnesses.

Reported-by: Leonid Bugaev <leonsbox@gmail.com>
(cherry picked from commit 078f3b99f4004510d418ee9d97d9b775dc8587bc)
2026-06-27 18:15:18 +10:00
Andrew TridgellandGreg Kroah-Hartman 4da4a7ce1e io/xattrs/rsync-ssl: EOF-sentinel guard, xattr ndx guard, gnutls CA refusal
io.c only treats a short read as the EOF sentinel when the fd is still
open; xattrs.c never stores a -1 from find_matching_xattr() and guards
ndx < 0 in set_xattr; rsync-ssl refuses the gnutls backend without
RSYNC_SSL_CA_CERT.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-06-22 09:21:59 +10:00
Andrew Tridgell a21e0f7660 xattrs: route copy_xattrs through a held fd, not lsetxattr-by-path
The secure_relpath hardening converted set_xattr()/rsync_xal_set() to
operate on a held, O_NOFOLLOW-opened fd so a parent-symlink race can't
redirect the metadata apply, but copy_xattrs() was missed: it still wrote
each attribute via sys_lsetxattr(dest, ...) on a path.  lsetxattr does
not follow the leaf symlink, but the kernel's path walk follows symlinks
in parent components.

All three callers pass peer-influenced destination paths, and two of them
called copy_xattrs() right after closing the fd the secure walker had
opened on that same path:

  util1.c copy_file()      -- close(ofd) then copy_xattrs(source, dest)
  generator.c back_file    -- close(f_copy) then copy_xattrs(fname, backupptr)
  generator.c alt-dest dir -- copy_xattrs(fnamecmpbuf, fname)

So on a non-chrooted daemon with -X plus --backup/--copy-dest/EXDEV-copy,
an attacker who races a parent-directory swap into the destination or
backup path between the close and the setxattr could set xattrs on a file
outside the module root (security.capabilities if root).

Give copy_xattrs() a dest_fd argument and use sys_fsetxattr() when one is
held, mirroring the fd>=0 ? fsetxattr : lsetxattr pattern already used by
rsync_xal_set/set_xattr.  Reorder the two regular-file callers to set the
xattrs before dropping ofd/f_copy, and add a gen_entry_copy_xattrs()
helper that pins the directory case via held_dfd_for + openat(O_NOFOLLOW)
the same way set_file_attrs() already does.

(cherry picked from commit 2f8142f39d1acef8fa08f394f86d669b6673dc82)
2026-06-15 15:24:42 +10:00
Andrew Tridgell 26783f76fe rsync/acls/xattrs: apply received xattrs and ACLs through the held fd
set_file_attrs() holds an O_RDONLY|O_NOFOLLOW fd (held_fd) for the entry's
regular-file/dir/FIFO inode so metadata is applied to the pinned inode rather
than by re-resolving the path, which a parent-symlink race could redirect.

- xattrs: the set/remove/list of -X xattrs use the f-variant calls on held_fd
  (lib/sysxattrs gains the wrappers); a raced FIFO can't block the open
  (O_NONBLOCK).
- ACLs (real root): get_acl_fdat()/set_acl_fdat() apply the ACL via lib/acl.c's
  fd path (xacl_*_fd), or setxattrat(AT_SYMLINK_NOFOLLOW) on the dirfd+leaf for
  a socket/device, covering both the access and default ACL.  pack_smb_acl()+
  change_sacl_perms() still build the entries, so the bytes written match
  acl_set_file() exactly.  With no safe primitive we skip rather than re-resolve
  the path.  The legacy path-based libacl calls remain only for the cross-tree
  (no held dirfd) and non-Linux cases.
- --fake-super ACL-as-xattr writes/deletes also go through held_fd, closing the
  last path-based metadata write under fake-super.
2026-06-15 15:24:42 +10:00
Andrew Tridgell cd61c1cc62 xattrs: cap peer-supplied wire data
Backport of the 3.5.0 fix bounding peer-supplied xattr wire data so a
malicious peer cannot drive an oversized allocation/read.

Test: xattr-wire-cap.
2026-06-12 15:55:30 +10:00
Andrew TridgellandClaude Opus 4.7 ddd7b59a4f defence-in-depth: bound wire-supplied counts and lengths
Multiple receiver-side fields read from the wire were trusted
without upper-bound checks. A hostile peer could either request
extreme allocations (DoS via --max-alloc) or, on platforms where
read_varint returned a negative value, push ~SIZE_MAX through the
size_t conversion to wrap downstream length checks.

Introduce read_int_bounded(), read_varint_bounded() and
read_varint_size() in io.c so wire-derived integer ranges are
checked at the read site rather than scattered across each
caller, with RERR_PROTOCOL on out-of-range input.

Apply the bounded primitives to:
  - sum->count (checksum count -- previously could overflow
    (size_t)count * xfer_sum_len on 32-bit with raised max-alloc)
  - xattrs: count, name_len, datum_len, plus rel_pos overflow
    detect to stop chain wrapping the num accumulator
  - acls: ida-entry count
  - flist: file mode S_IFMT validation, modtime_nsec range check
  - delete-stat counters in main: per-summand cap so the total
    can't overflow a signed 32-bit accumulator

Reporters include Joshua Rogers (checksum-count overflow finding).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 08:20:48 +10:00
Andrew TridgellandClaude Opus 4.7 72a6634479 syscall: add symlink-race-safe do_*_at() wrappers and harden secure_relative_open
Add the rest of the path-based syscall wrappers and migrate every
receiver-side caller:
  - do_lchown_at, do_rename_at, do_mkdir_at, do_symlink_at,
    do_mknod_at, do_link_at, do_unlink_at, do_rmdir_at,
    do_utimensat_at, do_stat_at, do_lstat_at

Same shape as do_chmod_at: open each parent under
secure_relative_open(), call the *at() variant against the dirfd,
fall through to the bare path-based syscall in non-daemon /
chrooted / absolute-path / no-parent cases. macOS's
setattrlist-based set_times tier is also routed through the
utimensat_at path on daemon-no-chroot.

Hardenings to secure_relative_open() itself:
  - confine basedir resolution under the same kernel mechanism
    used for relpath (basedirs from --copy-dest / --link-dest are
    sender-controllable in daemon mode)
  - reject any '..' component (bare '..', 'foo/..', 'subdir/..')
    so the per-component O_NOFOLLOW fallback can't escape
  - return the dirfd we built up from the per-component fallback
    when the caller passed O_DIRECTORY (otherwise every do_*_at
    failed with EINVAL on platforms without RESOLVE_BENEATH)

Adds testsuite/alt-dest-symlink-race.test and
testsuite/secure-relpath-validation.test (with t_secure_relpath
helper) as regression coverage for the new hardenings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 08:20:48 +10:00
Andrew TridgellandClaude Opus 4.7 d870b43a32 syscall+receiver: secure receiver-side do_chmod against symlink-race TOCTOU
CVE-2026-29518's fix routed the receiver's open() through
secure_relative_open(), but every other path-based syscall the
receiver runs on sender-controllable paths is vulnerable to the
same TOCTOU primitive. This commit closes the chmod variant.

Add do_chmod_at() that opens the parent of fname under
secure_relative_open() and uses fchmodat() against the resulting
dirfd. Gate the secure path on am_daemon && !am_chrooted (the same
gate use_secure_symlinks already uses for the receiver basis-file
open), so non-daemon callers and chrooted daemons keep the original
do_chmod() fast path.

Migrate the receiver-side do_chmod() call sites in delete.c,
generator.c, rsync.c, and xattrs.c.

Adds testsuite/chmod-symlink-race.test (with t_chmod_secure helper)
as regression coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 08:20:48 +10:00
Andrew Tridgell c009fcc8e6 xattrs: fixed count in qsort
this fixes the count passed to the sort of the xattr list. This issue
was reported here:

https://www.openwall.com/lists/oss-security/2026/04/16/2

the bug is not exploitable due to the fork-per-connection design of
rsync, the attack is the equivalent of the user closing the socket
themselves.
2026-05-07 06:35:39 +10:00
Wayne Davison e8161304f7 Use hashlittle2() for xattr hashing
- The non-zero key code is now in hashtable.c
- The hashtable_create() code already checks for OOM
2022-09-13 22:43:01 -07:00
Wayne Davison 7e2711bb2b Improve various things in the checksum code
- 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.
2022-09-10 11:39:37 -07:00
Wayne Davison c3b553a93f Preparing for release of 3.2.4pre2 2022-01-15 17:21:01 -08:00
Wayne Davison 3e44bbd313 Preparing for release of 3.2.4pre1 2022-01-02 15:13:19 -08:00
TomasKorbarandWayne Davison b2e16facd4 Eventually add write permission when setting extended attributes (#212)
* Eventually add write permission when setting extended attributes

When we need to set extended atributes of file which does not
allow write then temporarily add write permission and after
attributes are set, remove it again.

Resolves #208

Co-authored-by: Wayne Davison <wayne@opencoder.net>
2021-09-26 16:34:15 -07:00
Wayne Davison 8f1511184a Make gcc die on init overflow of an array.
- 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).
2020-09-29 13:18:28 -07:00
Wayne Davison 91fff802b9 Check for overflow the right way. 2020-07-12 22:45:01 -07:00
Wayne Davison 0eb82a7c90 Fix xattr issue with MIGHT_NEED_PRE.
Fixes bugzilla 13113.
2020-07-07 20:07:31 -07:00
Wayne Davison fb6fabc116 Fix an xattr free of the wrong object.
In uncache_tmp_xattrs() the code used to find the value to unlink,
update the single-linked list, and then free the wrong pointer.
This fixes bug #50.
2020-07-07 14:25:58 -07:00
Wayne Davison 11eb67eec9 Some memory allocation improvements
- 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.
2020-06-25 20:54:21 -07:00
Wayne Davison e63ff70eae Some indentation fixes. 2020-06-13 19:15:02 -07:00
Wayne Davison f800557824 Tweak the hashtable routines to be a little clearer and easier. 2020-06-12 17:42:41 -07:00
Wayne Davison 3ba4db7030 Two more spelling fixes and some year updates. 2020-04-16 09:31:02 -07:00
Wayne Davison d29702134a Spelling fixes from a Fossies run done by Jens. 2020-04-15 17:42:23 -07:00
Wayne Davison 3e2e4b5a33 Tweak the copyright year. 2019-03-16 09:15:49 -07:00
Wayne Davison 473108ae6e Tweak copyright date. 2018-01-14 19:55:07 -08:00
Wayne Davison 9f5dc9309d Use the right sum len. 2017-11-07 13:32:10 -08:00
Wayne Davison 47a63d90e7 Enforce trailing \0 when receiving xattr name values.
Fixes bug 13112.
2017-11-05 11:46:09 -08:00
Wayne Davison 3fb1634f21 Fix possible buffer overrun for some large name_len values.
Fixes bug 12568.
2017-10-07 17:54:05 -07:00
Wayne Davison 87bc224011 Add a way to specify xattr name filtering. 2017-01-22 16:01:45 -08:00
Wayne Davison a4e8b552d6 Join some lines. 2017-01-22 15:55:54 -08:00
Wayne Davison 62652202c4 Get rid of some superfluous double-quotes in error messages. 2017-01-22 15:42:36 -08:00
Stefan Metzmacher 6e3b2102bc xattrs: maintain a hashtable in order to speed up find_matching_xattr()
As a testcase I've used one directory on gpfs with 1000000 files,
each with an xattr called 'name$i' having a value of 'value$i'.
So we also have 1000000 unique xattrs. The source and dest directories
are already in sync before. So the rsync command is basically a noop,
just verifying that everything is already in sync.

The results before this patchset are:

  [gpfs]# time rsync -a -P -X -q source-xattr/ dest-with-xattr/

  real    8m46.191s
  user    6m29.016s
  sys     0m24.883s

  [gpfs]# time rsync -a -P -q source-xattr/ dest-without-xattr/

  real    1m58.462s
  user    0m0.957s
  sys     0m11.801s

With the patchset I got:

  [gpfs]# time /gpfs/rsync.install/bin/rsync -a -P -X -q source-xattr/ dest-with-xattr/

  real    2m4.150s
  user    0m1.917s
  sys     0m17.077s

  [gpfs]# time /gpfs/rsync.install/bin/rsync -a -P -q source-xattr/ dest-without-xattr/
  real    1m59.534s
  user    0m0.924s
  sys     0m11.599s

It means the time in userspace dropped from 6m29.016s down to 0m1.917s!
Without -X we get ~ 0m0.9s with or without the patch.

Part of a patchset for bug 5324.
2016-08-14 14:37:49 -07:00
Stefan Metzmacher 6eb71beaff xattrs: introduce a rsync_xa_list struct as layer between two nested item_lists
We have the global 'item_list rsync_xal_l', this maintains an array
of rsync_xa_list structure, one per file.

Each rsync_xa_list structure maintains an array of rsync_xa structure,
while each represent a single xattr with name and value.

Part of a patchset for bug 5324.
2016-08-14 14:20:16 -07:00
Stefan Metzmacher 39d7e3ec25 xattrs: let rsync_xal_store() return ndx.
Part of a patchset for bug 5324.
2016-08-14 13:54:34 -07:00
Stefan Metzmacher ac97bc14f6 xattrs: add const to empty_xattr
Part of a patchset for bug 5324.
2016-08-14 13:54:34 -07:00
Wayne Davison a720d81d0a Fix "could not find xattr #1" errors.
The abbreviated-xattr code can get requests that are not in the same
order as the xattr list, so we need to support wrap-around scanning
of the available xattrs. Fixes bug 6590.
2016-06-26 12:06:09 -07:00
Wayne Davison a5a7d3a297 Add --checksum-choice option to choose the checksum algorithms. 2016-05-01 17:06:54 -07:00
Wayne Davison 453914e35b Update the copyright year. 2015-08-08 12:47:03 -07:00
Wayne Davison 288e64a79f Avoid an xattr-finding glitch on the receiver.
Fixes bug 9594.
2014-05-26 14:08:31 -07:00
Wayne Davison dfa5b49110 Bump the year to 2014. 2014-01-26 09:29:15 -08:00
Wayne Davison bba31ddf12 Avoid ACL and/or xattr lookups on IS_MISSING_FILE() entries.
Fixes bug 10381.
2014-01-19 12:24:01 -08:00
Wayne Davison 836e0c5df4 Create and use write_bigbuf() function for extra-large buffer sizes. 2013-11-25 13:12:35 -08:00
Wayne Davison 7e1a9c4d79 Update copyright year. 2013-01-19 11:05:53 -08:00
Wayne Davison 15df927ae2 Fix xattr memory leak. Fixes bug 8475. 2011-09-22 09:13:31 -07:00
Wayne Davison 6500e0769a Avoid reading ACL/xattr info on filetypes not being copied.
Make OS X avoid xattr access on device/special files.
Fixes bug 5458.
2011-01-03 11:20:04 -08:00
Wayne Davison 14013906ca Use full_fname() for system error messages. 2011-01-01 13:09:58 -08:00
Wayne Davison 9cb20c605f Fix rsync_xal_set reference in an error. 2010-09-06 08:09:46 -07:00
Wayne Davison 0f6b683c8e Refer to the right lsetxattr() caller in a error message. 2010-07-03 08:14:02 -07:00
Wayne Davison e4c598c830 Make some RERR_* choices better, and another noop_io_until_death() tweak. 2009-11-16 12:35:17 -08:00