jehanne/sys/src/cmd/hjfs/auth.c

490 lines
9.2 KiB
C

/* Copyright (c) 20XX 9front
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <u.h>
#include <lib9.h>
#include <9P2000.h>
#include <thread.h>
#include "dat.h"
#include "fns.h"
typedef struct User User;
typedef struct PUser PUser;
struct User {
short uid;
char name[USERLEN];
short lead;
int nmemb;
short *memb;
};
struct PUser {
short uid;
char name[USERLEN];
char lead[USERLEN];
int nmemb;
char (*memb)[USERLEN];
};
User udef[] = {
{-1, "adm", -1, 0, nil},
{0, "none", -1, 0, nil},
{1, "tor", 1, 0, nil},
{2, "glenda", 2, 0, nil},
{10000, "sys", NOUID, 0, nil},
{10001, "map", 10001, 0, nil},
{10002, "doc", NOUID, 0, nil},
{10003, "upas", 10003, 0, nil},
{10004, "font", NOUID, 0, nil},
};
static int
validuser(char *n)
{
char *p;
if(*n == 0)
return 0;
for(p = n; *p != 0; p++)
if((uint8_t) *p < ' ' || strchr("?=+-/:", *p) != nil)
return 0;
return n - p < USERLEN;
}
static void
usersparseline(char *l, PUser **u, int *nu)
{
PUser v;
char *f[5], *r, *s;
int c;
if(*l == 0 || *l == '#')
return;
c = getfields(l, f, 5, 0, ":");
if(c < 4)
return;
v.uid = strtol(f[0], &r, 10);
if(*r != 0)
return;
if(!validuser(f[1]) || *f[2] != 0 && !validuser(f[2]))
return;
strcpy(v.name, f[1]);
strcpy(v.lead, f[2]);
v.memb = nil;
v.nmemb = 0;
r = f[3];
while(r != nil && *r != 0){
s = strchr(r, ',');
if(s != nil)
*s = 0;
if(!validuser(r)){
free(v.memb);
return;
}
v.memb = erealloc(v.memb, (v.nmemb + 1) * USERLEN);
strcpy(v.memb[v.nmemb++], r);
if(s == nil)
r = nil;
else
r = s + 1;
}
*u = erealloc(*u, (*nu + 1) * sizeof(PUser));
memcpy(&(*u)[(*nu)++], &v, sizeof(PUser));
}
static int
puserlook(PUser *u, int nu, char *name)
{
PUser *v;
if(*name == 0)
return NOUID;
for(v = u; v < u + nu; v++)
if(strcmp(v->name, name) == 0)
return v->uid;
return NOUID;
}
static int
uidcomp(const void *a, const void *b)
{
const short *aa, *bb;
aa = a;
bb = b;
return *aa - *bb;
}
static int
usercomp(const void *a, const void *b)
{
const User *aa, *bb;
aa = a;
bb = b;
return aa->uid - bb->uid;
}
int
usersload(Fs *fs, Chan *ch)
{
char *buf, *p, *q;
int bufl, i, j, rc, nu;
PUser *u;
User *v;
buf = nil;
bufl = 0;
u = nil;
v = nil;
nu = 0;
for(;;){
if((bufl & 1023) == 0)
buf = erealloc(buf, bufl + 1024);
rc = chanread(ch, buf + bufl, 1024, bufl);
if(rc < 0)
goto err;
if(rc == 0)
break;
bufl += rc;
}
if(buf == nil)
goto done;
buf[bufl] = 0;
for(p = buf; q = strchr(p, '\n'); p = q + 1){
*q = 0;
usersparseline(p, &u, &nu);
}
usersparseline(p, &u, &nu);
free(buf);
if(nu == 0)
goto done;
v = emalloc(sizeof(User) * nu);
for(i = 0; i < nu; i++){
v[i].uid = u[i].uid;
strcpy(v[i].name, u[i].name);
v[i].lead = puserlook(u, nu, u[i].lead);
v[i].nmemb = u[i].nmemb;
v[i].memb = emalloc(sizeof(short) * v[i].nmemb);
for(j = 0; j < v[i].nmemb; j++)
v[i].memb[j] = puserlook(u, nu, u[i].memb[j]);
qsort(v[i].memb, v[i].nmemb, sizeof(uint16_t), uidcomp);
}
qsort(v, nu, sizeof(User), usercomp);
done:
wlock(&fs->udatal);
if(fs->udata != nil){
for(i = 0; i < fs->nudata; i++)
free(((User *)fs->udata)[i].memb);
free(fs->udata);
}
fs->udata = v;
fs->nudata = nu;
wunlock(&fs->udatal);
return 0;
err:
free(buf);
return -1;
}
int
userssave(Fs *fs, Chan *ch)
{
User *u, *v;
int nu, i;
char buf[512], ubuf[USERLEN], *p, *e;
uint64_t off;
rlock(&fs->udatal);
u = fs->udata;
if(u == nil){
u = udef;
nu = nelem(udef);
}else
nu = fs->nudata;
off = 0;
for(v = u; v < u + nu; v++){
p = buf;
e = buf + sizeof(buf);
p = seprint(p, e, "%d:%s:", v->uid, v->name);
if(v->lead != NOUID)
p = strecpy(p, e, uid2name(fs, v->lead, ubuf));
if(p < e)
*p++ = ':';
for(i = 0; i < v->nmemb; i++){
if(v->memb[i] == NOUID)
continue;
if(p < e && i > 0)
*p++ = ',';
p = strecpy(p, e, uid2name(fs, v->memb[i], ubuf));
}
*p++ = '\n';
if(ch == nil)
jehanne_write(2, buf, p - buf);
else if(chanwrite(ch, buf, p - buf, off) < p - buf)
goto err;
off += p - buf;
}
runlock(&fs->udatal);
return 0;
err:
runlock(&fs->udatal);
return -1;
}
static User *
lookupuid(Fs *fs, short uid)
{
User *u;
int i, j, k;
u = fs->udata;
i = 0;
j = fs->nudata;
if(u == nil){
u = udef;
j = nelem(udef);
}
if(j == 0)
return nil;
while(i < j){
k = (i + j) / 2;
if(u[k].uid < uid)
i = k + 1;
else
j = k;
}
if(u[i].uid == uid)
return &u[i];
return nil;
}
int
ingroup(Fs *fs, short uid, short gid, int leader)
{
User *g;
int i, j, k;
if(uid == gid)
return 1;
rlock(&fs->udatal);
g = lookupuid(fs, gid);
if(g == nil)
goto nope;
if(g->lead == uid)
goto yes;
if(leader && g->lead != NOUID)
goto nope;
if(g->nmemb == 0)
goto nope;
i = 0;
j = g->nmemb;
while(i < j){
k = (i + j) / 2;
if(g->memb[k] < uid)
i = k + 1;
else
j = k;
}
if(g->memb[i] == uid)
goto yes;
nope:
runlock(&fs->udatal);
return 0;
yes:
runlock(&fs->udatal);
return 1;
}
int
permcheck(Fs *fs, Dentry *d, short uid, int mode)
{
int perm;
if((fs->flags & FSNOPERM) != 0)
return 1;
perm = d->mode & 0777;
if(d->uid == uid)
perm >>= 6;
else if(ingroup(fs, uid, d->gid, 0))
perm >>= 3;
switch(mode & 3){
case NP_OREAD:
return (perm & 4) != 0;
case NP_OWRITE:
return (perm & 2) != 0;
case NP_OEXEC:
return (perm & 1) != 0;
case NP_ORDWR:
return (perm & 6) == 6;
}
return 0;
}
char *
uid2name(Fs *fs, short uid, char *buf)
{
User *u;
rlock(&fs->udatal);
u = lookupuid(fs, uid);
if(buf == nil)
buf = emalloc(USERLEN);
if(u == nil)
snprint(buf, USERLEN, "%d", uid);
else
snprint(buf, USERLEN, "%s", u->name);
runlock(&fs->udatal);
return buf;
}
int
name2uid(Fs *fs, char *name, short *uid)
{
char *r;
User *u, *v;
*uid = strtol(name, &r, 10);
if(*r == 0)
return 1;
rlock(&fs->udatal);
u = fs->udata;
v = u + fs->nudata;
if(u == nil){
u = udef;
v = udef + nelem(udef);
}
for(; u < v; u++)
if(strcmp(u->name, name) == 0){
*uid = u->uid;
runlock(&fs->udatal);
return 1;
}
runlock(&fs->udatal);
werrstr(Einval);
return -1;
}
static void
createuserdir(Fs *fs, char *name, short uid)
{
Chan *ch;
ch = chanattach(fs, CHFNOPERM);
if(ch == nil)
return;
ch->uid = uid;
if(chanwalk(ch, "usr") > 0)
chancreat(ch, name, DMDIR | 0775, NP_OREAD);
chanclunk(ch);
}
int
cmdnewuser(int argc, char **argv)
{
short uid, gid;
User *u, *v;
Fs *fs;
int resort, createdir, i, j;
extern Fs *fsmain;
if(argc < 2)
return -9001;
if(!validuser(argv[1])){
werrstr(Einval);
return -1;
}
fs = fsmain;
resort = 0;
createdir = 0;
wlock(&fs->udatal);
if(fs->udata == nil){
wunlock(&fs->udatal);
werrstr("newuser: no user database");
return -1;
}
uid = 0;
gid = 10000;
for(u = fs->udata; u < fs->udata + fs->nudata; u++){
if(strcmp(u->name, argv[1]) == 0)
goto found;
if(u->uid == uid)
uid++;
if(u->uid == gid)
gid++;
}
resort = 1;
fs->udata = erealloc(fs->udata, sizeof(User) * (fs->nudata + 1));
u = fs->udata + fs->nudata++;
strcpy(u->name, argv[1]);
u->nmemb = 0;
u->memb = nil;
u->uid = gid;
u->lead = NOUID;
if(argc == 2 || strcmp(argv[2], ":") != 0){
u->lead = u->uid = uid;
createdir = 1;
}
found:
for(i = 2; i < argc; i++){
if(strcmp(argv[i], ":") == 0)
continue;
if(*argv[i] != '+' && *argv[i] != '-' && *argv[i] != '='){
if(!validuser(argv[i]))
goto erropt;
strcpy(u->name, argv[i]);
continue;
}
for(v = fs->udata; v < fs->udata + fs->nudata; v++)
if(strcmp(v->name, argv[i] + 1) == 0)
break;
if(v == fs->udata + fs->nudata)
goto erropt;
if(*argv[i] == '='){
u->lead = v->uid;
continue;
}
for(j = 0; j < u->nmemb && u->memb[j] < v->uid; j++)
;
if(*argv[i] == '-'){
if(u->memb[j] != v->uid)
goto erropt;
memmove(&u->memb[j], &u->memb[j + 1], sizeof(short) * (u->nmemb - j - 1));
u->memb = erealloc(u->memb, sizeof(short) * --u->nmemb);
}else{
u->memb = erealloc(u->memb, sizeof(short) * ++u->nmemb);
memmove(&u->memb[j + 1], &u->memb[j], sizeof(short) * (u->nmemb - j - 1));
u->memb[j] = v->uid;
}
continue;
erropt:
dprint("newuser: ignoring erroneous option %s\n", argv[i]);
}
if(resort)
qsort(fs->udata, fs->nudata, sizeof(User), usercomp);
wunlock(&fs->udatal);
writeusers(fs);
if(createdir)
createuserdir(fs, argv[1], uid);
return 1;
}