Files
mars-nwe/src/trustee.c
Mario Fetka 181c20620c
All checks were successful
Source release / source-package (push) Successful in 39s
Fix rename trustee rights and invalidate trustee cache
Align rename permission checks with NetWare trustee semantics. A same-directory
rename should be controlled by the Modify right instead of requiring a broader
R/W/M combination. For moves to another parent directory, require Create rights
on the destination parent.

Also invalidate the trustee rights cache after adding or deleting trustee
assignments so newly granted rights are visible immediately to subsequent
operations.

This fixes the MARIO trustee test where rename failed with R/W/M/F and even
R/W/C/E/M/F, while the same operation succeeded only with the broader all-rights
set.
2026-05-26 14:29:51 +02:00

984 lines
31 KiB
C

/* trustee.c 15-Apr-00 */
/* (C)opyright (C) 1998,2000 Martin Stover, Marburg, Germany
*
* 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Trusttee routines for mars_nwe */
/* history since 01-Jun-00
*
* mst:01-Jun-00: removed SIG_SEGV in get_eff_rights_by_trustees(),
* when stat error
* mst:01-Sep-00: pcz:added real unix rights patch from Przemyslaw Czerpak
* mst:07-Sep-00: corrected trustee rights of subdirs.
* mst:07-Oct-00: corrected result from tru_eff_rights_exists()
* and un_nw_rights().
*
*/
#include "net.h"
#include <dirent.h>
#include "unxfile.h"
#include "nwvolume.h"
#include "connect.h"
#include "trustee.h"
/* right access routines depending on unix rights */
static int un_nw_rights(int voloptions, uint8 *unixname, struct stat *stb)
/* returns eff rights of file/dir depending on unix rights only */
/* therefore only root gets TRUSTEE_S by this routine */
{
int rights=0xff; /* first all, pconsole needs TRUSTEE_O */
int is_pipe_command=(voloptions & VOL_OPTION_IS_PIPE)
&& !S_ISFIFO(stb->st_mode);
if (act_uid || is_pipe_command || (voloptions & VOL_OPTION_READONLY)) {
int is_dir = S_ISDIR(stb->st_mode);
int norights = TRUSTEE_A; /* no access control rights */
int acc;
int accp=0;
int isaccp=0;
struct stat stbp;
uint8 *p = unixname+strlen(unixname);
/* pcz:01-Sep-00 */
if (voloptions & VOL_OPTION_UNX_RIGHT)
acc=get_unix_access_rights(stb,unixname);
else
acc=get_unix_eff_rights(stb);
memset(&stbp, 0, sizeof(struct stat));
if (p > unixname){ /* now we must get parent rights */
--p;
while (p>unixname && *p=='/') --p; /* remove trailing slash */
while (p>unixname && *p!='/') --p; /* search for slash */
while (p>unixname && *p=='/') --p; /* and remove it */
if (p > unixname) { /* found subdir */
*(p+1)='\0';
if (stat(unixname, &stbp) ||
(stbp.st_dev==stb->st_dev && stbp.st_ino==stb->st_ino)
|| !S_ISDIR(stbp.st_mode) ){
/* something wrong here, clear rights */
errorp(0,"un_nw_rights", "wrong path=%s", unixname);
} else { /* pcz:01-Sep-00 */
if (voloptions & VOL_OPTION_UNX_RIGHT)
accp=get_unix_access_rights(&stbp,unixname);
else
accp=get_unix_eff_rights(&stbp);
isaccp=1;
}
*(p+1)='/';
} else if (!stat("/.", &stbp)) { /* pcz:01-Sep-00 */
if (voloptions & VOL_OPTION_UNX_RIGHT)
accp=get_unix_access_rights(&stbp,"/.");
else
accp=get_unix_eff_rights(&stbp);
isaccp=1;
}
}
if (isaccp == 0) { /* pcz:01-Sep-00 */
XDPRINTF((1,0, "no rights to parentdir of %s", unixname));
norights=rights;
} else {
if (!(accp & X_OK))
norights=rights;
else if (!(accp & W_OK)) {
norights |= TRUSTEE_E; /* no erase right */
norights |= TRUSTEE_M; /* no modify rights */
}
/* mst:07-Oct-00 */
if ( (!is_dir) && !(accp & R_OK) )
norights |= TRUSTEE_F; /* no file scan rights */
}
if (voloptions & VOL_OPTION_READONLY) {
norights |= TRUSTEE_E; /* no erase right */
norights |= TRUSTEE_M; /* no modify rights */
norights |= TRUSTEE_C; /* no creat rights */
} else if ((!acc||is_dir||is_pipe_command) && !(acc&X_OK)) {
norights = rights;
} else if (is_pipe_command) {
norights |= TRUSTEE_E; /* no erase right */
norights |= TRUSTEE_M; /* no modify rights */
norights |= TRUSTEE_C; /* no creat rights */
}
if (!(acc & 0x30)){ /* if not user and not in groups */
norights |= TRUSTEE_M; /* no modify rights */
}
if (!(acc & W_OK)) {
if (!(acc & 0x10) || (stb->st_uid == default_uid)){
norights |= TRUSTEE_M; /* no modify rights */
}
norights |= TRUSTEE_E; /* no erase */
norights |= TRUSTEE_C; /* no creat */
norights |= TRUSTEE_W; /* no write */
}
if (!(acc & R_OK)) {
norights |= TRUSTEE_R; /* No read rights for files */
if (is_dir)
norights |= TRUSTEE_F; /* no scan rights */
}
rights &= (~norights);
} else
rights |= TRUSTEE_S; /* Root always has all access rights */
return(rights);
}
#define MAX_TRUSTEES 100 /* max. trustee entries for one file/dir */
#define MAX_TRUSTEE_CACHE 50 /* max. trusttees in cache */
typedef struct {
int trustee;
uint32 id;
} IDS_TRUSTEE;
typedef struct {
int volume;
mars_dev_t dev;
mars_ino_t inode;
int idle; /* idle state */
int mode_flags; /*
* &0x01 is directory
* &0x02 is_root
* &0x04 is_symlink
* &0x08 dev changed, will be set by trustees scan
* &0x10 dev differs from volume's dev
* will be set by trustees scan
* is important to prevent trustee changes by
* normal user (not user 'root')
*/
int inherited_mask; /* for all users */
int eff_rights; /* for actual user */
/* trustees for this node */
int trustee_count;
IDS_TRUSTEE *trustees;
} FILE_TRUSTEE_NODE;
typedef struct {
int count;
FILE_TRUSTEE_NODE *tr[MAX_TRUSTEE_CACHE];
} TRUSTEE_CACHE;
static TRUSTEE_CACHE *tr_cache=NULL;
static void free_trustee_node(FILE_TRUSTEE_NODE *tn)
{
if (tn) {
xfree(tn->trustees);
xfree(tn);
}
}
void tru_free_cache(int volume)
/* free's cache for one volume or all volume's if volume == -1 */
{
if (tr_cache) {
int i=tr_cache->count;
while(i--) {
FILE_TRUSTEE_NODE *tr=tr_cache->tr[i];
if (tr && (volume == -1 || tr->volume == volume)){
free_trustee_node(tr);
tr_cache->tr[i]=NULL;
if (i+1 == tr_cache->count)
--tr_cache->count;
}
}
}
if (volume == -1)
xfree(tr_cache);
}
static void add_trustee_node(FILE_TRUSTEE_NODE *trn)
{
if (trn) {
int i;
int max_idle = 0;
int found_idle = -1;
int to_use = -1;
if (!tr_cache)
tr_cache=(TRUSTEE_CACHE*)xcmalloc(sizeof(TRUSTEE_CACHE));
for (i=0;i < tr_cache->count; i++) {
FILE_TRUSTEE_NODE *tr=tr_cache->tr[i];
if (!tr) {
if (to_use < 0) to_use=i;
} else {
if (tr->mode_flags&1) tr->idle++; /* dirs should not become idle so fast */
else tr->idle+=10; /* as files */
if (tr->idle > max_idle) {
found_idle=i;
max_idle=tr->idle;
}
}
}
if (to_use < 0) {
if (tr_cache->count < MAX_TRUSTEE_CACHE)
to_use=tr_cache->count++;
else {
to_use=found_idle;
free_trustee_node(tr_cache->tr[to_use]);
}
}
tr_cache->tr[to_use]=trn;
}
}
static FILE_TRUSTEE_NODE *find_trustee_node(int volume, mars_dev_t dev, mars_ino_t inode)
{
if (vol_trustees_were_changed(volume))
return(NULL);
if (tr_cache) {
int i=-1;
while (++i < tr_cache->count) {
FILE_TRUSTEE_NODE *tr=tr_cache->tr[i];
if (tr && tr->volume == volume && tr->dev == dev && tr->inode == inode){
tr->idle=0;
return(tr);
}
}
}
return(NULL);
}
static int find_id_trustee(FILE_TRUSTEE_NODE *tr, uint32 id)
{
int i=0;
IDS_TRUSTEE *ids=tr->trustees;
while (i++ < tr->trustee_count) {
if (ids->id == id)
return(ids->trustee);
ids++;
}
return(-1); /* not found */
}
static int grps_count=0;
static uint32 *grps_grps=NULL;
static int cmp_uint32(const void *e1, const void *e2)
{
if (*((uint32*)e1) < *((uint32*)e2)) return(-1);
if (*((uint32*)e1) > *((uint32*)e2)) return(1);
return(0);
}
static int grp_exist(uint32 grp_id)
/* returns 1 if grp_id exist */
{
return( (NULL == bsearch(&grp_id, grps_grps,
(size_t)grps_count, (size_t)sizeof(uint32), cmp_uint32))
? 0 : 1);
}
void tru_init_trustees(int count, uint32 *grps)
/* must be called after new loging */
{
tru_free_cache(-1);
xfree(grps_grps);
grps_count=count;
if (count) {
grps_grps=(uint32*)xmalloc(sizeof(uint32) * count);
memcpy(grps_grps, grps, sizeof(uint32) * count);
qsort(grps_grps, (size_t)grps_count, (size_t)sizeof(uint32), cmp_uint32);
}
}
static void creat_trustee_path(int volume, int dev, ino_t inode, uint8 *path)
/* is always called with uid = 0 */
{
char buf[256];
uint8 buf_uc[4];
char volname[100];
if (nw_get_volume_name(volume, volname, sizeof(volname) ) < 1) return;
U32_TO_BE32(inode, buf_uc);
slprintf(buf, sizeof(buf)-1, "%s/%s/%x/%x/%x/%x/n.%x", path_trustees, volname,
dev,
(int) buf_uc[0],
(int) buf_uc[1],
(int) buf_uc[2],
(int) buf_uc[3]);
unlink(buf);
if (symlink(path, buf)) {
XDPRINTF((0,0,"creat_trustee_path buf=`%s`, path=`%s` failed", buf, path));
}
}
static int put_trustee_to_disk(int volume, dev_t dev, ino_t inode, uint32 id, int trustee)
/* is always called with uid = 0 */
/* if id=0, it means inherited_mask */
{
char buf[256];
char btrustee[256];
int l;
uint8 buf_uc[4];
char volname[100];
if (nw_get_volume_name(volume, volname, sizeof(volname) ) < 1) return(-0xff);
U32_TO_BE32(inode, buf_uc);
l=slprintf(buf, sizeof(buf)-1, "%s/%s/%x/%x/%x/%x/t.%x", path_trustees, volname,
dev,
(int) buf_uc[0],
(int) buf_uc[1],
(int) buf_uc[2],
(int) buf_uc[3]);
unx_xmkdir(buf, 0755);
slprintf(buf+l, sizeof(buf) -l -1, "/%x", (unsigned int) id);
unlink(buf);
l=slprintf(btrustee, sizeof(btrustee)-1, "%04x", (unsigned int) trustee);
return(symlink(btrustee, buf) ? -0xff : 0);
}
static int get_trustee_from_disk(int volume, dev_t dev, ino_t inode, uint32 id, int *trustee)
/*
* if id=0, it means inherited_mask
* return 0 if 0, < 0 if error
*/
{
char buf[256];
char btrustee[256];
int l;
uint8 buf_uc[4];
char volname[100];
if (nw_get_volume_name(volume, volname, sizeof(volname) ) < 1)
return(-0xff);
U32_TO_BE32(inode, buf_uc);
slprintf(buf, sizeof(buf)-1, "%s/%s/%x/%x/%x/%x/t.%x/%x", path_trustees, volname,
dev,
(int) buf_uc[0],
(int) buf_uc[1],
(int) buf_uc[2],
(int) buf_uc[3],
(unsigned int)id);
l = readlink(buf, btrustee, 254);
if (l > 0) {
unsigned int utrustee=0;
btrustee[l]='\0';
if (1 == sscanf(btrustee, "%x", &utrustee)) {
*trustee = (int)utrustee;
return(0);
}
}
return(-0xff);
}
static int del_trustee_from_disk(int volume, dev_t dev, ino_t inode, uint32 id)
/* removes users id trustee */
{
char buf[256];
int result=-0xfe; /* no such trustee */
uint8 buf_uc[4];
char volname[100];
if (nw_get_volume_name(volume, volname, sizeof(volname) ) < 1) return(result);
U32_TO_BE32(inode, buf_uc);
slprintf(buf, sizeof(buf)-1, "%s/%s/%x/%x/%x/%x/t.%x/%x", path_trustees, volname,
dev,
(int) buf_uc[0],
(int) buf_uc[1],
(int) buf_uc[2],
(int) buf_uc[3],
(unsigned int)id);
if (seteuid(0)) {}
if (!unlink(buf))
result=0;
(void)reseteuid();
return(result);
}
unsigned int tru_vol_sernum(int volume, int mode)
/* mode == 0, reads sernum, else change sernum, returns new sernum */
{
char volname[100];
char buf[256];
char buf1[20];
int len;
unsigned int sernum=0;
if (nw_get_volume_name(volume, volname, sizeof(volname) ) < 1) return(-1);
slprintf(buf, sizeof(buf)-1, "%s/%s/ts", path_trustees, volname);
len=readlink(buf, buf1, sizeof(buf1)-1);
if (len>0) {
buf1[len]='\0';
if (1!=sscanf(buf1,"%x", &sernum))
sernum=0;
}
if (mode) {
if (++sernum==MAX_U32) sernum=1;
if (seteuid(0)) {}
unlink(buf);
slprintf(buf1, sizeof(buf1)-1, "%x", sernum);
if (symlink(buf1, buf))
errorp(0, "rw_trustee_sernum", "symlink %s %s failed", buf1, buf);
(void)reseteuid();
tru_free_cache(volume);
}
return(sernum);
}
void tru_free_file_trustees_from_disk(int volume, int dev, ino_t inode)
/* is called if directory/file is removed */
{
char buf[256];
uint8 buf_uc[4];
int len;
char volname[100];
if (nw_get_volume_name(volume, volname, sizeof(volname) ) < 1) return;
U32_TO_BE32(inode, buf_uc);
len=slprintf(buf, sizeof(buf)-1, "%s/%s/%x/%x/%x/%x/", path_trustees, volname,
dev,
(int) buf_uc[0],
(int) buf_uc[1],
(int) buf_uc[2]);
slprintf(buf+len, sizeof(buf) - len -1, "t.%x", (int)buf_uc[3]);
if (seteuid(0)) {}
unx_xrmdir(buf);
/* now we remove the name of the dir/file */
slprintf(buf+len, sizeof(buf) -len -1, "n.%x", (int)buf_uc[3]);
unlink(buf);
(void)reseteuid();
}
int tru_del_trustee(int volume, uint8 *unixname, struct stat *stb, uint32 id)
{
int result=-0x85; /* we say no privileges */
int voloptions = get_volume_options(volume);
if ( (voloptions & VOL_OPTION_TRUSTEES) &&
( (tru_get_eff_rights(volume, unixname, stb) & TRUSTEE_A)
|| (act_id_flags&1)) ) {
result=del_trustee_from_disk(volume, stb->st_dev, stb->st_ino, id);
if (!result) {
tru_vol_sernum(volume, 1); /* trustee sernum needs updated */
/* Trustee data changed on disk; cached effective rights are stale. */
tru_free_cache(volume);
}
}
MDEBUG(D_TRUSTEES, {
xdprintf(1,0, "tru_del_trustee: id=%08lx, volume=%d, file=`%s`, result=-0x%x",
id, volume, unixname, -result);
})
return(result);
}
static FILE_TRUSTEE_NODE *create_trustee_node(int volume, int dev,
ino_t inode, int mode_flags)
/*
* mode_flags: &1=directory, &2=root, &4=symlink, &0x8 dev changes
* &0x10 dev differs from volumes dev.
*/
{
char buf[256];
int l;
uint8 buf_uc[4];
DIR *d;
char volname[100];
FILE_TRUSTEE_NODE *tr = (FILE_TRUSTEE_NODE*)xcmalloc(sizeof(FILE_TRUSTEE_NODE));
tr->volume = volume;
tr->dev = dev;
tr->inode = inode;
tr->mode_flags = mode_flags;
tr->inherited_mask = (mode_flags&0xe)
? 0 /* root dir or symlink no rights */
: MAX_TRUSTEE_MASK; /* default all allowed */
tr->eff_rights = -1; /* not yet set */
U32_TO_BE32(inode, buf_uc);
(void)nw_get_volume_name(volume, volname, sizeof(volname) );
l=slprintf(buf, sizeof(buf)-1, "%s/%s/%x/%x/%x/%x/t.%x", path_trustees, volname,
dev,
(int) buf_uc[0],
(int) buf_uc[1],
(int) buf_uc[2],
(int) buf_uc[3]);
if (NULL != (d= opendir(buf)) ) {
uint8 *p=buf+l;
struct dirent *dirbuff;
int trustee_count=0;
IDS_TRUSTEE trustees[MAX_TRUSTEES];
*p++ = '/';
while (trustee_count < MAX_TRUSTEES &&
(dirbuff = readdir(d)) != (struct dirent*)NULL){
if (dirbuff->d_ino && dirbuff->d_name[0] != '.') {
char btrustee[255];
int len;
unsigned int id;
if (1 == sscanf(dirbuff->d_name, "%x", &id)) {
len = (int)(p - (uint8*)buf);
strmaxcpy(p, dirbuff->d_name, sizeof(buf) - len -1);
len=readlink(buf, btrustee, 254);
if (len > 0) {
unsigned int utrustee=0;
btrustee[len]='\0';
if (1 == sscanf(btrustee, "%x", &utrustee)) {
if (id) {
trustees[trustee_count].id = (uint32) id;
trustees[trustee_count].trustee = (int) utrustee;
trustee_count++;
} else
tr->inherited_mask=(int)utrustee;
}
}
}
}
} /* while */
closedir(d);
tr->trustee_count=trustee_count;
if (trustee_count) {
tr->trustees=(IDS_TRUSTEE*)xcmalloc(sizeof(IDS_TRUSTEE)*trustee_count);
while (trustee_count--){
tr->trustees[trustee_count].id = trustees[trustee_count].id;
tr->trustees[trustee_count].trustee = trustees[trustee_count].trustee;
}
}
}
return(tr);
}
static FILE_TRUSTEE_NODE *find_creat_add_trustee_node(
int volume, uint8 *unixname, struct stat *stb)
{
FILE_TRUSTEE_NODE *tr=find_trustee_node(volume, stb->st_dev, stb->st_ino);
if (!tr) {
struct stat lstatbuf;
int mode_flags=S_ISDIR(stb->st_mode) ? 1:0;
if ( lstat(unixname, &lstatbuf)
|| (lstatbuf.st_dev != stb->st_dev)
|| (lstatbuf.st_ino != stb->st_ino)
|| S_ISLNK(lstatbuf.st_mode) ) {
mode_flags|=4;
}
tr=create_trustee_node(volume, stb->st_dev, stb->st_ino, mode_flags);
add_trustee_node(tr);
}
return(tr);
}
int tru_get_id_trustee(int volume, uint8 *unixname, struct stat *stb, uint32 id)
/* is called by vol_trustee_scan */
{
int voloptions=get_volume_options(volume);
if (voloptions&VOL_OPTION_TRUSTEES){
FILE_TRUSTEE_NODE *tr=find_creat_add_trustee_node(volume, unixname, stb);
return(find_id_trustee(tr, id));
}
return(-0x85);
}
static FILE_TRUSTEE_NODE *find_build_trustee_node(int volume, uint8 *unixname, struct stat *stb);
static int local_tru_add_trustee_set(int volume, uint8 *unixname,
struct stat *stb,
int count, NW_OIC *nwoic)
{
int voloptions = get_volume_options(volume);
int own_eff_rights;
int result=-0x85; /* we say no privileges */
if ( (voloptions & VOL_OPTION_TRUSTEES) &&
( ((own_eff_rights=tru_get_eff_rights(volume, unixname, stb)) & TRUSTEE_A)
|| (act_id_flags&1) )) {
/* FILE_TRUSTEE_NODE *tr=find_trustee_node(volume, stb->st_dev, stb->st_ino); */
/* mst:11-May-00 */
FILE_TRUSTEE_NODE *tr=find_build_trustee_node(volume, unixname, stb);
if (tr && (!(tr->mode_flags&0x18) || !act_uid)) {
int volumenamelen = get_volume_unixnamlen(volume);
uint8 ufnbuf[2];
uint8 *ufn;
if (seteuid(0)) {}
while (count--) {
if (! ((own_eff_rights & TRUSTEE_S) || (act_id_flags&1)) ) {
/* only user with TRUSTEE_S are allowed to set TRUSTEE_S */
if (nwoic->trustee&TRUSTEE_S)
nwoic->trustee&=~TRUSTEE_S;
}
result=put_trustee_to_disk(volume, stb->st_dev, stb->st_ino, nwoic->id, nwoic->trustee);
MDEBUG(D_TRUSTEES, {
xdprintf(1,0, "tru_add_trustee_set: id=%08lx, trustee=0x%04x, volume=%d, file=`%s`, result=-0x%x",
nwoic->id, nwoic->trustee, volume, unixname, -result);
})
if (result){
(void)reseteuid();
goto func_err;
}
nwoic++;
}
ufn=unixname+min(strlen(unixname), volumenamelen);
if (!*ufn) { /* is volume direct */
ufn=ufnbuf;
*ufn='.';
*(ufn+1)='\0';
}
creat_trustee_path(volume, stb->st_dev, stb->st_ino, ufn);
(void)reseteuid();
#if 0 /* now in tru_add_trustee_set */
tru_vol_sernum(volume, 1); /* trustee sernum needs updated */
#endif
return(0);
}
}
func_err:
XDPRINTF((1,0, "user %08x tried to add trustees to %s, result=-0x%x",
act_obj_id, unixname, -result));
tru_free_cache(-1);
return(result); /* we say no privileges */
}
int tru_add_trustee_set(int volume, uint8 *unixname,
struct stat *stb,
int count, NW_OIC *nwoic)
{
int result = local_tru_add_trustee_set(volume, unixname, stb, count, nwoic);
if (!result) { /* mst: 13-Apr-00 */
int len = strlen(unixname);
int vollen = get_volume_unixnamlen(volume);
char *p = unixname+len;
char *volp = unixname+vollen;
if (seteuid(0)) {}
while (--p > volp) {
if (*p == '/') {
struct stat statb;
*p='\0';
if (!stat(unixname, &statb)){
int i;
NW_OIC *poic=nwoic;
for (i=0; i < count; i++) {
int trustee = 0;
if (poic->id) {
get_trustee_from_disk(volume, statb.st_dev, statb.st_ino, poic->id, &trustee);
if ( !(trustee & (TRUSTEE_T|TRUSTEE_F)) ) {
trustee |= TRUSTEE_T;
put_trustee_to_disk(volume, statb.st_dev, statb.st_ino, poic->id, trustee);
}
}
poic++;
}
}
*p='/';
}
}
(void)reseteuid();
tru_vol_sernum(volume, 1); /* trustee sernum needs updated */
/* local_tru_add_trustee_set() built/read the cache before writing the
* new trustee entry. Drop it so the next user sees the new rights. */
tru_free_cache(volume);
}
return (result);
}
int tru_get_trustee_set(int volume, uint8 *unixname,
struct stat *stb,
int sequence,
int maxcount, uint32 *ids, int *trustees)
{
int voloptions = get_volume_options(volume);
if (voloptions & VOL_OPTION_TRUSTEES) {
int offset = sequence*maxcount;
int count = 0;
FILE_TRUSTEE_NODE *tr=find_creat_add_trustee_node(volume, unixname, stb);
while (offset < tr->trustee_count && count < maxcount) {
*ids=tr->trustees[offset].id;
ids++;
*trustees=tr->trustees[offset].trustee;
trustees++;
offset++;
count++;
}
if (count) return(count);
}
return(-0x9c); /* no more trustees */
}
int tru_set_inherited_mask(int volume, uint8 *unixname,
struct stat *stb, int new_mask)
/* sets inherited mask of directory */
{
int voloptions = get_volume_options(volume);
if ( (voloptions & VOL_OPTION_TRUSTEES) &&
( (tru_get_eff_rights(volume, unixname, stb) & TRUSTEE_A)
|| (act_id_flags&1)) ) {
/*
* Build/create the trustee cache entry before writing the IRM.
* tru_get_eff_rights() normally does this indirectly, but for the real
* SUPERVISOR object get_eff_rights_by_trustees() returns MAX_TRUSTEE_MASK
* directly and does not build a FILE_TRUSTEE_NODE. Using only
* find_trustee_node() therefore made NCP 22/4 fail with no privileges
* for SUPERVISOR on otherwise untouched directories.
*/
FILE_TRUSTEE_NODE *tr=find_build_trustee_node(volume, unixname, stb);
if (tr && (!(tr->mode_flags&0x1e) || !act_uid)) {
int result;
if (seteuid(0)) {}
result=put_trustee_to_disk(volume, stb->st_dev, stb->st_ino, 0L, new_mask);
(void)reseteuid();
if (!result) {
/* Keep the in-memory trustee cache in sync with the value just
* written to disk. Otherwise a client which reads the IRM again
* in the same server run still sees the old cached mask until the
* node is rebuilt or the server is restarted.
*/
tr->inherited_mask = new_mask;
tr->eff_rights = -1;
tru_vol_sernum(volume, 1); /* trustee sernum needs updated */
}
return(result);
}
}
return(-0x85); /* we say no privileges */
}
int tru_get_inherited_mask(int volume, uint8 *unixname,
struct stat *stb)
/* returns inherited mask of directory */
{
int voloptions = get_volume_options(volume);
if (voloptions & VOL_OPTION_TRUSTEES){
FILE_TRUSTEE_NODE *tr=find_creat_add_trustee_node(volume, unixname, stb);
return(tr->inherited_mask);
}
return(0x01ff); /* default */
}
static int insert_ugid_trustee(IDS_TRUSTEE *ugid_trustees, int count,
uint32 id, int trustee)
/* return 1 if inserted, else 0 */
{
while (count--) {
if (ugid_trustees->id==id) {
/* ugid_trustees->trustee|=trustee; wrong */
ugid_trustees->trustee = trustee; /* correct, mst:07-Sep-00 */
/* new trustees must always override the current one */
return(0);
}
ugid_trustees++;
}
ugid_trustees->id=id;
ugid_trustees->trustee=trustee;
return(1);
}
static int build_trustee_rights(FILE_TRUSTEE_NODE *tr,
IDS_TRUSTEE *ugid_trustees, int count)
/* this routine must be called root to leaf */
{
if (tr) {
int i = tr->trustee_count;
IDS_TRUSTEE *trn = tr->trustees;
int k = count;
if (tr->mode_flags & (8|4|2)) {
/* dev changed or root volume or symlink */
if ( (tr->mode_flags&2) && (act_id_flags&1) ) {
/* root directory and supervisor equivalences */
/* get all trusttee rights */
if (insert_ugid_trustee(ugid_trustees, count,
act_obj_id, MAX_TRUSTEE_MASK))
count++;
} else {
/* trusteess will not be passed to childs */
while (k--) { /* first we set all to null */
(ugid_trustees+k)->trustee = 0;
}
}
/* inherited_mask will be 0 */
tr->inherited_mask=0;
} else {
while (k--) {
/* trusteess will be passed to childs but */
/* first we mask all with inherited_mask */
(ugid_trustees+k)->trustee &= tr->inherited_mask;
}
}
while (i--) { /* now we read all trustees for this node */
if (trn->id == act_obj_id || grp_exist(trn->id)){
if (insert_ugid_trustee(ugid_trustees, count,
trn->id, trn->trustee))
count++;
}
trn++;
} /* while */
/* now we build eff_rights for this node */
tr->eff_rights=0;
for (k=0; k < count; k++) {
tr->eff_rights |= (ugid_trustees+k)->trustee;
}
}
return(count);
}
static FILE_TRUSTEE_NODE *find_build_trustee_node(int volume, uint8 *unixname, struct stat *stb)
{
FILE_TRUSTEE_NODE *tr=find_creat_add_trustee_node(volume, unixname, stb);
if (tr->eff_rights < 0) { /* now we must rebuild eff rights */
int count=0;
IDS_TRUSTEE *ugid_trustees=
(IDS_TRUSTEE*)xcmalloc((grps_count+1)*sizeof(IDS_TRUSTEE));
struct stat stb1;
(void)get_volume_inode(volume, &stb1);
if (stb1.st_ino != stb->st_ino || stb1.st_dev != stb->st_dev) {
/* is not volumes root */
int volumenamelen = get_volume_unixnamlen(volume);
char *p = unixname+volumenamelen;
int last_dev = stb1.st_dev;
int volumes_dev = stb1.st_dev;
FILE_TRUSTEE_NODE *tr1=find_trustee_node(
volume, stb1.st_dev, stb1.st_ino);
if (!tr1) {
tr1=create_trustee_node(volume, stb1.st_dev, stb1.st_ino, 3);
add_trustee_node(tr1);
} else tr1->mode_flags|=3;
/* build trustees for unix volume */
count=build_trustee_rights(tr1, ugid_trustees, count);
while (*p=='/')++p;
while (NULL != (p=strchr(p, '/'))) {
*p = '\0';
if (!stat(unixname, &stb1)) {
if (stb1.st_ino != stb->st_ino || stb1.st_dev != stb->st_dev) {
int mode_flags=S_ISDIR(stb1.st_mode)?1:0;
tr1=find_trustee_node(volume, stb1.st_dev, stb1.st_ino);
if (last_dev != stb1.st_dev) {
last_dev = stb1.st_dev;
mode_flags |= 0x8;
}
if (volumes_dev != stb1.st_dev)
mode_flags|=0x10;
if (!tr1) {
struct stat lstatbuf;
if ( lstat(unixname, &lstatbuf)
|| (lstatbuf.st_dev != stb1.st_dev)
|| (lstatbuf.st_ino != stb1.st_ino)
|| S_ISLNK(lstatbuf.st_mode) ) {
mode_flags|=4;
}
tr1=create_trustee_node(volume, stb1.st_dev, stb1.st_ino,
mode_flags);
add_trustee_node(tr1);
} else
tr1->mode_flags|=mode_flags;
count=build_trustee_rights(tr1, ugid_trustees, count);
} else {
*p='/';
break;
}
} else {
errorp(10, "tru_get_eff_rights", "stat error `%s`", unixname);
*p='/';
xfree(ugid_trustees);
return(NULL);
}
*p='/';
while (*p=='/')++p;
} /* while */
if (last_dev != stb->st_dev)
tr->mode_flags|=0x8;
if (volumes_dev!=stb->st_dev)
tr->mode_flags|=0x10;
} else {
/* volumes directory */
tr->mode_flags|=(1|2);
}
count=build_trustee_rights(tr, ugid_trustees, count);
xfree(ugid_trustees);
} /* if eff_rights < 0 */
return(tr);
}
static int get_eff_rights_by_trustees(int volume, uint8 *unixname, struct stat *stb)
/* returns the eff. rights the actual user has as real trustees */
{
if ( (act_obj_id == 1) && (act_id_flags&1) ) /* supervisor */
return(MAX_TRUSTEE_MASK); /* all rights */
else {
FILE_TRUSTEE_NODE *tr = find_build_trustee_node(volume, unixname, stb);
return( (tr && tr->eff_rights > -1) ? tr->eff_rights : 0);
}
}
int tru_get_eff_rights(int volume, uint8 *unixname, struct stat *stb)
/* returns the eff. rights the actual user has */
{
int voloptions = get_volume_options(volume);
int rights = 0;
int rights1 = 0;
if (voloptions & VOL_OPTION_TRUSTEES){
rights=(get_eff_rights_by_trustees(volume, unixname, stb) & MAX_TRUSTEE_MASK);
}
if (!(voloptions & VOL_OPTION_IGNUNXRIGHT)){
rights1 = un_nw_rights(voloptions, unixname, stb);
}
MDEBUG(D_TRUSTEES, {
xdprintf(1,0, "eff_rights=%04x,%04x for`%s`",
rights, rights1, unixname);
})
return(rights|rights1);
}
int tru_eff_rights_exists(int volume, uint8 *unixname, struct stat *stb,
int lookfor)
/*
* returns 0 if lookfor right exist,
* otherwise returns the current rights
*/
{
int voloptions = get_volume_options(volume);
int rights = 0;
int rights1 = 0;
int result = -1;
if (voloptions & VOL_OPTION_TRUSTEES){
/* we look for trustee rights first */
rights=get_eff_rights_by_trustees(volume, unixname, stb);
if ((rights & TRUSTEE_S)||((rights&lookfor)==lookfor))
result = 0;
else if ((lookfor == TRUSTEE_T) && (rights&TRUSTEE_F) ) /* mst: 13-Apr-00 */
result=0;
}
if (result && !(voloptions & VOL_OPTION_IGNUNXRIGHT)){
rights1 = un_nw_rights(voloptions, unixname, stb);
}
MDEBUG(D_TRUSTEES, {
xdprintf(1,0, "lookfor=%04x, eff_rights_exists ? = %04x(tru),%04x(unx) for`%s`",
lookfor, rights, rights1, unixname);
})
if (!result) return(0);
rights |= rights1;
if ((lookfor == TRUSTEE_T) && (rights&TRUSTEE_F) ) /* mst: 13-Apr-00 */
return(0);
/* return(((rights & TRUSTEE_S)||((rights&lookfor)==lookfor)) ? 0 : -1);*/
if (rights) {
/* mst: 07-Oct-00, -1 as result was bad, because nw_search_file_dir()
* expected the real rights.
*/
return(((rights & TRUSTEE_S)||((rights&lookfor)==lookfor)) ? 0 : rights);
} else return(1 << 31); // no rights
}