auth/box: build restricted namespaces using components from the parent.

This commit is contained in:
Jacob Moody 2022-06-07 05:38:08 +00:00
parent f4840cdba5
commit 056ad652a4
3 changed files with 230 additions and 0 deletions

View file

@ -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

192
sys/src/cmd/auth/box.c Normal file
View file

@ -0,0 +1,192 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
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);
}

View file

@ -9,6 +9,7 @@ TARG=\
asn1dump\
asn12rsa\
authsrv\
box\
changeuser\
convkeys\
cron\