Commit Graph
11 Commits
Author SHA1 Message Date
TapTap 134dcd74cc refactor: drop dead filter_rules_apply, unify set_error, dedup path_is_within 2026-09-21 21:09:05 +02:00
TapTap 23552e823d feat(cli): rsync short-option clustering and inline/attached values (#285)
Implement rsync 3.4.1 client-CLI parity:
- cluster boolean shorts (-av, -aAX, -rlpt) and accept attached values
  (-B1048576, -essh, -Mfoo); add the -r, -b, -L and -B short aliases
  (-r is a faithful no-op since FastSync is always recursive)
- stop OPT_NOOP (-s/--secluded-args, -r/--recursive) from swallowing the
  next argv
- add inline --opt=value for every value-taking long option, including
  --exclude/--include/--exclude-from/--include-from/--log-file (#291)
- accept --port on the server CLI in addition to -p (#296)
- reject unknown flags naming the flag and stating it is unsupported

Unit tests cover clustering, attached/inline values, the OPT_NOOP
argument-consumption fix and rejected shorts.
2026-09-15 21:12:53 +02:00
TapTap 825ba69753 fix(server): reject --allow-super with --stdio, fix module host-list append
Re-review findings on the C3/C4 hardening branch:

- --stdio is the SSH transport whose remote argv is composed by the client
  (including via --remote-option), so accepting --allow-super there let a
  client defeat the C3 secure default for a root receiver.  Reject it at CLI
  parse time (standalone TCP only) and force the process-global flag off for
  --stdio as defense in depth.  Correct the help text and README/RSYNC_COMPAT:
  the --stdio argv is client-composed, super stays off, and a forced command is
  needed if the default must hold.
- daemon_conf: the per-module 'hosts allow'/'hosts deny' call sites passed
  module_name and replace in the wrong order, so multiple lines replaced
  instead of appended and the empty-value error omitted the module name.  Pass
  (module->name, false) like the global keys; add a unit test for two
  per-module allow/deny lines appending.
- tls: read the client CN via ASN1_STRING_to_UTF8 so an exactly-required-length
  name is accepted and only actual over-length CNs are rejected.
2026-09-14 17:16:01 +02:00
TapTap 9da5a0a9ed fix(server): gate --force by --allow-delete and secure root super default
C2: --force is deletion authority (an incoming regular file may remove a
non-empty destination directory tree, and --delete-missing-args may
remove a non-empty directory mirror), but it was not masked by the
operator --allow-delete policy.  The handler now clears
config->force_delete unless --allow-delete was given, exactly like
--delete and --delete-missing-args.

C3: a standalone TCP / --stdio server running as root defaulted to
SUPER_MODE_AUTO, so an untrusted client --devices/--write-devices/
--super could make it create device nodes, write raw devices, or apply
client-chosen ownership.  A privileged standalone receiver now forces
SUPER_MODE_OFF unless the operator opts in with the new server-only
--allow-super flag.  Non-root receivers are unchanged, and the daemon
path keeps its per-module `client owner = yes` gate.  --allow-super is
rejected with --no-super or --daemon.

C6: tls_client_identity_allowed now rejects a CN whose reported length
reached the buffer bound, so a truncated over-long CN cannot be matched
by a required --client-cn prefix.

Tests: an integration regression proving --force cannot replace a
destination directory without --allow-delete; standalone-default tests
for --copy-as refusal and (root-only) skipped device creation; a CLI
unit test for the new flag.  The integration shared_server fixture opts
in with --allow-super so the existing root-only ownership/device/copy-as
tests continue to exercise the opted-in configuration.  README and
RSYNC_COMPAT document the flag and the force/delete gating.
2026-09-14 16:09:44 +02:00
TapTap eaf67f6257 fix(a7-auth): address SCRAM auth review findings A-G
- tests: pass CREDENTIAL_KEY_LEN to unhex for the 32-byte KAT proof/sig
  (sizeof(expect) is 348, over-reading the 65-byte hex literal under ASan)
- credentials: close the username-enumeration oracle with a store-wide
  dummy_key and a deterministic per-username dummy salt; make the store's
  iteration count uniform (reject intra-file and layered disagreements) and
  answer a miss with the store-wide count; run the constant-time key compare
  even when found=false and fold the decision with bitwise AND
- credentials_compute_keys: enforce [CREDENTIAL_MIN_ITERS, CREDENTIAL_MAX_ITERS]
- tests: recompute the whole KAT independently at CREDENTIAL_DEFAULT_ITERS
  (600000) and pin the golden store line; add non-uniform-store rejection,
  bound and deterministic-dummy-salt assertions
- server: send exactly one generic STATUS_AUTH_FAILED on every failure path
  (including credentials_get_verifier failure); route all handshake exits
  through one burn path
- credentials/server: burn the base64 decoders' scratch on error, the
  hash_store_line base64/line buffers on failure, and all handshake key/proof
  material
- fuzz: guard the auth-offset scan against size_t underflow and use a found flag
- docs: drop stale digest wording, use CREDENTIAL_MIN_ITERS as the --iterations
  bound, document 0600 output for --hash-credentials (plus a stderr warning on
  a group/other-accessible stdout file), and describe the deterministic dummy
  salt in the no-oracle claims
2026-09-12 17:56:52 +02:00
TapTap 8c94ec9886 feat(a7): SCRAM-SHA-256 daemon auth to replace replayable digest
Replace the challenge-less static-SHA-256 daemon bearer credential with a
SCRAM-SHA-256-style challenge/response and a salted PBKDF2 verifier store.
PROTOCOL_VERSION 2.18.0 -> 2.19.0; legacy user:SHA256HEX stores hard-reject.

- credentials: b64/rand/PBKDF2/HMAC primitives, verifier store parser,
  constant-time proof verify + ServerSignature, --hash-credentials helper
- config: auth block is now [present][username]; client runs the challenge
  exchange; config_burn_auth wipes plaintext/derived secrets (A7-4)
- server: gate drives the challenge, dummy verifier for unknown/off-list users
- tests: independent Python KAT, replay + legacy integration tests, fuzz paths
- docs: new store format, --hash-credentials, 2.19.0 bump

TLS verification behavior (A7-3/S1) is intentionally unchanged.
2026-09-12 17:19:33 +02:00
TapTap 7e45891257 refactor(cli): dedupe server/client option parsing and tighten CLI tests
- server_cli: handle --password-file/--early-input/--iconv via arg_has_value
  in one place, removing the unreachable duplicate separate-form arms while
  keeping both --opt VALUE and --opt=VALUE working
- client_cli: factor the triplicated --delta-block/--block-size range check
  into set_delta_block_size(); drop the redundant use_metadata assignment
  after identity_parse_copy_as (the parser already forces it)
- tests: cover both spellings of --iconv/--delta-block, make the archive
  short-form test actually call parse_args, add delta-block invalid cases
2026-09-12 14:54:47 +02:00
TapTap fdc0f238c9 fix(p7-privilege): harden copy-as/super gates, own dirs/specials
- fake-super owner replay honors --no-super and an active --copy-as
- copy-as/identity ownership now applied to directories and special nodes
- reject copy_as_set && !use_metadata (receiver + client --no-preserve)
- daemon refuses --copy-as; add server-side --no-super operator veto
- implement identity_copy_as_refused/identity_copy_as_active
- reject copy-as ids that overflow int32; escape spec in log errors
- copy-as chown EPERM/EACCES logged at ERROR (still non-fatal)
- identity_wire_valid copy-as bounds; CLI help and RSYNC_COMPAT docs
- add unit tests and root-gated integration coverage
2026-09-12 12:34:43 +02:00
TapTap 6e02a24232 feat(p6-iconv): --iconv charset conversion + PROTOCOL 2.16.0 2026-09-10 17:13:01 +02:00
TapTap dd5ae60459 feat(d5-daemon-auth): password auth, --password-file, --early-input 2026-09-10 12:50:56 +02:00
TapTap b3d7d64347 feat(d5-daemon-core): daemon lifecycle, module config, ::dest, PROTOCOL 2.15.0 2026-09-09 17:48:07 +02:00