Replace small local option helper wrappers with the shared tools.c helpers. SLIST now uses tool_is_help_arg() directly, and REMOVE/REVOKE use tool_is_files_option() and tool_is_subdirs_option() instead of Trustee-specific wrappers. This keeps trustee.c focused on Rights/Trustee behavior and leaves generic command-line option handling in tools.c. No behavior change.
376 lines
11 KiB
C
376 lines
11 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: REVOKE utility for deleting trustee assignments from NetWare directories and files.
|
|
* Depends on: net.h, ncpapi.h, trustee.h/trustee.c trustee helpers, netcall.c requester helpers, tools.c shared utility routines.
|
|
*/
|
|
|
|
#include "net.h"
|
|
#include "ncpapi.h"
|
|
#include "trustee.h"
|
|
|
|
#ifndef _A_NORMAL
|
|
#define _A_NORMAL 0x00
|
|
#endif
|
|
|
|
static int revoke_last_rc = 0;
|
|
|
|
static void revoke_usage_short(int leading_blank, int bell_first)
|
|
{
|
|
if (bell_first)
|
|
fprintf(stdout, "\a");
|
|
if (leading_blank || bell_first)
|
|
fprintf(stdout, "\n");
|
|
|
|
fprintf(stdout, "Usage: REVOKE rightslist* [FOR path] FROM [USER|GROUP] name [options]\n");
|
|
fprintf(stdout, "Options: /SubDirectories | /Files\n");
|
|
fprintf(stdout, " ");
|
|
if (bell_first)
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
static void revoke_usage_full(int leading_blank, int bell_before_tail)
|
|
{
|
|
if (leading_blank)
|
|
fprintf(stdout, "\n");
|
|
|
|
fprintf(stdout, "Usage: REVOKE rightslist* [FOR path] FROM [USER|GROUP] name [options]\n");
|
|
fprintf(stdout, "Options: /SubDirectories | /Files\n");
|
|
fprintf(stdout, " \n");
|
|
fprintf(stdout, "286 Rights:\t\t386 Rights:\n");
|
|
fprintf(stdout, "---------------\t\t--------------------\n");
|
|
fprintf(stdout, "ALL = All\t\tALL = All\n");
|
|
fprintf(stdout, "R = Read\t\tS = Supervisor\n");
|
|
fprintf(stdout, "W = Write\t\tR = Read\n");
|
|
fprintf(stdout, "O = Open\t\tW = Write\n");
|
|
fprintf(stdout, "C = Create\t\tC = Create\n");
|
|
fprintf(stdout, "D = Delete\t\tE = Erase\n");
|
|
fprintf(stdout, "P = Parental\t\tM = Modify\n");
|
|
fprintf(stdout, "S = Search\t\tF = File Scan\n");
|
|
fprintf(stdout, "M = Modify\t\tA = Access Control\n");
|
|
fprintf(stdout, "\n");
|
|
if (bell_before_tail)
|
|
fprintf(stdout, "\a");
|
|
fprintf(stdout, "* Use abbreviations listed above, separated by spaces.\n");
|
|
}
|
|
|
|
static void revoke_header_path(char *out, char *path, int max)
|
|
{
|
|
char *p;
|
|
|
|
tool_header_path(out, path, max);
|
|
|
|
/* Novell REVOKE 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 int revoke_one(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 revoke_mask, int forced_is_file)
|
|
{
|
|
uint16 old_rights = 0;
|
|
uint16 new_rights;
|
|
int is_dir;
|
|
int rc;
|
|
|
|
/* Without /FILES, Novell REVOKE reports directory-oriented messages even
|
|
* if the path cannot be located. */
|
|
is_dir = forced_is_file ? 0 : 1;
|
|
|
|
rc = ncp87_05_find_trustee_rights(path, dhandle, object_id, &old_rights,
|
|
NULL, NULL, NULL);
|
|
revoke_last_rc = rc;
|
|
if (rc) {
|
|
if (rc == 0xff)
|
|
fprintf(stdout, "\aNo trustee for the specified %s.\n", is_dir ? "directory" : "file");
|
|
else
|
|
fprintf(stdout, "\aError scanning trustee list.\n");
|
|
return(1);
|
|
}
|
|
|
|
new_rights = (uint16)(old_rights & ~revoke_mask);
|
|
|
|
if (new_rights == 0) {
|
|
/* Novell REVOKE removes the trustee through the old NCP22 trustee path.
|
|
* Keep the Client32/NCP87 delete call as a compatibility fallback. */
|
|
rc = ncp22_2b_delete_trustee_rights(path, dhandle, object_id);
|
|
if (rc)
|
|
rc = ncp87_0b_delete_trustee_rights(path, dhandle, object_id,
|
|
NULL, NULL, NULL);
|
|
revoke_last_rc = rc;
|
|
if (rc) {
|
|
fprintf(stdout, "Error deleting trustee.\n");
|
|
return(1);
|
|
}
|
|
} else {
|
|
/* Updating the remaining trustee mask is the same old SetTrustee flow
|
|
* used by GRANT. Fall back to NCP87 add-trustee if a server/client
|
|
* does not accept NCP22/27. */
|
|
rc = ncp22_27_set_trustee_rights(path, dhandle, object_id, new_rights);
|
|
if (rc)
|
|
rc = ncp87_0a_add_trustee_rights(path, dhandle, object_id, new_rights,
|
|
0xffff, NULL, NULL, NULL);
|
|
revoke_last_rc = rc;
|
|
if (rc) {
|
|
fprintf(stdout, "Fatal error revoking access rights.\n");
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
{
|
|
char header[300];
|
|
char bracket[10];
|
|
revoke_header_path(header, path, sizeof(header));
|
|
trustee_rights_bracket(new_rights, bracket);
|
|
fprintf(stdout, "%s\n", header);
|
|
fprintf(stdout, "Trustee's access rights set to [%s]\r\n\n", bracket);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int revoke_subdirs_inner(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 revoke_mask, int *count,
|
|
int include_this)
|
|
{
|
|
struct find_t ff;
|
|
char pattern[260];
|
|
char child[260];
|
|
|
|
/* Novell REVOKE /SUBDIRECTORIES follows the REMOVE behaviour: the
|
|
* specified start directory is a container for the recursive operation,
|
|
* not itself part of the operation. Once a listed subdirectory reports
|
|
* no direct trustee, Novell stops after that first failure. */
|
|
if (include_this) {
|
|
if (revoke_one(path, dhandle, object_id, revoke_mask, 0) == 0)
|
|
(*count)++;
|
|
else
|
|
return(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 (revoke_subdirs_inner(child, dhandle, object_id, revoke_mask,
|
|
count, 1))
|
|
return(1);
|
|
}
|
|
} while (_dos_findnext(&ff) == 0);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int revoke_subdirs(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 revoke_mask, int *count)
|
|
{
|
|
return(revoke_subdirs_inner(path, dhandle, object_id, revoke_mask,
|
|
count, 0));
|
|
}
|
|
|
|
static int revoke_files(char *path, uint16 dhandle, uint32 object_id,
|
|
uint16 revoke_mask, int *count)
|
|
{
|
|
struct find_t ff;
|
|
char dir[260];
|
|
char pat[80];
|
|
char spec[260];
|
|
char target[260];
|
|
int rc = 0;
|
|
|
|
if (trustee_path_is_dir(path)) {
|
|
strmaxcpy(dir, path, sizeof(dir) - 1);
|
|
strmaxcpy(pat, "*.*", sizeof(pat) - 1);
|
|
} else if (tool_has_wildcards(path)) {
|
|
tool_parent_pattern(dir, pat, path, sizeof(dir), sizeof(pat));
|
|
} else {
|
|
if (revoke_one(path, dhandle, object_id, revoke_mask, 1) == 0)
|
|
(*count)++;
|
|
else
|
|
rc = 1;
|
|
return(rc);
|
|
}
|
|
|
|
tool_join_path(spec, dir, pat, sizeof(spec));
|
|
if (_dos_findfirst(spec, _A_NORMAL | _A_HIDDEN | _A_SYSTEM | _A_ARCH, &ff) == 0) {
|
|
do {
|
|
if (!(ff.attrib & _A_SUBDIR)) {
|
|
tool_join_path(target, dir, ff.name, sizeof(target));
|
|
if (revoke_one(target, dhandle, object_id, revoke_mask, 1) == 0)
|
|
(*count)++;
|
|
else
|
|
rc = 1;
|
|
}
|
|
} while (_dos_findnext(&ff) == 0);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
int func_revoke(int argc, char *argv[], int mode)
|
|
{
|
|
uint16 rights = 0;
|
|
char *path = ".";
|
|
char *objname = NULL;
|
|
char objprint[48];
|
|
uint16 objtype = TRUSTEE_BINDERY_USER;
|
|
int objtype_given = 0;
|
|
uint8 connid = 0;
|
|
uint8 dhandle = 0;
|
|
uint32 object_id;
|
|
int use_subdirs = 0;
|
|
int use_files = 0;
|
|
int count = 0;
|
|
int i = 1;
|
|
int have_rights = 0;
|
|
int rc;
|
|
|
|
(void)mode;
|
|
|
|
if (argc < 2 || tool_is_help_arg(argv[1])) {
|
|
if (argc < 2)
|
|
revoke_usage_full(1, 1);
|
|
else
|
|
revoke_usage_short(1, 0);
|
|
return(argc < 2 ? 1 : 0);
|
|
}
|
|
|
|
while (i < argc) {
|
|
if (tool_strsame(argv[i], "FOR") || tool_strsame(argv[i], "FROM"))
|
|
break;
|
|
if (tool_is_option(argv[i]))
|
|
break;
|
|
if (trustee_parse_right_word(argv[i], &rights)) {
|
|
fprintf(stdout, "Invalid right specified.\n\n");
|
|
revoke_usage_full(0, 1);
|
|
return(1);
|
|
}
|
|
have_rights = 1;
|
|
i++;
|
|
}
|
|
|
|
if (!have_rights || i >= argc) {
|
|
revoke_usage_short(0, 1);
|
|
return(1);
|
|
}
|
|
|
|
if (tool_strsame(argv[i], "FOR")) {
|
|
i++;
|
|
if (i >= argc) {
|
|
revoke_usage_short(0, 1);
|
|
return(1);
|
|
}
|
|
path = argv[i++];
|
|
}
|
|
|
|
if (i >= argc || !tool_strsame(argv[i], "FROM")) {
|
|
revoke_usage_short(0, 1);
|
|
return(1);
|
|
}
|
|
i++;
|
|
|
|
if (i < argc && tool_strsame(argv[i], "USER")) {
|
|
objtype = TRUSTEE_BINDERY_USER;
|
|
objtype_given = 1;
|
|
i++;
|
|
} else if (i < argc && tool_strsame(argv[i], "GROUP")) {
|
|
objtype = TRUSTEE_BINDERY_GROUP;
|
|
objtype_given = 1;
|
|
i++;
|
|
}
|
|
|
|
if (i >= argc) {
|
|
revoke_usage_short(0, 1);
|
|
return(1);
|
|
}
|
|
|
|
objname = argv[i++];
|
|
|
|
while (i < argc) {
|
|
if (!tool_is_option(argv[i])) {
|
|
revoke_usage_short(0, 1);
|
|
return(1);
|
|
}
|
|
|
|
if (tool_is_files_option(argv[i])) {
|
|
use_files = 1;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
if (tool_is_subdirs_option(argv[i])) {
|
|
use_subdirs = 1;
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
revoke_usage_short(0, 1);
|
|
return(1);
|
|
}
|
|
|
|
/* Novell REVOKE does not emit a grammar-specific message for /FILES plus
|
|
* /SUBDIRECTORIES. It reports the same directory trustee failure text. */
|
|
|
|
if (tool_current_dhandle(&connid, &dhandle)) {
|
|
fprintf(stdout, "Error: Drive not mapped to Network.\n");
|
|
return(1);
|
|
}
|
|
|
|
object_id = trustee_lookup_object(objname, &objtype, objtype_given);
|
|
if (!object_id) {
|
|
if (objtype_given && objtype == TRUSTEE_BINDERY_GROUP)
|
|
fprintf(stdout, "\aGroup \"%s\" not found.\n", objname);
|
|
else if (objtype_given)
|
|
fprintf(stdout, "\aUser \"%s\" not found.\n", objname);
|
|
else
|
|
fprintf(stdout, "\aUser or group \"%s\" not found.\n", objname);
|
|
return(1);
|
|
}
|
|
|
|
tool_upcopy(objprint, objname, sizeof(objprint));
|
|
|
|
if (use_files && use_subdirs) {
|
|
fprintf(stdout, "\aNo trustee for the specified directory.\n");
|
|
revoke_last_rc = 0xff;
|
|
return(1);
|
|
} else if (use_subdirs)
|
|
rc = revoke_subdirs(path, (uint16)dhandle, object_id, rights, &count);
|
|
else if (use_files)
|
|
rc = revoke_files(path, (uint16)dhandle, object_id, rights, &count);
|
|
else {
|
|
rc = revoke_one(path, (uint16)dhandle, object_id, rights, 0);
|
|
if (!rc)
|
|
count = 1;
|
|
}
|
|
|
|
if ((use_subdirs || (!use_files && !rc)) && count > 0)
|
|
fprintf(stdout, "Rights for %d directories were changed for %s.\n\n", count, objprint);
|
|
else if (use_files && count > 0)
|
|
fprintf(stdout, "Rights for %d files were changed for %s.\n\n", count, objprint);
|
|
|
|
return(rc ? (revoke_last_rc ? revoke_last_rc : 1) : 0);
|
|
}
|