Files
mars-dosutils/c32ncp.c
Mario Fetka 4cafe16980 dosutils: add Novell-style REVOKE and REMOVE trustee tools
Implement REVOKE and REMOVE for the Client32 DOS utilities.

REVOKE now supports Novell-style syntax:

  REVOKE rightslist* [FOR path] FROM [USER|GROUP] name [options]

and removes rights from explicit trustee assignments.  It scans the
trustee list first, updates the trustee rights mask, and deletes the
trustee entry when no rights remain.

REMOVE now supports Novell-style syntax:

  REMOVE [USER | GROUP] name [FROM path] [option]

and deletes explicit trustee assignments for users or groups.

Both tools support USER/GROUP lookup, /FILES, /SUBDIRS,
/SUBDIRECTORIES, wildcard file targets, recursive directory handling,
Novell-style help text and summary output.  Missing trustee entries are
reported with Novell-style "No trustee for the specified ..." messages.

Add shared trustee helpers and Client32 NCP87 trustee scan/delete
support.  Also adjust GRANT ALL so it matches Novell behavior by not
granting Supervisor implicitly; Supervisor must be granted explicitly.
2026-05-24 20:23:43 +02:00

778 lines
21 KiB
C

#include "net.h"
#include "c32ncp.h"
/* c32ncp.c - Client32 NCP helpers for mars-dosutils */
static void c32_put_word_lh(uint8 *p, uint16 v)
{
p[0] = (uint8)(v & 0xff);
p[1] = (uint8)((v >> 8) & 0xff);
}
static void c32_put_dword_lh(uint8 *p, uint32 v)
{
p[0] = (uint8)(v & 0xff);
p[1] = (uint8)((v >> 8) & 0xff);
p[2] = (uint8)((v >> 16) & 0xff);
p[3] = (uint8)((v >> 24) & 0xff);
}
static void c32_put_dword_hl(uint8 *p, uint32 v)
{
p[0] = (uint8)((v >> 24) & 0xff);
p[1] = (uint8)((v >> 16) & 0xff);
p[2] = (uint8)((v >> 8) & 0xff);
p[3] = (uint8)(v & 0xff);
}
static uint16 c32_get_word_lh(uint8 *p)
{
return((uint16)(p[0] | ((uint16)p[1] << 8)));
}
static uint32 c32_get_dword_lh(uint8 *p)
{
return((uint32)p[0] |
((uint32)p[1] << 8) |
((uint32)p[2] << 16) |
((uint32)p[3] << 24));
}
static uint32 c32_get_dword_hl(uint8 *p)
{
return(((uint32)p[0] << 24) |
((uint32)p[1] << 16) |
((uint32)p[2] << 8) |
(uint32)p[3]);
}
static UI c32_build_handle_path(uint8 *buf, uint8 dhandle,
uint16 dirbase, uint8 style,
int count,
const char *c1, const char *c2, const char *c3)
{
uint8 *p;
int l;
UI used;
/*
* DeveloperNet/ncpdos16 path structure used by NCP87/S6 through
* Client32 COMPATNcpRequestReply.
*
* This is the exact shape verified by TESTS NCP87C32AUTO:
* 00 02 00 00 00 00 01 09 4C 4F 47 49 4E 2E 45 58 45
*
* Meaning:
* word[1] = short dir handle
* word[3] = dir base
* byte[5] = dirstyle
* byte[6] = component count
* then len/name components
*
* The old/simple struct used by the INT 21h F257 fallback is not accepted
* by this Client32 path.
*/
memset(buf, 0, 0x140);
if (dhandle) {
c32_put_word_lh(buf + 1, (uint16)dhandle);
c32_put_word_lh(buf + 3, dirbase);
buf[5] = style;
} else {
buf[5] = 0xff;
}
p = buf + 6;
*p++ = (uint8)count;
if (count > 0 && c1) {
l = strlen(c1);
if (l > 255) l = 255;
*p++ = (uint8)l;
memcpy(p, c1, l);
p += l;
}
if (count > 1 && c2) {
l = strlen(c2);
if (l > 255) l = 255;
*p++ = (uint8)l;
memcpy(p, c2, l);
p += l;
}
if (count > 2 && c3) {
l = strlen(c3);
if (l > 255) l = 255;
*p++ = (uint8)l;
memcpy(p, c3, l);
p += l;
}
used = (UI)(p - buf);
c32_put_word_lh(buf + 0x13c, used);
return(used);
}
static UI c32_build_handle_path_from_dos_path(uint8 *buf, uint8 dhandle,
uint16 dirbase, uint8 style,
const char *dospath)
{
uint8 *p;
uint8 *countp;
int count = 0;
const char *s;
UI used;
memset(buf, 0, 0x140);
if (dhandle) {
c32_put_word_lh(buf + 1, (uint16)dhandle);
c32_put_word_lh(buf + 3, dirbase);
buf[5] = style;
} else {
buf[5] = 0xff;
}
p = buf + 6;
countp = p++;
s = dospath;
if (!s) s = "";
/*
* DOS tools mostly pass relative paths against the current directory
* handle. Accept simple DOS decoration here so RIGHTS can pass "." or
* ".\\UDIR\\FILE" without constructing path components in the caller.
*/
if (s[0] && s[1] == ':')
s += 2;
while (*s == '\\' || *s == '/')
s++;
while (*s && p < buf + 0x138 && count < 32) {
const char *start;
int len;
while (*s == '\\' || *s == '/')
s++;
if (*s == '.'
&& (s[1] == '\0' || s[1] == '\\' || s[1] == '/')) {
s++;
continue;
}
start = s;
while (*s && *s != '\\' && *s != '/')
s++;
len = (int)(s - start);
if (len <= 0)
continue;
if (len > 255)
len = 255;
if (p + 1 + len >= buf + 0x138)
break;
*p++ = (uint8)len;
memcpy(p, start, len);
p += len;
count++;
}
*countp = (uint8)count;
used = (UI)(p - buf);
c32_put_word_lh(buf + 0x13c, used);
return(used);
}
/*
* Current verified Client32 path for mars-nwe DOS utilities:
*
* C32_MapVar_Probe(4,0) -> connRefLocal FFFF:FFFE
* C32_OpenRef_Probe(connRefLocal) -> Client32 handle, e.g. 0101:0001
*
* C32_MapVar_Probe currently contains the confirmed Mars server-name scan
* shape. It is intentionally kept small and isolated here so FLAG and later
* tools do not carry the old exploratory tests.
*/
int c32_get_ncp_handle(uint16 *handle_lo, uint16 *handle_hi)
{
uint8 mapout[32];
uint8 openout[32];
uint16 map_ret_ax, map_ret_dx;
uint16 cref_lo, cref_hi;
uint16 open_ret_ax, open_ret_dx;
if (!handle_lo || !handle_hi)
return(1);
*handle_lo = 0;
*handle_hi = 0;
memset(mapout, 0, sizeof(mapout));
C32_MapVar_Probe(4, 0, mapout);
map_ret_ax = c32_get_word_lh(mapout + 14);
map_ret_dx = c32_get_word_lh(mapout + 16);
cref_lo = c32_get_word_lh(mapout + 22);
cref_hi = c32_get_word_lh(mapout + 24);
if (map_ret_ax != 0 || map_ret_dx != 0 || (cref_lo == 0 && cref_hi == 0))
return(2);
memset(openout, 0, sizeof(openout));
C32_OpenRef_Probe(cref_lo, cref_hi, openout);
open_ret_ax = c32_get_word_lh(openout + 14);
open_ret_dx = c32_get_word_lh(openout + 16);
*handle_lo = c32_get_word_lh(openout + 18);
*handle_hi = c32_get_word_lh(openout + 20);
if (open_ret_ax != 0 || open_ret_dx != 0 || (*handle_lo == 0 && *handle_hi == 0))
return(3);
return(0);
}
int c32_ncp87_obtain_rim_attributes(const char *name,
uint16 dir_handle,
uint32 *attr_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x60];
uint8 rep1[0x110];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
int path_len;
int rc;
if (!name || !attr_out)
return(1);
*attr_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
memset(hdr, 0, sizeof(hdr));
hdr[0] = 6; /* NCP87 subfunction 6 */
hdr[1] = 0; /* source namespace DOS */
hdr[2] = 0; /* target namespace DOS */
c32_put_word_lh(hdr + 3, 0x0006); /* SA_ALL */
c32_put_dword_lh(hdr + 5, 0x00000004UL); /* RIM_ATTRIBUTES */
path_len = c32_build_handle_path(path, (uint8)dir_handle, 0, 0, 1,
name, NULL, NULL);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, (UI)path_len,
rep0, 0x4d,
rep1, 0x100,
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
/*
* Verified reply layout for RIM_ATTRIBUTES:
* REP0+4 little-endian dword = DOS attributes
* Example LOGIN.EXE: 20h archive.
*/
*attr_out = c32_get_dword_lh(rep0 + 4);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
return(0);
}
int c32_ncp87_modify_dos_attributes(char *name,
uint16 dir_handle,
uint32 attrs,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 modbuf[64];
uint8 path[0x140];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint8 *p;
UI mod_len;
UI path_len;
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
int rc;
if (!name)
return(1);
if (actual_out)
*actual_out = 0;
if (handle_lo_out)
*handle_lo_out = 0;
if (handle_hi_out)
*handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP 87 subfunction 7: Modify DOS information.
*
* First request fragment contains the fixed header and DOS info structure.
* Second request fragment contains the verified SDK-style path structure.
*
* This avoids the old INT 21h F257 modify path, which can hang under DOS
* Client32 for high FLAG bits such as Transactional.
*/
memset(modbuf, 0, sizeof(modbuf));
p = modbuf;
*p++ = 7; /* subfunction: modify DOS info */
*p++ = 0; /* namespace DOS */
*p++ = 0; /* reserved */
c32_put_word_lh(p, 0x0006); p += 2; /* SA_ALL */
c32_put_dword_lh(p, 0x00000002UL); p += 4; /* DM_ATTRIBUTES: attributes */
c32_put_dword_lh(p, attrs); p += 4; /* Attributes */
memset(p, 0, 34); p += 34; /* rest of DOS info */
mod_len = (UI)(p - modbuf);
path_len = c32_build_handle_path(path, (uint8)dir_handle, 0, 0, 1,
name, NULL, NULL);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
modbuf, mod_len,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out)
*actual_out = actual_lo;
if (handle_lo_out)
*handle_lo_out = handle_lo;
if (handle_hi_out)
*handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
return(0);
}
int c32_ncp87_get_effective_rights(const char *path_name,
uint16 dir_handle,
uint16 *rights_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
uint16 rights0;
uint16 rights4;
UI path_len;
int rc;
if (!rights_out)
return(1);
*rights_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP 87 subfunction 29: Get effective rights.
*
* This mirrors ncpfs ncp_get_eff_directory_rights():
* byte 29
* byte source namespace
* byte target namespace
* word search attributes, little endian
* dword reserved, zero
* handle/path
*
* Reply is a little-endian word with NCP rights bits.
*/
memset(hdr, 0, sizeof(hdr));
hdr[0] = 29;
hdr[1] = 0; /* source namespace DOS */
hdr[2] = 0; /* target namespace DOS */
c32_put_word_lh(hdr + 3, 0x0006); /* SA_ALL */
c32_put_dword_lh(hdr + 5, 0L); /* reserved */
path_len = c32_build_handle_path_from_dos_path(path, (uint8)dir_handle,
0, 0, path_name);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
rights0 = c32_get_word_lh(rep0 + 0);
rights4 = c32_get_word_lh(rep0 + 4);
/*
* Most NCP replies start at REP0+0. The existing RIM_ATTRIBUTES helper
* found attributes at REP0+4 on Client32. Accept the +4 location only
* if +0 is empty, so restricted rights value 0 still works when +4 is
* also zero.
*/
if (rights0 == 0 && rights4 != 0)
rights0 = rights4;
*rights_out = rights0;
return(0);
}
int c32_ncp87_add_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id,
uint16 rights,
uint16 rights_mask,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 reqpath[0x180];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
UI path_struct_len;
UI reqpath_len;
uint8 *tp;
int rc;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
/*
* NCP 87 subfunction 10: Add trustee.
*
* This mirrors ncpfs ncp_ns_trustee_add():
* byte 10
* byte namespace DOS
* byte reserved
* word search attributes, little endian
* word rights mask, little endian
* word object count, little endian
* handle/path
* trustee array at request offset 16 + 307
*
* Client32 Raw5 has two request fragments, so the second fragment carries
* the handle/path and the padded trustee record.
*/
memset(hdr, 0, sizeof(hdr));
hdr[0] = 10;
hdr[1] = 0; /* DOS namespace */
hdr[2] = 0; /* reserved */
c32_put_word_lh(hdr + 3, 0x8006); /* SA_ALL: files/subdirs + system + hidden */
c32_put_word_lh(hdr + 5, rights_mask);
c32_put_word_lh(hdr + 7, 1); /* one trustee */
memset(reqpath, 0, sizeof(reqpath));
path_struct_len = c32_build_handle_path_from_dos_path(reqpath,
(uint8)dir_handle,
0, 0,
path_name);
/*
* ncpfs seeks to absolute packet offset 16+307 before writing the
* trustee list. The NCP request header is 7 bytes, so inside the NCP
* payload the trustee list begins at:
*
* (16 + 307) - 7 = 316
*
* Our first Raw5 request fragment is the 9-byte subfunction header,
* so the trustee list begins in the second fragment at:
*
* 316 - 9 = 307
*/
if (path_struct_len > 307)
return(2);
tp = reqpath + 307;
c32_put_dword_hl(tp, object_id); tp += 4;
c32_put_word_lh(tp, rights); tp += 2;
reqpath_len = (UI)(tp - reqpath);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
reqpath, reqpath_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
return(0);
}
int c32_ncp87_find_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id,
uint16 *rights_out,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 path[0x140];
uint8 rep0[0x120];
uint8 rep1[0x120];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
uint32 seq = 0;
UI path_len;
int rc;
if (rights_out) *rights_out = 0;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
if (!rights_out)
return(1);
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
for (;;) {
uint16 count;
uint16 i;
uint8 *tp;
uint32 next_seq;
memset(hdr, 0, sizeof(hdr));
hdr[0] = 5; /* NCP87 subfunction 5: scan trustees */
hdr[1] = 0; /* DOS namespace */
hdr[2] = 0; /* reserved */
c32_put_word_lh(hdr + 3, 0x8006); /* SA_ALL: files/subdirs + system + hidden */
c32_put_dword_lh(hdr + 5, seq); /* search sequence, starts at zero */
path_len = c32_build_handle_path_from_dos_path(path,
(uint8)dir_handle,
0, 0,
path_name);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 9,
path, path_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(0xff); /* Client32 returns an error when no trustees are present. */
next_seq = c32_get_dword_lh(rep0 + 0);
count = c32_get_word_lh(rep0 + 4);
if (count > 20)
count = 20;
tp = rep0 + 6;
for (i = 0; i < count; i++) {
uint32 tid = c32_get_dword_hl(tp);
uint16 trights = c32_get_word_lh(tp + 4);
if (tid == object_id || c32_get_dword_lh(tp) == object_id) {
*rights_out = trights;
return(0);
}
tp += 6;
}
if (next_seq == 0xffffffffUL || next_seq == seq)
break;
seq = next_seq;
}
return(0xff); /* no trustee found / no more entries */
}
int c32_ncp87_delete_trustee_rights(const char *path_name,
uint16 dir_handle,
uint32 object_id,
uint16 *actual_out,
uint16 *handle_lo_out,
uint16 *handle_hi_out)
{
uint16 handle_lo, handle_hi;
uint8 hdr[16];
uint8 reqpath[0x180];
uint8 rep0[0x20];
uint8 rep1[0x20];
uint8 rawout[32];
uint16 raw_ret_ax, raw_ret_dx;
uint16 actual_lo;
UI path_struct_len;
UI reqpath_len;
uint8 *tp;
int rc;
if (actual_out) *actual_out = 0;
if (handle_lo_out) *handle_lo_out = 0;
if (handle_hi_out) *handle_hi_out = 0;
rc = c32_get_ncp_handle(&handle_lo, &handle_hi);
if (rc)
return(10 + rc);
memset(hdr, 0, sizeof(hdr));
hdr[0] = 11; /* NCP87 subfunction 11: delete trustee */
hdr[1] = 0; /* DOS namespace */
hdr[2] = 0; /* reserved */
c32_put_word_lh(hdr + 3, 1); /* one trustee */
memset(reqpath, 0, sizeof(reqpath));
path_struct_len = c32_build_handle_path_from_dos_path(reqpath,
(uint8)dir_handle,
0, 0,
path_name);
if (path_struct_len > 307)
return(2);
tp = reqpath + 307;
c32_put_dword_hl(tp, object_id); tp += 4;
c32_put_word_lh(tp, 0); tp += 2;
reqpath_len = (UI)(tp - reqpath);
memset(rep0, 0, sizeof(rep0));
memset(rep1, 0, sizeof(rep1));
memset(rawout, 0, sizeof(rawout));
C32_NCP87_Raw5_Probe(handle_lo, handle_hi,
hdr, 5,
reqpath, reqpath_len,
rep0, sizeof(rep0),
rep1, sizeof(rep1),
rawout);
raw_ret_ax = c32_get_word_lh(rawout + 14);
raw_ret_dx = c32_get_word_lh(rawout + 16);
actual_lo = c32_get_word_lh(rawout + 18);
if (actual_out) *actual_out = actual_lo;
if (handle_lo_out) *handle_lo_out = handle_lo;
if (handle_hi_out) *handle_hi_out = handle_hi;
if (raw_ret_ax != 0 || raw_ret_dx != 0)
return(20);
return(0);
}