feat(p7-super): implement --super/--no-super safe-subset privilege gate (protocol 2.18.0)

Add the receiver-side --super / --no-super tri-state (Config->super_mode)
under the safe-subset + clear-refusal privilege model: FastSync never
elevates privileges, it only permits super-user attempts that are already
confined fd-relative below the authorized receive root.

- identity: privilege_super_permitted() gate (OFF=false, ON=true, AUTO follows
  geteuid()==0); identity_apply_ownership/_link become no-ops when not
  permitted; --super with no explicit identity policy implies raw numeric-id
  preservation (explicit usermap/groupmap/chown/numeric-ids still win); warn
  exactly once when --super is requested by a non-root receiver.
- file_receive: gate char/block device-node creation on the gate; FIFO/socket
  handling is unchanged.
- wire: trailing super_mode int after the --iconv spec, validated 0..2 in
  receive_privilege_options and validate_received_config; PROTOCOL_VERSION
  2.17.0 -> 2.18.0; version-sensitive tests and docs updated.
- CLI: --super/--no-super parsed explicitly before the generic --no-* branch
  (malformed --super=x rejected); usage text added.
- tests: config wire round-trip + invalid-value rejection, privilege-gate mode
  unit test, CLI parse test, integration transfer + root-gated ownership
  suppression/appliance tests.
- docs: RSYNC_COMPAT --super row + Wave E note, protocol mentions, README.
This commit is contained in:
2026-09-12 12:01:19 +02:00
parent f64d252faf
commit a785ec13c4
13 changed files with 366 additions and 26 deletions
+14
View File
@@ -848,6 +848,20 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
config->no_motd = true;
continue;
}
/* "--super" / "--no-super" are real rsync option names controlling the
* receiver's super-user activity policy (ownership, device nodes), not a
* Boolean pair for the generic --no-* negation branch: both map onto the
* Config->super_mode tri-state. Handle them explicitly (exact match only,
* so a malformed "--super=x" still falls through to the unknown-option
* error) before the generic negation branch would mis-reject "--no-super". */
if (strcmp(argv[i], "--super") == 0) {
config->super_mode = SUPER_MODE_ON;
continue;
}
if (strcmp(argv[i], "--no-super") == 0) {
config->super_mode = SUPER_MODE_OFF;
continue;
}
if (strncmp(argv[i], "--no-", strlen("--no-")) == 0) {
if (strcmp(argv[i], "--no-delta") == 0)
no_delta = true;