Skip to content

fix(metadata): wire fake-super through receiver and gate mknod#3530

Merged
oferchen merged 3 commits into
masterfrom
fix/fake-super-receiver-mknod-gate
May 1, 2026
Merged

fix(metadata): wire fake-super through receiver and gate mknod#3530
oferchen merged 3 commits into
masterfrom
fix/fake-super-receiver-mknod-gate

Conversation

@oferchen
Copy link
Copy Markdown
Owner

@oferchen oferchen commented May 1, 2026

Addresses findings F1 (HIGH) and F2 (HIGH) from docs/audits/fake-super-privilege.md (audit landed in #3435).

Summary

  • F2 (gate mknod) Add create_fifo_with_fake_super and create_device_node_with_fake_super to metadata::special. When fake_super is true these substitute a regular 0600 placeholder file for the privileged mknod(2) call, mirroring upstream syscall.c:do_mknod()'s am_root < 0 branch. The local-copy executors copy_device and copy_fifo now thread MetadataOptions::fake_super_enabled() through these wrappers, then store the would-be mode/uid/gid/rdev in the user.rsync.%stat xattr via store_fake_super.
  • F1 (use load_fake_super) apply_ownership_via_fake_super now reads the existing xattr via load_fake_super and short-circuits when the stored stat already matches the desired one, mirroring upstream xattrs.c:read_stat_xattr / set_file_attrs's "no-op when state matches" fast path.
  • S_IFMT bug Fix a latent loss of file-type bits: entry.permissions() returns mode & 0o7777, but xattrs.c:set_stat_xattr encodes the full mode (S_IFMT + perms). Use entry.mode() so a later read_stat_xattr can rebuild the file type.

All new code is gated #[cfg(unix)] / #[cfg(all(unix, feature = "xattr"))]. Non-Unix targets fall through to the existing no-op stubs. No new unsafe code; this change uses only safe std / rustix / xattr crate APIs.

Files touched

  • crates/metadata/src/special.rs (+177) fake-super placeholder helper, public wrappers, 3 unit tests
  • crates/metadata/src/lib.rs (+4) re-exports for the new wrappers
  • crates/metadata/src/apply/ownership.rs (+13) load_fake_super short-circuit, S_IFMT fix
  • crates/metadata/src/apply/tests.rs (+124) 3 integration tests for the receiver-side wire path
  • crates/engine/src/local_copy/executor/special/device.rs (+35) thread fake_super through copy_device, capture xattr after placeholder
  • crates/engine/src/local_copy/executor/special/fifo.rs (+32) same change for copy_fifo

Upstream references cited in code

  • xattrs.c:set_stat_xattr() and read_stat_xattr()
  • syscall.c:do_mknod() (am_root < 0 branch, lines 90-174)
  • rsync.c:set_file_attrs()

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features --no-deps -- -D warnings
  • cargo nextest run -p metadata --all-features covers:
    • fake_super_replaces_mkfifo_with_regular_placeholder
    • fake_super_replaces_mknod_with_regular_placeholder
    • create_fifo_with_fake_super_disabled_creates_real_fifo
    • fake_super_writes_rsync_stat_xattr_for_regular_file
    • fake_super_does_not_chown_destination
    • fake_super_skips_rewrite_when_xattr_already_matches
  • cargo nextest run -p engine --all-features covers existing copy_fifo / copy_device paths (unchanged when fake_super=false)
  • CI Linux musl, macOS, Windows all green (Windows hits the #[cfg(not(unix))] arms)

Follow-ups (out of scope, separate PRs)

  • F3: daemon fake super = yes still does not demote am_root or rewrite --super. Tracked in audit; needs clientserver.c:1080-1107 parity.
  • F5: chmod path doesn't honour fake-super; touch-up-dirs missing.
  • F6: am_root() is binary instead of upstream tri-state.
  • F7-F10 as enumerated in the audit.

Implements F1 and F2 from docs/audits/fake-super-privilege.md (#3435):

- F1: load_fake_super was exported but never called. Add a fast-path
  read in apply_ownership_via_fake_super so a re-application skips the
  rewrite when the existing rsync.%stat xattr already matches the
  desired stat. Mirrors xattrs.c:read_stat_xattr() / set_file_attrs().
- F2: copy_device and copy_fifo invoked mknod(2) unconditionally,
  failing without CAP_MKNOD under --fake-super. Add
  create_{fifo,device_node}_with_fake_super wrappers in metadata::special
  which substitute a regular 0600 placeholder when fake-super is active,
  mirroring upstream syscall.c:do_mknod()'s am_root < 0 branch. After
  placeholder creation, the local-copy executors store the would-be
  mode/uid/gid/rdev in the rsync.%stat xattr so the destination can be
  faithfully restored on a later --fake-super pull.

Also fixes a latent S_IFMT loss in apply_ownership_via_fake_super:
entry.permissions() drops the file-type bits, but xattrs.c:set_stat_xattr
encodes the full mode. Use entry.mode() instead.

Tests:
- 3 unit tests in metadata::special covering fake-super placeholder
  substitution for FIFO and device, and the disabled fall-through.
- 3 integration tests in metadata::apply::tests covering the wire-side
  receiver path: xattr is written, chown is not invoked, and an
  identical second application is a no-op.

All new code is gated by #[cfg(unix)] / #[cfg(all(unix, feature =
"xattr"))]. Non-Unix targets continue to fall through to the existing
no-op stubs. No unsafe code is introduced; metadata is on the
permitted-unsafe list but this change uses only safe std/rustix APIs.
@github-actions github-actions Bot added the bug Something isn't working label May 1, 2026
@oferchen oferchen merged commit 252a5ef into master May 1, 2026
41 checks passed
@oferchen oferchen deleted the fix/fake-super-receiver-mknod-gate branch May 1, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant