1610 lines
47 KiB
C
1610 lines
47 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/* trustee.c 15-Apr-00 */
|
|
/* (C)opyright (C) 1998,2000 Martin Stover, Marburg, Germany
|
|
* 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; version 2 only.
|
|
*
|
|
* 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"
|
|
#include "nwxattr.h"
|
|
#include "tools.h"
|
|
#include "nwdbm.h"
|
|
#include <public/zXattr.h>
|
|
#include <comn/authsys/zasAuthModel.h>
|
|
#include <errno.h>
|
|
#include <grp.h>
|
|
#include <strings.h>
|
|
#ifdef USE_GDBM
|
|
#include <gdbm.h>
|
|
#endif
|
|
#ifdef MARS_NWE_HAVE_POSIX_ACL
|
|
#include <sys/acl.h>
|
|
#include <acl/libacl.h>
|
|
#endif
|
|
|
|
|
|
/* 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 uint32 nwfs_guid_to_mars_id(const GUID_t *guid);
|
|
|
|
/*
|
|
* NSS keeps NetWare trustees in its ZAS/EACL metadata and has a separate Unix
|
|
* auth view. mars-nwe runs on a normal host filesystem, so keep
|
|
* netware.metadata authoritative and add a best-effort host projection for
|
|
* tools that only see Unix permissions.
|
|
*
|
|
* Projection order:
|
|
* 1. Resolve bindery object id -> UNIX_USER / UNIX_GROUP.
|
|
* 2. Write a named POSIX ACL entry for that uid/gid when libacl is present.
|
|
* 3. Fall back to the historical coarse mode-bit projection only when no
|
|
* concrete uid/gid mapping is available.
|
|
*/
|
|
static mode_t trustee_rights_to_unix_bits(int rights, int is_dir)
|
|
{
|
|
mode_t mode = 0;
|
|
|
|
rights = (int)ZAS_ExpandSupervisorRights((QUAD)rights);
|
|
|
|
if (rights & (TRUSTEE_R | TRUSTEE_F))
|
|
mode |= 04;
|
|
if (rights & (TRUSTEE_W | TRUSTEE_C | TRUSTEE_E | TRUSTEE_M))
|
|
mode |= 02;
|
|
if (is_dir) {
|
|
if (rights & (TRUSTEE_R | TRUSTEE_F | TRUSTEE_W | TRUSTEE_C |
|
|
TRUSTEE_E | TRUSTEE_M | TRUSTEE_O))
|
|
mode |= 01;
|
|
} else if (rights & TRUSTEE_O) {
|
|
mode |= 04;
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
typedef enum {
|
|
TRUSTEE_UNIX_NONE = 0,
|
|
TRUSTEE_UNIX_USER = 1,
|
|
TRUSTEE_UNIX_GROUP = 2
|
|
} TRUSTEE_UNIX_KIND;
|
|
|
|
typedef struct {
|
|
TRUSTEE_UNIX_KIND kind;
|
|
uid_t uid;
|
|
gid_t gid;
|
|
char name[64];
|
|
} TRUSTEE_UNIX_ID;
|
|
|
|
#ifdef USE_GDBM
|
|
static int trustee_dbm_fetch_obj(uint32 id, NETOBJ *obj)
|
|
{
|
|
char path[256];
|
|
GDBM_FILE db;
|
|
datum key;
|
|
datum data;
|
|
NETOBJ key_obj;
|
|
|
|
if (!obj)
|
|
return -1;
|
|
|
|
get_div_pathes(path, "nwobj", 1, ".pag");
|
|
db = gdbm_open(path, 0, GDBM_READER, 0600, NULL);
|
|
if (!db)
|
|
return -1;
|
|
|
|
memset(&key_obj, 0, sizeof(key_obj));
|
|
key_obj.id = id;
|
|
key.dptr = (char *)&key_obj;
|
|
key.dsize = NETOBJ_KEY_SIZE;
|
|
data = gdbm_fetch(db, key);
|
|
if (!data.dptr) {
|
|
gdbm_close(db);
|
|
return -1;
|
|
}
|
|
|
|
if (data.dsize >= (int)sizeof(*obj)) {
|
|
memcpy(obj, data.dptr, sizeof(*obj));
|
|
free(data.dptr);
|
|
gdbm_close(db);
|
|
return 0;
|
|
}
|
|
|
|
free(data.dptr);
|
|
gdbm_close(db);
|
|
return -1;
|
|
}
|
|
|
|
static int trustee_dbm_find_prop_id(uint32 obj_id, const char *prop_name,
|
|
NETPROP *prop)
|
|
{
|
|
char path[256];
|
|
GDBM_FILE db;
|
|
datum key;
|
|
|
|
if (!prop_name || !prop)
|
|
return -1;
|
|
|
|
get_div_pathes(path, "nwprop", 1, ".pag");
|
|
db = gdbm_open(path, 0, GDBM_READER, 0600, NULL);
|
|
if (!db)
|
|
return -1;
|
|
|
|
for (key = gdbm_firstkey(db); key.dptr;) {
|
|
datum next = gdbm_nextkey(db, key);
|
|
datum data = gdbm_fetch(db, key);
|
|
if (data.dptr && data.dsize >= (int)sizeof(*prop)) {
|
|
NETPROP *candidate = (NETPROP *)data.dptr;
|
|
if (candidate->obj_id == obj_id &&
|
|
!strncasecmp((const char *)candidate->name, prop_name, sizeof(candidate->name)) &&
|
|
strlen(prop_name) < sizeof(candidate->name)) {
|
|
memcpy(prop, candidate, sizeof(*prop));
|
|
free(data.dptr);
|
|
free(key.dptr);
|
|
if (next.dptr)
|
|
free(next.dptr);
|
|
gdbm_close(db);
|
|
return 0;
|
|
}
|
|
}
|
|
if (data.dptr)
|
|
free(data.dptr);
|
|
free(key.dptr);
|
|
key = next;
|
|
}
|
|
|
|
gdbm_close(db);
|
|
return -1;
|
|
}
|
|
|
|
static int trustee_dbm_fetch_prop_string(uint32 obj_id, const char *prop_name,
|
|
char *value, size_t value_size)
|
|
{
|
|
char path[256];
|
|
GDBM_FILE db;
|
|
datum key;
|
|
datum data;
|
|
NETPROP prop;
|
|
NETVAL val;
|
|
|
|
if (!value || value_size == 0)
|
|
return -1;
|
|
value[0] = '\0';
|
|
|
|
if (trustee_dbm_find_prop_id(obj_id, prop_name, &prop))
|
|
return -1;
|
|
|
|
get_div_pathes(path, "nwval", 1, ".pag");
|
|
db = gdbm_open(path, 0, GDBM_READER, 0600, NULL);
|
|
if (!db)
|
|
return -1;
|
|
|
|
memset(&val, 0, sizeof(val));
|
|
val.obj_id = obj_id;
|
|
val.prop_id = prop.id;
|
|
val.segment = 1;
|
|
key.dptr = (char *)&val;
|
|
key.dsize = NETVAL_KEY_SIZE;
|
|
data = gdbm_fetch(db, key);
|
|
if (!data.dptr) {
|
|
gdbm_close(db);
|
|
return -1;
|
|
}
|
|
|
|
if (data.dsize >= (int)sizeof(val)) {
|
|
NETVAL *stored = (NETVAL *)data.dptr;
|
|
size_t len = strnlen((const char *)stored->value, sizeof(stored->value));
|
|
if (len >= value_size)
|
|
len = value_size - 1;
|
|
memcpy(value, stored->value, len);
|
|
value[len] = '\0';
|
|
free(data.dptr);
|
|
gdbm_close(db);
|
|
return value[0] ? 0 : -1;
|
|
}
|
|
|
|
free(data.dptr);
|
|
gdbm_close(db);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
static int trustee_resolve_unix_id(uint32 id, TRUSTEE_UNIX_ID *out)
|
|
{
|
|
NETOBJ obj;
|
|
char unix_name[128];
|
|
|
|
if (!out)
|
|
return -1;
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
if (id == 1) {
|
|
struct passwd *pw = getpwuid(0);
|
|
out->kind = TRUSTEE_UNIX_USER;
|
|
out->uid = 0;
|
|
out->gid = 0;
|
|
strmaxcpy((uint8 *)out->name, (uint8 *)(pw ? pw->pw_name : "root"), sizeof(out->name)-1);
|
|
return 0;
|
|
}
|
|
|
|
if (id == act_obj_id && act_uid >= 0) {
|
|
struct passwd *pw = getpwuid((uid_t)act_uid);
|
|
out->kind = TRUSTEE_UNIX_USER;
|
|
out->uid = (uid_t)act_uid;
|
|
out->gid = (gid_t)act_gid;
|
|
strmaxcpy((uint8 *)out->name, (uint8 *)(pw ? pw->pw_name : ""), sizeof(out->name)-1);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef USE_GDBM
|
|
if (trustee_dbm_fetch_obj(id, &obj))
|
|
return -1;
|
|
|
|
if (obj.type == 1) {
|
|
struct passwd *pw;
|
|
if (trustee_dbm_fetch_prop_string(id, "UNIX_USER", unix_name, sizeof(unix_name)))
|
|
return -1;
|
|
pw = getpwnam(unix_name);
|
|
if (!pw)
|
|
return -1;
|
|
if (id != 1 && pw->pw_uid == 0)
|
|
return -1;
|
|
out->kind = TRUSTEE_UNIX_USER;
|
|
out->uid = pw->pw_uid;
|
|
out->gid = pw->pw_gid;
|
|
strmaxcpy((uint8 *)out->name, (uint8 *)pw->pw_name, sizeof(out->name)-1);
|
|
return 0;
|
|
}
|
|
|
|
if (obj.type == 2) {
|
|
struct group *gr = NULL;
|
|
if (!trustee_dbm_fetch_prop_string(id, "UNIX_GROUP", unix_name, sizeof(unix_name)))
|
|
gr = getgrnam(unix_name);
|
|
if (!gr) {
|
|
strmaxcpy((uint8 *)unix_name, obj.name, sizeof(unix_name)-1);
|
|
downstr((uint8 *)unix_name);
|
|
gr = getgrnam(unix_name);
|
|
}
|
|
if (!gr)
|
|
return -1;
|
|
out->kind = TRUSTEE_UNIX_GROUP;
|
|
out->gid = gr->gr_gid;
|
|
strmaxcpy((uint8 *)out->name, (uint8 *)gr->gr_name, sizeof(out->name)-1);
|
|
return 0;
|
|
}
|
|
#else
|
|
(void)obj;
|
|
(void)unix_name;
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
#ifdef MARS_NWE_HAVE_POSIX_ACL
|
|
static int acl_entry_matches(acl_entry_t entry, TRUSTEE_UNIX_KIND kind, id_t id)
|
|
{
|
|
acl_tag_t tag;
|
|
void *qual;
|
|
int match = 0;
|
|
|
|
if (acl_get_tag_type(entry, &tag))
|
|
return 0;
|
|
if ((kind == TRUSTEE_UNIX_USER && tag != ACL_USER) ||
|
|
(kind == TRUSTEE_UNIX_GROUP && tag != ACL_GROUP))
|
|
return 0;
|
|
qual = acl_get_qualifier(entry);
|
|
if (!qual)
|
|
return 0;
|
|
if (kind == TRUSTEE_UNIX_USER)
|
|
match = (*(uid_t *)qual == (uid_t)id);
|
|
else
|
|
match = (*(gid_t *)qual == (gid_t)id);
|
|
acl_free(qual);
|
|
return match;
|
|
}
|
|
|
|
static int acl_set_perm_from_trustee(acl_permset_t perms, int rights, int is_dir)
|
|
{
|
|
mode_t bits = trustee_rights_to_unix_bits(rights, is_dir);
|
|
|
|
if (acl_clear_perms(perms))
|
|
return -1;
|
|
if ((bits & 04) && acl_add_perm(perms, ACL_READ))
|
|
return -1;
|
|
if ((bits & 02) && acl_add_perm(perms, ACL_WRITE))
|
|
return -1;
|
|
if ((bits & 01) && acl_add_perm(perms, ACL_EXECUTE))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int project_one_trustee_to_posix_acl(const uint8 *unixname, uint32 id,
|
|
int rights, int remove_entry)
|
|
{
|
|
TRUSTEE_UNIX_ID unix_id;
|
|
struct stat st;
|
|
acl_t acl;
|
|
acl_entry_t entry;
|
|
int entry_id;
|
|
int found = 0;
|
|
|
|
if (!unixname)
|
|
return -1;
|
|
if (trustee_resolve_unix_id(id, &unix_id))
|
|
return -1;
|
|
if (stat((const char *)unixname, &st))
|
|
return -1;
|
|
|
|
acl = acl_get_file((const char *)unixname, ACL_TYPE_ACCESS);
|
|
if (!acl)
|
|
return -1;
|
|
|
|
for (entry_id = ACL_FIRST_ENTRY;
|
|
acl_get_entry(acl, entry_id, &entry) == 1;
|
|
entry_id = ACL_NEXT_ENTRY) {
|
|
id_t qid = (unix_id.kind == TRUSTEE_UNIX_USER) ? (id_t)unix_id.uid : (id_t)unix_id.gid;
|
|
if (acl_entry_matches(entry, unix_id.kind, qid)) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (remove_entry || rights == 0) {
|
|
if (found && acl_delete_entry(acl, entry)) {
|
|
acl_free(acl);
|
|
return -1;
|
|
}
|
|
} else {
|
|
acl_permset_t perms;
|
|
if (!found) {
|
|
if (acl_create_entry(&acl, &entry)) {
|
|
acl_free(acl);
|
|
return -1;
|
|
}
|
|
if (acl_set_tag_type(entry, unix_id.kind == TRUSTEE_UNIX_USER ? ACL_USER : ACL_GROUP)) {
|
|
acl_free(acl);
|
|
return -1;
|
|
}
|
|
if (unix_id.kind == TRUSTEE_UNIX_USER) {
|
|
uid_t uid = unix_id.uid;
|
|
if (acl_set_qualifier(entry, &uid)) {
|
|
acl_free(acl);
|
|
return -1;
|
|
}
|
|
} else {
|
|
gid_t gid = unix_id.gid;
|
|
if (acl_set_qualifier(entry, &gid)) {
|
|
acl_free(acl);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
if (acl_get_permset(entry, &perms) ||
|
|
acl_set_perm_from_trustee(perms, rights, S_ISDIR(st.st_mode))) {
|
|
acl_free(acl);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (acl_calc_mask(&acl) || acl_valid(acl) ||
|
|
acl_set_file((const char *)unixname, ACL_TYPE_ACCESS, acl)) {
|
|
int save_errno = errno;
|
|
acl_free(acl);
|
|
errno = save_errno;
|
|
return -1;
|
|
}
|
|
|
|
XDPRINTF((5,0, "trustee POSIX ACL projection: `%s` id=0x%08x rights=0x%x remove=%d",
|
|
unixname, id, rights, remove_entry));
|
|
acl_free(acl);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int project_metadata_trustees_to_unix_mode(const uint8 *unixname,
|
|
const zNW_metadata_s *metadata)
|
|
{
|
|
struct stat st;
|
|
mode_t other_bits = 0;
|
|
mode_t new_mode;
|
|
int i;
|
|
int projected_acl = 0;
|
|
|
|
if (!unixname || !metadata)
|
|
return 0;
|
|
|
|
if (!(metadata->nwm_modify_mask & zMOD_ALL_TRUSTEES))
|
|
return 0;
|
|
|
|
if (stat((const char *)unixname, &st))
|
|
return -1;
|
|
|
|
for (i = 0; i < metadata->nwm_trustee_num; i++) {
|
|
uint32 id = nwfs_guid_to_mars_id(&metadata->nwm_trustee[i].nwt_id);
|
|
int rights = (int)metadata->nwm_trustee[i].nwt_rights;
|
|
#ifdef MARS_NWE_HAVE_POSIX_ACL
|
|
if (!project_one_trustee_to_posix_acl(unixname, id, rights, 0)) {
|
|
projected_acl++;
|
|
continue;
|
|
}
|
|
#endif
|
|
other_bits |= trustee_rights_to_unix_bits(rights, S_ISDIR(st.st_mode));
|
|
}
|
|
|
|
if (projected_acl > 0)
|
|
return 0;
|
|
|
|
new_mode = (st.st_mode & ~0007) | other_bits;
|
|
if ((st.st_mode & 07777) == (new_mode & 07777))
|
|
return 0;
|
|
|
|
if (chmod((const char *)unixname, new_mode & 07777)) {
|
|
XDPRINTF((2,0, "trustee unix mode projection failed for `%s`: errno=%d",
|
|
unixname, errno));
|
|
return -1;
|
|
}
|
|
|
|
XDPRINTF((5,0, "trustee unix mode projection: `%s` mode %04o -> %04o",
|
|
unixname, (unsigned)(st.st_mode & 07777), (unsigned)(new_mode & 07777)));
|
|
return 0;
|
|
}
|
|
|
|
static int project_deleted_trustee_to_host(const uint8 *unixname, uint32 id)
|
|
{
|
|
#ifdef MARS_NWE_HAVE_POSIX_ACL
|
|
if (!project_one_trustee_to_posix_acl(unixname, id, 0, 1))
|
|
return 0;
|
|
#else
|
|
(void)id;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void mars_id_to_nwfs_guid(uint32 id, GUID_t *guid)
|
|
{
|
|
memset(guid, 0, sizeof(*guid));
|
|
guid->timeLow = (LONG)id;
|
|
}
|
|
|
|
static uint32 nwfs_guid_to_mars_id(const GUID_t *guid)
|
|
{
|
|
return((uint32)guid->timeLow);
|
|
}
|
|
|
|
static int read_metadata_from_path(const uint8 *unixname, zNW_metadata_s *metadata)
|
|
{
|
|
ssize_t len;
|
|
|
|
if (!unixname || !metadata)
|
|
return(-1);
|
|
|
|
memset(metadata, 0, sizeof(*metadata));
|
|
len = mars_nwe_getxattr((const char*)unixname, zNW_METADATA, metadata, sizeof(*metadata));
|
|
if (len < 0)
|
|
return(-1);
|
|
if (nwfs_metadata_validate(metadata, (size_t)len) != NWFS_OK)
|
|
return(-1);
|
|
return(0);
|
|
}
|
|
|
|
static int write_metadata_to_path(const uint8 *unixname, zNW_metadata_s *metadata)
|
|
{
|
|
size_t size;
|
|
|
|
if (!unixname || !metadata)
|
|
return(-1);
|
|
|
|
size = nwfs_metadata_size_for_trustees(metadata->nwm_trustee_num);
|
|
if (size == 0 || nwfs_metadata_validate(metadata, size) != NWFS_OK)
|
|
return(-1);
|
|
|
|
return(mars_nwe_setxattr((const char*)unixname, zNW_METADATA, metadata, size, 0));
|
|
}
|
|
|
|
static int load_trustee_node_from_metadata(FILE_TRUSTEE_NODE *tr, const uint8 *unixname)
|
|
{
|
|
zNW_metadata_s metadata;
|
|
int i;
|
|
|
|
if (!tr || !unixname || read_metadata_from_path(unixname, &metadata))
|
|
return(-1);
|
|
|
|
if (metadata.nwm_modify_mask & zMOD_INH_RIGHTS_MASK)
|
|
tr->inherited_mask = (int)metadata.nwm_inherited_rights_mask;
|
|
|
|
if (!(metadata.nwm_modify_mask & zMOD_ALL_TRUSTEES) || metadata.nwm_trustee_num <= 0)
|
|
return(0);
|
|
|
|
xfree(tr->trustees);
|
|
tr->trustee_count = (int)metadata.nwm_trustee_num;
|
|
tr->trustees=(IDS_TRUSTEE*)xcmalloc(sizeof(IDS_TRUSTEE)*tr->trustee_count);
|
|
for (i = 0; i < tr->trustee_count; i++) {
|
|
tr->trustees[i].id = nwfs_guid_to_mars_id(&metadata.nwm_trustee[i].nwt_id);
|
|
tr->trustees[i].trustee = (int)metadata.nwm_trustee[i].nwt_rights;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int get_trustee_from_metadata(uint8 *unixname, uint32 id, int *trustee)
|
|
{
|
|
zNW_metadata_s metadata;
|
|
int i;
|
|
|
|
if (!trustee || read_metadata_from_path(unixname, &metadata))
|
|
return(-0xff);
|
|
|
|
if (!id) {
|
|
if (!(metadata.nwm_modify_mask & zMOD_INH_RIGHTS_MASK))
|
|
return(-0xff);
|
|
*trustee = (int)metadata.nwm_inherited_rights_mask;
|
|
return(0);
|
|
}
|
|
|
|
if (!(metadata.nwm_modify_mask & zMOD_ALL_TRUSTEES))
|
|
return(-0xff);
|
|
|
|
for (i = 0; i < metadata.nwm_trustee_num; i++) {
|
|
if (nwfs_guid_to_mars_id(&metadata.nwm_trustee[i].nwt_id) == id) {
|
|
*trustee = (int)metadata.nwm_trustee[i].nwt_rights;
|
|
return(0);
|
|
}
|
|
}
|
|
return(-0xff);
|
|
}
|
|
|
|
static int put_trustee_to_metadata(uint8 *unixname, uint32 id, int trustee)
|
|
{
|
|
zNW_metadata_s metadata;
|
|
int i;
|
|
|
|
if (read_metadata_from_path(unixname, &metadata))
|
|
nwfs_metadata_init(&metadata);
|
|
|
|
if (!id) {
|
|
if (nwfs_metadata_set_inherited_rights_mask(&metadata, trustee) != NWFS_OK)
|
|
return(-0xff);
|
|
metadata.nwm_modify_mask |= zMOD_INH_RIGHTS_MASK;
|
|
return(write_metadata_to_path(unixname, &metadata) ? -0xff : 0);
|
|
}
|
|
|
|
if (metadata.nwm_trustee_num < 0 || metadata.nwm_trustee_num > zMAX_TRUSTEES)
|
|
return(-0xff);
|
|
|
|
for (i = 0; i < metadata.nwm_trustee_num; i++) {
|
|
if (nwfs_guid_to_mars_id(&metadata.nwm_trustee[i].nwt_id) == id) {
|
|
metadata.nwm_trustee[i].nwt_rights = (QUAD)trustee;
|
|
metadata.nwm_modify_mask |= zMOD_ALL_TRUSTEES;
|
|
if (write_metadata_to_path(unixname, &metadata))
|
|
return(-0xff);
|
|
project_metadata_trustees_to_unix_mode(unixname, &metadata);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if (metadata.nwm_trustee_num >= zMAX_TRUSTEES)
|
|
return(-0xff);
|
|
|
|
mars_id_to_nwfs_guid(id, &metadata.nwm_trustee[metadata.nwm_trustee_num].nwt_id);
|
|
metadata.nwm_trustee[metadata.nwm_trustee_num].nwt_rights = (QUAD)trustee;
|
|
metadata.nwm_trustee[metadata.nwm_trustee_num].nwt_reserved_2 = 0;
|
|
metadata.nwm_trustee_num++;
|
|
metadata.nwm_modify_mask |= zMOD_ALL_TRUSTEES;
|
|
if (write_metadata_to_path(unixname, &metadata))
|
|
return(-0xff);
|
|
project_metadata_trustees_to_unix_mode(unixname, &metadata);
|
|
return(0);
|
|
}
|
|
|
|
static int del_trustee_from_metadata(uint8 *unixname, uint32 id)
|
|
{
|
|
zNW_metadata_s metadata;
|
|
int i;
|
|
|
|
if (!id || read_metadata_from_path(unixname, &metadata))
|
|
return(-0xfe);
|
|
|
|
for (i = 0; i < metadata.nwm_trustee_num; i++) {
|
|
if (nwfs_guid_to_mars_id(&metadata.nwm_trustee[i].nwt_id) == id) {
|
|
if (i + 1 < metadata.nwm_trustee_num) {
|
|
memmove(&metadata.nwm_trustee[i], &metadata.nwm_trustee[i + 1],
|
|
(size_t)(metadata.nwm_trustee_num - i - 1) * sizeof(metadata.nwm_trustee[0]));
|
|
}
|
|
metadata.nwm_trustee_num--;
|
|
metadata.nwm_modify_mask |= zMOD_ALL_TRUSTEES;
|
|
if (write_metadata_to_path(unixname, &metadata))
|
|
return(-0xff);
|
|
project_deleted_trustee_to_host(unixname, id);
|
|
project_metadata_trustees_to_unix_mode(unixname, &metadata);
|
|
return(0);
|
|
}
|
|
}
|
|
return(-0xfe);
|
|
}
|
|
|
|
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_metadata(unixname, id);
|
|
if (result)
|
|
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);
|
|
load_trustee_node_from_metadata(tr, unixname);
|
|
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 legacy_disk_trustees_used = 0;
|
|
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_metadata(unixname, nwoic->id, nwoic->trustee);
|
|
if (result) {
|
|
result=put_trustee_to_disk(volume, stb->st_dev, stb->st_ino, nwoic->id, nwoic->trustee);
|
|
if (!result)
|
|
legacy_disk_trustees_used = 1;
|
|
}
|
|
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++;
|
|
}
|
|
if (legacy_disk_trustees_used) {
|
|
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) {
|
|
if (get_trustee_from_metadata(unixname, poic->id, &trustee))
|
|
get_trustee_from_disk(volume, statb.st_dev, statb.st_ino, poic->id, &trustee);
|
|
if ( !(trustee & (TRUSTEE_T|TRUSTEE_F)) ) {
|
|
trustee |= TRUSTEE_T;
|
|
if (put_trustee_to_metadata(unixname, poic->id, trustee))
|
|
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_metadata(unixname, 0L, new_mask);
|
|
if (result)
|
|
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((int)ZAS_ExpandSupervisorRights(TRUSTEE_S)); /* 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
|
|
}
|
|
|
|
|