Files
mars-dosutils/grant.c
Mario Fetka 5da600c2a5 dosutils: match Novell paths for flags and trustees
Move FLAG, GRANT and REMOVE closer to the request paths used by the
Novell tools and extend the DOS comparison tests.

FLAG now reads attributes through the old NCP22 directory scan path and
writes them through NCP22/25 Set Directory/File Information. This keeps
extended attributes such as Delete Inhibit and Rename Inhibit intact and
matches the Novell behavior observed in the server logs.

GRANT now prefers NCP22/27 SetTrustee with an NCP87 fallback. Supervisor
rights are expanded like Novell does, so granting S sends and reports the
full SRWCEMFA mask. The visible output, path formatting and error text
are adjusted to match the Novell baseline.

REMOVE now prefers NCP22/2B DelTrustee with an NCP87 fallback. The
DelTrustee request layout is corrected, /SUBDIRS handling is aligned
with Novell, and the output/error text is matched to the baseline.

The FLAG, FLAGDIR, GRANT and REMOVE tests now compare NPUBLIC baselines
against the PUBLIC implementations and add delayed NOPASSUSER readback
checks using DLYSTRT and the maintainer LOGIN password option.
2026-05-28 07:54:41 +02:00

438 lines
11 KiB
C

/* grant.c - Novell GRANT-like DOS utility, first Client32 implementation */
#include "net.h"
#include "c32ncp.h"
#define GRANT_BINDERY_USER 0x0001
#define GRANT_BINDERY_GROUP 0x0002
#define NCP_RIGHT_READ 0x0001
#define NCP_RIGHT_WRITE 0x0002
#define NCP_RIGHT_OPEN 0x0004
#define NCP_RIGHT_CREATE 0x0008
#define NCP_RIGHT_DELETE 0x0010
#define NCP_RIGHT_OWNER 0x0020
#define NCP_RIGHT_SEARCH 0x0040
#define NCP_RIGHT_MODIFY 0x0080
#define NCP_RIGHT_SUPER 0x0100
/* Novell GRANT ALL assigns all normal trustee rights, not Supervisor. */
#define NCP_RIGHT_ALL_386 (NCP_RIGHT_READ | NCP_RIGHT_WRITE | \
NCP_RIGHT_CREATE | NCP_RIGHT_DELETE | \
NCP_RIGHT_MODIFY | NCP_RIGHT_SEARCH | \
NCP_RIGHT_OWNER)
static void grant_usage_error(void)
{
fprintf(stdout, "Command line arguments violate grammar defined for GRANT.\n\n");
}
static void grant_invalid_right_error(void)
{
fprintf(stdout, "Specified rights unreadable or invalid. Spaces required between each right.\n\n");
}
static void grant_usage_ex(int leading_blanks, int bell_after_options,
int trailing_blanks)
{
int i;
for (i = 0; i < leading_blanks; i++)
fprintf(stdout, "\n");
fprintf(stdout, "Usage: GRANT rightslist* [FOR path] TO [USER | GROUP] name [options]\n");
fprintf(stdout, "Options: /SubDirectories | /Files\n");
if (bell_after_options)
fprintf(stdout, "\a");
fprintf(stdout, "\n");
fprintf(stdout, "386 Rights:\n");
fprintf(stdout, "--------------------\n");
fprintf(stdout, "ALL = All\n");
fprintf(stdout, "N = No Rights\n");
fprintf(stdout, "S = Supervisor\n");
fprintf(stdout, "R = Read\n");
fprintf(stdout, "W = Write\n");
fprintf(stdout, "C = Create\n");
fprintf(stdout, "E = Erase\n");
fprintf(stdout, "M = Modify\n");
fprintf(stdout, "F = File Scan\n");
fprintf(stdout, "A = Access Control\n");
for (i = 0; i < trailing_blanks; i++)
fprintf(stdout, "\n");
}
static void grant_usage(void)
{
grant_usage_ex(0, 0, 0);
}
static void grant_usage_help(void)
{
grant_usage_ex(2, 0, 1);
}
static void grant_usage_after_error(void)
{
grant_usage_ex(0, 1, 2);
}
static void grant_header_path(char *out, char *path, int max)
{
char *p;
tool_header_path(out, path, max);
/* Novell GRANT displays server and volume as SERVER/SYS: while
* RIGHTS uses SERVER\SYS:. Keep the rest of the path with DOS
* backslashes. */
p = strchr(out, '\\');
if (p && strchr(out, ':') && p < strchr(out, ':'))
*p = '/';
}
static void grant_rights_bracket(uint16 rights, char *out)
{
/* Novell displays Supervisor as the full effective right mask. */
if (rights & NCP_RIGHT_SUPER)
rights |= NCP_RIGHT_ALL_386;
out[0] = (rights & NCP_RIGHT_SUPER) ? 'S' : ' ';
out[1] = (rights & NCP_RIGHT_READ) ? 'R' : ' ';
out[2] = (rights & NCP_RIGHT_WRITE) ? 'W' : ' ';
out[3] = (rights & NCP_RIGHT_CREATE) ? 'C' : ' ';
out[4] = (rights & NCP_RIGHT_DELETE) ? 'E' : ' ';
out[5] = (rights & NCP_RIGHT_MODIFY) ? 'M' : ' ';
out[6] = (rights & NCP_RIGHT_SEARCH) ? 'F' : ' ';
out[7] = (rights & NCP_RIGHT_OWNER) ? 'A' : ' ';
out[8] = '\0';
}
static uint16 grant_expand_supervisor_rights(uint16 rights)
{
/* Novell GRANT expands Supervisor to the full trustee mask before
* storing it. This keeps the wire value and the printed result in
* sync with Novell: GRANT S reports and sends [SRWCEMFA], not just
* [S ]. */
if (rights & NCP_RIGHT_SUPER)
rights |= NCP_RIGHT_ALL_386;
return(rights);
}
static void grant_rights_string(uint16 rights, char *out)
{
char *p = out;
if (rights == 0) {
strcpy(out, "N");
return;
}
if ((rights & NCP_RIGHT_ALL_386) == NCP_RIGHT_ALL_386) {
strcpy(out, "ALL");
return;
}
if (rights & NCP_RIGHT_SUPER) *p++ = 'S';
if (rights & NCP_RIGHT_READ) *p++ = 'R';
if (rights & NCP_RIGHT_WRITE) *p++ = 'W';
if (rights & NCP_RIGHT_CREATE) *p++ = 'C';
if (rights & NCP_RIGHT_DELETE) *p++ = 'E';
if (rights & NCP_RIGHT_MODIFY) *p++ = 'M';
if (rights & NCP_RIGHT_SEARCH) *p++ = 'F';
if (rights & NCP_RIGHT_OWNER) *p++ = 'A';
*p = '\0';
}
static int grant_add_right_word(char *s, uint16 *rights)
{
if (tool_strsame(s, "ALL")) {
*rights = NCP_RIGHT_ALL_386;
return(0);
}
if (tool_strsame(s, "N") || tool_strsame(s, "NONE")) {
*rights = 0;
return(0);
}
if (tool_strsame(s, "S") || tool_strsame(s, "SUPERVISOR")) {
*rights |= NCP_RIGHT_SUPER;
return(0);
}
if (tool_strsame(s, "R") || tool_strsame(s, "READ")) {
*rights |= NCP_RIGHT_READ;
return(0);
}
if (tool_strsame(s, "W") || tool_strsame(s, "WRITE")) {
*rights |= NCP_RIGHT_WRITE;
return(0);
}
if (tool_strsame(s, "C") || tool_strsame(s, "CREATE")) {
*rights |= NCP_RIGHT_CREATE;
return(0);
}
if (tool_strsame(s, "E") || tool_strsame(s, "ERASE")) {
*rights |= NCP_RIGHT_DELETE;
return(0);
}
if (tool_strsame(s, "M") || tool_strsame(s, "MODIFY")) {
*rights |= NCP_RIGHT_MODIFY;
return(0);
}
if (tool_strsame(s, "F") || tool_strsame(s, "FILESCAN") ||
tool_strsame(s, "FILE") || tool_strsame(s, "SCAN")) {
*rights |= NCP_RIGHT_SEARCH;
return(0);
}
if (tool_strsame(s, "A") || tool_strsame(s, "ACCESS") ||
tool_strsame(s, "CONTROL") || tool_strsame(s, "ACCESSCONTROL")) {
*rights |= NCP_RIGHT_OWNER;
return(0);
}
return(-1);
}
static int grant_last_rc = 0;
static int grant_set_one(char *path, uint16 dhandle,
uint32 object_id, uint16 rights)
{
int rc;
/* Novell GRANT uses the old NCP22/27 SetTrustee path. Prefer that
* path and keep the NCP87 add-trustee call only as a compatibility
* fallback for clients/servers that do not accept the old call. */
rc = c32_ncp22_set_trustee_rights(path, dhandle, object_id, rights);
if (rc) {
rc = c32_ncp87_add_trustee_rights(path,
dhandle,
object_id,
rights,
0xffff,
NULL, NULL, NULL);
}
grant_last_rc = rc;
return(rc);
}
/*
* Apply the grant to PATH and every directory below it.
*
* This intentionally walks through the DOS redirector, not through another
* NCP search helper, so it works with the same mapped-drive view that the
* user passed to GRANT.
*/
static int grant_set_subdirs(char *path, uint16 dhandle,
uint32 object_id, uint16 rights)
{
struct find_t ff;
char pattern[260];
char child[260];
int rc = 0;
if (grant_set_one(path, dhandle, object_id, rights))
rc = 1;
tool_join_path(pattern, path, "*.*", sizeof(pattern));
if (_dos_findfirst(pattern, _A_SUBDIR, &ff) == 0) {
do {
if ((ff.attrib & _A_SUBDIR) && !tool_is_dot_dir(ff.name)) {
tool_join_path(child, path, ff.name, sizeof(child));
if (grant_set_subdirs(child, dhandle, object_id, rights))
rc = 1;
}
} while (_dos_findnext(&ff) == 0);
}
return(rc);
}
int func_grant(int argc, char *argv[], int mode)
{
uint16 rights = 0;
char *path = ".";
char *objname = NULL;
uint16 objtype = GRANT_BINDERY_USER;
uint8 connid = 0;
uint8 dhandle = 0;
uint8 namebuf[48];
uint32 object_id;
int recurse_subdirs = 0;
int i = 1;
int have_rights = 0;
int rc;
(void)mode;
if (argc < 2 || tool_is_help_arg(argv[1])) {
if (argc < 2) {
fprintf(stdout, "\n");
grant_usage_error();
grant_usage_after_error();
return(1);
}
grant_usage_help();
return(0);
}
/*
* GRANT rightslist* [FOR path] TO [USER|GROUP] name [options]
*/
while (i < argc) {
if (tool_strsame(argv[i], "FOR") || tool_strsame(argv[i], "TO"))
break;
if (tool_is_option(argv[i]))
break;
if (grant_add_right_word(argv[i], &rights)) {
fprintf(stdout, "\n");
grant_invalid_right_error();
grant_usage_after_error();
return(1);
}
have_rights = 1;
i++;
}
if (!have_rights || i >= argc) {
fprintf(stdout, "\n");
grant_usage_error();
grant_usage_after_error();
return(1);
}
if (tool_strsame(argv[i], "FOR")) {
i++;
if (i >= argc) {
fprintf(stdout, "\n");
grant_usage_error();
grant_usage_after_error();
return(1);
}
path = argv[i++];
}
if (i >= argc || !tool_strsame(argv[i], "TO")) {
fprintf(stdout, "\n");
grant_usage_error();
grant_usage_after_error();
return(1);
}
i++;
if (i < argc && tool_strsame(argv[i], "USER")) {
objtype = GRANT_BINDERY_USER;
i++;
} else if (i < argc && tool_strsame(argv[i], "GROUP")) {
objtype = GRANT_BINDERY_GROUP;
i++;
}
if (i >= argc) {
fprintf(stdout, "\n");
grant_usage_error();
grant_usage_after_error();
return(1);
}
objname = argv[i++];
while (i < argc) {
if (!tool_is_option(argv[i])) {
fprintf(stdout, "\n");
grant_usage_error();
grant_usage_after_error();
return(1);
}
/*
* /FILES is harmless because the explicit path is passed to the
* NCP87 trustee-add call. /SUBDIRECTORIES recursively applies the
* same grant to all subdirectories below the given path.
*/
if (tool_strsame(argv[i], "/FILES") || tool_strsame(argv[i], "-FILES") ||
tool_strsame(argv[i], "/F") || tool_strsame(argv[i], "-F")) {
i++;
continue;
}
if (tool_strsame(argv[i], "/SUBDIRECTORIES") ||
tool_strsame(argv[i], "-SUBDIRECTORIES") ||
tool_strsame(argv[i], "/S") || tool_strsame(argv[i], "-S")) {
recurse_subdirs = 1;
i++;
continue;
}
fprintf(stdout, "\n");
grant_usage_error();
grant_usage_after_error();
return(1);
}
rights = grant_expand_supervisor_rights(rights);
if (tool_current_dhandle(&connid, &dhandle)) {
fprintf(stdout, "Specified path not locatable.\n");
return(1);
}
strmaxcpy(namebuf, objname, sizeof(namebuf) - 1);
upstr(namebuf);
object_id = ncp_17_35(namebuf, objtype);
if (!object_id) {
char header[300];
grant_header_path(header, path, sizeof(header));
fprintf(stdout, "\n%s\n", header);
if (objtype == GRANT_BINDERY_GROUP)
fprintf(stdout, "\aGroup \"%s\" not found.\n", objname);
else
fprintf(stdout, "\aUser \"%s\" not found.\n", objname);
return(1);
}
if (recurse_subdirs)
rc = grant_set_subdirs(path, (uint16)dhandle, object_id, rights);
else
rc = grant_set_one(path, (uint16)dhandle, object_id, rights);
if (rc) {
char header[300];
grant_header_path(header, path, sizeof(header));
fprintf(stdout, "\n%s\n", header);
fprintf(stdout, "Invalid path or no match for pattern specified.\n\n");
return(grant_last_rc ? grant_last_rc : 1);
}
{
char header[300];
char base[80];
char bracket[10];
grant_header_path(header, path, sizeof(header));
tool_basename(base, path, sizeof(base));
grant_rights_bracket(rights, bracket);
fprintf(stdout, "\n%s\n", header);
fprintf(stdout, "%-33.33sRights set to [%s]\n\n", base, bracket);
}
return(0);
}