kernel: add chdev command to devcons
This commit is contained in:
parent
3351b67480
commit
e9bb7876e1
10 changed files with 209 additions and 36 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue