From 056ad652a41fde51755aedb8119b37fe5946b12c Mon Sep 17 00:00:00 2001 From: Jacob Moody Date: Tue, 7 Jun 2022 05:38:08 +0000 Subject: [PATCH] auth/box: build restricted namespaces using components from the parent. --- sys/man/8/auth | 37 ++++++++ sys/src/cmd/auth/box.c | 192 ++++++++++++++++++++++++++++++++++++++++ sys/src/cmd/auth/mkfile | 1 + 3 files changed, 230 insertions(+) create mode 100644 sys/src/cmd/auth/box.c diff --git a/sys/man/8/auth b/sys/man/8/auth index e6c3c343d..fcfe8fcc9 100644 --- a/sys/man/8/auth +++ b/sys/man/8/auth @@ -60,6 +60,20 @@ changeuser, convkeys, printnetkey, status, enable, disable, authsrv, guard.srv, .I arg \&... .PP +.B auth/box +[ +.B -d +] [ +.B -rc +.I file +] [ +.B -e +.I devs +] +.I command +.I arg +\&... +.PP .B auth/as [ .B -d @@ -264,6 +278,29 @@ If there are no arguments, it It's an easy way to run a command as .IR none . .PP +.I Box +sets up a restricted namespace and +.IR exec's +its arguments as the user +.IR none . +Components of the current namespace are bound +into the child namespace with the +.B -r +and +.B -c +flags, using either +.I MREPL +or +.I MCREATE +respectively. The only components +in the child namespace will be those +defined this way. +By default all further kernel driver +access is blocked. The +.B -e +flag specifies a string of driver +characters to keep in the child namespace. +.PP .I As executes .I command diff --git a/sys/src/cmd/auth/box.c b/sys/src/cmd/auth/box.c new file mode 100644 index 000000000..779cd0f9d --- /dev/null +++ b/sys/src/cmd/auth/box.c @@ -0,0 +1,192 @@ +#include +#include +#include + +static int debug; + +static void +binderr(char *new, char *old, int flag) +{ + char dash[4] = { '-' }; + + if(debug){ + if(flag & MCREATE){ + dash[2] = 'c'; + flag &= ~MCREATE; + } + switch(flag){ + case MREPL: + dash[0] = ' '; + if(dash[2] == 'c') + dash[1] = '-'; + else + dash[1] = ' '; + break; + case MBEFORE: + dash[1] = 'b'; + break; + case MAFTER: + dash[1] = 'a'; + break; + } + print("bind %s %s %s\n", dash, new, old); + } + if(bind(new, old, flag) < 0) + sysfatal("bind: %r"); +} + +static void +resolvenames(char **names, int nname) +{ + int i; + char buf[8192]; + int fd; + + fd = open(".", OREAD|OCEXEC); + if(fd < 0) + sysfatal("could not open .: %r"); + fd2path(fd, buf, sizeof buf); + for(i = 0; i < nname; i++){ + if(names[i] == nil) + continue; + cleanname(names[i]); + switch(names[i][0]){ + case '#': + case '/': + break; + case '.': + if(names[i][1] == '/') + break; + default: + names[i] = cleanname(smprint("%s/%s", buf, names[i])); + } + } + close(fd); +} + +static void +sandbox(char **names, int *flags, int nname) +{ + char *parts[32]; + char rootskel[128]; + char src[8192], targ[8192], dir[8192], skel[8192]; + char name[8192]; + char *newroot; + Dir *d; + int i, j, n; + + snprint(rootskel, sizeof rootskel, "#zd/newroot.%d", getpid()); + binderr(rootskel, "/", MBEFORE); + + newroot = rootskel + strlen("#zd"); + + for(j = 0; j < nname; j++){ + if(names[j] == nil) + continue; + utfecpy(name, &name[sizeof name-1], names[j]); + n = gettokens(name, parts, nelem(parts), "/"); + utfecpy(targ, &targ[sizeof targ-1], newroot); + memset(src, 0, sizeof src); + for(i = 0; i < n; i++){ + utfecpy(dir, &dir[sizeof dir-1], targ); + snprint(targ, sizeof targ, "%s/%s", targ, parts[i]); + snprint(src, sizeof src, "%s/%s", src, parts[i]); + d = dirstat(targ); + if(d != nil){ + free(d); + continue; + } + d = dirstat(src); + if(d == nil) + continue; + if(d->mode & DMDIR) + snprint(skel, sizeof skel, "#zd/%s", parts[i]); + else + snprint(skel, sizeof skel, "#zf/%s", parts[i]); + free(d); + binderr(skel, dir, MBEFORE); + } + binderr(names[j], targ, flags[j]); + } + binderr(newroot, "/", MREPL); +} + +static void +run(char **a) +{ + exec(a[0], a); + + if(a[0][0] != '/' && a[0][0] != '#' && + (a[0][0] != '.' || (a[0][1] != '/' && + (a[0][1] != '.' || a[0][2] != '/')))) + exec(smprint("/bin/%s", a[0]), a); + + sysfatal("exec: %s: %r", a[0]); +} + +void +usage(void) +{ + fprint(2, "usage %s: [ -d ] [ -r file ] [ -c dir ] [ -e devs ] cmd args...\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + char devs[1024]; + int dfd; + char *parts[256]; + int mflags[256]; + int nparts; + + nparts = 0; + memset(devs, 0, sizeof devs); + ARGBEGIN{ + case 'd': + debug = 1; + break; + case 'r': + parts[nparts] = EARGF(usage()); + mflags[nparts++] = MREPL; + break; + case 'c': + parts[nparts] = EARGF(usage()); + mflags[nparts++] = MCREATE|MREPL; + break; + case 'e': + snprint(devs, sizeof devs, "%s%s", devs, EARGF(usage())); + break; + default: + usage(); + break; + }ARGEND + if(argc == 0) + usage(); + + rfork(RFNAMEG|RFENVG); + dfd = open("/dev/drivers", OWRITE|OCEXEC); + if(dfd < 0) + sysfatal("could not /dev/drivers: %r"); + + resolvenames(parts, nparts); + + if(procsetuser("none") < 0) + sysfatal("cant become none: %r"); + putenv("user", "none"); + + sandbox(parts, mflags, nparts); + + if(debug) + print("chdev %s\n", devs); + + if(devs[0] != '\0'){ + if(fprint(dfd, "chdev & %s", devs) <= 0) + sysfatal("could not write chdev: %r"); + } else { + if(fprint(dfd, "chdev ~") <= 0) + sysfatal("could not write chdev: %r"); + } + close(dfd); + run(argv); +} diff --git a/sys/src/cmd/auth/mkfile b/sys/src/cmd/auth/mkfile index f8f6ec829..1dbc46941 100644 --- a/sys/src/cmd/auth/mkfile +++ b/sys/src/cmd/auth/mkfile @@ -9,6 +9,7 @@ TARG=\ asn1dump\ asn12rsa\ authsrv\ + box\ changeuser\ convkeys\ cron\