Move the shared GRANT/REMOVE/REVOKE header path formatting into trustee.c. The Novell trustee tools display the server and volume separator as SERVER/SYS: while keeping the remaining path separators as DOS backslashes. Use one trustee_header_path() helper for that formatting instead of keeping separate local copies in GRANT, REMOVE and REVOKE. No behavior change.
448 lines
12 KiB
C
448 lines
12 KiB
C
/*
|
|
* mars-nwe-dosutils - NetWare/DOS utility tools.
|
|
*
|
|
* Copyright (C) 2026 Mario Fetka
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Purpose: GRANT utility for assigning trustee rights on NetWare directories and files.
|
|
* Depends on: net.h, ncpapi.h, netcall.c requester helpers, ncpapi.c namespace/NCP helpers, tools.c shared utility routines.
|
|
*/
|
|
/* grant.c - Novell GRANT-like DOS utility, first Client32 implementation */
|
|
|
|
#include "net.h"
|
|
#include "ncpapi.h"
|
|
#include "trustee.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_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 = ncp22_27_set_trustee_rights(path, dhandle, object_id, rights);
|
|
if (rc) {
|
|
rc = ncp87_0a_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 = ncp17_35_get_bindery_object_id(namebuf, objtype);
|
|
if (!object_id) {
|
|
char header[300];
|
|
|
|
trustee_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];
|
|
|
|
trustee_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];
|
|
|
|
trustee_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);
|
|
}
|