Skip to content

v1.0.0 release + v1.1 features + crates.io publish prep - #22

Merged
StrongWind1 merged 56 commits into
mainfrom
v1-parity-and-fixes
Aug 8, 2026
Merged

v1.0.0 release + v1.1 features + crates.io publish prep#22
StrongWind1 merged 56 commits into
mainfrom
v1-parity-and-fixes

Conversation

@StrongWind1

@StrongWind1 StrongWind1 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

This branch covers three phases of work since v0.8.0: the v1.0.0 release (auth flavor enumeration, IANA registries, crate restructuring), post-v1.0.0 feature work (NFSv4 escape, handle matrix, SECINFO_NO_NAME oracle, OS fingerprinting), and crates.io publish preparation (standardized READMEs, version bump, metadata).

v1.0.0 release highlights

  • Comprehensive auth flavor enumeration: SECINFO GSS decoding, AUTH_TOOWEAK oracle (F-1.8), AUTH_SHORT finding (F-3.9), unified flavor_name()
  • Complete IANA registries: 1251 RPC program numbers, 19 auth flavors, 19 auth status codes
  • 6 new analyzer checks: AUTH_DH (F-3.7), AUTH_TLS (F-3.8), AUTH_SHORT (F-3.9), PATHCONF chown (F-4.6), case-insensitive FS (F-5.7), AUTH_NONE leak (F-5.8)
  • NFSv2 on PooledTransport (pooling, circuit breaker, stealth, SOCKS5)
  • NFSv4 all 37 v4.0 ops + v4.1/v4.2 extensions (EXCHANGE_ID, SECINFO_NO_NAME, GETDEVICEINFO, SEC_LABEL)
  • Eight-crate workspace rename: nfswolf-* to vendor-neutral names (onc-xdr, onc-rpc-client, nfs-v3, etc.)

Post-v1.0.0 features

  • NFSv4 escape via LOOKUPP (pseudo-filesystem traversal to real root)
  • Handle acquisition matrix: MOUNT v1/v3 cross-version with pad/trim variants
  • SECINFO_NO_NAME fallback + WRONGSEC oracle for auth negotiation
  • OS fingerprinting from EXCHANGE_ID vendor/version strings
  • pNFS topology enumeration (GETDEVICEINFO/GETDEVICELIST)
  • Per-path SECINFO probing and xattr detection
  • Golden vector tests for MOUNT and portmapper wire types

crates.io publish prep

  • Standardized all 8 crate READMEs: centered headers, 6 badges, nav links, API reference tables, ASCII dependency graph, protocol coverage, safety sections
  • Bumped all crate versions to 1.0.0, removed publish = false
  • Added documentation field (docs.rs) to all packages
  • Fixed readme path: workspace-inherited resolved outside the package; now crate-local
  • Fixed provenance for nfs-v2 and nfs-v4 (were incorrectly listed as Vaiz/nfs3 derived)
  • Top-level README: added crates.io/docs.rs badges, cargo install option, protocol crates table, WEPWolf

Testing

make check-all passes: fmt, clippy (zero warnings), cargo deny, 543 tests, doc, ascii-check, lf-check, machete.

Publish order (after merge)

1. onc-xdr-derive  (--no-verify, circular dev-dep with onc-xdr)
2. onc-xdr
3. onc-rpc-client
4. onc-rpcbind + nfs-mount
5. nfs-v2 + nfs-v3 + nfs-v4
6. nfswolf

Error taxonomy (CRATE-DESIGN.md rules 3-6):
- Rename NfsStat to Nfs2Stat, add Unknown(u32) for unrecognized wire values
- Rename MountError::Denied to MountError::Status for cross-version consistency
- Remove blanket From<E> for MountError<E>; construct Rpc(e) explicitly
- Drop thiserror from nfswolf-nfs3; manual Display/Error impls
- Add Nfs4Status classification methods (is_ok, is_permission_denied,
  is_stale, is_not_found, is_transient) and std::error::Error impl

Binary-level fixes:
- Fix auth.rs DRC comment: stamp is not part of the Linux knfsd DRC key
  (xid+proc+addr+version+arg_len+checksum); XID uniqueness is what matters
- Document owner-override in credential ladder (NFSD_MAY_OWNER_OVERRIDE,
  C702 sec. 12.3.3): owner UID is unconditionally tried regardless of mode
- Delete private is_permission_refusal from fuse.rs; use the library
  Nfs3Error::is_permission_denied instead (CRATE-DESIGN rule 5)
- Fix WebNFS public-handle proxy bypass in analyzer: _proxy parameter was
  accepted but never used, leaking operator IP on both v3 and v2 probes
…tack

Add src/proto/nfs2.rs with the Nfs2Client type alias and PooledNfs2 trait,
mirroring the existing PooledNfs3 pattern. Extract common pool/circuit/transport
setup into make_pooled_transport in probe.rs; add make_v2_client_with_hostname.

Migrate all NFSv2 code paths to the shared helpers:
- run_nfs2_shell: use make_mount_client + make_v2_client_with_hostname
- V2Ops: collapse change_identity from 44-line TCP reconnect to 4-line
  with_credential swap (zero network round trips)
- find_escape_v2: use make_v2_client_with_hostname, globals.hostname
- sweep_inodes_v2: use make_v2_client_with_hostname, globals.hostname
- Thread --aux-gids through all V2 credentials via build_gid_list

Every global option now applies equally to v2 and v3:
--proxy, --delay, --jitter, --uid, --gid, --hostname, --aux-gids,
--nfs-port, --privileged-port, --mount-port. The per-encode stamp
counter and circuit breaker carry over automatically from PooledTransport.

Net -61 lines of boilerplate.
Phase 0 completion:
- Null-string filename fingerprinting: send LOOKUP with zero-length name
  to classify server implementation (NFS3ERR_ACCES = spec-conformant,
  GARBAGE_ARGS = Linux knfsd, other = unknown). Added to scanner output.
- Execute-implies-read: secrets-scan now tries reading execute-only files
  (nfsd_permission falls back to MAY_EXEC, C702 sec. 12.3.3). Analyzer
  reports when execute-only files are readable.
- WebNFS public handle probe: try_webnfs_escape attempts the well-known
  public handles (v2: all-zero 32 bytes, v3: zero-length) with multi-
  component LOOKUP path traversal before falling back to handle forging.
- Metadata leak on NFS3ERR_ACCES: analyzer harvests post_op_attr from
  denied operations, reporting uid/gid/size/mtime on access denial.

Phase 1 foundation:
- Portmapper: implement PMAPPROC_SET (1), PMAPPROC_UNSET (2),
  PMAPPROC_CALLIT (5). All 6 procedures now covered.
- Program name table: program_name() and known_programs() with 10
  well-known RPC programs (NFS, mountd, NLM, NSM, ypserv, etc.).
  Wired into scanner and report output.
- Absorb udp.rs: RPC-over-UDP transport moved from src/proto/ to
  crates/nfswolf-rpc/src/transport/udp.rs where it belongs.
- Delete rpc_probe.rs: 266 lines of parallel RPC parsing removed.
  RpcClient already preserves PROG_MISMATCH version ranges; the
  workaround outlived the limitation it was written for. Scanner
  migrated to use RpcClient directly.
- trybuild tests: compile-fail test suite for xdr-derive with 4
  test cases (missing attr, multi-field variant, named fields,
  union type) plus round-trip pass tests.
Phase 0 final:
- NFSv4 on PooledTransport: add proto/nfs4/ with Nfs4Client type alias,
  PooledNfs4 trait (with_credential, machinename, uid, gid), and pooled
  compound helper. v4 shell now uses the same pool/circuit/stealth stack
  as v2/v3. Nfs4DirectClient remains for non-pooled contexts.

Phase 2 extractions:
- nfswolf-mount crate: extract MOUNT v1/v3 protocol into its own
  workspace crate (crates/nfswolf-mount/). Unified MountClient<T>
  parameterized by version. MountedHandle carries auth_flavors with
  accepts_auth_sys() and is_auth_sys_only() classification methods.
  Both nfswolf-nfs2 and nfswolf-nfs3 now depend on nfswolf-mount
  and re-export its types for backwards compatibility.
- Nfs2RawClient<T>: wire-level client added to nfswolf-nfs2. Takes
  wire types in and returns wire types out, including call_raw() for
  arbitrary procedure numbers. Completes the rule: every crate that
  exposes wire types must also expose a wire-level client.
- Nfs4Client<T>: generic client added to nfswolf-nfs4 (client.rs),
  mirroring Nfs3Client's pattern for the COMPOUND procedure.
Move portmapper v2 client (6 procedures: NULL, SET, UNSET, GETPORT,
DUMP, CALLIT), rpcbind v3/v4 client (GETTIME, GETSTAT), PortmapError,
all wire types, and the program_name/known_programs table from
crates/nfswolf-rpc/ into crates/nfswolf-rpcbind/.

The portmapper is program 100000 — a specific RPC service, not part
of the RPC machinery. nfswolf-rpcbind depends on nfswolf-rpc for the
transport seam; nfswolf-rpc no longer knows about portmapper concepts.

Net -656 lines from the RPC crate. All callers updated.
…vectors

Crate renames (CRATE-DESIGN.md target names):
  nfswolf-xdr-derive  →  onc-xdr-derive
  nfswolf-xdr         →  onc-xdr
  nfswolf-rpc         →  onc-rpc-client
  nfswolf-rpcbind     →  onc-rpcbind
  nfswolf-mount       →  nfs-mount
  nfswolf-nfs2        →  nfs-v2
  nfswolf-nfs3        →  nfs-v3
  nfswolf-nfs4        →  nfs-v4
All Rust import paths updated (onc_xdr, onc_rpc_client, nfs_v2, etc.).
Derive macro generates ::onc_xdr:: paths.

Publication metadata:
- keywords and categories on all 8 sub-crates
- [package.metadata.docs.rs] with all-features + docsrs cfg
- #[non_exhaustive] on all public enums (~50 enums)
- Independent versioning: each sub-crate at 0.1.0
- Pre-1.0 README.md for every sub-crate

Testing:
- Golden-vector tests for onc-xdr, onc-rpc-client, nfs-v2, nfs-v3, nfs-v4
- MSRV check target added to Makefile

529 tests passing across 3 feature matrix configurations.
…tion

Wire all 37 NFSv4.0 operations (RFC 7530 ops 3-39 + ILLEGAL) into
ArgOp and ResOpData with full Pack/Unpack. Previously had 9; now
complete. Supporting types: stateid4, open_owner4, lock_owner4,
open_claim4, createhow4, locker4, state_protect4_a, cb_client4,
nfs_client_id4, clientaddr4, OPEN4_RESULT_* flags, etc.

Recon features:
- SETCLIENTID compound builder convenience method for callback
  coercion probing (RFC 7530 §16.33)
- OPEN compound builder for honest write testing (OPEN4_SHARE_ACCESS_READ)
- RDMA presence detection: scanner checks rpcbind for rdma/rdma6 netids
  and probes port 20049; reports when RDMA transport is available
- NFSv4 public filehandle escape: PUTPUBFH + LOOKUP(traversal path) + GETFH
  added to escape subcommand alongside existing v2/v3 probes

560 tests passing across 3 feature matrix configurations.
Add v41 and v42 Cargo features to nfs-v4 crate (v42 implies v41).

v4.1 operations (RFC 8881), behind #[cfg(feature = "v41")]:
- EXCHANGE_ID (op 42): server implementation fingerprinting via
  nfs_impl_id4 (vendor domain, product name, build date)
- SECINFO_NO_NAME (op 52): security flavors for current filehandle
  without needing a filename; strictly better than v4.0 SECINFO
- GETDEVICEINFO (op 47): pNFS device addressing — data-server IPs
- GETDEVICELIST (op 48): enumerate all storage devices behind the
  filesystem for lateral-movement reconnaissance

v4.2 attribute (RFC 7862), behind #[cfg(feature = "v42")]:
- FATTR4_SEC_LABEL (attr 80): MAC/SELinux label as an attribute read

Supporting types: client_owner4, nfs_impl_id4, state_protect_how4,
secinfo4, sec_label4, deviceid4.

Compiles cleanly with no features, v41 only, and all features.
All Phase 5 v4.0/v4.1/v4.2 operations complete. Phase 4 (publishing)
removed from scope. Tier 3 sideband protocols annotated with specific
research prerequisites per protocol — implementation blocked until
spec reading, wire format verification, and live server testing are done.
Complete XDR type declarations, procedure tables, and constants for
every Tier 3 protocol, extracted from authoritative specs:

- NLM v3/v4: C702 ch 10 (pp 127-159), ch 14 (pp 263-269)
  24 procedures, locking types, share reservations, async _MSG/_RES,
  NLM v4 64-bit deltas, spec erratum on procedure 12 numbering
- NSM: C702 ch 11 (pp 161-174)
  7 procedures, state model (even=down/odd=up), SM_MON callback
- RQUOTA: Sun rquota.x
  2 procedures, quota struct, v2 group quota extension
- NFS_ACL: observed from Linux nfsacl.h + fs/nfsd/ (no public spec)
  5 procedures, POSIX ACL entry types, every constant marked as
  observed deviation
- NIS/YP: Sun yp.x
  12 ypserv + 3 ypbind procedures, map types, well-known map names
- RPCSEC_GSS: RFC 2203/5403/7861
  Credential structure, mechanism OIDs with byte encodings, service
  enums, what recon-only parsing needs vs full context establishment
- PCNFSD: D030 pp 88-108
  4 procedures, auth XOR obfuscation algorithm with worked example,
  AUTH_RES status codes, print spool types

This is research only — no Rust implementation. Wire format sections
are placed after each protocol's existing security analysis.
…attack chains, C702 deep dive

WebNFS wire format (C702 Appendix E pp 299-319):
- MCL canonical and native (0x80 prefix) encoding rules
- Public handle error semantics per version
- Implementation variations (Linux/Solaris/NetApp/FreeBSD)

Security analysis deepening:
- RQUOTA: UID enumeration oracle mechanics, timing, quota-to-uid-spray pipeline
- NIS: domain discovery methods, netgroup exploitation for export ACL bypass,
  per-map security analysis (passwd, group, netgroup, hosts, ethers)
- NFS_ACL: mode-bit vs ACL bypass scenarios, GETACL as credential recon,
  default ACL inheritance as privilege widening vector

Cross-protocol attack chains:
- 8 compound chains connecting sideband protocols with NFS operations
- NIS->NFS credential theft, NSM->NLM->NFS write, RQUOTA->uid-spray,
  NFS_ACL->hidden permission discovery, WebNFS->MOUNT bypass,
  PCNFSD->authenticated access, write verifier->DRC replay, metadata leak

C702 deep dive (ch 9, ss12.3-12.4):
- Write verifier reboot oracle exploitation details and HA detection
- DRC replay window timing and dangerous operations
- Locking architecture security (advisory-only, grace period, caller_name)
- Server/client implementation vulnerabilities (attribute caching, silly rename)
HVS Consulting gap analysis:
- Compared nfs_analyze (1906 lines) + fuse_nfs (538 lines) against NFSWolf's
  41 findings. Identified 14 specific gaps with implementation estimates:
  NFSv4 pseudo-root UUID-v5 detection, fsid-based export inference, Windows
  NFSv4.1 handle types, OS fingerprinting heuristics, symlink escape
  feasibility, all_squash detection, shadow GID fallback, NetApp workaround,
  HP-UX TCP handling, FreeBSD subnet ambiguity, ZFS escape, disk group
  privesc, client mount option risk assessment

RFC security audit (15 RFCs):
- Systematic extraction of every file read, code execution, info leak,
  callback coercion, cache/replay, and implementation pitfall path
- Organized by attack class with RFC section citations
- Cross-referenced against NFSWolf's existing finding coverage

New sections:
- Privilege escalation via NFS: 10 client/server chains including the novel
  disk group GID 6 attack (works WITHOUT no_root_squash)
- Filesystem-specific escape mechanics: handle structures for ext4, xfs,
  btrfs (subvolume 5), ZFS (inode 0x22), Windows (3 NFSv4.1 types)
- OS fingerprinting signal catalog: 8 signals (handle prefix, length,
  version pattern, subnet format, TCP behavior, NetApp protocol, null LOOKUP)
- Detection and monitoring gaps: why NFS attacks bypass auditd and IDS
- RPC-with-TLS (RFC 9289): security model, AUTH_SYS-inside-TLS weakness,
  downgrade attack, implementation status

3113 lines, 164 subsections. Research-only — no Rust implementation.
Move tracing, fastrand, and tokio from inline version pins in sub-crate
Cargo.toml files to [workspace.dependencies] in the root. All sub-crates
now reference these via { workspace = true }, making version management
uniform across the 8-crate workspace.

docs: HVS gap analysis, RFC security audit, privilege escalation catalog,
filesystem escape mechanics, OS fingerprinting, detection gaps, RPC-with-TLS

FUTURE-RESEARCH.md now 3113 lines covering the complete NFS security
attack surface from HVS Consulting research, all 15 RFCs, C702, D030,
and cross-protocol attack chains.
All sub-crate Cargo.toml dependencies now use { workspace = true }.
Zero inline version pins remain. Audited every non-uniform line:
each is justified by the dependency graph (see commit message body).
NFSv4 is one protocol with one operation namespace (RFC 8178). The crate
now unconditionally includes all v4.0, v4.1, and v4.2 types: 37 v4.0
operations, 4 v4.1 recon operations (EXCHANGE_ID, SECINFO_NO_NAME,
GETDEVICEINFO, GETDEVICELIST), and the v4.2 FATTR4_SEC_LABEL attribute.

Removed 33 #[cfg(feature)] gates and the [features] section from Cargo.toml.
None of the 8 sub-crates have Cargo features. The all-features = true
in [package.metadata.docs.rs] was a no-op on every one of them.
Ensures only source, README, and upstream attribution (NOTICE) ship
in published packages. Prevents accidental leakage of workspace files.

- 6 crates with NOTICE: src/**, README.md, NOTICE
- 2 crates without NOTICE: src/**, README.md
- onc-xdr-derive also includes tests/** (trybuild suite)
homepage = https://strongwind.dev/NFSWolf
Defined in [workspace.package], inherited by all 8 sub-crates + binary.
readme = "README.md" defined once in [workspace.package], inherited
by all 9 packages via readme.workspace = true.
- Fix stale comments (eight crates, not two; nfs-v4 covers v4.0/4.1/4.2)
- Use members glob: [".", "crates/*"] instead of listing all 8
- Deduplicate tokio and tracing: binary now uses workspace refs
- Workspace tokio uses features = ["full"] (superset of the old split)
- Trim verbose docs.rs comment to zero lines
- Remove redundant dependency grouping comments
- Trim lint policy comment to two lines
Crate READMEs (8 crates, 56-79 lines each):
- Professional library-grade documentation with quick-start code examples
  using actual public API types (DirectTransport, Nfs3Client, etc.)
- API overview tables listing key public types
- Protocol coverage sections
- Safety/hardening notes (codec crates) and security properties (NFS crates)

Root README:
- Test count: 469 -> 560
- Workspace crates: 6 -> 8
- Credits: nfswolf-* -> onc-*/nfs-* crate names

Project instructions:
- Crate table updated to current names and 8-crate layout
- Protocol stack API examples use onc_rpc_client/nfs_v3/nfs_v2 paths
- Test count and module references updated

docs/ARCHITECTURE.md:
- All crate name references updated
- New modules documented (proto/nfs2.rs, nfs-mount, onc-rpcbind)
- Deleted modules removed (proto/rpc_probe.rs)

docs/DESIGN.md, NFSv2.md, NFSv3.md, NFSv4.md:
- Stale crate name references updated throughout
New analyzer checks:
- F-3.7: AUTH_DH (flavor 3) detection in MOUNT and SECINFO
- F-3.8: AUTH_TLS STARTTLS probe (RFC 9289) for TLS detection
- F-4.6: PATHCONF chown_restricted=false (ownership hijacking)
- F-5.7: PATHCONF case_insensitive=true (Windows/NTFS fingerprint)
- F-5.8: AUTH_NONE GETATTR metadata leak (RFC 2623 S2.3.2)

Scanner enrichment:
- Security notes for sideband RPC programs (NLM, NSM, RQUOTA,
  NFS_ACL, NIS, PCNFSD) in console and JSON output
- NetApp management program 400010 in program table
- Auth flavor enumeration per export via MNT probe

Protocol crate changes:
- AUTH_TLS (7) and RPCSEC_GSS (6) added to auth_flavor enum
- security_note() function in onc-rpcbind for program labeling
- Removed cfg(fuse) gate from NfsMountClient::unmount()
- auth_flavors field added to ExportEntry

Documentation:
- FINDINGS.md: 6 new finding entries (F-3.7/3.8/4.6/5.6/5.7/5.8)
- REQUIREMENTS.md: traceability for all new checks
- TASKLIST.md: moved to project root, tracks v1.0 progress
- Removed stale dead-code comment from main.rs
Numbers updated across all docs:
- Finding count: 41 -> 47 (README, CLAUDE.md, DESIGN.md,
  ARCHITECTURE.md, REQUIREMENTS.md, findings/README.md)
- Test count: 560 -> 542 (README, CLAUDE.md)
- Shell command count: 44 -> 52 (README, CLAUDE.md, DESIGN.md,
  ARCHITECTURE.md)

docs/NFSv4.md:
- Operation coverage: all 37 v4.0 ops now representable as ArgOp
  variants, CompoundBuilder has 13+ typed methods
- Added v4.1 ops (GETDEVICEINFO, GETDEVICELIST) and v4.2 security
  labels (FATTR4_SEC_LABEL, SecLabel4)
- Updated shell commands table with v0.8.0 aliases
- Nfs4Status variant count: 26 (25 named + Unknown)

docs/DESIGN.md:
- Backpressure: Notify -> Semaphore (matches actual pool code)

docs/FINDINGS.md:
- Cross-reference table: added 6 new findings

docs/findings/:
- Created F-5.6-metadata-on-access-denial.md (was missing)
- findings/README.md: added F-3.7, F-3.8, F-4.6, F-5.6, F-5.7, F-5.8

Crate READMEs:
- Fixed dirpath compilation errors in nfs-mount, nfs-v2, nfs-v3
- onc-rpcbind: added security_note() and netapp_mgmt to API table
- onc-rpc-client: added auth_flavor with AUTH_TLS/RPCSEC_GSS
- nfs-v4: updated operation coverage, v4.1/v4.2 extensions

TASKLIST.md: replaced non-ASCII characters with ASCII equivalents
Wire layer:
- Parse rpcsec_gss_info from SECINFO responses (mechanism OID,
  QOP, service level) instead of skipping it. New SecInfoEntry
  struct carries the full GSS tuple alongside the flavor number.
- ResOpData::SecFlavors now returns Vec<SecInfoEntry> instead of
  Vec<u32>, enabling krb5/krb5i/krb5p discrimination from SECINFO
  without relying on Linux krb5 pseudo-flavors.

Auth infrastructure:
- Added AuthFlavor::Tls (7) for RFC 9289 AUTH_TLS classification.
- Added flavor_name() in src/proto/auth.rs as the single source
  of truth for flavor-to-name mapping. Covers AUTH_NONE through
  AUTH_TLS and krb5 pseudo-flavors (390003-390005).
- Replaced three separate inline match blocks (analyzer, scanner,
  mount) with calls to flavor_name().
- Fixed parse_flavor() in mount.rs to delegate to AuthFlavor::from_u32()
  which correctly handles krb5 pseudo-flavors (previously mapped them
  to Unknown).

Analyzer:
- check_nfs4_secinfo() now reports GSS service levels in evidence
  strings (RPCSEC_GSS(krb5) vs krb5i vs krb5p) decoded from the
  SECINFO rpcsec_gss_info, not just from MOUNT pseudo-flavors.
Emit F-3.9 when AUTH_SHORT (flavor 2) appears in MOUNT auth_flavors
or NFSv4 SECINFO results. AUTH_SHORT opaque tokens are replayable
from wire captures without knowing the original UID/GID (RFC 1057
S9.2, RFC 5531 Appendix A).
Scanner now issues SECINFO per NFSv4 pseudo-root entry to discover
auth flavors. V4ExportEntry gains auth_flavors field. Console output
shows [AUTH_SYS,RPCSEC_GSS(krb5p)] alongside v4 export paths. JSON
output includes auth_flavors array for v4 exports.
Detect Kerberos enforcement at the NFS operation level via the
AUTH_TOOWEAK RPC error (RFC 5531 S8.3). When MOUNT accepts AUTH_SYS
but a subsequent GETATTR returns AUTH_TOOWEAK, the export enforces
stronger authentication for data operations. Emits F-1.8 as an
informational finding (positive security indicator).
flavor_name() now covers every assigned value from the IANA 'RPC
Authentication Flavor Numbers' registry:
- Added AUTH_KERB (4), AUTH_RSA (5), AUTH_NW (30001), AUTH_SEC (200000),
  AUTH_ESV (200004), AUTH_NQNFS (300000), AUTH_GSSAPI (300001),
  AUTH_ILU_UGEN (300002), AUTH_SPNEGO (390000)
- Source: https://www.iana.org/assignments/rpc-authentication-numbers/flavor.csv

AuthFlavor::from_u32 now classifies AUTH_SPNEGO (390000) as Gss.

auth_stat enum now covers values 0-14 from the IANA 'RPC Authentication
Status Numbers' registry, adding AUTH_KERB_GENERIC (8) through
RPCSEC_GSS_CTXPROBLEM (14). RFC 7861 values 15-18 are omitted (require
RPCSEC_GSS v3 context, which nfswolf does not establish).
- Source: https://www.iana.org/assignments/rpc-authentication-numbers/status.csv
Complete the auth_stat enum with the remaining 4 IANA-assigned values:
RPCSEC_GSS_INNER_CREDPROBLEM (15), RPCSEC_GSS_LABEL_PROBLEM (16),
RPCSEC_GSS_PRIVILEGE_PROBLEM (17), RPCSEC_GSS_UNKNOWN_MESSAGE (18).

auth_stat now covers 19/19 assigned values from the IANA registry.
Replace the hand-curated 13-entry program table with the full IANA
'RPC Program Numbers' registry. Every assigned program number from
the CSV is now decoded by name in portmapper DUMP output.

Source: https://www.iana.org/assignments/rpc-program-numbers/rpc-program-numbers-data.csv

- 1251 entries (removed unassigned ranges, [unknown] entries, and
  duplicate program numbers)
- Binary-searchable static table, zero heap allocation
- Security notes retained for NFS-relevant sideband programs
- Added security notes for rstatd, rusersd, yppasswdd, rexec,
  ypupdate, keyserv, ypxfrd, ttdbserverd, nsm_addr, sadmind
Version bump to 1.0.0. CHANGELOG documents all changes since v0.8.0:
comprehensive auth enumeration, 8 new findings (F-1.8, F-3.7-3.9,
F-4.6, F-5.6-5.8), complete IANA registries (1251 programs, 19 auth
flavors, 19 status codes), scanner SECINFO probing, and NFSv4 wire
coverage expansion.

49 documented findings. 543 tests. Live-tested across 6 VMs (32
exports, all write ops verified).

Removed TASKLIST.md (all items complete). Added F-1.8 and F-3.9
write-ups. Updated finding counts across all docs to 49.
The verifier body was double-length-prefixed: a manual
\x00\x00\x00\x07 was prepended to 'STARTTLS' (8 bytes, not 7),
then the opaque_auth XDR encoding added another length prefix.

RFC 9289 S4.1: 'The length of the verifier body field is eight'
as a fixed-length opaque. The XDR layer handles the length prefix;
the body should be the raw 8 bytes only.
Scanner:
- OS fingerprint column from MNT handle (table, CSV, JSON)
- CSV auth flavor column (deduplicated across v2/v3/v4 exports)

Analyzer:
- Windows v4.1 (28-byte) handle signing detection
- Windows version pattern (v3+v4 without v2) in OS fingerprint
- FreeBSD truncated subnet warning in EXPORT ACL
- FSINFO time_delta (Solaris fingerprint) + properties bitmask
- FSSTAT free-space and inode exhaustion check
- .nfs* silly-rename detection (open-unlinked indicator)
- Write verifier reboot oracle via zero-count COMMIT
- EXCHANGE_ID (v4.1) vendor/version fingerprinting

Shell:
- verifier command (writeverf3 hex dump via COMMIT)

Protocol crates:
- commit_verifier() domain API on Nfs3Client
- compound_v41() on Nfs4DirectClient (minorversion=1)
- OsGuess::HpUx variant (reserved)
- Independent crate versioning (0.2.0 for stable crates)

Other:
- FUSE mount suid+dev passthrough warning
…CINFO, xattrs

Escape:
- NFSv4 LOOKUPP chain escape (PUTROOTFH + LOOKUPP*N + GETFH)
- verify_nfs4_root() confirms FS root via well-known entry LOOKUPs
- Wired as final fallback in escape and scan --auto-escape

Analyzer:
- probe_pnfs_topology(): EXCHANGE_ID + GETDEVICELIST for pNFS MDS detection (F-3.5)
- check_nfs4_sec_label(): FATTR4_SEC_LABEL SELinux label extraction (F-4.5)
- check_nfs4_secinfo_per_path(): per-subdirectory SECINFO comparison (F-3.6)
- check_nfs4_xattrs(): OPENATTR + READDIR for named attributes (F-5.13)

Wire:
- FATTR4_SEC_LABEL decode in GETATTR responses (RFC 7862 S12.2.4)
- compound_v41() and lookup_fh() now used (dead_code removed)
…ocks 1-4)

Handle variant derivation (file_handle.rs):
- derive_handle_variants(): raw, trimmed, pad32, pad64 for any handle
- dedup_variants(): remove byte-identical duplicates

Handle probe orchestrator (probe.rs):
- acquire_and_test_handles(): MOUNT v3 + v1, derive all variants, test
  each against NFSv3 + NFSv2 GETATTR
- HandleProbeResult with best_v3() and escape_seeds()
- TestedHandle records v3_ok, v3_stale, v2_ok per variant

Analyzer integration (analyzer.rs):
- analyze_export() now uses the handle matrix instead of MOUNT v3-only
- Emits F-1.6 when MOUNT v1 leaks a handle that v3 denied
- Selects the best working v3 handle for all subsequent checks
- nfs_port field added to Analyzer struct for probe forwarding
… variants (Block 5)

New find_escape_matrix() in escape.rs:
- Uses acquire_and_test_handles() to get ALL handle variants from both MOUNT versions
- For each working seed, constructs root candidates and derives length variants
  (raw, trimmed, pad32, pad64) of each candidate
- Tests every variant against both NFSv3 and NFSv2 GETATTR
- Catches F-1.6: MOUNT v1 leaks handle that v3 denies, used with v3 ops

Integration:
- run_inner(): matrix runs between v3 failure and v2 fallback
- find_escape_any(): same insertion point for scan --auto-escape
Handle matrix probe client now connects directly to port 2049 instead of
relying on the PooledTransport's lazy MOUNT (which fails when mountd v3
is disabled). This fixes the critical scenario where MOUNT v3 is off but
NFSv3 is still running -- the v1 handle can now be tested against v3 ops.

Analyzer export enumeration falls back to MOUNT v1 EXPORT when v3 EXPORT
returns no results (covers mountd -N 3 configurations).

Live-tested: .14 with mountd v3 disabled achieved full filesystem escape
via MOUNT v1 handle -> NFSv3 GETATTR, including /etc/shadow read.
Scanner:
- ExportEntry now has handle_hex field, populated from MNT response
- v3 exports: calls MOUNT v3 MNT per export (handle + auth flavors)
  Falls back to MOUNT v1 MNT if v3 fails (F-1.6)
- v2 exports: calls MOUNT v1 MNT per export (handle + auth flavors)
  Previously only called EXPORT (no MNT, no handles)
- Handles shown in console output (fh=01000700... truncated)
- Handles included in JSON output per export

Shell:
- v3 shell now falls back to MOUNT v1 when v3 fails
- Forces direct port 2049 on v1 fallback to avoid lazy MOUNT v3

Live-tested: .14 with mountd v3 disabled -- shell connected via v1 handle
Previously only tested NFSv2 GETATTR on handles that happened to be
exactly 32 bytes. Now every variant (raw, trimmed, pad32, pad64) gets
its 32-byte form tested against NFSv2 via Nfs2FileHandle::from_bytes
(which pads short and truncates long handles to exactly FHSIZE=32).

This means a 20-byte trimmed v3 handle or a 28-byte raw v3 handle
now also gets tested as a zero-padded 32-byte v2 handle -- catching
cases where the v2 daemon accepts the padded form.
ROADMAP.md updated:
- Analyzer/scanner enhancements: FSINFO, FSSTAT, xattrs, per-path SECINFO, CSV auth column marked Done
- OS fingerprinting: Windows version pattern, FreeBSD subnet, write verifier marked Done
- NFSv4 recon: EXCHANGE_ID, GETDEVICELIST, FATTR4_SEC_LABEL marked Done
- Pre-publish checklist: keywords, cargo-hack, MSRV CI, versioning, name check all Done
- HVS gaps: 5 of 6 Done (ZFS remains, needs lab)
- Write verifier oracle + silly-rename detection marked Done
- Publication phases updated (Phase 3 done, Phase 4 ready, Phase 5 done)

tasklist.md removed -- served its purpose as a working document, all items
either completed or tracked in ROADMAP.md
WebNFS MCL path traversal: marked Done (was incorrectly listed as
pending -- try_webnfs_escape() with ../../../etc/passwd traversal
has been implemented for v2/v3/v4 since v1.0.0).

#[non_exhaustive] audit: corrected from 'all covered' to '16 pub enum
types still lack it' -- mostly XDR wire types with derive macros.
Phase 3 publication status updated to reflect this gap.

OPEN honest write test: noted partial wire types exist (ArgOp::Open,
encode_open_read, setclientid) even though integration is not done.
…fied

Analyzer SECINFO cascade (check_nfs4_secinfo):
  1. SECINFO (op 33, v4.0) -- existing path
  2. SECINFO_NO_NAME (op 52, v4.1) -- new fallback via compound_v41()
  3. WRONGSEC oracle -- iterates AUTH_NONE/AUTH_SYS, tests each via
     PUTROOTFH+LOOKUP, NFS4ERR_WRONGSEC (10016) = rejected

Extracted emit_secinfo_findings() shared helper so all three paths
emit the same findings (F-3.4, F-1.7, F-3.7, F-3.9) with source
attribution in evidence.

non_exhaustive: verified all 48 pub enum types are annotated (previous
audit claiming 16 gaps was incorrect -- automated scan confirms 0 gaps).
ROADMAP.md updated.
MOUNT v3 (nfswolf-nfs3/wire/mount.rs):
- golden_mountres3_ok: 28-byte lab handle (fsid_type=7) + auth flavors
- golden_mountres3_err_acces: MNT3ERR_ACCES status
- golden_export_node: export list entry with path + allowed groups

MOUNT v1 (nfswolf-nfs2/mount.rs):
- golden_fhstatus_ok: 32-byte lab handle (fsid_type=4)

Portmapper (nfswolf-rpc/portmap/types.rs):
- golden_pmaplist_single_nfsv3: DUMP response with NFSv3/TCP/2049
- golden_getport_response: GETPORT returning port 2049

All 6 tests verify pack -> golden bytes -> unpack round-trip with real
handle bytes from lab VM 10.252.0.14.
Removed all 'Done' entries: WebNFS MCL, write verifier, silly-rename,
FSINFO/FSSTAT/xattrs, per-path SECINFO, WRONGSEC, CSV auth column,
EXCHANGE_ID, SECINFO_NO_NAME, GETDEVICELIST, FATTR4_SEC_LABEL, Windows
v4.1 handles, FreeBSD subnet, NFSv4 LOOKUPP escape, FUSE warning, and
all pre-publish checklist items. Pre-publish checklist section replaced
with 'complete' note. HVS gap table reduced to the one remaining item
(ZFS). OS fingerprint table reduced to 2 pending items (HP-UX, CALLIT).
…crates.io

- Standardize all 8 crate READMEs: centered headers, badges (CI,
  crates.io, edition, MSRV, license, docs.rs), nav links, API reference
  tables, ASCII dependency graph, protocol coverage, safety sections
- Bump all crate versions from 0.x to 1.0.0
- Remove publish = false from all crates
- Add documentation field (docs.rs) to all crates
- Fix provenance text for nfs-v2 and nfs-v4 (were incorrectly listed
  as derived from Vaiz/nfs3; NOTICE files say original work)
- Add crates.io and docs.rs badges to top-level README
- Add cargo install nfswolf installation option
- Add Protocol crates section with crates.io links
- Add WEPWolf to Related tools
Each crate has its own README.md. The workspace-inherited readme
resolved to ../../README.md (the workspace root), causing cargo
publish to warn and fall back. Point each crate at its own file.
@StrongWind1
StrongWind1 force-pushed the v1-parity-and-fixes branch from a9dfe6c to 2d17e72 Compare August 7, 2026 16:37
@StrongWind1 StrongWind1 changed the title v1.0.0 parity: protocol crate publish prep, standardized READMEs, version bump v1.0.0 release + v1.1 features + crates.io publish prep Aug 7, 2026
@StrongWind1
StrongWind1 merged commit c7194c8 into main Aug 8, 2026
18 checks passed
@StrongWind1
StrongWind1 deleted the v1-parity-and-fixes branch August 8, 2026 07:06
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.

1 participant