Files
mars-dosutils/grant.c
Mario Fetka f940d2d88e dosutils: implement GRANT trustee management
- add GRANT as a new multi-call DOS utility
- implement Client32 NCP87 trustee-add helper
- resolve USER and GROUP bindery objects before granting rights
- support Novell-style rights lists, including ALL and N
- support directory trustee grants
- support file trustee grants via /FILES
- implement recursive grants via /SUBDIRECTORIES
- accept /SUBDIRS as a compatibility alias
- format GRANT success output close to Novell GRANT
- add GRANT comparison scripts for normal and recursive test cases
2026-05-24 13:27:07 +02:00

520 lines
12 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
#define NCP_RIGHT_ALL_386 (NCP_RIGHT_SUPER | NCP_RIGHT_READ | \
NCP_RIGHT_WRITE | NCP_RIGHT_CREATE | \
NCP_RIGHT_DELETE | NCP_RIGHT_MODIFY | \
NCP_RIGHT_SEARCH | NCP_RIGHT_OWNER)
static int grant_same(char *a, char *b)
{
while (*a || *b) {
int ca = *a++;
int cb = *b++;
if (ca >= 'a' && ca <= 'z') ca -= 32;
if (cb >= 'a' && cb <= 'z') cb -= 32;
if (ca != cb) return(0);
}
return(1);
}
static int grant_is_help(char *s)
{
if (!s) return(0);
return(grant_same(s, "/?") || grant_same(s, "-?") || grant_same(s, "?"));
}
static void grant_usage_error(void)
{
fprintf(stdout, "Command line arguments violate grammar defined for GRANT.\n\n");
}
static void grant_usage(void)
{
fprintf(stdout, "Usage: GRANT rightslist* [FOR path] TO [USER | GROUP] name [options]\n");
fprintf(stdout, "Options: /SubDirectories | /Files\n\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");
}
static int grant_get_current_drive(void)
{
REGS regs;
regs.h.ah = 0x19;
int86(0x21, &regs, &regs);
return((int)regs.h.al);
}
static int grant_current_dhandle(uint8 *connid, uint8 *dhandle)
{
uint8 flags = 0;
int drive = grant_get_current_drive();
if (get_drive_info((uint8)drive, connid, dhandle, &flags))
return(-1);
if (!*connid || (flags & 0x80))
return(-1);
return(0);
}
static int grant_current_prefix(char *out, int max)
{
uint8 connid = 0;
uint8 dhandle = 0;
uint8 flags = 0;
int drive;
char server[52];
char dpath[260];
char volume[32];
char *p;
int i = 0;
if (!out || max < 8)
return(-1);
out[0] = '\0';
drive = grant_get_current_drive();
if (get_drive_info((uint8)drive, &connid, &dhandle, &flags))
return(-1);
if (!connid || (flags & 0x80))
return(-1);
server[0] = '\0';
if (get_fs_name(connid, server))
server[0] = '\0';
dpath[0] = '\0';
if (get_dir_path(dhandle, dpath) || !dpath[0])
return(-1);
p = strchr(dpath, ':');
if (!p)
return(-1);
while (dpath + i < p && i < (int)sizeof(volume) - 1) {
volume[i] = dpath[i];
i++;
}
volume[i] = '\0';
if (!volume[0])
return(-1);
if (server[0])
sprintf(out, "%s/%s:", server, volume);
else
sprintf(out, "%s:", volume);
return(0);
}
static void grant_upcopy(char *dst, char *src, int max)
{
int i = 0;
if (!src) src = "";
while (*src && i < max - 1) {
char c = *src++;
if (c == '/') c = '\\';
if (c >= 'a' && c <= 'z') c -= 32;
dst[i++] = c;
}
dst[i] = 0;
}
static void grant_basename(char *dst, char *src, int max)
{
char up[260];
char *p;
grant_upcopy(up, src, sizeof(up));
p = strrchr(up, '\\');
if (!p) p = strrchr(up, ':');
if (p)
strmaxcpy(dst, p + 1, max - 1);
else
strmaxcpy(dst, up, max - 1);
}
static void grant_header_path(char *out, char *path, int max)
{
char prefix[90];
char up[260];
if (grant_current_prefix(prefix, sizeof(prefix)))
prefix[0] = '\0';
grant_upcopy(up, path, sizeof(up));
strmaxcpy(out, prefix, max - 1);
if ((int)(strlen(out) + strlen(up)) < max - 1)
strcat(out, up);
}
static void grant_rights_bracket(uint16 rights, char *out)
{
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 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 (grant_same(s, "ALL")) {
*rights = NCP_RIGHT_ALL_386;
return(0);
}
if (grant_same(s, "N") || grant_same(s, "NONE")) {
*rights = 0;
return(0);
}
if (grant_same(s, "S") || grant_same(s, "SUPERVISOR")) {
*rights |= NCP_RIGHT_SUPER;
return(0);
}
if (grant_same(s, "R") || grant_same(s, "READ")) {
*rights |= NCP_RIGHT_READ;
return(0);
}
if (grant_same(s, "W") || grant_same(s, "WRITE")) {
*rights |= NCP_RIGHT_WRITE;
return(0);
}
if (grant_same(s, "C") || grant_same(s, "CREATE")) {
*rights |= NCP_RIGHT_CREATE;
return(0);
}
if (grant_same(s, "E") || grant_same(s, "ERASE")) {
*rights |= NCP_RIGHT_DELETE;
return(0);
}
if (grant_same(s, "M") || grant_same(s, "MODIFY")) {
*rights |= NCP_RIGHT_MODIFY;
return(0);
}
if (grant_same(s, "F") || grant_same(s, "FILESCAN") ||
grant_same(s, "FILE") || grant_same(s, "SCAN")) {
*rights |= NCP_RIGHT_SEARCH;
return(0);
}
if (grant_same(s, "A") || grant_same(s, "ACCESS") ||
grant_same(s, "CONTROL") || grant_same(s, "ACCESSCONTROL")) {
*rights |= NCP_RIGHT_OWNER;
return(0);
}
return(-1);
}
static int grant_is_option(char *s)
{
if (!s) return(0);
return(s[0] == '/' || s[0] == '-');
}
static int grant_last_rc = 0;
static int grant_set_one(char *path, uint16 dhandle,
uint32 object_id, uint16 rights)
{
int rc;
rc = c32_ncp87_add_trustee_rights(path,
dhandle,
object_id,
rights,
0xffff,
NULL, NULL, NULL);
grant_last_rc = rc;
return(rc);
}
static int grant_is_dot_dir(char *name)
{
if (!name) return(0);
if (name[0] == '.' && name[1] == '\0') return(1);
if (name[0] == '.' && name[1] == '.' && name[2] == '\0') return(1);
return(0);
}
static void grant_join_path(char *out, char *base, char *name, int max)
{
int len;
out[0] = '\0';
strmaxcpy(out, base, max - 1);
len = strlen(out);
if (len > 0 && out[len - 1] != '\\' && out[len - 1] != '/' &&
out[len - 1] != ':') {
if (len < max - 1) {
out[len++] = '\\';
out[len] = '\0';
}
}
if ((int)(strlen(out) + strlen(name)) < max - 1)
strcat(out, name);
}
/*
* 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;
grant_join_path(pattern, path, "*.*", sizeof(pattern));
if (_dos_findfirst(pattern, _A_SUBDIR, &ff) == 0) {
do {
if ((ff.attrib & _A_SUBDIR) && !grant_is_dot_dir(ff.name)) {
grant_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 || grant_is_help(argv[1])) {
if (argc < 2)
grant_usage_error();
grant_usage();
return(argc < 2 ? 1 : 0);
}
/*
* GRANT rightslist* [FOR path] TO [USER|GROUP] name [options]
*/
while (i < argc) {
if (grant_same(argv[i], "FOR") || grant_same(argv[i], "TO"))
break;
if (grant_is_option(argv[i]))
break;
if (grant_add_right_word(argv[i], &rights)) {
grant_usage_error();
grant_usage();
return(1);
}
have_rights = 1;
i++;
}
if (!have_rights || i >= argc) {
grant_usage_error();
grant_usage();
return(1);
}
if (grant_same(argv[i], "FOR")) {
i++;
if (i >= argc) {
grant_usage_error();
grant_usage();
return(1);
}
path = argv[i++];
}
if (i >= argc || !grant_same(argv[i], "TO")) {
grant_usage_error();
grant_usage();
return(1);
}
i++;
if (i < argc && grant_same(argv[i], "USER")) {
objtype = GRANT_BINDERY_USER;
i++;
} else if (i < argc && grant_same(argv[i], "GROUP")) {
objtype = GRANT_BINDERY_GROUP;
i++;
}
if (i >= argc) {
grant_usage_error();
grant_usage();
return(1);
}
objname = argv[i++];
while (i < argc) {
if (!grant_is_option(argv[i])) {
grant_usage_error();
grant_usage();
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 (grant_same(argv[i], "/FILES") || grant_same(argv[i], "-FILES") ||
grant_same(argv[i], "/F") || grant_same(argv[i], "-F")) {
i++;
continue;
}
if (grant_same(argv[i], "/SUBDIRECTORIES") ||
grant_same(argv[i], "-SUBDIRECTORIES") ||
grant_same(argv[i], "/S") || grant_same(argv[i], "-S")) {
recurse_subdirs = 1;
i++;
continue;
}
grant_usage_error();
grant_usage();
return(1);
}
if (grant_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) {
fprintf(stdout, "Object not found.\n");
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) {
fprintf(stdout, "Could not add trustee rights. rc=%d\n", grant_last_rc);
return(grant_last_rc ? grant_last_rc : 1);
}
{
char header[300];
char base[80];
char bracket[10];
grant_header_path(header, path, sizeof(header));
grant_basename(base, path, sizeof(base));
grant_rights_bracket(rights, bracket);
fprintf(stdout, "%s\n", header);
fprintf(stdout, "%-29.29sRights set to [%s]\n", base, bracket);
}
return(0);
}