Skip to content

receiver.c: Tighten alt-dest path resolution and partial-dir state validation - #1077

Open
seks99x wants to merge 1 commit into
RsyncProject:masterfrom
seks99x:harden-alt-dest-state
Open

receiver.c: Tighten alt-dest path resolution and partial-dir state validation#1077
seks99x wants to merge 1 commit into
RsyncProject:masterfrom
seks99x:harden-alt-dest-state

Conversation

@seks99x

@seks99x seks99x commented Sep 4, 2026

Copy link
Copy Markdown
Member

This introduces a couple of architectural best-practices to harden the receiver's state machine and harmonize symlink handling across the delta-basis engine, addressing protocol edge-cases reported by Fyyre (fyyre@fyyre.net).

  1. Strict State Validation for partial_dir (receiver.c)
    Added explicit validation to ensure one_inplace is only triggered when partial_dir is actually configured by the client and the FNAMECMP_PARTIAL_DIR token is legitimate. This prevents any potential protocol state confusion where a peer sending unexpected/forged tokens could bypass temp-file cleanup or misroute in-place writes.

  2. Harmonized Leaf Symlink Handling (receiver.c)
    When an operator path was sent, secure_basis_open() called open_no_attacker_symlinks() for partial-dir/link-dest/fuzzy-dest path resolving, which followed leaf symlinks. Since standard operator-path behavior dictates that leaves should not be followed (consistent with standard do_*_at style, copy_file()) I've updated this logic to enforce O_NOFOLLOW on the final component and walk the parent with owner_walk_parent, bringing our path resolution into perfect alignment with standard operator-path behavior.

This introduces architectural best-practices to harden the receiver's state machine
and harmonize symlink handling across the delta-basis engine, addressing protocol
edge-cases reported by Fyyre (James).

- receiver.c (recv_files): Added explicit validation to ensure one_inplace is
  only triggered when partial_dir is configured and the FNAMECMP_PARTIAL_DIR
  token is legitimate.
- receiver.c (secure_basis_open): Enforced O_NOFOLLOW on leaf components when
  resolving operator-supplied paths, aligning it on operator-path behavior.

Co-authored-by: Fyyre <fyyre@fyyre.net>
@seks99x
seks99x force-pushed the harden-alt-dest-state branch 4 times, most recently from a68e9b6 to 90b5f7f Compare September 4, 2026 04:55

@steadytao steadytao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The partial_dir state guard looks reasonable, but the leaf-opening rewrite changes established operator-path and --insecure-links behaviour without tests. Please preserve the explicit compatibility opt-out and add focused positive and negative cases before this merges. The diff also needs normal rsync indentation and comment cleanup.

Comment thread receiver.c
dfd = owner_walk_parent(p, &leaf);
if (dfd < 0)
return -1;
fd = openat(dfd, leaf, flags | O_NOFOLLOW, mode);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bypasses the existing operator-path symlink policy and unconditionally rejects the leaf. In particular, --insecure-links can no longer restore legacy following for an operator-owned leaf symlink while open_no_attacker_symlinks() deliberately follows trusted leaves or honours the explicit opt-out. Please preserve that contract and add regressions for a trusted leaf, an untrusted leaf and --insecure-links.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants