From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 24231 invoked from network); 11 May 2022 16:14:13 -0000 Received: from 9front.inri.net (168.235.81.73) by inbox.vuxu.org with ESMTPUTF8; 11 May 2022 16:14:13 -0000 Received: from gaff.inri.net ([168.235.71.243]) by 9front; Wed May 11 12:11:29 -0400 2022 Received: from smtpclient.apple ([104.59.85.219]) by gaff; Wed May 11 12:11:29 -0400 2022 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: Stanley Lieber Mime-Version: 1.0 (1.0) Message-Id: References: <51d86b8f-ac14-3460-0fea-979c93c45877@posixcafe.org> In-Reply-To: <51d86b8f-ac14-3460-0fea-979c93c45877@posixcafe.org> To: 9front@9front.org Date: Wed, 11 May 2022 12:11:28 -0400 X-Mailer: iPhone Mail (19E258) List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: extensible immutable package base-based solution Subject: Re: [9front] [PATCH] Unmount to remove sharp devices. Reply-To: 9front@9front.org Precedence: bulk in some cases, would it make more sense to invoke auth/newns at the listener= level, rather than every time rc-httpd is called? al > On May 11, 2022, at 10:49 AM, Jacob Moody wrote= : >=20 > =EF=BB=BFHello, >=20 > Another minor iteration. This tweaks the rc-httpd sandboxing > and documents it better in the man page. >=20 > thanks, > moody >=20 > diff 9126ee3eea90d639f4e877c01400248581d10f65 uncommitted > --- a/lib/namespace > +++ b/lib/namespace > @@ -1,5 +1,6 @@ > # root > mount -aC #s/boot /root $rootspec > +bind $rootdir'/rc' /rc > bind -a $rootdir / >=20 > # kernel devices > --- a/lib/namespace.ftp > +++ b/lib/namespace.ftp > @@ -8,5 +8,6 @@ > # bind a personal incoming directory below incoming > bind -c /usr/none/incoming /usr/web/incoming/none >=20 > +permit |MedIa/ > # this cuts off everything not mounted below /usr/web > bind /usr/web / > --- /tmp/diff100001081045 > +++ b/lib/namespace.rc-httpd > @@ -1,0 +1,19 @@ > +mount -aC #s/boot /root $rootspec > + > +# kernel devices > +bind #c /dev > +bind #d /fd > +bind -c #e /env > +bind #p /proc > + > +bind /root/$cputype/bin /bin > +bind /root/rc /rc > +bind -a /rc/bin /bin > + > +permit Mcde|ps/ > + > +. /root/cfg/$sysname/namespace.rc-httpd > + > +unmount /root > +block Mcs > +unmount #c /dev > --- a/rc/bin/service/!tcp80 > +++ b/rc/bin/service/!tcp80 > @@ -1,2 +1,2 @@ > #!/bin/rc > -exec /rc/bin/rc-httpd/rc-httpd >>[2]/sys/log/www > +exec auth/newns -n /lib/namespace.rc-httpd /rc/bin/rc-httpd/rc-httpd >>[2= ]/sys/log/www > --- a/sys/man/3/cons > +++ b/sys/man/3/cons > @@ -90,10 +90,30 @@ > .PP > The > .B drivers > -file contains, one per line, a listing of the drivers configured in the k= ernel, in the format > +file contains, one per line, a listing of available kernel drivers, in th= e format > .IP > .EX > #c cons > +.EE > +.PP > +A process can forfeit access to a driver, for itself and it's future chil= dren, through a write to > +.B drivers. > +A message is one of: > +.IP "block \f2drivers\fP" > +block access to the listed > +.I drivers. > +.IP "permit \f2drivers\fP" > +permit access to just the provided > +.I drivers. > +.PP > +Drivers are identified by their short hand, the first column returned on r= eads, > +without the leading sharp. The following blocks access to > +.IR env (3) > +and > +.IR sd (3): > +.IP > +.EX > +block se > .EE > .PP > The > --- a/sys/man/6/namespace > +++ b/sys/man/6/namespace > @@ -59,6 +59,15 @@ > .I new > is missing. > .TP > +.BI block \ drivers > +Removes access to the listed kernel > +.I drivers. > +Devices are identified by their sharp name. > +.TP > +.BI permit \ drivers > +Permit access to only the listed kernel > +.I drivers. > +.TP > .BR clear > Clear the name space with > .BR rfork(RFCNAMEG) . > @@ -80,4 +89,5 @@ > .SH "SEE ALSO" > .IR bind (1), > .IR namespace (4), > -.IR init (8) > +.IR init (8), > +.IR cons (3) > --- a/sys/man/8/rc-httpd > +++ b/sys/man/8/rc-httpd > @@ -85,6 +85,36 @@ > .I REMOTE_USER > variable provides a user identification string supplied by the > client as part of user authentication. > +.SH STARTUP > +.I Rc-httpd > +is run from a file in the directory scanned by > +.IR listen (8), > +or called as an argument to aux/listen1. > +The program's standard error may be captured to a log file: > +.RS > +.EX > +exec /rc/bin/rc-httpd/rc-httpd >>[2]/sys/log/www > +.EE > +.RE > +.PP > +It is recommended to provide a strict namespace for > +.I rc-httpd. > +.B /lib/namespace.rc-httpd > +provides an example of such a namespace, and serves as > +a default. > +.PP > +When using the default, > +.B /cfg/$sysname/namespace.rc-httpd > +is sourced to allow for binding of files to a place > +.B select-handler > +may expect to find them. > +.PP > +.I Rc-httpd > +can be run using > +.IR auth/newns (8) > +to switch to the restrictive namespace before starting. > +.B /rc/bin/service/!tcp80 > +provides a sample invocation. > .SH EXAMPLES > The following examples demonstrate possible ways to configure > .BR select-handler. > @@ -153,15 +183,19 @@ > } > .EE > .RE > -.SH STARTUP > -.I Rc-httpd > -is run from a file in the directory scanned by > -.IR listen (8), > -or called as an argument to aux/listen1. > -The program's standard error may be captured to a log file: > +.PP > +An example > +.B /cfg/$sysname/namespace.rc-httpd: > .RS > .EX > -exec /rc/bin/rc-httpd/rc-httpd >>[2]/sys/log/www > +# #s/boot is present on /root. > +# /root is unmounted after we finish. > + > +# srv is unused, we can use it as our base FS_ROOT. > +bind /root/usr/moody/www /srv > + > +# use a specific select-handler. > +bind -b /root/usr/moody/lib/rc-httpd /rc/bin/rc-httpd > .EE > .RE > .SH FILES > @@ -190,6 +224,10 @@ > .B /rc/bin/service/tcp80 > .TP > .B /sys/log/www > +.TP > +.B /lib/namespace.rc-httpd > +.TP > +.B /cfg/$sysname/namespace.rc-httpd > .SH SOURCE > .B /rc/bin/rc-httpd > .SH "SEE ALSO" > --- a/sys/src/9/boot/boot.c > +++ b/sys/src/9/boot/boot.c > @@ -19,6 +19,7 @@ > if(await(buf, sizeof(buf)) < 0) > goto Err; >=20 > + bind("/root/rc", "/rc", MREPL); > bind(root, "/", MAFTER); >=20 > buf[0] =3D '/'; > --- a/sys/src/9/port/chan.c > +++ b/sys/src/9/port/chan.c > @@ -1272,7 +1272,7 @@ > 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; > @@ -1292,6 +1292,24 @@ > name =3D aname; >=20 > /* > + * 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. > + */ > + if(amode =3D=3D Aunmount){ > + /* > + * Doing any walks down the device could leak information > + * about the existence of files. > + */ > + if(name[0] =3D=3D '#' && utflen(name) =3D=3D 2) > + devunmount =3D 1; > + amode =3D Aopen; > + } else > + devunmount =3D 0; > + > + /* > * Find the starting off point (the current slash, the root of > * a device tree, or the current dot) as well as the name to > * evaluate starting there. > @@ -1313,24 +1331,13 @@ > up->genbuf[n++] =3D *name++; > } > up->genbuf[n] =3D '\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 =3D chartorune(&r, up->genbuf+1)+1; > - if(up->pgrp->noattach && utfrune("|decp", r)=3D=3Dnil) > - error(Enoattach); > t =3D devno(r, 1); > if(t =3D=3D -1) > error(Ebadsharp); > + if(!devunmount && !driversallowed(up->pgrp, r)) > + error(Enoattach); > + > c =3D devtab[t]->attach(up->genbuf+n); > break; >=20 > --- a/sys/src/9/port/devcons.c > +++ b/sys/src/9/port/devcons.c > @@ -39,6 +39,18 @@ > CMrdb, "rdb", 0, > }; >=20 > +enum > +{ > + CMblock, > + CMpermit, > +}; > + > +Cmdtab drivermsg[] =3D > +{ > + CMblock, "block", 0, > + CMpermit, "permit", 0, > +}; > + > void > printinit(void) > { > @@ -332,7 +344,7 @@ > "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 +595,15 @@ > case Qdrivers: > b =3D smalloc(READSTR); > k =3D 0; > - for(i =3D 0; devtab[i] !=3D nil; i++) > + =20 > + rlock(&up->pgrp->ns); > + for(i =3D 0; devtab[i] !=3D nil; i++){ > + if(up->pgrp->notallowed[i/(sizeof(u64int)*8)] & 1< + continue; > k +=3D snprint(b+k, READSTR-k, "#%C %s\n", > devtab[i]->dc, devtab[i]->name); > + } > + runlock(&up->pgrp->ns); > if(waserror()){ > free(b); > nexterror(); > @@ -676,6 +694,26 @@ > error(Eperm); > break; >=20 > + case Qdrivers: > + cb =3D parsecmd(a, n); > + > + if(waserror()) { > + free(cb); > + nexterror(); > + } > + ct =3D lookupcmd(cb, drivermsg, nelem(drivermsg)); > + switch(ct->index) { > + case CMblock: > + driverscmd(up->pgrp, 0, cb->nf-1, cb->f+1); > + break; > + case CMpermit: > + driverscmd(up->pgrp, 1, cb->nf-1, cb->f+1); > + break; > + } > + poperror(); > + free(cb); > + break; > + > case Qreboot: > if(!iseve()) > error(Eperm); > @@ -935,6 +973,64 @@ > break; > } > return n+1; > +} > + > +void > +driverscmd(Pgrp *pgrp, int invert, int argc, char *argv[]) > +{ > + 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 =3D sizeof mask[0] * 8; > + for(i=3D0; i < argc; i++) > + for(p =3D argv[i]; *p !=3D '\0';){ > + p +=3D chartorune(&r, p); > + t =3D devno(r, 1); > + if(t =3D=3D -1) > + continue; > + if(invert) > + mask[t/w] &=3D ~(1< + else > + mask[t/w] |=3D 1< + } > + > + wlock(&pgrp->ns); > + for(i=3D0; i < nelem(pgrp->notallowed); i++) > + pgrp->notallowed[i] |=3D mask[i]; > + wunlock(&pgrp->ns); > +} > + > +int > +driversallowed(Pgrp *pgrp, int r) > +{ > + int t, w, b; > + > + t =3D devno(r, 1); > + if(t =3D=3D -1) > + return 0; > + > + w =3D sizeof(u64int) * 8; > + rlock(&pgrp->ns); > + b =3D !(pgrp->notallowed[t/w] & 1< + 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 driversallowed(pgrp, 'M'); > } >=20 > void > --- a/sys/src/9/port/devroot.c > +++ b/sys/src/9/port/devroot.c > @@ -105,6 +105,7 @@ > addrootdir("net"); > addrootdir("net.alt"); > addrootdir("proc"); > + addrootdir("rc"); > addrootdir("root"); > addrootdir("srv"); > addrootdir("shr"); > --- a/sys/src/9/port/devshr.c > +++ b/sys/src/9/port/devshr.c > @@ -464,7 +464,7 @@ > cclose(c); > return nc; =20 > case Qcroot: > - if(up->pgrp->noattach) > + if(!canmount(up->pgrp)) > error(Enoattach); > if((perm & DMDIR) =3D=3D 0 || mode !=3D OREAD) > error(Eperm); > @@ -498,7 +498,7 @@ > sch->shr =3D shr; > break; > case Qcshr: > - if(up->pgrp->noattach) > + if(!canmount(up->pgrp)) > error(Enoattach); > if((perm & DMDIR) !=3D 0 || mode !=3D OWRITE) > error(Eperm); > @@ -731,7 +731,7 @@ > Mhead *h; > Mount *m; >=20 > - if(up->pgrp->noattach) > + if(!canmount(up->pgrp)) > error(Enoattach); > sch =3D tosch(c); > if(sch->level !=3D Qcmpt) > --- a/sys/src/9/port/mkdevc > +++ b/sys/src/9/port/mkdevc > @@ -78,6 +78,9 @@ > if(ARGC < 2) > exit "usage" >=20 > + if(ndev >=3D 256) > + exit "device count will overflow Pgrp.notallowed" > + > printf "#include \"u.h\"\n"; > printf "#include \"../port/lib.h\"\n"; > printf "#include \"mem.h\"\n"; > --- a/sys/src/9/port/portdat.h > +++ b/sys/src/9/port/portdat.h > @@ -121,6 +121,7 @@ > Amount, /* to be mounted or mounted upon */ > Acreate, /* is to be created */ > Aremove, /* will be removed by caller */ > + Aunmount, /* unmount arg[0] */ >=20 > COPEN =3D 0x0001, /* for i/o */ > CMSG =3D 0x0002, /* the message channel for a mount */ > @@ -484,7 +485,7 @@ > { > Ref; > RWlock ns; /* Namespace n read/one write lock */ > - int noattach; > + u64int notallowed[4]; /* Room for 256 devices */ > Mhead *mnthash[MNTHASH]; > }; >=20 > --- a/sys/src/9/port/portfns.h > +++ b/sys/src/9/port/portfns.h > @@ -413,6 +413,9 @@ > ushort nhgets(void*); > ulong =C2=B5s(void); > long lcycles(void); > +void driverscmd(Pgrp*,int,int,char**); > +int driversallowed(Pgrp*, int); > +int canmount(Pgrp*); >=20 > #pragma varargck argpos iprint 1 > #pragma varargck argpos panic 1 > --- a/sys/src/9/port/sysfile.c > +++ b/sys/src/9/port/sysfile.c > @@ -1048,7 +1048,7 @@ > nexterror(); > } >=20 > - if(up->pgrp->noattach) > + if(!canmount(up->pgrp)) > error(Enoattach); >=20 > ac =3D nil; > @@ -1160,14 +1160,8 @@ > nexterror(); > } > if(name !=3D 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 =3D namec(name, Aopen, OREAD, 0); > + cmounted =3D namec(name, Aunmount, OREAD, 0); > } > cunmount(cmount, cmounted); > poperror(); > --- a/sys/src/9/port/sysproc.c > +++ b/sys/src/9/port/sysproc.c > @@ -34,6 +34,7 @@ > Egrp *oeg; > ulong pid, flag; > Mach *wm; > + char *devs; >=20 > flag =3D va_arg(list, ulong); > /* Check flags before we commit */ > @@ -44,6 +45,11 @@ > if((flag & (RFENVG|RFCENVG)) =3D=3D (RFENVG|RFCENVG)) > error(Ebadarg); >=20 > + /* > + * Code using RFNOMNT expects to block all but > + * the following devices. > + */ > + devs =3D "|decp"; > if((flag&RFPROC) =3D=3D 0) { > if(flag & (RFMEM|RFNOWAIT)) > error(Ebadarg); > @@ -60,12 +66,12 @@ > up->pgrp =3D newpgrp(); > if(flag & RFNAMEG) > pgrpcpy(up->pgrp, opg); > - /* inherit noattach */ > - up->pgrp->noattach =3D opg->noattach; > + /* inherit notallowed */ > + memmove(up->pgrp->notallowed, opg->notallowed, sizeof up->pgr= p->notallowed); > closepgrp(opg); > } > if(flag & RFNOMNT) > - up->pgrp->noattach =3D 1; > + driverscmd(up->pgrp, 1, 1, &devs); > if(flag & RFREND) { > org =3D up->rgrp; > up->rgrp =3D newrgrp(); > @@ -177,8 +183,8 @@ > p->pgrp =3D newpgrp(); > if(flag & RFNAMEG) > pgrpcpy(p->pgrp, up->pgrp); > - /* inherit noattach */ > - p->pgrp->noattach =3D up->pgrp->noattach; > + /* inherit notallowed */ > + memmove(p->pgrp->notallowed, up->pgrp->notallowed, sizeof p->pgrp= ->notallowed); > } > else { > p->pgrp =3D up->pgrp; > @@ -185,7 +191,7 @@ > incref(p->pgrp); > } > if(flag & RFNOMNT) > - p->pgrp->noattach =3D 1; > + driverscmd(p->pgrp, 1, 1, &devs); >=20 > if(flag & RFREND) > p->rgrp =3D newrgrp(); > --- a/sys/src/libauth/newns.c > +++ b/sys/src/libauth/newns.c > @@ -14,8 +14,8 @@ > static int setenv(char*, char*); > static char *expandarg(char*, char*); > static int splitargs(char*, char*[], char*, int); > -static int nsfile(char*, Biobuf *, AuthRpc *); > -static int nsop(char*, int, char*[], AuthRpc*); > +static int nsfile(char*, Biobuf *, AuthRpc *, int); > +static int nsop(char*, int, char*[], AuthRpc*, int); > static int catch(void*, char*); >=20 > int newnsdebug; > @@ -35,7 +35,7 @@ > { > Biobuf *b; > char home[4*ANAMELEN]; > - int afd, cdroot; > + int afd, cdroot, dfd; > char *path; > AuthRpc *rpc; >=20 > @@ -51,6 +51,10 @@ > } > /* rpc !=3D nil iff afd >=3D 0 */ >=20 > + dfd =3D open("#c/drivers", OWRITE|OCEXEC); > + if(dfd < 0 && newnsdebug) > + fprint(2, "open #c/drivers: %r\n"); > + > if(file =3D=3D nil){ > if(!newns){ > werrstr("no namespace file specified"); > @@ -70,7 +74,8 @@ > setenv("home", home); > } >=20 > - cdroot =3D nsfile(newns ? "newns" : "addns", b, rpc); > + cdroot =3D nsfile(newns ? "newns" : "addns", b, rpc, dfd); > + close(dfd); > Bterm(b); > freecloserpc(rpc); >=20 > @@ -87,7 +92,7 @@ > } >=20 > static int > -nsfile(char *fn, Biobuf *b, AuthRpc *rpc) > +nsfile(char *fn, Biobuf *b, AuthRpc *rpc, int dfd) > { > int argc; > char *cmd, *argv[NARG+1], argbuf[MAXARG*NARG]; > @@ -103,7 +108,7 @@ > continue; > argc =3D splitargs(cmd, argv, argbuf, NARG); > if(argc) > - cdroot |=3D nsop(fn, argc, argv, rpc); > + cdroot |=3D nsop(fn, argc, argv, rpc, dfd); > } > atnotify(catch, 0); > return cdroot; > @@ -143,7 +148,7 @@ > } >=20 > static int > -nsop(char *fn, int argc, char *argv[], AuthRpc *rpc) > +nsop(char *fn, int argc, char *argv[], AuthRpc *rpc, int dfd) > { > char *argv0; > ulong flags; > @@ -181,7 +186,7 @@ > b =3D Bopen(argv[0], OREAD|OCEXEC); > if(b =3D=3D nil) > return 0; > - cdroot |=3D nsfile(fn, b, rpc); > + cdroot |=3D nsfile(fn, b, rpc, dfd); > Bterm(b); > }else if(strcmp(argv0, "clear") =3D=3D 0 && argc =3D=3D 0){ > rfork(RFCNAMEG); > @@ -212,6 +217,14 @@ > }else if(strcmp(argv0, "cd") =3D=3D 0 && argc =3D=3D 1){ > if(chdir(argv[0]) =3D=3D 0 && *argv[0] =3D=3D '/') > cdroot =3D 1; > + }else if(argc >=3D 1 && (strcmp(argv0, "permit") =3D=3D 0 || strcmp(a= rgv0, "block") =3D=3D 0)){ > + //We should not silently fail if we can not honor a permit/block > + //due to the parent namespace missing #c/drivers. > + if(dfd <=3D 0) > + sysfatal("%s requested, but could not open #c/drivers", argv0= ); > + for(i=3D0; i < argc; i++) > + if(fprint(dfd, "%s %s\n", argv0, argv[i]) < 0 && newnsdebug) > + fprint(2, "%s: %s %s %r\n", fn, argv0, argv[i]); > } > return cdroot; > } >=20