From: Jacob Moody <moody@mail.posixcafe.org> To: 9front@9front.org Subject: Re: [9front] [PATCH] Unmount to remove sharp devices. Date: Wed, 11 May 2022 08:47:34 -0600 [thread overview] Message-ID: <51d86b8f-ac14-3460-0fea-979c93c45877@posixcafe.org> (raw) In-Reply-To: <0eb711a7-6d11-bc9c-f2a0-31bbdf83cbc6@posixcafe.org> Hello, Another minor iteration. This tweaks the rc-httpd sandboxing and documents it better in the man page. thanks, moody 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 / # 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 +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 kernel, in the format +file contains, one per line, a listing of available kernel drivers, in the format .IP .EX #c cons +.EE +.PP +A process can forfeit access to a driver, for itself and it's future children, 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 reads, +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; + bind("/root/rc", "/rc", MREPL); bind(root, "/", MAFTER); buf[0] = '/'; --- 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 = 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. + */ + 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; + } else + devunmount = 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++] = *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 && !driversallowed(up->pgrp, r)) + error(Enoattach); + c = devtab[t]->attach(up->genbuf+n); break; --- a/sys/src/9/port/devcons.c +++ b/sys/src/9/port/devcons.c @@ -39,6 +39,18 @@ CMrdb, "rdb", 0, }; +enum +{ + CMblock, + CMpermit, +}; + +Cmdtab drivermsg[] = +{ + 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 = 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(); @@ -676,6 +694,26 @@ error(Eperm); break; + case Qdrivers: + cb = parsecmd(a, n); + + if(waserror()) { + free(cb); + nexterror(); + } + ct = 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 = sizeof mask[0] * 8; + for(i=0; i < argc; i++) + for(p = argv[i]; *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 +driversallowed(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 driversallowed(pgrp, 'M'); } 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; case Qcroot: - if(up->pgrp->noattach) + if(!canmount(up->pgrp)) error(Enoattach); if((perm & DMDIR) == 0 || mode != OREAD) error(Eperm); @@ -498,7 +498,7 @@ 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 @@ Mhead *h; Mount *m; - if(up->pgrp->noattach) + if(!canmount(up->pgrp)) error(Enoattach); sch = tosch(c); if(sch->level != Qcmpt) --- a/sys/src/9/port/mkdevc +++ b/sys/src/9/port/mkdevc @@ -78,6 +78,9 @@ 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"; --- 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] */ COPEN = 0x0001, /* for i/o */ CMSG = 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]; }; --- a/sys/src/9/port/portfns.h +++ b/sys/src/9/port/portfns.h @@ -413,6 +413,9 @@ ushort nhgets(void*); ulong µs(void); long lcycles(void); +void driverscmd(Pgrp*,int,int,char**); +int driversallowed(Pgrp*, int); +int canmount(Pgrp*); #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(); } - if(up->pgrp->noattach) + if(!canmount(up->pgrp)) error(Enoattach); ac = nil; @@ -1160,14 +1160,8 @@ 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(); --- 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; flag = va_arg(list, ulong); /* Check flags before we commit */ @@ -44,6 +45,11 @@ 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 @@ 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; + driverscmd(up->pgrp, 1, 1, &devs); if(flag & RFREND) { org = up->rgrp; up->rgrp = newrgrp(); @@ -177,8 +183,8 @@ 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; @@ -185,7 +191,7 @@ incref(p->pgrp); } if(flag & RFNOMNT) - p->pgrp->noattach = 1; + driverscmd(p->pgrp, 1, 1, &devs); if(flag & RFREND) p->rgrp = 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*); int newnsdebug; @@ -35,7 +35,7 @@ { Biobuf *b; char home[4*ANAMELEN]; - int afd, cdroot; + int afd, cdroot, dfd; char *path; AuthRpc *rpc; @@ -51,6 +51,10 @@ } /* rpc != nil iff afd >= 0 */ + dfd = open("#c/drivers", OWRITE|OCEXEC); + if(dfd < 0 && newnsdebug) + fprint(2, "open #c/drivers: %r\n"); + if(file == nil){ if(!newns){ werrstr("no namespace file specified"); @@ -70,7 +74,8 @@ setenv("home", home); } - cdroot = nsfile(newns ? "newns" : "addns", b, rpc); + cdroot = nsfile(newns ? "newns" : "addns", b, rpc, dfd); + close(dfd); Bterm(b); freecloserpc(rpc); @@ -87,7 +92,7 @@ } 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 = splitargs(cmd, argv, argbuf, NARG); if(argc) - cdroot |= nsop(fn, argc, argv, rpc); + cdroot |= nsop(fn, argc, argv, rpc, dfd); } atnotify(catch, 0); return cdroot; @@ -143,7 +148,7 @@ } 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 = Bopen(argv[0], OREAD|OCEXEC); if(b == nil) return 0; - cdroot |= nsfile(fn, b, rpc); + cdroot |= nsfile(fn, b, rpc, dfd); Bterm(b); }else if(strcmp(argv0, "clear") == 0 && argc == 0){ rfork(RFCNAMEG); @@ -212,6 +217,14 @@ }else if(strcmp(argv0, "cd") == 0 && argc == 1){ if(chdir(argv[0]) == 0 && *argv[0] == '/') cdroot = 1; + }else if(argc >= 1 && (strcmp(argv0, "permit") == 0 || strcmp(argv0, "block") == 0)){ + //We should not silently fail if we can not honor a permit/block + //due to the parent namespace missing #c/drivers. + if(dfd <= 0) + sysfatal("%s requested, but could not open #c/drivers", argv0); + for(i=0; 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; }
next prev parent reply other threads:[~2022-05-11 14:50 UTC|newest] Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top 2022-05-04 14:09 Jacob Moody 2022-05-04 15:05 ` ori 2022-05-04 15:31 ` ori 2022-05-04 16:15 ` Stanley Lieber 2022-05-04 17:41 ` Lyndon Nerenberg (VE7TFX/VE6BBM) 2022-05-04 17:55 ` Jacob Moody 2022-05-05 1:59 ` Alex Musolino 2022-05-05 16:07 ` Jacob Moody 2022-05-08 2:55 ` Jacob Moody 2022-05-11 14:47 ` Jacob Moody [this message] 2022-05-11 16:11 ` Stanley Lieber 2022-05-12 4:29 ` Jacob Moody 2022-05-12 3:18 ` ori 2022-05-12 5:10 ` Jacob Moody 2022-05-12 14:21 ` ori 2022-05-23 5:42 ` Jacob Moody 2022-05-23 17:06 ` cinap_lenrek 2022-05-23 17:37 ` Jacob Moody 2022-05-25 19:03 ` Jacob Moody 2022-05-25 20:53 ` hiro 2022-05-25 21:20 ` Jacob Moody 2022-05-26 5:55 ` Jacob Moody 2022-05-26 23:36 ` unobe 2022-05-27 0:33 ` Jacob Moody 2022-05-27 3:25 ` unobe 2022-05-26 3:13 ` ori 2022-05-27 1:11 ` Lyndon Nerenberg (VE7TFX/VE6BBM) 2022-05-27 2:25 ` Frank D. Engel, Jr.
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=51d86b8f-ac14-3460-0fea-979c93c45877@posixcafe.org \ --to=moody@mail.posixcafe.org \ --cc=9front@9front.org \ --subject='Re: [9front] [PATCH] Unmount to remove sharp devices.' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).