From e9bb7876e1bd7f9aaef4e1cd0c64c97f411ffde5 Mon Sep 17 00:00:00 2001 From: Jacob Moody Date: Sat, 28 May 2022 02:23:29 +0000 Subject: [PATCH] kernel: add chdev command to devcons --- sys/man/3/cons | 52 +++++++++++++++++++++++++++++++++++- sys/src/9/port/chan.c | 37 +++++++++++++++----------- sys/src/9/port/dev.c | 57 ++++++++++++++++++++++++++++++++++++++++ sys/src/9/port/devcons.c | 56 +++++++++++++++++++++++++++++++++++++-- sys/src/9/port/devshr.c | 6 ++--- sys/src/9/port/mkdevc | 3 +++ sys/src/9/port/portdat.h | 3 ++- sys/src/9/port/portfns.h | 3 +++ sys/src/9/port/sysfile.c | 10 ++----- sys/src/9/port/sysproc.c | 18 ++++++++----- 10 files changed, 209 insertions(+), 36 deletions(-) diff --git a/sys/man/3/cons b/sys/man/3/cons index 96d6d6b62..c92e393a3 100644 --- a/sys/man/3/cons +++ b/sys/man/3/cons @@ -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 diff --git a/sys/src/9/port/chan.c b/sys/src/9/port/chan.c index e69e5d7b6..04d072ea4 100644 --- a/sys/src/9/port/chan.c +++ b/sys/src/9/port/chan.c @@ -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; diff --git a/sys/src/9/port/dev.c b/sys/src/9/port/dev.c index e875c50e4..185d7ffd0 100644 --- a/sys/src/9/port/dev.c +++ b/sys/src/9/port/dev.c @@ -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<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<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) { diff --git a/sys/src/9/port/devcons.c b/sys/src/9/port/devcons.c index eb60d411e..756a47936 100644 --- a/sys/src/9/port/devcons.c +++ b/sys/src/9/port/devcons.c @@ -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<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); diff --git a/sys/src/9/port/devshr.c b/sys/src/9/port/devshr.c index 225ba6c93..6f2431951 100644 --- a/sys/src/9/port/devshr.c +++ b/sys/src/9/port/devshr.c @@ -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) diff --git a/sys/src/9/port/mkdevc b/sys/src/9/port/mkdevc index 8e87636d8..601d1adba 100755 --- a/sys/src/9/port/mkdevc +++ b/sys/src/9/port/mkdevc @@ -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"; diff --git a/sys/src/9/port/portdat.h b/sys/src/9/port/portdat.h index 421f673e6..e9bb02f1e 100644 --- a/sys/src/9/port/portdat.h +++ b/sys/src/9/port/portdat.h @@ -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]; }; diff --git a/sys/src/9/port/portfns.h b/sys/src/9/port/portfns.h index d2345c508..13666ef30 100644 --- a/sys/src/9/port/portfns.h +++ b/sys/src/9/port/portfns.h @@ -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 diff --git a/sys/src/9/port/sysfile.c b/sys/src/9/port/sysfile.c index b7b0c51aa..f98fbf841 100644 --- a/sys/src/9/port/sysfile.c +++ b/sys/src/9/port/sysfile.c @@ -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(); diff --git a/sys/src/9/port/sysproc.c b/sys/src/9/port/sysproc.c index b3b7a0439..bb25b2b4b 100644 --- a/sys/src/9/port/sysproc.c +++ b/sys/src/9/port/sysproc.c @@ -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();