1042 lines
26 KiB
C
1042 lines
26 KiB
C
/* Copyright (c) 2008, 2009
|
||
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
|
||
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
|
||
* Micah Cowan (micah@cowan.name)
|
||
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
|
||
* Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
|
||
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
|
||
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
|
||
* Copyright (c) 1987 Oliver Laumann
|
||
*
|
||
* 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 3, 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 (see the file COPYING); if not, see
|
||
* https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
|
||
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
|
||
*
|
||
****************************************************************
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <sys/types.h>
|
||
|
||
/* XXX: WHY IS THIS HERE?? :XXX */
|
||
|
||
#ifdef CHECKLOGIN
|
||
# ifdef _SEQUENT_
|
||
# include <stdio.h> /* needed by <pwd.h> */
|
||
# endif /* _SEQUENT_ */
|
||
# include <pwd.h>
|
||
# ifdef SHADOWPW
|
||
# include <shadow.h>
|
||
# endif /* SHADOWPW */
|
||
#endif /* CHECKLOGIN */
|
||
|
||
#ifndef NOSYSLOG
|
||
# include <syslog.h>
|
||
#endif
|
||
|
||
#include "screen.h" /* includes acls.h */
|
||
#include "extern.h"
|
||
|
||
/************************************************************************
|
||
* user managing code, this does not really belong into the acl stuff *
|
||
************************************************************************/
|
||
|
||
extern struct comm comms[];
|
||
extern struct win *windows, **wtab;
|
||
extern char NullStr[];
|
||
extern char SockPath[];
|
||
extern struct display *display, *displays;
|
||
struct acluser *users;
|
||
|
||
#ifdef MULTIUSER
|
||
int maxusercount = 0; /* used in process.c: RC_MONITOR, RC_SILENCE */
|
||
|
||
/* record given user ids here */
|
||
static AclBits userbits;
|
||
|
||
/*
|
||
* rights a new unknown user will have on windows and cmds.
|
||
* These are changed by a "umask ?-..." command:
|
||
*/
|
||
static char default_w_bit[ACL_BITS_PER_WIN] =
|
||
{
|
||
1, /* EXEC */
|
||
1, /* WRITE */
|
||
1 /* READ */
|
||
};
|
||
|
||
static char default_c_bit[ACL_BITS_PER_CMD] =
|
||
{
|
||
0 /* EXEC */
|
||
};
|
||
|
||
/* rights of all users per newly created window */
|
||
/*
|
||
* are now stored per user (umask)
|
||
* static AclBits default_w_userbits[ACL_BITS_PER_WIN];
|
||
* static AclBits default_c_userbits[ACL_BITS_PER_CMD];
|
||
*/
|
||
|
||
static int GrowBitfield __P((AclBits *, int, int, int));
|
||
static struct aclusergroup **FindGroupPtr __P((struct aclusergroup **, struct acluser *, int));
|
||
static int AclSetPermCmd __P((struct acluser *, char *, struct comm *));
|
||
static int AclSetPermWin __P((struct acluser *, struct acluser *, char *, struct win *));
|
||
static int UserAcl __P((struct acluser *, struct acluser **, int, char **));
|
||
static int UserAclCopy __P((struct acluser **, struct acluser **));
|
||
|
||
|
||
static int
|
||
GrowBitfield(AclBits *bfp, int len, int delta, int defaultbit)
|
||
{
|
||
AclBits n, o = *bfp;
|
||
int i;
|
||
|
||
if (!(n = (AclBits)calloc(1, (unsigned long)(&ACLBYTE((char *)0, len + delta + 1)))))
|
||
return -1;
|
||
for (i = 0; i < (len + delta); i++) {
|
||
if (((i < len) && (ACLBIT(i) & ACLBYTE(o, i))) || ((i >= len) && (defaultbit)))
|
||
ACLBYTE(n, i) |= ACLBIT(i);
|
||
}
|
||
if (len)
|
||
free((char *)o);
|
||
*bfp = n;
|
||
return 0;
|
||
}
|
||
|
||
#endif /* MULTIUSER */
|
||
|
||
/*
|
||
* Returns an nonzero Address. Its contents is either a User-ptr,
|
||
* or NULL which may be replaced by a User-ptr to create the entry.
|
||
*/
|
||
struct acluser **
|
||
FindUserPtr(char *name)
|
||
{
|
||
struct acluser **u;
|
||
|
||
for (u = &users; *u; u = &(*u)->u_next)
|
||
if (!strcmp((*u)->u_name, name))
|
||
break;
|
||
#ifdef MULTIUSER
|
||
debug3("FindUserPtr %s %sfound, id %d\n", name, (*u) ? "" : "not ", (*u) ? (*u)->u_id : -1);
|
||
#else /* MULTIUSER */
|
||
debug2("FindUserPtr %s %sfound\n", name, (*u) ? "" : "not ");
|
||
#endif /* MULTIUSER */
|
||
return u;
|
||
}
|
||
|
||
int DefaultEsc = -1; /* initialised by screen.c:main() */
|
||
int DefaultMetaEsc = -1;
|
||
|
||
/*
|
||
* Add a new user. His password may be NULL or "" if none. His name must not
|
||
* be "none", as this represents the NULL-pointer when dealing with groups.
|
||
* He has default rights, determined by umask.
|
||
*/
|
||
int
|
||
UserAdd(char *name, char *pass, struct acluser **up)
|
||
{
|
||
#ifdef MULTIUSER
|
||
int j;
|
||
#endif
|
||
|
||
if (!up)
|
||
up = FindUserPtr(name);
|
||
if (*up) {
|
||
if (pass)
|
||
(*up)->u_password = SaveStr(pass);
|
||
return 1; /* he is already there */
|
||
}
|
||
if (strcmp("none", name)) /* "none" is a reserved word */
|
||
*up = (struct acluser *)calloc(1, sizeof(struct acluser));
|
||
if (!*up)
|
||
return -1; /* he still does not exist */
|
||
#ifdef COPY_PASTE
|
||
(*up)->u_plop.buf = NULL;
|
||
(*up)->u_plop.len = 0;
|
||
# ifdef ENCODINGS
|
||
(*up)->u_plop.enc = 0;
|
||
# endif
|
||
#endif
|
||
(*up)->u_Esc = DefaultEsc;
|
||
(*up)->u_MetaEsc = DefaultMetaEsc;
|
||
strncpy((*up)->u_name, name, MAXLOGINLEN);
|
||
(*up)->u_password = NULL;
|
||
if (pass)
|
||
(*up)->u_password = SaveStr(pass);
|
||
if (!(*up)->u_password)
|
||
(*up)->u_password = NullStr;
|
||
(*up)->u_detachwin = -1;
|
||
(*up)->u_detachotherwin = -1;
|
||
|
||
#ifdef MULTIUSER
|
||
(*up)->u_group = NULL;
|
||
/* now find an unused index */
|
||
for ((*up)->u_id = 0; (*up)->u_id < maxusercount; (*up)->u_id++)
|
||
if (!(ACLBIT((*up)->u_id) & ACLBYTE(userbits, (*up)->u_id)))
|
||
break;
|
||
debug2("UserAdd %s id %d\n", name, (*up)->u_id);
|
||
if ((*up)->u_id == maxusercount) {
|
||
int j;
|
||
struct win *w;
|
||
struct acluser *u;
|
||
|
||
debug2("growing all bitfields %d += %d\n", maxusercount, USER_CHUNK);
|
||
/* the bitfields are full, grow a chunk */
|
||
/* first, the used_uid_indicator: */
|
||
if (GrowBitfield(&userbits, maxusercount, USER_CHUNK, 0)) {
|
||
free((char *)*up);
|
||
*up = NULL;
|
||
return -1;
|
||
}
|
||
/* second, default command bits */
|
||
/* (only if we generate commands dynamically) */
|
||
/*
|
||
for (j = 0; j < ACL_BITS_PER_CMD; j++)
|
||
if (GrowBitfield(&default_c_userbits[j], maxusercount, USER_CHUNK,
|
||
default_c_bit[j]))
|
||
{
|
||
free((char *)*up); *up = NULL; return -1;
|
||
}
|
||
*/
|
||
/* third, the bits for each commands */
|
||
for (j = 0; j <= RC_LAST; j++) {
|
||
int i;
|
||
|
||
for (i = 0; i < ACL_BITS_PER_CMD; i++)
|
||
if (GrowBitfield(&comms[j].userbits[i], maxusercount, USER_CHUNK, default_c_bit[i])) {
|
||
free((char *)*up);
|
||
*up = NULL;
|
||
return -1;
|
||
}
|
||
}
|
||
/* fourth, default window creation bits per user */
|
||
for (u = users; u != *up; u = u->u_next) {
|
||
for (j = 0; j < ACL_BITS_PER_WIN; j++) {
|
||
if (GrowBitfield(&u->u_umask_w_bits[j], maxusercount, USER_CHUNK, default_w_bit[j])) {
|
||
free((char *)*up);
|
||
*up = NULL;
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* fifth, the bits for each window */
|
||
/* keep these in sync with NewWindowAcl() */
|
||
|
||
for (w = windows; w; w = w->w_next) {
|
||
/* five a: the access control list */
|
||
for (j = 0; j < ACL_BITS_PER_WIN; j++)
|
||
if (GrowBitfield(&w->w_userbits[j], maxusercount, USER_CHUNK, default_w_bit[j])) {
|
||
free((char *)*up);
|
||
*up = NULL;
|
||
return -1;
|
||
}
|
||
/* five b: the activity notify list */
|
||
/* five c: the silence notify list */
|
||
if (GrowBitfield(&w->w_mon_notify, maxusercount, USER_CHUNK, 0) ||
|
||
GrowBitfield(&w->w_lio_notify, maxusercount, USER_CHUNK, 0)) {
|
||
free((char *)*up);
|
||
*up = NULL;
|
||
return -1;
|
||
}
|
||
}
|
||
maxusercount += USER_CHUNK;
|
||
}
|
||
|
||
/* mark the user-entry as "in-use" */
|
||
ACLBYTE(userbits, (*up)->u_id) |= ACLBIT((*up)->u_id);
|
||
|
||
/* user id 0 is the session creator, he has all rights */
|
||
if ((*up)->u_id == 0)
|
||
AclSetPerm(NULL, *up, "+a", "#?");
|
||
|
||
/* user nobody has a fixed set of rights: */
|
||
if (!strcmp((*up)->u_name, "nobody")) {
|
||
AclSetPerm(NULL, *up, "-rwx", "#?");
|
||
AclSetPerm(NULL, *up, "+x", "su");
|
||
AclSetPerm(NULL, *up, "+x", "detach");
|
||
AclSetPerm(NULL, *up, "+x", "displays");
|
||
AclSetPerm(NULL, *up, "+x", "version");
|
||
}
|
||
|
||
/*
|
||
* Create his umask:
|
||
* Give default_w_bit's for all users,
|
||
* but allow himself everything on "his" windows.
|
||
*/
|
||
for (j = 0; j < ACL_BITS_PER_WIN; j++) {
|
||
if (GrowBitfield(&(*up)->u_umask_w_bits[j], 0, maxusercount, default_w_bit[j])) {
|
||
free((char *)*up);
|
||
*up = NULL;
|
||
return -1;
|
||
}
|
||
ACLBYTE((*up)->u_umask_w_bits[j], (*up)->u_id) |= ACLBIT((*up)->u_id);
|
||
}
|
||
#else /* MULTIUSER */
|
||
debug1("UserAdd %s\n", name);
|
||
#endif /* MULTIUSER */
|
||
return 0;
|
||
}
|
||
|
||
#if 0
|
||
/* change user's password */
|
||
int
|
||
UserSetPass(char *name, char *pass, struct acluser **up)
|
||
{
|
||
if (!up)
|
||
up = FindUserPtr(name);
|
||
if (!*up)
|
||
return UserAdd(name, pass, up);
|
||
if (!strcmp(name,
|
||
"nobody")) /* he remains without password */
|
||
return -1;
|
||
strncpy((*up)->u_password, pass ? pass : "", 20);
|
||
(*up)->u_password[20] = '\0';
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* Remove a user from the list.
|
||
* Destroy all his permissions and completely detach him from the session.
|
||
*/
|
||
int
|
||
UserDel(char *name, struct acluser **up)
|
||
{
|
||
struct acluser *u;
|
||
#ifdef MULTIUSER
|
||
int i;
|
||
#endif
|
||
struct display *old, *next;
|
||
|
||
if (!up)
|
||
up = FindUserPtr(name);
|
||
if (!(u = *up))
|
||
return -1; /* he who does not exist cannot be removed */
|
||
old = display;
|
||
for (display = displays; display; display = next) {
|
||
next = display->d_next; /* read the next ptr now, Detach may zap it. */
|
||
if (D_user != u)
|
||
continue;
|
||
if (display == old)
|
||
old = NULL;
|
||
Detach(D_REMOTE);
|
||
}
|
||
display = old;
|
||
*up = u->u_next;
|
||
|
||
#ifdef MULTIUSER
|
||
for (up = &users; *up; up = &(*up)->u_next) {
|
||
/* unlink all group references to this user */
|
||
struct aclusergroup **g = &(*up)->u_group;
|
||
|
||
while (*g) {
|
||
if ((*g)->u == u) {
|
||
struct aclusergroup *next = (*g)->next;
|
||
|
||
free((char *)(*g));
|
||
*g = next;
|
||
} else
|
||
g = &(*g)->next;
|
||
}
|
||
}
|
||
ACLBYTE(userbits, u->u_id) &= ~ACLBIT(u->u_id);
|
||
/* restore the bits in his slot to default: */
|
||
AclSetPerm(NULL, u, default_w_bit[ACL_READ] ? "+r" : "-r", "#");
|
||
AclSetPerm(NULL, u, default_w_bit[ACL_WRITE] ? "+w" : "-w", "#");
|
||
AclSetPerm(NULL, u, default_w_bit[ACL_EXEC] ? "+x" : "-x", "#");
|
||
AclSetPerm(NULL, u, default_c_bit[ACL_EXEC] ? "+x" : "-x", "?");
|
||
for (i = 0; i < ACL_BITS_PER_WIN; i++)
|
||
free((char *)u->u_umask_w_bits[i]);
|
||
#endif /* MULTIUSER */
|
||
debug1("FREEING user structure for %s\n", u->u_name);
|
||
#ifdef COPY_PASTE
|
||
UserFreeCopyBuffer(u);
|
||
#endif
|
||
free((char *)u);
|
||
if (!users) {
|
||
debug("Last user deleted. Feierabend.\n");
|
||
Finit(0); /* Destroying whole session. No one could ever attach again. */
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#ifdef COPY_PASTE
|
||
|
||
/*
|
||
* returns 0 if the copy buffer was really deleted.
|
||
* Also removes any references into the user's copybuffer
|
||
*/
|
||
int
|
||
UserFreeCopyBuffer(struct acluser *u)
|
||
{
|
||
struct win *w;
|
||
struct paster *pa;
|
||
|
||
if (!u->u_plop.buf)
|
||
return 1;
|
||
for (w = windows; w; w = w->w_next) {
|
||
pa = &w->w_paster;
|
||
if (pa->pa_pasteptr >= u->u_plop.buf &&
|
||
pa->pa_pasteptr - u->u_plop.buf < u->u_plop.len)
|
||
FreePaster(pa);
|
||
}
|
||
free((char *)u->u_plop.buf);
|
||
u->u_plop.len = 0;
|
||
u->u_plop.buf = 0;
|
||
return 0;
|
||
}
|
||
#endif /* COPY_PASTE */
|
||
|
||
#ifdef MULTIUSER
|
||
/*
|
||
* Traverses group nodes. It searches for a node that references user u.
|
||
* If recursive is true, nodes found in the users are also searched using
|
||
* depth first method. If none of the nodes references u, the address of
|
||
* the last next pointer is returned. This address will contain NULL.
|
||
*/
|
||
static struct aclusergroup **
|
||
FindGroupPtr(struct aclusergroup **gp, struct acluser *u, int recursive)
|
||
{
|
||
struct aclusergroup **g;
|
||
|
||
ASSERT(recursive < 1000); /* Ouch, cycle detection failed */
|
||
while (*gp) {
|
||
if ((*gp)->u == u)
|
||
return gp; /* found him here. */
|
||
|
||
if (recursive && *(g = FindGroupPtr(&(*gp)->u->u_group, u, recursive + 1)))
|
||
return g; /* found him there. */
|
||
|
||
gp = &(*gp)->next;
|
||
}
|
||
return gp; /* *gp is NULL */
|
||
}
|
||
|
||
static int
|
||
PasswordMatches(const char *pw, const char *password)
|
||
{
|
||
if (!*password)
|
||
return 0;
|
||
char *buf = crypt((char *)pw, (char *)password);
|
||
return (buf && !strcmp(buf, password));
|
||
}
|
||
|
||
/*
|
||
* Returns nonzero if failed or already linked.
|
||
* Both users are created on demand.
|
||
* Cyclic links are prevented.
|
||
*/
|
||
int
|
||
AclLinkUser(char *from, char *to)
|
||
{
|
||
struct acluser **u1, **u2;
|
||
struct aclusergroup **g;
|
||
|
||
if (!*(u1 = FindUserPtr(from)) && UserAdd(from, NULL, u1))
|
||
return -1;
|
||
if (!*(u2 = FindUserPtr(to)) && UserAdd(to, NULL, u2))
|
||
return -1; /* hmm, could not find both users. */
|
||
|
||
if (*FindGroupPtr(&(*u2)->u_group, *u1, 1))
|
||
return 1; /* cyclic link detected! */
|
||
if (*(g = FindGroupPtr(&(*u1)->u_group, *u2, 0)))
|
||
return 2; /* aha, we are already linked! */
|
||
|
||
if (!(*g = (struct aclusergroup *)malloc(sizeof(struct aclusergroup))))
|
||
return -1; /* Could not alloc link. Poor screen */
|
||
|
||
(*g)->u = (*u2);
|
||
(*g)->next = NULL;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* The user pointer stored at *up will be substituted by a pointer
|
||
* to the named user's structure, if passwords match.
|
||
* returns NULL if successful, an static error string otherwise
|
||
*/
|
||
char *
|
||
DoSu(struct acluser **up, char *name, char *pw1, char *pw2)
|
||
{
|
||
struct acluser *u;
|
||
int sorry = 0;
|
||
|
||
if (!(u = *FindUserPtr(name)))
|
||
sorry++;
|
||
else {
|
||
#ifdef CHECKLOGIN
|
||
struct passwd *pp;
|
||
#ifdef SHADOWPW
|
||
struct spwd *ss;
|
||
int t, c;
|
||
#endif
|
||
char *pass = "";
|
||
|
||
if (!(pp = getpwnam(name))) {
|
||
debug1("getpwnam(\"%s\") failed\n", name);
|
||
if (!(pw1 && *pw1 && *pw1 != '\377')) {
|
||
debug("no unix account, no screen passwd\n");
|
||
sorry++;
|
||
}
|
||
} else
|
||
pass = pp->pw_passwd;
|
||
#ifdef SHADOWPW
|
||
for (t = 0; t < 13; t++) {
|
||
c = pass[t];
|
||
if (!(c == '.' || c == '/' ||
|
||
(c >= '0' && c <= '9') ||
|
||
(c >= 'a' && c <= 'z') ||
|
||
(c >= 'A' && c <= 'Z')))
|
||
break;
|
||
}
|
||
if (t < 13) {
|
||
if (!(ss = getspnam(name))) {
|
||
debug1("getspnam(\"%s\") failed\n", name);
|
||
sorry++;
|
||
} else
|
||
pass = ss->sp_pwdp;
|
||
}
|
||
#endif /* SHADOWPW */
|
||
|
||
if (pw2 && *pw2 && *pw2 != '\377') { /* provided a system password */
|
||
if (!PasswordMatches(pw2, pass)) {
|
||
debug("System password mismatch\n");
|
||
sorry++;
|
||
}
|
||
} else /* no password provided */
|
||
if (*pass) /* but need one */
|
||
sorry++;
|
||
#endif /* CHECKLOGIN */
|
||
if (pw1 && *pw1 && *pw1 != '\377') { /* provided a screen password */
|
||
if (!PasswordMatches(pw1, u->u_password)) {
|
||
debug("screen password mismatch\n");
|
||
sorry++;
|
||
}
|
||
} else /* no password provided */
|
||
if (*u->u_password) /* but need one */
|
||
sorry++;
|
||
}
|
||
|
||
debug2("syslog(LOG_NOTICE, \"screen %s: \"su %s\" ", SockPath, name);
|
||
debug2("%s for \"%s\"\n", sorry ? "failed" : "succeeded", (*up)->u_name);
|
||
#ifndef NOSYSLOG
|
||
|
||
# ifdef BSD_42
|
||
openlog("screen", LOG_PID);
|
||
# else
|
||
openlog("screen", LOG_PID, LOG_AUTH);
|
||
# endif /* BSD_42 */
|
||
|
||
syslog(LOG_NOTICE, "%s: \"su %s\" %s for \"%s\"", SockPath, name, sorry ? "failed" : "succeeded", (*up)->u_name);
|
||
closelog();
|
||
#else
|
||
debug("NOT LOGGED.\n");
|
||
#endif /* NOSYSLOG */
|
||
|
||
if (sorry)
|
||
return "Sorry.";
|
||
else
|
||
*up = u; /* substitute user now */
|
||
return NULL;
|
||
}
|
||
#endif /* MULTIUSER */
|
||
|
||
/************************************************************************
|
||
* end of user managing code *
|
||
************************************************************************/
|
||
|
||
#ifdef MULTIUSER
|
||
|
||
/* This gives the users default rights to the new window w created by u */
|
||
int
|
||
NewWindowAcl(struct win *w, struct acluser *u)
|
||
{
|
||
int i, j;
|
||
|
||
debug2("NewWindowAcl %s's umask_w_bits for window %d\n", u ? u->u_name : "everybody", w->w_number);
|
||
|
||
/* keep these in sync with UserAdd part five. */
|
||
if (GrowBitfield(&w->w_mon_notify, 0, maxusercount, 0) ||
|
||
GrowBitfield(&w->w_lio_notify, 0, maxusercount, 0))
|
||
return -1;
|
||
for (j = 0; j < ACL_BITS_PER_WIN; j++) {
|
||
/* we start with len 0 for the new bitfield size and add maxusercount */
|
||
if (GrowBitfield(&w->w_userbits[j], 0, maxusercount, 0)) {
|
||
while (--j >= 0)
|
||
free((char *)w->w_userbits[j]);
|
||
free((char *)w->w_mon_notify);
|
||
free((char *)w->w_lio_notify);
|
||
return -1;
|
||
}
|
||
for (i = 0; i < maxusercount; i++)
|
||
if (u ? (ACLBIT(i) & ACLBYTE(u->u_umask_w_bits[j], i)) : default_w_bit[j])
|
||
ACLBYTE(w->w_userbits[j], i) |= ACLBIT(i);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
FreeWindowAcl(struct win *w)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < ACL_BITS_PER_WIN; i++)
|
||
free((char *)w->w_userbits[i]);
|
||
|
||
free((char *)w->w_mon_notify);
|
||
free((char *)w->w_lio_notify);
|
||
}
|
||
|
||
/* if mode starts with '-' we remove the user's exec bit for cmd */
|
||
/*
|
||
* NOTE: before you make this function look the same as
|
||
* AclSetPermWin, try to merge both functions.
|
||
*/
|
||
static int
|
||
AclSetPermCmd(struct acluser *u, char *mode, struct comm *cmd)
|
||
{
|
||
int neg = 0;
|
||
char *m = mode;
|
||
|
||
while (*m) {
|
||
switch (*m++) {
|
||
case '-':
|
||
neg = 1;
|
||
continue;
|
||
case '+':
|
||
neg = 0;
|
||
continue;
|
||
case 'a':
|
||
case 'e':
|
||
case 'x':
|
||
/* debug3("AclSetPermCmd %s %s %s\n", u->u_name, mode, cmd->name); */
|
||
if (neg)
|
||
ACLBYTE(cmd->userbits[ACL_EXEC], u->u_id) &= ~ACLBIT(
|
||
u->u_id);
|
||
else
|
||
ACLBYTE(cmd->userbits[ACL_EXEC], u->u_id) |= ACLBIT(
|
||
u->u_id);
|
||
break;
|
||
case 'r':
|
||
case 'w':
|
||
break;
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* mode strings of the form +rwx -w+rx r -wx are parsed and evaluated */
|
||
/*
|
||
* aclchg nerd -w+w 2
|
||
* releases a writelock on window 2 held by user nerd.
|
||
* Letter n allows network access on a window.
|
||
* uu should be NULL, except if you want to change his umask.
|
||
*/
|
||
static int
|
||
AclSetPermWin(struct acluser *uu, struct acluser *u, char *mode, struct win *win)
|
||
{
|
||
int neg = 0;
|
||
int bit, bits;
|
||
AclBits *bitarray;
|
||
char *m = mode;
|
||
|
||
if (uu) {
|
||
debug3("AclSetPermWin %s UMASK %s %s\n", uu->u_name, u->u_name, mode);
|
||
bitarray = uu->u_umask_w_bits;
|
||
} else {
|
||
ASSERT(win);
|
||
bitarray = win->w_userbits;
|
||
debug3("AclSetPermWin %s %s %d\n", u->u_name, mode, win->w_number);
|
||
}
|
||
|
||
while (*m) {
|
||
switch (*m++) {
|
||
case '-':
|
||
neg = 1;
|
||
continue;
|
||
case '+':
|
||
neg = 0;
|
||
continue;
|
||
case 'r':
|
||
bits = (1 << ACL_READ);
|
||
break;
|
||
case 'w':
|
||
bits = (1 << ACL_WRITE);
|
||
break;
|
||
case 'x':
|
||
bits = (1 << ACL_EXEC);
|
||
break;
|
||
case 'a':
|
||
bits = (1 << ACL_BITS_PER_WIN) - 1;
|
||
break;
|
||
default:
|
||
return -1;
|
||
}
|
||
|
||
for (bit = 0; bit < ACL_BITS_PER_WIN; bit++) {
|
||
if (!(bits & (1 << bit)))
|
||
continue;
|
||
if (neg)
|
||
ACLBYTE(bitarray[bit], u->u_id) &= ~ACLBIT(
|
||
u->u_id);
|
||
else
|
||
ACLBYTE(bitarray[bit], u->u_id) |= ACLBIT(
|
||
u->u_id);
|
||
if (!uu && (win->w_wlockuser == u) && neg &&
|
||
(bit == ACL_WRITE)) {
|
||
debug2("%s lost writelock on win %d\n", u->u_name, win->w_number);
|
||
win->w_wlockuser = NULL;
|
||
if (win->w_wlock == WLOCK_ON)
|
||
win->w_wlock = WLOCK_AUTO;
|
||
}
|
||
}
|
||
}
|
||
if (uu && u->u_name[0] == '?' && u->u_name[1] == '\0') {
|
||
/*
|
||
* It is Mr. '?', the unknown user. He deserves special treatment as
|
||
* he defines the defaults. Sorry, this is global, not per user.
|
||
*/
|
||
if (win) {
|
||
debug1("AclSetPermWin: default_w_bits '%s'.\n", mode);
|
||
for (bit = 0; bit < ACL_BITS_PER_WIN; bit++)
|
||
default_w_bit[bit] = (ACLBYTE(bitarray[bit], u->u_id) & ACLBIT(u->u_id)) ? 1 : 0;
|
||
} else {
|
||
/*
|
||
* Hack. I do not want to duplicate all the above code for
|
||
* AclSetPermCmd. This assumes that there are not more bits
|
||
* per cmd than per win.
|
||
*/
|
||
debug1("AclSetPermWin: default_c_bits '%s'.\n", mode);
|
||
for (bit = 0; bit < ACL_BITS_PER_CMD; bit++)
|
||
default_c_bit[bit] = (ACLBYTE(bitarray[bit], u->u_id) & ACLBIT(u->u_id)) ? 1 : 0;
|
||
}
|
||
UserDel(u->u_name, NULL);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* String is broken down into command and window names, mode applies
|
||
* A command name matches first, so do not use these as window names.
|
||
* uu should be NULL, except if you want to change his umask.
|
||
*/
|
||
int
|
||
AclSetPerm(struct acluser *uu, struct acluser *u, char *mode, char *s)
|
||
{
|
||
struct win *w;
|
||
int i;
|
||
char *p, ch;
|
||
|
||
debug3("AclSetPerm(uu, user '%s', mode '%s', object '%s')\n", u->u_name, mode, s);
|
||
while (*s) {
|
||
switch (*s) {
|
||
case '*': /* all windows and all commands */
|
||
return AclSetPerm(uu, u, mode, "#?");
|
||
case '#':
|
||
if (uu) /* window umask or .. */
|
||
AclSetPermWin(uu, u, mode, (struct win *)1);
|
||
else /* .. or all windows */
|
||
for (w = windows; w; w = w->w_next)
|
||
AclSetPermWin((struct acluser *)0, u,
|
||
mode, w);
|
||
s++;
|
||
break;
|
||
case '?':
|
||
if (uu) /* command umask or .. */
|
||
AclSetPermWin(uu, u, mode, (struct win *)0);
|
||
else /* .. or all commands */
|
||
for (i = 0; i <= RC_LAST; i++)
|
||
AclSetPermCmd(u, mode, &comms[i]);
|
||
s++;
|
||
break;
|
||
default:
|
||
for (p = s; *p && *p != ' ' && *p != '\t' && *p != ','; p++)
|
||
;
|
||
if ((ch = *p))
|
||
*p++ = '\0';
|
||
if ((i = FindCommnr(s)) != RC_ILLEGAL)
|
||
AclSetPermCmd(u, mode, &comms[i]);
|
||
else if (((i = WindowByNoN(s)) >= 0) && wtab[i])
|
||
AclSetPermWin((struct acluser *)0, u, mode,
|
||
wtab[i]);
|
||
else
|
||
/* checking group name */
|
||
return -1;
|
||
|
||
if (ch)
|
||
p[-1] = ch;
|
||
s = p;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Generic ACL Manager:
|
||
*
|
||
* This handles acladd and aclchg identical.
|
||
* With 2 or 4 parameters, the second parameter is a password.
|
||
* With 3 or 4 parameters the last two parameters specify the permissions
|
||
* else user is added with full permissions.
|
||
* With 1 parameter the users permissions are copied from user *argv.
|
||
* Unlike the other cases, u->u_name should not match *argv here.
|
||
* uu should be NULL, except if you want to change his umask.
|
||
*/
|
||
static int
|
||
UserAcl(struct acluser *uu, struct acluser **u, int argc, char **argv)
|
||
{
|
||
if ((*u && !strcmp((*u)->u_name, "nobody")) || (argc > 1 && !strcmp(argv[0], "nobody")))
|
||
return -1; /* do not change nobody! */
|
||
|
||
switch (argc) {
|
||
case 1 + 1 + 2:
|
||
debug2("UserAcl: user '%s', password '%s':", argv[0], argv[1]);
|
||
return (UserAdd(argv[0], argv[1], u) < 0) || AclSetPerm(uu, *u, argv[2], argv[3]);
|
||
|
||
case 1 + 2:
|
||
debug1("UserAcl: user '%s', no password:", argv[0]);
|
||
return (UserAdd(argv[0], NULL, u) < 0) || AclSetPerm(uu, *u, argv[1], argv[2]);
|
||
|
||
case 1 + 1:
|
||
debug2("UserAcl: user '%s', password '%s'\n", argv[0], argv[1]);
|
||
return UserAdd(argv[0], argv[1], u) < 0;
|
||
|
||
case 1:
|
||
debug1("UserAcl: user '%s', no password:", argv[0]);
|
||
return (UserAdd(argv[0], NULL, u) < 0) || AclSetPerm(uu, *u, "+a", "#?");
|
||
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
static int
|
||
UserAclCopy(struct acluser **to_up, struct acluser **from_up)
|
||
{
|
||
struct win *w;
|
||
int i, j, to_id, from_id;
|
||
|
||
if (!*to_up || !*from_up)
|
||
return -1;
|
||
debug2("UserAclCopy: from user '%s' to user '%s'\n", (*from_up)->u_name, (*to_up)->u_name);
|
||
|
||
if ((to_id = (*to_up)->u_id) == (from_id = (*from_up)->u_id))
|
||
return -1;
|
||
|
||
for (w = windows; w; w = w->w_next) {
|
||
for (i = 0; i < ACL_BITS_PER_WIN; i++) {
|
||
if (ACLBYTE(w->w_userbits[i], from_id) & ACLBIT(from_id))
|
||
ACLBYTE(w->w_userbits[i], to_id) |= ACLBIT(to_id);
|
||
else {
|
||
ACLBYTE(w->w_userbits[i], to_id) &= ~ACLBIT(to_id);
|
||
if ((w->w_wlockuser == *to_up) && (i == ACL_WRITE)) {
|
||
debug2("%s lost wlock on win %d\n", (*to_up)->u_name, w->w_number);
|
||
w->w_wlockuser = NULL;
|
||
if (w->w_wlock == WLOCK_ON)
|
||
w->w_wlock = WLOCK_AUTO;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for (j = 0; j <= RC_LAST; j++){
|
||
for (i = 0; i < ACL_BITS_PER_CMD; i++) {
|
||
if (ACLBYTE(comms[j].userbits[i], from_id) & ACLBIT(from_id))
|
||
ACLBYTE(comms[j].userbits[i], to_id) |= ACLBIT(to_id);
|
||
else
|
||
ACLBYTE(comms[j].userbits[i], to_id) &= ~ACLBIT(to_id);
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Syntax:
|
||
* user [password] [+rwx #?]
|
||
* * [password] [+rwx #?]
|
||
* user1,user2,user3 [password] [+rwx #?]
|
||
* user1,user2,user3=user
|
||
* uu should be NULL, except if you want to change his umask.
|
||
*/
|
||
int
|
||
UsersAcl(struct acluser *uu, int argc, char **argv)
|
||
{
|
||
char *s;
|
||
int r;
|
||
struct acluser **cf_u = NULL;
|
||
|
||
if (argc == 1) {
|
||
char *p = NULL;
|
||
|
||
s = argv[0];
|
||
while (*s)
|
||
if (*s++ == '=')
|
||
p = s;
|
||
if (p) {
|
||
p[-1] = '\0';
|
||
cf_u = FindUserPtr(p);
|
||
}
|
||
}
|
||
|
||
if (argv[0][0] == '*' && argv[0][1] == '\0') {
|
||
struct acluser **u;
|
||
|
||
debug("all users acls.\n");
|
||
for (u = &users; *u; u = &(*u)->u_next)
|
||
if (strcmp("nobody", (*u)->u_name) && ((cf_u) ?
|
||
((r = UserAclCopy(u, cf_u)) < 0) :
|
||
((r = UserAcl(uu, u, argc, argv)) < 0)))
|
||
return -1;
|
||
return 0;
|
||
}
|
||
|
||
do {
|
||
for (s = argv[0];
|
||
*s && *s != ' ' && *s != '\t' && *s != ',' && *s != '='; s++)
|
||
;
|
||
*s ? (*s++ = '\0') : (*s = '\0');
|
||
|
||
debug2("UsersAcl(uu, \"%s\", argc=%d)\n", argv[0], argc);
|
||
if ((cf_u) ?
|
||
((r = UserAclCopy(FindUserPtr(argv[0]), cf_u)) < 0) :
|
||
((r = UserAcl(uu, FindUserPtr(argv[0]), argc, argv)) < 0))
|
||
return -1;
|
||
} while (*(argv[0] = s));
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Preprocess arguments, so that umask can be set with UsersAcl
|
||
*
|
||
* all current users umask <20>rwxn
|
||
* one specific user umask user1<72>rwxn
|
||
* several users umask user1,user2,...<2E>rwxn
|
||
* default_w_bits umask ?<3F>rwxn
|
||
* default_c_bits umask ??<3F>rwxn
|
||
*/
|
||
int
|
||
AclUmask(struct acluser *u, char *str, char **errp)
|
||
{
|
||
char mode[16];
|
||
char *av[3];
|
||
char *p, c = '\0';
|
||
|
||
/* split str into user and bits section. */
|
||
for (p = str; *p; p++)
|
||
if ((c = *p) == '+' || c == '-')
|
||
break;
|
||
if (!*p) {
|
||
*errp = "Bad argument. Should be ``[user[,user...]{+|-}rwxn''.";
|
||
return -1;
|
||
}
|
||
strncpy(mode, p, 15);
|
||
mode[15] = '\0';
|
||
*p = '\0';
|
||
|
||
/* construct argument vector */
|
||
if (!strcmp("??", str)) {
|
||
str++;
|
||
av[2] = "?";
|
||
} else
|
||
av[2] = "#";
|
||
av[1] = mode;
|
||
av[0] = *str ? str : "*";
|
||
/* call UsersAcl */
|
||
if (UsersAcl(u, 3, av)) {
|
||
*errp = "UsersAcl failed. Hmmm.";
|
||
*p = c;
|
||
return -1;
|
||
}
|
||
*p = c;
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
AclWinSwap(int a, int b)
|
||
{
|
||
debug2("AclWinSwap(%d, %d) NOP.\n", a, b);
|
||
}
|
||
|
||
struct acluser *EffectiveAclUser = NULL; /* hook for AT command permission */
|
||
|
||
int
|
||
AclCheckPermWin(struct acluser *u, int mode, struct win *w)
|
||
{
|
||
int ok;
|
||
|
||
if (mode < 0 || mode >= ACL_BITS_PER_WIN)
|
||
return -1;
|
||
if (EffectiveAclUser) {
|
||
debug1("AclCheckPermWin: WARNING user %s overridden!\n", u->u_name);
|
||
u = EffectiveAclUser;
|
||
}
|
||
ok = ACLBYTE(w->w_userbits[mode], u->u_id) & ACLBIT(u->u_id);
|
||
debug3("AclCheckPermWin(%s, %d, %d) = ", u->u_name, mode, w->w_number);
|
||
|
||
if (!ok) {
|
||
struct aclusergroup **g = &u->u_group;
|
||
struct acluser *saved_eff = EffectiveAclUser;
|
||
|
||
EffectiveAclUser = NULL;
|
||
while (*g) {
|
||
if (!AclCheckPermWin((*g)->u, mode, w))
|
||
break;
|
||
g = &(*g)->next;
|
||
}
|
||
EffectiveAclUser = saved_eff;
|
||
if (*g)
|
||
ok = 1;
|
||
}
|
||
debug1("%d\n", !ok);
|
||
return !ok;
|
||
}
|
||
|
||
int
|
||
AclCheckPermCmd(struct acluser *u, int mode, struct comm *c)
|
||
{
|
||
int ok;
|
||
|
||
if (mode < 0 || mode >= ACL_BITS_PER_CMD)
|
||
return -1;
|
||
if (EffectiveAclUser) {
|
||
debug1("AclCheckPermCmd: WARNING user %s overridden!\n", u->u_name);
|
||
u = EffectiveAclUser;
|
||
}
|
||
ok = ACLBYTE(c->userbits[mode], u->u_id) & ACLBIT(u->u_id);
|
||
debug3("AclCheckPermCmd(%s %d %s) = ", u->u_name, mode, c->name);
|
||
if (!ok) {
|
||
struct aclusergroup **g = &u->u_group;
|
||
struct acluser *saved_eff = EffectiveAclUser;
|
||
|
||
EffectiveAclUser = NULL;
|
||
while (*g) {
|
||
if (!AclCheckPermCmd((*g)->u, mode, c))
|
||
break;
|
||
g = &(*g)->next;
|
||
}
|
||
EffectiveAclUser = saved_eff;
|
||
if (*g)
|
||
ok = 1;
|
||
}
|
||
debug1("%d\n", !ok);
|
||
return !ok;
|
||
}
|
||
|
||
#endif /* MULTIUSER */
|