From dacfc0f7a1c539fcfdf13784ef4f4861e6351ac3 Mon Sep 17 00:00:00 2001 From: OpenAI Date: Sat, 30 May 2026 14:56:54 +0000 Subject: [PATCH] nwconn: gate AFP metadata writes with Modify rights AFP Set File Information intentionally stores some Apple-specific metadata in mars_nwe-owned xattrs because FinderInfo and the narrow Invisible/System AFP bits do not have a complete NetWare-side representation yet. Those xattrs are storage details, however, and should not let the AFP adapter bypass the same NetWare policy that protects ordinary metadata changes. Add a small Modify-rights gate for AFP-specific metadata writes after the path-backed request has been resolved to a mars_nwe volume and Unix node. The check uses the existing trustee/effective-rights helper with TRUSTEE_M before writing FinderInfo or AFP-only attribute xattrs. Archive remains routed through the NetWare FILE_ATTR_A attribute helper, and Modify timestamp remains routed through nw_utime_node(), so their existing mars_nwe policy paths are unchanged. This keeps the WebSDK/NWAFP Set File Information handler as an Apple-facing adapter over existing mars_nwe access control rather than a parallel metadata writer. It also documents the convergence rule in TODO.md so later Create, Rename, and Delete work can continue to prefer existing NetWare helpers or thin wrappers over duplicated AFP-local file server logic. Tests: git diff --check TODO: add non-SUPERVISOR negative smoke coverage for missing Modify rights once a stable low-privilege test user and trustee setup are available. --- TODO.md | 19 ++++++++++++++++--- src/nwconn.c | 26 ++++++++++++++++++++++++++ tests/linux/README.md | 11 +++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index 880a72a..2cdbac7 100644 --- a/TODO.md +++ b/TODO.md @@ -406,9 +406,9 @@ Refactor/wrapper follow-up: reusing existing mars_nwe volume, namespace, and directory-handle helpers. - Route all AFP writes through existing mars_nwe policy checks before changing metadata. Archive and Modify timestamp already do this through NetWare - helpers; FinderInfo and AFP-only Invisible/System xattr writes still need an - explicit Modify-rights gate using the same trustee/effective-rights policy as - normal NetWare metadata writes. + helpers. FinderInfo and AFP-only Invisible/System xattr writes are now gated + by the same trustee/effective-rights Modify policy before the AFP metadata + xattrs are updated. - Continue moving AFP-visible NetWare attributes onto the existing NetWare attribute store. Archive is already mapped to `FILE_ATTR_A`; ReadOnly, Hidden, System, ExecuteOnly, and Shareable still need either a real mapping @@ -428,6 +428,19 @@ Refactor/wrapper follow-up: Entry ID/CNID index or an existing canonical path lookup once the entry-id backend grows beyond xattr/stat-derived compatibility IDs. + +AFP Set File Information metadata-rights convergence: + +- AFP FinderInfo writes and AFP-only Invisible/System attribute xattr writes no + longer bypass NetWare policy just because their storage is AFP-specific. + `afp_set_file_information()` now checks the resolved file through the existing + trustee/effective-rights Modify policy before writing `org.mars-nwe.afp.*` + metadata. +- Archive continues to route through the NetWare attribute helper (`FILE_ATTR_A`), + and Modify timestamp continues to route through `nw_utime_node()`. The new + check only covers metadata that has to remain AFP-specific, keeping AFP as an + Apple-facing adapter over mars_nwe policy rather than a parallel file server. + Endpoint order: - First finish the non-destructive convergence work above. diff --git a/src/nwconn.c b/src/nwconn.c index c05be94..4f390b2 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -1305,6 +1305,20 @@ static int afp_set_netware_archive_attribute(int volume, char *unixname, return(set_nw_attrib_word(volume, unixname, stb, (int)(nw_attrs & 0xffff))); } +static int afp_check_metadata_modify_rights(int volume, char *unixname, + struct stat *stb, + const char *call_name, + uint8 *path, int path_len) +{ + if (!unixname || !stb) return(-0x9c); + if (tru_eff_rights_exists(volume, (uint8 *)unixname, stb, TRUSTEE_M)) { + XDPRINTF((2,0, "%s rejected: no Modify rights for AFP metadata path='%s'", + call_name, visable_data(path, path_len))); + return(-0x8c); /* no modify rights */ + } + return(0); +} + static int afp_set_file_information(uint8 *afp_req, int afp_len, const char *call_name) /* @@ -1333,6 +1347,7 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len, int result; uint16 log_attrs = 0; time_t log_mtime = (time_t)-1; + int needs_afp_metadata_modify = 0; if (afp_len < 9) { XDPRINTF((2,0, "%s rejected: short request len=%d", @@ -1390,6 +1405,8 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len, call_name, requested_attrs, visable_data(afp_req + 9, path_len))); return(-0x9c); } + if (requested_bits & AFP_ATTR_XATTR_MASK) + needs_afp_metadata_modify = 1; data_off += 2; } if ((request_mask & AFP_FILE_BITMAP_MODIFY_DATE) && afp_len < data_off + 4) { @@ -1400,6 +1417,8 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len, if (request_mask & AFP_FILE_BITMAP_MODIFY_DATE) data_off += 4; if ((request_mask & AFP_FILE_BITMAP_FINDER_INFO) && data_off & 1) data_off++; + if (request_mask & AFP_FILE_BITMAP_FINDER_INFO) + needs_afp_metadata_modify = 1; if ((request_mask & AFP_FILE_BITMAP_FINDER_INFO) && afp_len < data_off + NWATALK_FINDER_INFO_LEN) { XDPRINTF((2,0, "%s rejected: short FinderInfo data len=%d data_off=%d", @@ -1429,6 +1448,13 @@ static int afp_set_file_information(uint8 *afp_req, int afp_len, return(-0x9c); } + if (needs_afp_metadata_modify) { + result = afp_check_metadata_modify_rights(path_volume, unixname, &stbuff, + call_name, afp_req + 9, path_len); + if (result < 0) + return(result); + } + data_off = 9 + path_len; if (data_off & 1) data_off++; if (request_mask & AFP_FILE_BITMAP_ATTRIBUTES) { diff --git a/tests/linux/README.md b/tests/linux/README.md index f377879..bca2975 100644 --- a/tests/linux/README.md +++ b/tests/linux/README.md @@ -87,6 +87,17 @@ collected separately. Use `--stop-on-failure` for strict bisect-style runs; by default the script keeps going so one failing endpoint does not hide later AFP output from the report. +AFP metadata writes and NetWare Modify rights: + +FinderInfo and AFP-only attribute metadata are stored in `org.mars-nwe.afp.*` +xattrs, but those writes are still file metadata changes. The Set File +Information handler now resolves the target through the normal mars_nwe path and +checks the existing NetWare Modify trustee policy before updating FinderInfo or +AFP-only Invisible/System metadata. Archive uses the existing NetWare attribute +path, and Modify timestamp uses `nw_utime_node()`, so the smoke suite should +continue to pass for SUPERVISOR while non-supervisor negative coverage can later +exercise the same policy gate. + A verified suite run after the FinderInfo payload-alignment fix completed with `failures=0` for `SYS:PUBLIC/pmdflts.ini`. The report covered Entry ID by path, Entry ID from NetWare handle, Get File Information, Scan File Information,