kernel: add chdev command to devcons

This commit is contained in:
Jacob Moody 2022-05-28 02:23:29 +00:00 committed by xfnw
parent 54328d2e65
commit 00b7d2385a
10 changed files with 209 additions and 36 deletions

View file

@ -90,12 +90,62 @@ file is a read-only file that produces an infinite stream of zero-valued bytes w
.PP
The
.B drivers
file contains, one per line, a listing of the drivers configured in the kernel, in the format
file contains, one per line, a listing of available kernel drivers, in the format
.IP
.EX
#c cons
.EE
.PP
Each process name group (see
.IR fork (2))
maintains a mask of allowed drivers.
A process can modify that mask through writes to the
.B drivers
file. Messages are in the format of:
.IP
.EX
chdev \f2op\fP \f2devmask\fP
.EE
.PP
.I Devmask
is a string of driver characters.
.I Op
specifies how
.I devmask
is interpreted.
.I Op
is one of:
.TP
.B &
Permit access to just the drivers specified in
.IR devmask .
.TP
.B &~
Permit access to all but the drivers specified in
.IR devmask .
.TP
.B ~
Remove access to all devices.
.I Devmask
is ignored.
.PP
Access may only be removed. No
.I op
permits a process to regain access to a driver
once it has been removed.
Removing access to
.IR mnt (3)
additionally prevents new mounts into the current namespace.
The following removes access to all drivers except
.IR draw (3),
.IR cons (3),
and
.IR fs (3):
.IP
.EX
chdev & ick
.EE
.PP
The
.B hostdomain
file contains the name of the authentication domain that

View file

@ -1272,7 +1272,7 @@ nameerror(char *name, char *err)
Chan*
namec(char *aname, int amode, int omode, ulong perm)
{
int len, n, t, nomount;
int len, n, t, nomount, devunmount;
Chan *c;
Chan *volatile cnew;
Path *volatile path;
@ -1291,6 +1291,24 @@ namec(char *aname, int amode, int omode, ulong perm)
}
name = aname;
/*
* When unmounting, the name parameter must be accessed
* using Aopen in order to get the real chan from
* something like /srv/cs or /fd/0. However when sandboxing,
* unmounting a sharp from a union is a valid operation even
* if the device is blocked.
*/
devunmount = 0;
if(amode == Aunmount){
/*
* Doing any walks down the device could leak information
* about the existence of files.
*/
if(name[0] == '#' && utflen(name) == 2)
devunmount = 1;
amode = Aopen;
}
/*
* Find the starting off point (the current slash, the root of
* a device tree, or the current dot) as well as the name to
@ -1313,24 +1331,13 @@ namec(char *aname, int amode, int omode, ulong perm)
up->genbuf[n++] = *name++;
}
up->genbuf[n] = '\0';
/*
* noattach is sandboxing.
*
* the OK exceptions are:
* | it only gives access to pipes you create
* d this process's file descriptors
* e this process's environment
* the iffy exceptions are:
* c time and pid, but also cons and consctl
* p control of your own processes (and unfortunately
* any others left unprotected)
*/
n = chartorune(&r, up->genbuf+1)+1;
if(up->pgrp->noattach && utfrune("|decp", r)==nil)
error(Enoattach);
t = devno(r, 1);
if(t == -1)
error(Ebadsharp);
if(!devunmount && !devallowed(up->pgrp, r))
error(Enoattach);
c = devtab[t]->attach(up->genbuf+n);
break;

View file

@ -30,6 +30,63 @@ devno(int c, int user)
return -1;
}
void
devmask(Pgrp *pgrp, int invert, char *devs)
{
int i, t, w;
char *p;
Rune r;
u64int mask[nelem(pgrp->notallowed)];
if(invert)
memset(mask, 0xFF, sizeof mask);
else
memset(mask, 0, sizeof mask);
w = sizeof mask[0] * 8;
for(p = devs; *p != '\0';){
p += chartorune(&r, p);
t = devno(r, 1);
if(t == -1)
continue;
if(invert)
mask[t/w] &= ~(1<<t%w);
else
mask[t/w] |= 1<<t%w;
}
wlock(&pgrp->ns);
for(i=0; i < nelem(pgrp->notallowed); i++)
pgrp->notallowed[i] |= mask[i];
wunlock(&pgrp->ns);
}
int
devallowed(Pgrp *pgrp, int r)
{
int t, w, b;
t = devno(r, 1);
if(t == -1)
return 0;
w = sizeof(u64int) * 8;
rlock(&pgrp->ns);
b = !(pgrp->notallowed[t/w] & 1<<t%w);
runlock(&pgrp->ns);
return b;
}
int
canmount(Pgrp *pgrp)
{
/*
* Devmnt is not usable directly from user procs, so
* having it removed is interpreted to block any mounts.
*/
return devallowed(pgrp, 'M');
}
void
devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db)
{

View file

@ -39,6 +39,16 @@ Cmdtab rebootmsg[] =
CMrdb, "rdb", 0,
};
enum
{
CMchdev,
};
Cmdtab drivermsg[] =
{
CMchdev, "chdev", 0,
};
void
printinit(void)
{
@ -332,7 +342,7 @@ static Dirtab consdir[]={
"cons", {Qcons}, 0, 0660,
"consctl", {Qconsctl}, 0, 0220,
"cputime", {Qcputime}, 6*NUMSIZE, 0444,
"drivers", {Qdrivers}, 0, 0444,
"drivers", {Qdrivers}, 0, 0666,
"hostdomain", {Qhostdomain}, DOMLEN, 0664,
"hostowner", {Qhostowner}, 0, 0664,
"kmesg", {Qkmesg}, 0, 0440,
@ -583,9 +593,15 @@ consread(Chan *c, void *buf, long n, vlong off)
case Qdrivers:
b = smalloc(READSTR);
k = 0;
for(i = 0; devtab[i] != nil; i++)
rlock(&up->pgrp->ns);
for(i = 0; devtab[i] != nil; i++){
if(up->pgrp->notallowed[i/(sizeof(u64int)*8)] & 1<<i%(sizeof(u64int)*8))
continue;
k += snprint(b+k, READSTR-k, "#%C %s\n",
devtab[i]->dc, devtab[i]->name);
}
runlock(&up->pgrp->ns);
if(waserror()){
free(b);
nexterror();
@ -620,6 +636,7 @@ conswrite(Chan *c, void *va, long n, vlong off)
{
char buf[256];
long l, bp;
int invert;
char *a;
Mach *mp;
int id;
@ -676,6 +693,41 @@ conswrite(Chan *c, void *va, long n, vlong off)
error(Eperm);
break;
case Qdrivers:
cb = parsecmd(a, n);
if(waserror()) {
free(cb);
nexterror();
}
ct = lookupcmd(cb, drivermsg, nelem(drivermsg));
if(ct == nil)
error(Ebadarg);
if(ct->index != CMchdev)
error(Ebadarg);
if(cb->nf < 2 || cb->nf > 3)
error(Ebadarg);
invert = 1;
a = cb->f[2];
switch(cb->f[1][0]){
case '&':
if(cb->f[1][1] == '~')
invert--;
break;
case '~':
a = "";
break;
default:
error(Ebadarg);
break;
}
devmask(up->pgrp, invert, a);
poperror();
free(cb);
break;
case Qreboot:
if(!iseve())
error(Eperm);

View file

@ -464,7 +464,7 @@ shrcreate(Chan *c, char *name, int omode, ulong perm)
cclose(c);
return nc;
case Qcroot:
if(up->pgrp->noattach)
if(!canmount(up->pgrp))
error(Enoattach);
if((perm & DMDIR) == 0 || mode != OREAD)
error(Eperm);
@ -498,7 +498,7 @@ shrcreate(Chan *c, char *name, int omode, ulong perm)
sch->shr = shr;
break;
case Qcshr:
if(up->pgrp->noattach)
if(!canmount(up->pgrp))
error(Enoattach);
if((perm & DMDIR) != 0 || mode != OWRITE)
error(Eperm);
@ -731,7 +731,7 @@ shrwrite(Chan *c, void *va, long n, vlong)
Mhead *h;
Mount *m;
if(up->pgrp->noattach)
if(!canmount(up->pgrp))
error(Enoattach);
sch = tosch(c);
if(sch->level != Qcmpt)

View file

@ -78,6 +78,9 @@ END{
if(ARGC < 2)
exit "usage"
if(ndev >= 256)
exit "device count will overflow Pgrp.notallowed"
printf "#include \"u.h\"\n";
printf "#include \"../port/lib.h\"\n";
printf "#include \"mem.h\"\n";

View file

@ -121,6 +121,7 @@ enum
Amount, /* to be mounted or mounted upon */
Acreate, /* is to be created */
Aremove, /* will be removed by caller */
Aunmount, /* unmount arg[0] */
COPEN = 0x0001, /* for i/o */
CMSG = 0x0002, /* the message channel for a mount */
@ -484,7 +485,7 @@ struct Pgrp
{
Ref;
RWlock ns; /* Namespace n read/one write lock */
int noattach;
u64int notallowed[4]; /* Room for 256 devices */
Mhead *mnthash[MNTHASH];
};

View file

@ -413,6 +413,9 @@ uint nhgetl(void*);
ushort nhgets(void*);
ulong µs(void);
long lcycles(void);
void devmask(Pgrp*,int,char*);
int devallowed(Pgrp*, int);
int canmount(Pgrp*);
#pragma varargck argpos iprint 1
#pragma varargck argpos panic 1

View file

@ -1048,7 +1048,7 @@ bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, int flag, char*
nexterror();
}
if(up->pgrp->noattach)
if(!canmount(up->pgrp))
error(Enoattach);
ac = nil;
@ -1160,14 +1160,8 @@ sysunmount(va_list list)
nexterror();
}
if(name != nil) {
/*
* This has to be namec(..., Aopen, ...) because
* if arg[0] is something like /srv/cs or /fd/0,
* opening it is the only way to get at the real
* Chan underneath.
*/
validaddr((uintptr)name, 1, 0);
cmounted = namec(name, Aopen, OREAD, 0);
cmounted = namec(name, Aunmount, OREAD, 0);
}
cunmount(cmount, cmounted);
poperror();

View file

@ -34,6 +34,7 @@ sysrfork(va_list list)
Egrp *oeg;
ulong pid, flag;
Mach *wm;
char *devs;
flag = va_arg(list, ulong);
/* Check flags before we commit */
@ -44,6 +45,11 @@ sysrfork(va_list list)
if((flag & (RFENVG|RFCENVG)) == (RFENVG|RFCENVG))
error(Ebadarg);
/*
* Code using RFNOMNT expects to block all but
* the following devices.
*/
devs = "|decp";
if((flag&RFPROC) == 0) {
if(flag & (RFMEM|RFNOWAIT))
error(Ebadarg);
@ -60,12 +66,12 @@ sysrfork(va_list list)
up->pgrp = newpgrp();
if(flag & RFNAMEG)
pgrpcpy(up->pgrp, opg);
/* inherit noattach */
up->pgrp->noattach = opg->noattach;
/* inherit notallowed */
memmove(up->pgrp->notallowed, opg->notallowed, sizeof up->pgrp->notallowed);
closepgrp(opg);
}
if(flag & RFNOMNT)
up->pgrp->noattach = 1;
devmask(up->pgrp, 1, devs);
if(flag & RFREND) {
org = up->rgrp;
up->rgrp = newrgrp();
@ -177,15 +183,15 @@ sysrfork(va_list list)
p->pgrp = newpgrp();
if(flag & RFNAMEG)
pgrpcpy(p->pgrp, up->pgrp);
/* inherit noattach */
p->pgrp->noattach = up->pgrp->noattach;
/* inherit notallowed */
memmove(p->pgrp->notallowed, up->pgrp->notallowed, sizeof p->pgrp->notallowed);
}
else {
p->pgrp = up->pgrp;
incref(p->pgrp);
}
if(flag & RFNOMNT)
p->pgrp->noattach = 1;
devmask(p->pgrp, 1, devs);
if(flag & RFREND)
p->rgrp = newrgrp();