621 lines
11 KiB
C
621 lines
11 KiB
C
/*
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
* in the LICENSE file.
|
|
*/
|
|
/* Portions of this file are Copyright (C) 9front's team.
|
|
* See /doc/license/9front-mit for details about the licensing.
|
|
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
|
|
*/
|
|
#include "dat.h"
|
|
|
|
int askforkeys = 1;
|
|
char *authaddr[8];
|
|
int debug;
|
|
int doprivate = 1;
|
|
int gflag;
|
|
char *owner;
|
|
int kflag;
|
|
char *mtpt = "/mnt";
|
|
Keyring *ring;
|
|
char *service;
|
|
int sflag;
|
|
int uflag;
|
|
|
|
extern Srv fs;
|
|
static void notifyf(void*, char*);
|
|
static void private(void);
|
|
|
|
char Easproto[] = "auth server protocol botch";
|
|
char Ebadarg[] = "invalid argument";
|
|
char Ebadkey[] = "bad key";
|
|
char Enegotiation[] = "negotiation failed, no common protocols or keys";
|
|
char Etoolarge[] = "rpc too large";
|
|
|
|
Proto*
|
|
prototab[] =
|
|
{
|
|
&apop,
|
|
&chap,
|
|
&cram,
|
|
&httpdigest,
|
|
&mschap,
|
|
&mschapv2,
|
|
&mschap2,
|
|
&p9any,
|
|
&p9cr,
|
|
&p9sk1,
|
|
&dp9ik,
|
|
&pass,
|
|
/* &srs, */
|
|
&rsa,
|
|
&vnc,
|
|
&wep,
|
|
&ecdsa,
|
|
&wpapsk,
|
|
nil,
|
|
};
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: %s [-DSdknpu] [-a authaddr] [-m mtpt] [-s service]\n",
|
|
argv0);
|
|
fprint(2, "or %s -g 'params'\n", argv0);
|
|
exits("usage");
|
|
}
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
int i, trysecstore;
|
|
char *s;
|
|
Dir d;
|
|
Proto *p;
|
|
|
|
trysecstore = 1;
|
|
|
|
ARGBEGIN{
|
|
case 'D':
|
|
chatty9p++;
|
|
break;
|
|
case 'S': /* server: read nvram, no prompting for keys */
|
|
askforkeys = 0;
|
|
trysecstore = 0;
|
|
sflag = 1;
|
|
break;
|
|
case 'a':
|
|
for(i=0; i < nelem(authaddr)-2 && authaddr[i] != nil; i++)
|
|
;
|
|
authaddr[i] = EARGF(usage());
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
doprivate = 0;
|
|
break;
|
|
case 'g': /* get: prompt for key for name and domain */
|
|
gflag = 1;
|
|
break;
|
|
case 'k': /* reinitialize nvram */
|
|
kflag = 1;
|
|
break;
|
|
case 'm': /* set default mount point */
|
|
mtpt = EARGF(usage());
|
|
break;
|
|
case 'n':
|
|
trysecstore = 0;
|
|
break;
|
|
case 'p':
|
|
doprivate = 0;
|
|
break;
|
|
case 's': /* set service name */
|
|
service = EARGF(usage());
|
|
break;
|
|
case 'u': /* user: set hostowner */
|
|
uflag = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
if(argc != 0 && !gflag)
|
|
usage();
|
|
if(doprivate)
|
|
private();
|
|
|
|
initcap();
|
|
|
|
quotefmtinstall();
|
|
fmtinstall('A', _attrfmt);
|
|
fmtinstall('N', attrnamefmt);
|
|
fmtinstall('H', encodefmt);
|
|
|
|
ring = emalloc(sizeof(*ring));
|
|
sys_notify(notifyf);
|
|
|
|
if(gflag){
|
|
if(argc != 1)
|
|
usage();
|
|
askuser(argv[0]);
|
|
exits(nil);
|
|
}
|
|
|
|
for(i=0; prototab[i]; i++){
|
|
p = prototab[i];
|
|
if(p->name == nil)
|
|
sysfatal("protocol %d has no name", i);
|
|
if(p->init == nil)
|
|
sysfatal("protocol %s has no init", p->name);
|
|
if(p->write == nil)
|
|
sysfatal("protocol %s has no write", p->name);
|
|
if(p->read == nil)
|
|
sysfatal("protocol %s has no read", p->name);
|
|
if(p->close == nil)
|
|
sysfatal("protocol %s has no close", p->name);
|
|
if(p->keyprompt == nil)
|
|
p->keyprompt = "";
|
|
}
|
|
|
|
if(sflag){
|
|
if(getnvramkey(kflag ? NVwrite : NVwriteonerr) < 0)
|
|
fprint(2, "factotum warning: cannot read nvram: %r\n");
|
|
} else if(uflag)
|
|
promptforhostowner();
|
|
owner = getuser();
|
|
|
|
postmountsrv(&fs, service, mtpt, MBEFORE);
|
|
if(service){
|
|
nulldir(&d);
|
|
d.mode = 0666;
|
|
s = emalloc(10+strlen(service));
|
|
strcpy(s, "/srv/");
|
|
strcat(s, service);
|
|
if(dirwstat(s, &d) < 0)
|
|
fprint(2, "factotum warning: cannot chmod 666 %s: %r\n", s);
|
|
free(s);
|
|
}
|
|
if(trysecstore){
|
|
if(fork() == 0){
|
|
int fd;
|
|
|
|
if((fd = sys_open(smprint("%s/factotum/ctl", mtpt), OWRITE)) < 0)
|
|
sysfatal("can't open factotum: %r");
|
|
dup(fd, 1);
|
|
execl("/bin/auth/secstore", "secstore", "-G", "factotum", nil);
|
|
exits(nil);
|
|
}
|
|
waitpid();
|
|
}
|
|
exits(nil);
|
|
}
|
|
|
|
char *pmsg = "Warning! %s can't protect itself from debugging: %r\n";
|
|
char *smsg = "Warning! %s can't turn off swapping: %r\n";
|
|
|
|
/* don't allow other processes to debug us and steal keys */
|
|
static void
|
|
private(void)
|
|
{
|
|
int fd;
|
|
char buf[64];
|
|
|
|
snprint(buf, sizeof(buf), "#p/%d/ctl", getpid());
|
|
fd = sys_open(buf, OWRITE);
|
|
if(fd < 0){
|
|
fprint(2, pmsg, argv0);
|
|
return;
|
|
}
|
|
if(fprint(fd, "private") < 0)
|
|
fprint(2, pmsg, argv0);
|
|
if(fprint(fd, "noswap") < 0)
|
|
fprint(2, smsg, argv0);
|
|
sys_close(fd);
|
|
}
|
|
|
|
static void
|
|
notifyf(void* _, char *s)
|
|
{
|
|
if(strncmp(s, "interrupt", 9) == 0 || strncmp(s, "alarm", 5) == 0)
|
|
sys_noted(NCONT);
|
|
sys_noted(NDFLT);
|
|
}
|
|
|
|
enum
|
|
{
|
|
Qroot,
|
|
Qfactotum,
|
|
Qrpc,
|
|
Qkeylist,
|
|
Qprotolist,
|
|
Qconfirm,
|
|
Qlog,
|
|
Qctl,
|
|
Qneedkey,
|
|
};
|
|
|
|
Qid
|
|
mkqid(int type, int path)
|
|
{
|
|
Qid q;
|
|
|
|
q.type = type;
|
|
q.path = path;
|
|
q.vers = 0;
|
|
return q;
|
|
}
|
|
|
|
static void
|
|
fsattach(Req *r)
|
|
{
|
|
r->fid->qid = mkqid(QTDIR, Qroot);
|
|
r->ofcall.qid = r->fid->qid;
|
|
respond(r, nil);
|
|
}
|
|
|
|
static struct {
|
|
char *name;
|
|
int qidpath;
|
|
uint32_t perm;
|
|
} dirtab[] = {
|
|
"confirm", Qconfirm, 0600|DMEXCL, /* we know this is slot #0 below */
|
|
"needkey", Qneedkey, 0600|DMEXCL, /* we know this is slot #1 below */
|
|
"ctl", Qctl, 0644,
|
|
"rpc", Qrpc, 0666,
|
|
"proto", Qprotolist, 0444,
|
|
"log", Qlog, 0400|DMEXCL,
|
|
};
|
|
static int inuse[nelem(dirtab)];
|
|
int *confirminuse = &inuse[0];
|
|
int *needkeyinuse = &inuse[1];
|
|
|
|
static void
|
|
fillstat(Dir *dir, char *name, int type, int path, uint32_t perm)
|
|
{
|
|
dir->name = estrdup(name);
|
|
dir->uid = estrdup(owner);
|
|
dir->gid = estrdup(owner);
|
|
dir->mode = perm;
|
|
dir->length = 0;
|
|
dir->qid = mkqid(type, path);
|
|
dir->atime = time(0);
|
|
dir->mtime = time(0);
|
|
dir->muid = estrdup("");
|
|
}
|
|
|
|
static int
|
|
rootdirgen(int n, Dir *dir, void* _)
|
|
{
|
|
if(n > 0)
|
|
return -1;
|
|
fillstat(dir, "factotum", QTDIR, Qfactotum, DMDIR|0555);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fsdirgen(int n, Dir *dir, void* _)
|
|
{
|
|
if(n >= nelem(dirtab))
|
|
return -1;
|
|
fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm);
|
|
return 0;
|
|
}
|
|
|
|
static char*
|
|
fswalk1(Fid *fid, char *name, Qid *qid)
|
|
{
|
|
int i;
|
|
|
|
switch((uint32_t)fid->qid.path){
|
|
default:
|
|
return "cannot happen";
|
|
case Qroot:
|
|
if(strcmp(name, "factotum") == 0){
|
|
*qid = mkqid(QTDIR, Qfactotum);
|
|
fid->qid = *qid;
|
|
return nil;
|
|
}
|
|
if(strcmp(name, "..") == 0){
|
|
*qid = fid->qid;
|
|
return nil;
|
|
}
|
|
return "not found";
|
|
case Qfactotum:
|
|
for(i=0; i<nelem(dirtab); i++)
|
|
if(strcmp(name, dirtab[i].name) == 0){
|
|
*qid = mkqid(0, dirtab[i].qidpath);
|
|
fid->qid = *qid;
|
|
return nil;
|
|
}
|
|
if(strcmp(name, "..") == 0){
|
|
*qid = mkqid(QTDIR, Qroot);
|
|
fid->qid = *qid;
|
|
return nil;
|
|
}
|
|
return "not found";
|
|
}
|
|
}
|
|
|
|
static void
|
|
fsstat(Req *r)
|
|
{
|
|
int i;
|
|
uint32_t path;
|
|
|
|
path = r->fid->qid.path;
|
|
if(path == Qroot){
|
|
fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR);
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
if(path == Qfactotum){
|
|
fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR);
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
for(i=0; i<nelem(dirtab); i++)
|
|
if(dirtab[i].qidpath == path){
|
|
fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm);
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
respond(r, "file not found");
|
|
}
|
|
|
|
static void
|
|
fsopen(Req *r)
|
|
{
|
|
int i, *p, perm;
|
|
static int need[8] = {0, 4, 2, 6, 1, 0xff, 0xff, 0xff};
|
|
int n;
|
|
Fsstate *fss;
|
|
|
|
p = nil;
|
|
for(i=0; i<nelem(dirtab); i++)
|
|
if(dirtab[i].qidpath == r->fid->qid.path)
|
|
break;
|
|
if(i < nelem(dirtab)){
|
|
if(dirtab[i].perm & DMEXCL)
|
|
p = &inuse[i];
|
|
if(strcmp(r->fid->uid, owner) == 0)
|
|
perm = dirtab[i].perm>>6;
|
|
else
|
|
perm = dirtab[i].perm;
|
|
}else
|
|
perm = 5;
|
|
|
|
n = need[r->ifcall.mode&3];
|
|
if((r->ifcall.mode&~(3|NP_OTRUNC)) || ((perm&n) != n)){
|
|
respond(r, "permission denied");
|
|
return;
|
|
}
|
|
if(p){
|
|
if(*p){
|
|
respond(r, "file in use");
|
|
return;
|
|
}
|
|
(*p)++;
|
|
}
|
|
|
|
r->fid->aux = fss = emalloc(sizeof(Fsstate));
|
|
fss->phase = Notstarted;
|
|
fss->sysuser = r->fid->uid;
|
|
fss->attr = nil;
|
|
strcpy(fss->err, "factotum/fs.c no error");
|
|
respond(r, nil);
|
|
}
|
|
|
|
static void
|
|
fsdestroyfid(Fid *fid)
|
|
{
|
|
int i;
|
|
Fsstate *fss;
|
|
|
|
if(fid->omode != -1){
|
|
for(i=0; i<nelem(dirtab); i++)
|
|
if(dirtab[i].qidpath == fid->qid.path)
|
|
if(dirtab[i].perm&DMEXCL)
|
|
inuse[i] = 0;
|
|
}
|
|
|
|
fss = fid->aux;
|
|
if(fss == nil)
|
|
return;
|
|
qlock(fss);
|
|
if(fss->ps)
|
|
(*fss->proto->close)(fss);
|
|
_freeattr(fss->attr);
|
|
qunlock(fss);
|
|
free(fss);
|
|
}
|
|
|
|
static int
|
|
readlist(int off, int (*gen)(int, char*, uint32_t, Fsstate*), Req *r, Fsstate *fss)
|
|
{
|
|
char *a, *ea;
|
|
int n;
|
|
|
|
a = r->ofcall.data;
|
|
ea = a+r->ifcall.count;
|
|
for(;;){
|
|
n = (*gen)(off, a, ea-a, fss);
|
|
if(n == 0){
|
|
r->ofcall.count = a - (char*)r->ofcall.data;
|
|
return off;
|
|
}
|
|
a += n;
|
|
off++;
|
|
}
|
|
}
|
|
|
|
enum { Nearend = 2, }; /* at least room for \n and NUL */
|
|
|
|
/* result in `a', of `n' bytes maximum */
|
|
static int
|
|
keylist(int i, char *a, uint32_t n, Fsstate *fss)
|
|
{
|
|
int wb;
|
|
Keyinfo ki;
|
|
Key *k;
|
|
|
|
k = nil;
|
|
mkkeyinfo(&ki, fss, nil);
|
|
ki.attr = nil;
|
|
ki.noconf = 1;
|
|
ki.skip = i;
|
|
ki.usedisabled = 1;
|
|
if(findkey(&k, &ki, nil) != RpcOk)
|
|
return 0;
|
|
|
|
memset(a + n - Nearend, 0, Nearend);
|
|
wb = snprint(a, n, "key %A %N\n", k->attr, k->privattr);
|
|
closekey(k);
|
|
if (wb >= n - 1 && a[n - 2] != '\n' && a[n - 2] != '\0') {
|
|
/* line won't fit in `a', so just truncate */
|
|
strcpy(a + n - 2, "\n");
|
|
return 0;
|
|
}
|
|
return wb;
|
|
}
|
|
|
|
static int
|
|
protolist(int i, char *a, uint32_t n, Fsstate *fss)
|
|
{
|
|
USED(fss);
|
|
|
|
if(i >= nelem(prototab)-1)
|
|
return 0;
|
|
if(strlen(prototab[i]->name)+1 > n)
|
|
return 0;
|
|
n = strlen(prototab[i]->name)+1;
|
|
memmove(a, prototab[i]->name, n-1);
|
|
a[n-1] = '\n';
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
fsrpcio(Req *r)
|
|
{
|
|
Fsstate *fss;
|
|
Srv *srv;
|
|
|
|
fss = r->fid->aux;
|
|
srv = r->srv;
|
|
srvrelease(srv);
|
|
qlock(fss);
|
|
if(r->ifcall.type == Tread)
|
|
rpcread(r);
|
|
else
|
|
rpcwrite(r);
|
|
qunlock(fss);
|
|
srvacquire(srv);
|
|
}
|
|
|
|
static void
|
|
fsread(Req *r)
|
|
{
|
|
Fsstate *s;
|
|
|
|
s = r->fid->aux;
|
|
switch((uint32_t)r->fid->qid.path){
|
|
default:
|
|
respond(r, "bug in fsread");
|
|
break;
|
|
case Qroot:
|
|
dirread9p(r, rootdirgen, nil);
|
|
respond(r, nil);
|
|
break;
|
|
case Qfactotum:
|
|
dirread9p(r, fsdirgen, nil);
|
|
respond(r, nil);
|
|
break;
|
|
case Qrpc:
|
|
fsrpcio(r);
|
|
break;
|
|
case Qneedkey:
|
|
needkeyread(r);
|
|
break;
|
|
case Qconfirm:
|
|
confirmread(r);
|
|
break;
|
|
case Qlog:
|
|
logread(r);
|
|
break;
|
|
case Qctl:
|
|
s->listoff = readlist(s->listoff, keylist, r, s);
|
|
respond(r, nil);
|
|
break;
|
|
case Qprotolist:
|
|
s->listoff = readlist(s->listoff, protolist, r, s);
|
|
respond(r, nil);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fswrite(Req *r)
|
|
{
|
|
int ret;
|
|
char err[ERRMAX], *s;
|
|
|
|
switch((uint32_t)r->fid->qid.path){
|
|
default:
|
|
respond(r, "bug in fswrite");
|
|
break;
|
|
case Qrpc:
|
|
fsrpcio(r);
|
|
break;
|
|
case Qneedkey:
|
|
case Qconfirm:
|
|
case Qctl:
|
|
s = emalloc(r->ifcall.count+1);
|
|
memmove(s, r->ifcall.data, r->ifcall.count);
|
|
s[r->ifcall.count] = '\0';
|
|
switch((uint32_t)r->fid->qid.path){
|
|
default:
|
|
abort();
|
|
case Qneedkey:
|
|
ret = needkeywrite(r->srv, s);
|
|
break;
|
|
case Qconfirm:
|
|
ret = confirmwrite(r->srv, s);
|
|
break;
|
|
case Qctl:
|
|
ret = ctlwrite(s, 0);
|
|
break;
|
|
}
|
|
free(s);
|
|
if(ret < 0){
|
|
rerrstr(err, sizeof err);
|
|
respond(r, err);
|
|
}else{
|
|
r->ofcall.count = r->ifcall.count;
|
|
respond(r, nil);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fsflush(Req *r)
|
|
{
|
|
confirmflush(r->oldreq);
|
|
needkeyflush(r->oldreq);
|
|
logflush(r->oldreq);
|
|
respond(r, nil);
|
|
}
|
|
|
|
Srv fs = {
|
|
.attach= fsattach,
|
|
.walk1= fswalk1,
|
|
.open= fsopen,
|
|
.read= fsread,
|
|
.write= fswrite,
|
|
.stat= fsstat,
|
|
.flush= fsflush,
|
|
.destroyfid= fsdestroyfid,
|
|
};
|
|
|