Three receiver security fixes from the audit: 1. --temp-dir symlink escape (High): file_open_temp_dir() opened the client-controlled scratch dir with a bare open(), so a symlink planted under the receive root let a peer redirect receiver scratch files outside the authorized root. The opened dir is now judged by the REAL path of its fd (via /proc/self/fd), and any target outside the authorized receive root is refused with a logged error (EACCES). An in-root symlink (the EXDEV cross-filesystem fallback case) still works, and the no-root local batch path is unchanged. 2. setuid/setgid/sticky under SUPER_MODE_OFF (High): the special bits were applied under --perms (and via --chmod) even when the connection forbade super-user activities. FileAttrPolicy gains super_permitted, set by file_attr_policy_from_config() from privilege_super_mode_permitted(); metadata_mode_for_policy(), the symlink path, the special-node creation path, and the deferred directory-mode apply now strip the special bits when it is false. Exact rsync semantics are preserved when permitted. 3. daemon umask (Low): daemonize() forced umask(0), so implied parent directories created without -p were world-writable 0777. Set the conventional daemon umask 022 instead (rsync never forces 0); -p/-a mode preservation is unaffected because it restores modes via fchmod. Tests: new unit tests for file_open_temp_dir confinement and the masked/unmasked special-bit policy (incl. the --chmod path), a daemon world-writable-dir regression test, an integration escape test, and a root-only integration test asserting special bits are masked without --allow-super. The old cross-filesystem test encoded the vulnerable behavior (symlink target outside the root) and is replaced by the escape test; the EXDEV fallback code is retained for in-root links.
45 lines
2.2 KiB
C
45 lines
2.2 KiB
C
#ifndef FILE_ATTR_H
|
|
#define FILE_ATTR_H
|
|
|
|
#include "config.h"
|
|
#include <stdbool.h>
|
|
#include <sys/stat.h>
|
|
|
|
/*
|
|
* Per-attribute receiver policy for applying a transmitted FileMetadata. This
|
|
* is the split-out replacement for the former single use_metadata bundle: each
|
|
* flag is applied independently, matching rsync's -p/-t/-o/-g/-E/-U semantics.
|
|
* `use_metadata` remains the transport/presence gate (whether the metadata frame
|
|
* travelled at all); this struct decides which attributes are ACTUALLY applied.
|
|
*
|
|
* It lives in its own header (rather than metadata.h) because xattr.h's
|
|
* fake_super_restore_fd() takes one and metadata.h <-> file_types.h form an
|
|
* include cycle that must not be entered from xattr.h.
|
|
*
|
|
* The mode leg is: perms wins over executability; an exec-bits-only change is
|
|
* made only when perms is off; when neither is set the receiver deliberately
|
|
* sets no source mode. file.c then substitutes the pre-existing destination
|
|
* mode for a brand-new destination with metadata it uses the sanitized
|
|
* source-mode-&-umask base (S_IWGRP|S_IWOTH cleared), and the fixed 0644
|
|
* default only when no metadata is available at all, so a no--p overwrite
|
|
* does not lose the destination's perms.
|
|
*/
|
|
typedef struct FileAttrPolicy {
|
|
bool perms; /* config->preserve_perms: apply the source mode bits */
|
|
bool times; /* config->preserve_times: apply the source mtime */
|
|
bool atimes; /* config->preserve_atimes (-U): apply the source atime */
|
|
bool executability; /* config->use_executability (-E): exec-bits-only mode */
|
|
/* privilege_super_mode_permitted(): when false (SUPER_MODE_OFF / --no-super,
|
|
or a daemon that did not grant `client owner = yes`), the setuid/setgid/
|
|
sticky bits are stripped from every applied mode (source mode and any
|
|
--chmod result) even under --perms. When true, rsync's exact semantics are
|
|
preserved: -p copies the special bits and the kernel decides. */
|
|
bool super_permitted;
|
|
} FileAttrPolicy;
|
|
|
|
/* Build the per-attribute policy from a connection's Config. A NULL config
|
|
* yields the all-off policy (no attribute application). */
|
|
FileAttrPolicy file_attr_policy_from_config(const Config* config);
|
|
|
|
#endif
|