plan9fox/sys/src/libauth/newns.c
cinap_lenrek eb3d055eb3 backout OCEXEC changes when potentially opening /srv files
Opening a /srv file sets the close-on-exec flag on the
shared channel breaking the exportfs openmount() hack.

The devsrv tries to prevent posting a channel with the
close-on-exec or remove-on-close flags. but nothing
currently prevents this poisoning on open.

Until this gets fixed in eigther exportfs or devsrv,
i'll back out the changes that could have potential side
effects like this.
2020-12-09 01:04:03 +01:00

361 lines
6.7 KiB
C

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <authsrv.h>
#include "authlocal.h"
enum
{
NARG = 15, /* max number of arguments */
MAXARG = 10*ANAMELEN, /* max length of an argument */
};
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 catch(void*, char*);
int newnsdebug;
static int
freecloserpc(AuthRpc *rpc)
{
if(rpc){
close(rpc->afd);
auth_freerpc(rpc);
}
return -1;
}
static int
buildns(int newns, char *user, char *file)
{
Biobuf *b;
char home[4*ANAMELEN];
int afd, cdroot;
char *path;
AuthRpc *rpc;
rpc = nil;
/* try for factotum now because later is impossible */
afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC);
if(afd < 0 && newnsdebug)
fprint(2, "open /mnt/factotum/rpc: %r\n");
if(afd >= 0){
rpc = auth_allocrpc(afd);
if(rpc == nil)
close(afd);
}
/* rpc != nil iff afd >= 0 */
if(file == nil){
if(!newns){
werrstr("no namespace file specified");
return freecloserpc(rpc);
}
file = "/lib/namespace";
}
b = Bopen(file, OREAD|OCEXEC);
if(b == nil){
werrstr("can't open %s: %r", file);
return freecloserpc(rpc);
}
if(newns){
rfork(RFENVG|RFCNAMEG);
setenv("user", user);
snprint(home, sizeof home, "/usr/%s", user);
setenv("home", home);
}
cdroot = nsfile(newns ? "newns" : "addns", b, rpc);
Bterm(b);
freecloserpc(rpc);
/* make sure we managed to cd into the new name space */
if(newns && !cdroot){
path = malloc(1024);
if(path == nil || getwd(path, 1024) == 0 || chdir(path) < 0)
chdir("/");
if(path != nil)
free(path);
}
return 0;
}
static int
nsfile(char *fn, Biobuf *b, AuthRpc *rpc)
{
int argc;
char *cmd, *argv[NARG+1], argbuf[MAXARG*NARG];
int cdroot;
cdroot = 0;
atnotify(catch, 1);
while(cmd = Brdline(b, '\n')){
cmd[Blinelen(b)-1] = '\0';
while(*cmd==' ' || *cmd=='\t')
cmd++;
if(*cmd == '#')
continue;
argc = splitargs(cmd, argv, argbuf, NARG);
if(argc)
cdroot |= nsop(fn, argc, argv, rpc);
}
atnotify(catch, 0);
return cdroot;
}
int
newns(char *user, char *file)
{
return buildns(1, user, file);
}
int
addns(char *user, char *file)
{
return buildns(0, user, file);
}
static int
famount(int fd, AuthRpc *rpc, char *mntpt, int flags, char *aname)
{
int afd;
AuthInfo *ai;
int ret;
afd = fauth(fd, aname);
if(afd >= 0){
ai = fauth_proxy(afd, rpc, amount_getkey, "proto=p9any role=client");
if(ai != nil)
auth_freeAI(ai);
}
ret = mount(fd, afd, mntpt, flags, aname);
if(ret == -1)
close(fd);
if(afd >= 0)
close(afd);
return ret;
}
static int
nsop(char *fn, int argc, char *argv[], AuthRpc *rpc)
{
char *argv0;
ulong flags;
int fd, i;
Biobuf *b;
int cdroot;
cdroot = 0;
flags = 0;
argv0 = nil;
if(newnsdebug){
for (i = 0; i < argc; i++)
fprint(2, "%s ", argv[i]);
fprint(2, "\n");
}
ARGBEGIN{
case 'a':
flags |= MAFTER;
break;
case 'b':
flags |= MBEFORE;
break;
case 'c':
flags |= MCREATE;
break;
case 'C':
flags |= MCACHE;
break;
}ARGEND
if(!(flags & (MAFTER|MBEFORE)))
flags |= MREPL;
if(strcmp(argv0, ".") == 0 && argc == 1){
b = Bopen(argv[0], OREAD|OCEXEC);
if(b == nil)
return 0;
cdroot |= nsfile(fn, b, rpc);
Bterm(b);
}else if(strcmp(argv0, "clear") == 0 && argc == 0){
rfork(RFCNAMEG);
}else if(strcmp(argv0, "bind") == 0 && argc == 2){
if(bind(argv[0], argv[1], flags) == -1 && newnsdebug)
fprint(2, "%s: bind: %s %s: %r\n", fn, argv[0], argv[1]);
}else if(strcmp(argv0, "unmount") == 0){
if(argc == 1)
unmount(nil, argv[0]);
else if(argc == 2)
unmount(argv[0], argv[1]);
}else if(strcmp(argv0, "mount") == 0){
fd = open(argv[0], ORDWR);
if(fd < 0){
if(newnsdebug)
fprint(2, "%s: mount: %s: %r\n", fn, argv[0]);
return 0;
}
if(argc == 2){
if(famount(fd, rpc, argv[1], flags, "") == -1 && newnsdebug)
fprint(2, "%s: mount: %s %s: %r\n", fn, argv[0], argv[1]);
}else if(argc == 3){
if(famount(fd, rpc, argv[1], flags, argv[2]) == -1 && newnsdebug)
fprint(2, "%s: mount: %s %s %s: %r\n", fn, argv[0], argv[1], argv[2]);
} else {
close(fd);
}
}else if(strcmp(argv0, "cd") == 0 && argc == 1){
if(chdir(argv[0]) == 0 && *argv[0] == '/')
cdroot = 1;
}
return cdroot;
}
static char *wocp = "sys: write on closed pipe";
static int
catch(void *x, char *m)
{
USED(x);
return strncmp(m, wocp, strlen(wocp)) == 0;
}
static char*
unquote(char *s)
{
char *r, *w;
int inquote;
inquote = 0;
for(r=w=s; *r; r++){
if(*r != '\''){
*w++ = *r;
continue;
}
if(inquote){
if(*(r+1) == '\''){
*w++ = '\'';
r++;
}else
inquote = 0;
}else
inquote = 1;
}
*w = 0;
return s;
}
static int
splitargs(char *p, char *argv[], char *argbuf, int nargv)
{
char *q;
int i, n;
n = gettokens(p, argv, nargv, " \t\r");
if(n == nargv)
return 0;
for(i = 0; i < n; i++){
q = argv[i];
argv[i] = argbuf;
argbuf = expandarg(q, argbuf);
if(argbuf == nil)
return 0;
unquote(argv[i]);
}
return n;
}
static char*
nextdollar(char *arg)
{
char *p;
int inquote;
inquote = 0;
for(p=arg; *p; p++){
if(*p == '\'')
inquote = !inquote;
if(*p == '$' && !inquote)
return p;
}
return nil;
}
/*
* copy the arg into the buffer,
* expanding any environment variables.
* environment variables are assumed to be
* names (ie. < ANAMELEN long)
* the entire argument is expanded to be at
* most MAXARG long and null terminated
* the address of the byte after the terminating null is returned
* any problems cause a 0 return;
*/
static char *
expandarg(char *arg, char *buf)
{
char env[3+ANAMELEN], *p, *x;
int fd, n, len;
n = 0;
while(p = nextdollar(arg)){
len = p - arg;
if(n + len + ANAMELEN >= MAXARG-1)
return 0;
memmove(&buf[n], arg, len);
n += len;
p++;
arg = strpbrk(p, "/.!'$");
if(arg == nil)
arg = p+strlen(p);
len = arg - p;
if(len == 0 || len >= ANAMELEN)
continue;
strcpy(env, "#e/");
strncpy(env+3, p, len);
env[3+len] = '\0';
fd = open(env, OREAD|OCEXEC);
if(fd >= 0){
len = read(fd, &buf[n], ANAMELEN - 1);
/* some singleton environment variables have trailing NULs */
/* lists separate entries with NULs; we arbitrarily take the first element */
if(len > 0){
x = memchr(&buf[n], 0, len);
if(x != nil)
len = x - &buf[n];
n += len;
}
close(fd);
}
}
len = strlen(arg);
if(n + len >= MAXARG - 1)
return 0;
strcpy(&buf[n], arg);
return &buf[n+len+1];
}
static int
setenv(char *name, char *val)
{
int f;
char ename[ANAMELEN+6];
long s;
sprint(ename, "#e/%s", name);
f = create(ename, OWRITE|OCEXEC, 0664);
if(f < 0)
return -1;
s = strlen(val);
if(write(f, val, s) != s){
close(f);
return -1;
}
close(f);
return 0;
}