#include "dat.h"
static char secstore[100]; /* server name */
/* bind in the default network and cs */
static int
bindnetcs(void)
{
int srvfd;
if(access("/net/tcp", AEXIST) < 0)
bind("#I", "/net", MBEFORE);
if(access("/net/cs", AEXIST) < 0){
if((srvfd = open("#s/cs", ORDWR)) >= 0){
if(mount(srvfd, -1, "/net", MBEFORE, "") >= 0)
return 0;
close(srvfd);
}
return -1;
}
return 0;
}
int
_authdial(char *net, char *authdom)
{
int fd, vanilla;
vanilla = net==nil || strcmp(net, "/net")==0;
if(!vanilla || bindnetcs()>=0)
return authdial(net, authdom);
/*
* If we failed to mount /srv/cs, assume that
* we're still bootstrapping the system and dial
* the one auth server passed to us on the command line.
* In normal operation, it is important *not* to do this,
* because the bootstrap auth server is only good for
* a single auth domain.
*
* The ticket request code should really check the
* remote authentication domain too.
*/
/* use the auth server passed to us as an arg */
if(authaddr == nil)
return -1;
fd = dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0);
if(fd >= 0)
return fd;
return dial(netmkaddr(authaddr, "il", "566"), 0, 0, 0);
}
int
secdial(void)
{
char *p, buf[80], *f[3];
int fd, nf;
p = secstore; /* take it from writehostowner, if set there */
if(*p == 0) /* else use the authserver */
p = "$auth";
if(bindnetcs() >= 0)
return dial(netmkaddr(p, "net", "secstore"), 0, 0, 0);
/* translate $auth ourselves.
* authaddr is something like il!host!566 or tcp!host!567.
* extract host, accounting for a change of format to something
* like il!host or tcp!host or host.
*/
if(strcmp(p, "$auth")==0){
if(authaddr == nil)
return -1;
safecpy(buf, authaddr, sizeof buf);
nf = getfields(buf, f, nelem(f), 0, "!");
switch(nf){
default:
return -1;
case 1:
p = f[0];
break;
case 2:
case 3:
p = f[1];
break;
}
}
fd = dial(netmkaddr(p, "tcp", "5356"), 0, 0, 0);
if(fd >= 0)
return fd;
return -1;
}
/*
* prompt user for a key. don't care about memory leaks, runs standalone
*/
static Attr*
promptforkey(char *params)
{
char *v;
int fd;
Attr *a, *attr;
char *def;
fd = open("/dev/cons", ORDWR);
if(fd < 0)
sysfatal("opening /dev/cons: %r");
attr = _parseattr(params);
fprint(fd, "\n!Adding key:");
for(a=attr; a; a=a->next)
if(a->type != AttrQuery && a->name[0] != '!')
fprint(fd, " %q=%q", a->name, a->val);
fprint(fd, "\n");
for(a=attr; a; a=a->next){
v = a->name;
if(a->type != AttrQuery || v[0]=='!')
continue;
def = nil;
if(strcmp(v, "user") == 0)
def = getuser();
a->val = readcons(v, def, 0);
if(a->val == nil)
sysfatal("user terminated key input");
a->type = AttrNameval;
}
for(a=attr; a; a=a->next){
v = a->name;
if(a->type != AttrQuery || v[0]!='!')
continue;
def = nil;
if(strcmp(v+1, "user") == 0)
def = getuser();
a->val = readcons(v+1, def, 1);
if(a->val == nil)
sysfatal("user terminated key input");
a->type = AttrNameval;
}
fprint(fd, "!\n");
close(fd);
return attr;
}
/*
* send a key to the mounted factotum
*/
static int
sendkey(Attr *attr)
{
int fd, rv;
char buf[1024];
fd = open("/mnt/factotum/ctl", ORDWR);
if(fd < 0)
sysfatal("opening /mnt/factotum/ctl: %r");
rv = fprint(fd, "key %A\n", attr);
read(fd, buf, sizeof buf);
close(fd);
return rv;
}
/* askuser */
void
askuser(char *params)
{
Attr *attr;
attr = promptforkey(params);
if(attr == nil)
sysfatal("no key supplied");
if(sendkey(attr) < 0)
sysfatal("sending key to factotum: %r");
}
ulong conftaggen;
int
canusekey(Fsstate *fss, Key *k)
{
int i;
if(_strfindattr(k->attr, "confirm")){
for(i=0; i<fss->nconf; i++)
if(fss->conf[i].key == k)
return fss->conf[i].canuse;
if(fss->nconf%16 == 0)
fss->conf = erealloc(fss->conf, (fss->nconf+16)*(sizeof(fss->conf[0])));
fss->conf[fss->nconf].key = k;
k->ref++;
fss->conf[fss->nconf].canuse = -1;
fss->conf[fss->nconf].tag = conftaggen++;
fss->nconf++;
return -1;
}
return 1;
}
/* closekey */
void
closekey(Key *k)
{
if(k == nil)
return;
if(--k->ref != 0)
return;
if(k->proto && k->proto->closekey)
(*k->proto->closekey)(k);
_freeattr(k->attr);
_freeattr(k->privattr);
k->attr = (void*)~1;
k->privattr = (void*)~1;
k->proto = nil;
free(k);
}
static uchar*
pstring(uchar *p, uchar *e, char *s)
{
uint n;
if(p == nil)
return nil;
if(s == nil)
s = "";
n = strlen(s);
if(p+n+BIT16SZ >= e)
return nil;
PBIT16(p, n);
p += BIT16SZ;
memmove(p, s, n);
p += n;
return p;
}
static uchar*
pcarray(uchar *p, uchar *e, uchar *s, uint n)
{
if(p == nil)
return nil;
if(s == nil){
if(n > 0)
sysfatal("pcarray");
s = (uchar*)"";
}
if(p+n+BIT16SZ >= e)
return nil;
PBIT16(p, n);
p += BIT16SZ;
memmove(p, s, n);
p += n;
return p;
}
uchar*
convAI2M(AuthInfo *ai, uchar *p, int n)
{
uchar *e = p+n;
p = pstring(p, e, ai->cuid);
p = pstring(p, e, ai->suid);
p = pstring(p, e, ai->cap);
p = pcarray(p, e, ai->secret, ai->nsecret);
return p;
}
int
failure(Fsstate *s, char *fmt, ...)
{
char e[ERRMAX];
va_list arg;
if(fmt == nil)
rerrstr(s->err, sizeof(s->err));
else {
va_start(arg, fmt);
vsnprint(e, sizeof e, fmt, arg);
va_end(arg);
strecpy(s->err, s->err+sizeof(s->err), e);
werrstr(e);
}
flog("%d: failure %s", s->seqnum, s->err);
return RpcFailure;
}
static int
hasqueries(Attr *a)
{
for(; a; a=a->next)
if(a->type == AttrQuery)
return 1;
return 0;
}
char *ignored[] = {
"role",
"disabled",
};
static int
ignoreattr(char *s)
{
int i;
for(i=0; i<nelem(ignored); i++)
if(strcmp(ignored[i], s)==0)
return 1;
return 0;
}
Keyinfo*
mkkeyinfo(Keyinfo *k, Fsstate *fss, Attr *attr)
{
memset(k, 0, sizeof *k);
k->fss = fss;
k->user = fss->sysuser;
if(attr)
k->attr = attr;
else
k->attr = fss->attr;
return k;
}
int
findkey(Key **ret, Keyinfo *ki, char *fmt, ...)
{
int i, s, nmatch;
char buf[1024], *p, *who;
va_list arg;
Attr *a, *attr0, *attr1, *attr2, *attr3, **l;
Key *k;
*ret = nil;
who = ki->user;
attr0 = ki->attr;
if(fmt){
va_start(arg, fmt);
vseprint(buf, buf+sizeof buf, fmt, arg);
va_end(arg);
attr1 = _parseattr(buf);
}else
attr1 = nil;
if(who && strcmp(who, owner) == 0)
who = nil;
if(who){
snprint(buf, sizeof buf, "owner=%q", who);
attr2 = _parseattr(buf);
attr3 = _parseattr("owner=*");
}else
attr2 = attr3 = nil;
p = _strfindattr(attr0, "proto");
if(p == nil)
p = _strfindattr(attr1, "proto");
if(p && findproto(p) == nil){
werrstr("unknown protocol %s", p);
_freeattr(attr1);
_freeattr(attr2);
_freeattr(attr3);
return failure(ki->fss, nil);
}
nmatch = 0;
for(i=0; i<ring->nkey; i++){
k = ring->key[i];
if(_strfindattr(k->attr, "disabled") && !ki->usedisabled)
continue;
if(matchattr(attr0, k->attr, k->privattr) && matchattr(attr1, k->attr, k->privattr)){
/* check ownership */
if(!matchattr(attr2, k->attr, nil) && !matchattr(attr3, k->attr, nil))
continue;
if(nmatch++ < ki->skip)
continue;
if(!ki->noconf){
switch(canusekey(ki->fss, k)){
case -1:
_freeattr(attr1);
return RpcConfirm;
case 0:
continue;
case 1:
break;
}
}
_freeattr(attr1);
_freeattr(attr2);
_freeattr(attr3);
k->ref++;
*ret = k;
return RpcOk;
}
}
flog("%d: no key matches %A %A %A %A", ki->fss->seqnum, attr0, attr1, attr2, attr3);
werrstr("no key matches %A %A", attr0, attr1);
_freeattr(attr2);
_freeattr(attr3);
s = RpcFailure;
if(askforkeys && who==nil && (hasqueries(attr0) || hasqueries(attr1))){
if(nmatch == 0){
attr0 = _copyattr(attr0);
for(l=&attr0; *l; l=&(*l)->next)
;
*l = attr1;
for(l=&attr0; *l; ){
if(ignoreattr((*l)->name)){
a = *l;
*l = (*l)->next;
a->next = nil;
_freeattr(a);
}else
l = &(*l)->next;
}
attr0 = sortattr(attr0);
snprint(ki->fss->keyinfo, sizeof ki->fss->keyinfo, "%A", attr0);
_freeattr(attr0);
attr1 = nil; /* attr1 was linked to attr0 */
}else
ki->fss->keyinfo[0] = '\0';
s = RpcNeedkey;
}
_freeattr(attr1);
if(s == RpcFailure)
return failure(ki->fss, nil); /* loads error string */
return s;
}
int
findp9authkey(Key **k, Fsstate *fss)
{
char *dom;
Keyinfo ki;
/*
* We don't use fss->attr here because we don't
* care about what the user name is set to, for instance.
*/
mkkeyinfo(&ki, fss, nil);
ki.attr = nil;
ki.user = nil;
if(dom = _strfindattr(fss->attr, "dom"))
return findkey(k, &ki, "proto=p9sk1 dom=%q role=server user?", dom);
else
return findkey(k, &ki, "proto=p9sk1 role=server dom? user?");
}
Proto*
findproto(char *name)
{
int i;
for(i=0; prototab[i]; i++)
if(strcmp(name, prototab[i]->name) == 0)
return prototab[i];
return nil;
}
char*
getnvramkey(int flag, char **secstorepw)
{
char *s;
Nvrsafe safe;
char spw[CONFIGLEN+1];
int i;
memset(&safe, 0, sizeof safe);
/*
* readnvram can return -1 meaning nvram wasn't written,
* but safe still holds good data.
*/
if(readnvram(&safe, flag)<0 && safe.authid[0]==0)
return nil;
/*
* we're using the config area to hold the secstore
* password. if there's anything there, return it.
*/
memmove(spw, safe.config, CONFIGLEN);
spw[CONFIGLEN] = 0;
if(spw[0] != 0)
*secstorepw = estrdup(spw);
/*
* only use nvram key if it is non-zero
*/
for(i = 0; i < DESKEYLEN; i++)
if(safe.machkey[i] != 0)
break;
if(i == DESKEYLEN)
return nil;
s = emalloc(512);
fmtinstall('H', encodefmt);
sprint(s, "key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______",
safe.authid, safe.authdom, DESKEYLEN, safe.machkey);
writehostowner(safe.authid);
return s;
}
int
isclient(char *role)
{
if(role == nil){
werrstr("role not specified");
return -1;
}
if(strcmp(role, "server") == 0)
return 0;
if(strcmp(role, "client") == 0)
return 1;
werrstr("unknown role %q", role);
return -1;
}
static int
hasname(Attr *a0, Attr *a1, char *name)
{
return _findattr(a0, name) || _findattr(a1, name);
}
static int
hasnameval(Attr *a0, Attr *a1, char *name, char *val)
{
Attr *a;
for(a=_findattr(a0, name); a; a=_findattr(a->next, name))
if(strcmp(a->val, val) == 0)
return 1;
for(a=_findattr(a1, name); a; a=_findattr(a->next, name))
if(strcmp(a->val, val) == 0)
return 1;
return 0;
}
int
matchattr(Attr *pat, Attr *a0, Attr *a1)
{
int type;
for(; pat; pat=pat->next){
type = pat->type;
if(ignoreattr(pat->name))
type = AttrDefault;
switch(type){
case AttrQuery: /* name=something be present */
if(!hasname(a0, a1, pat->name))
return 0;
break;
case AttrNameval: /* name=val must be present */
if(!hasnameval(a0, a1, pat->name, pat->val))
return 0;
break;
case AttrDefault: /* name=val must be present if name=anything is present */
if(hasname(a0, a1, pat->name) && !hasnameval(a0, a1, pat->name, pat->val))
return 0;
break;
}
}
return 1;
}
void
memrandom(void *p, int n)
{
uchar *cp;
for(cp = (uchar*)p; n > 0; n--)
*cp++ = fastrand();
}
/*
* keep caphash fd open since opens of it could be disabled
*/
static int caphashfd;
void
initcap(void)
{
caphashfd = open("#¤/caphash", OWRITE);
// if(caphashfd < 0)
// fprint(2, "%s: opening #¤/caphash: %r\n", argv0);
}
/*
* create a change uid capability
*/
char*
mkcap(char *from, char *to)
{
uchar rand[20];
char *cap;
char *key;
int nfrom, nto;
uchar hash[SHA1dlen];
if(caphashfd < 0)
return nil;
/* create the capability */
nto = strlen(to);
nfrom = strlen(from);
cap = emalloc(nfrom+1+nto+1+sizeof(rand)*3+1);
sprint(cap, "%s@%s", from, to);
memrandom(rand, sizeof(rand));
key = cap+nfrom+1+nto+1;
enc64(key, sizeof(rand)*3, rand, sizeof(rand));
/* hash the capability */
hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
/* give the kernel the hash */
key[-1] = '@';
if(write(caphashfd, hash, SHA1dlen) < 0){
free(cap);
return nil;
}
return cap;
}
int
phaseerror(Fsstate *s, char *op)
{
char tmp[32];
werrstr("protocol phase error: %s in state %s", op, phasename(s, s->phase, tmp));
return RpcPhase;
}
char*
phasename(Fsstate *fss, int phase, char *tmp)
{
char *name;
if(fss->phase == Broken)
name = "Broken";
else if(phase == Established)
name = "Established";
else if(phase == Notstarted)
name = "Notstarted";
else if(phase < 0 || phase >= fss->maxphase
|| (name = fss->phasename[phase]) == nil){
sprint(tmp, "%d", phase);
name = tmp;
}
return name;
}
static int
outin(char *prompt, char *def, int len)
{
char *s;
s = readcons(prompt, def, 0);
if(s == nil)
return -1;
if(s == nil)
sysfatal("s==nil???");
strncpy(def, s, len);
def[len-1] = 0;
free(s);
return strlen(def);
}
/*
* get host owner and set it
*/
void
promptforhostowner(void)
{
char owner[64], *p;
/* hack for bitsy; can't prompt during boot */
if(p = getenv("user")){
writehostowner(p);
free(p);
return;
}
free(p);
strcpy(owner, "none");
do{
outin("user", owner, sizeof(owner));
} while(*owner == 0);
writehostowner(owner);
}
char*
estrappend(char *s, char *fmt, ...)
{
char *t;
va_list arg;
va_start(arg, fmt);
t = vsmprint(fmt, arg);
if(t == nil)
sysfatal("out of memory");
va_end(arg);
s = erealloc(s, strlen(s)+strlen(t)+1);
strcat(s, t);
free(t);
return s;
}
/*
* prompt for a string with a possible default response
*/
char*
readcons(char *prompt, char *def, int raw)
{
int fdin, fdout, ctl, n;
char line[10];
char *s;
fdin = open("/dev/cons", OREAD);
if(fdin < 0)
fdin = 0;
fdout = open("/dev/cons", OWRITE);
if(fdout < 0)
fdout = 1;
if(def != nil)
fprint(fdout, "%s[%s]: ", prompt, def);
else
fprint(fdout, "%s: ", prompt);
if(raw){
ctl = open("/dev/consctl", OWRITE);
if(ctl >= 0)
write(ctl, "rawon", 5);
} else
ctl = -1;
s = estrdup("");
for(;;){
n = read(fdin, line, 1);
if(n == 0){
Error:
close(fdin);
close(fdout);
if(ctl >= 0)
close(ctl);
free(s);
return nil;
}
if(n < 0)
goto Error;
if(line[0] == 0x7f)
goto Error;
if(n == 0 || line[0] == '\n' || line[0] == '\r'){
if(raw){
write(ctl, "rawoff", 6);
write(fdout, "\n", 1);
}
close(ctl);
close(fdin);
close(fdout);
if(*s == 0 && def != nil)
s = estrappend(s, "%s", def);
return s;
}
if(line[0] == '\b'){
if(strlen(s) > 0)
s[strlen(s)-1] = 0;
} else if(line[0] == 0x15) { /* ^U: line kill */
if(def != nil)
fprint(fdout, "\n%s[%s]: ", prompt, def);
else
fprint(fdout, "\n%s: ", prompt);
s[0] = 0;
} else {
s = estrappend(s, "%c", line[0]);
}
}
}
/*
* Insert a key into the keyring.
* If the public attributes are identical to some other key, replace that one.
*/
int
replacekey(Key *kn, int before)
{
int i;
Key *k;
for(i=0; i<ring->nkey; i++){
k = ring->key[i];
if(matchattr(kn->attr, k->attr, nil) && matchattr(k->attr, kn->attr, nil)){
closekey(k);
kn->ref++;
ring->key[i] = kn;
return 0;
}
}
if(ring->nkey%16 == 0)
ring->key = erealloc(ring->key, (ring->nkey+16)*sizeof(ring->key[0]));
kn->ref++;
if(before){
memmove(ring->key+1, ring->key, ring->nkey*sizeof ring->key[0]);
ring->key[0] = kn;
ring->nkey++;
}else
ring->key[ring->nkey++] = kn;
return 0;
}
char*
safecpy(char *to, char *from, int n)
{
memset(to, 0, n);
if(n == 1)
return to;
if(from==nil)
sysfatal("safecpy called with from==nil, pc=%#p",
getcallerpc(&to));
strncpy(to, from, n-1);
return to;
}
Attr*
setattr(Attr *a, char *fmt, ...)
{
char buf[1024];
va_list arg;
Attr *b;
va_start(arg, fmt);
vseprint(buf, buf+sizeof buf, fmt, arg);
va_end(arg);
b = _parseattr(buf);
a = setattrs(a, b);
setmalloctag(a, getcallerpc(&a));
_freeattr(b);
return a;
}
/*
* add attributes in list b to list a. If any attributes are in
* both lists, replace those in a by those in b.
*/
Attr*
setattrs(Attr *a, Attr *b)
{
int found;
Attr **l, *freea;
for(; b; b=b->next){
found = 0;
for(l=&a; *l; ){
if(strcmp(b->name, (*l)->name) == 0){
switch(b->type){
case AttrNameval:
if(!found){
found = 1;
free((*l)->val);
(*l)->val = estrdup(b->val);
(*l)->type = AttrNameval;
l = &(*l)->next;
}else{
freea = *l;
*l = (*l)->next;
freea->next = nil;
_freeattr(freea);
}
break;
case AttrQuery:
goto continue2;
}
}else
l = &(*l)->next;
}
if(found == 0){
*l = _mkattr(b->type, b->name, b->val, nil);
setmalloctag(*l, getcallerpc(&a));
}
continue2:;
}
return a;
}
void
setmalloctaghere(void *v)
{
setmalloctag(v, getcallerpc(&v));
}
Attr*
sortattr(Attr *a)
{
int i;
Attr *anext, *a0, *a1, **l;
if(a == nil || a->next == nil)
return a;
/* cut list in halves */
a0 = nil;
a1 = nil;
i = 0;
for(; a; a=anext){
anext = a->next;
if(i++%2){
a->next = a0;
a0 = a;
}else{
a->next = a1;
a1 = a;
}
}
/* sort */
a0 = sortattr(a0);
a1 = sortattr(a1);
/* merge */
l = &a;
while(a0 || a1){
if(a1==nil){
anext = a0;
a0 = a0->next;
}else if(a0==nil){
anext = a1;
a1 = a1->next;
}else if(strcmp(a0->name, a1->name) < 0){
anext = a0;
a0 = a0->next;
}else{
anext = a1;
a1 = a1->next;
}
*l = anext;
l = &(*l)->next;
}
*l = nil;
return a;
}
int
toosmall(Fsstate *fss, uint n)
{
fss->rpc.nwant = n;
return RpcToosmall;
}
void
writehostowner(char *owner)
{
int fd;
char *s;
if((s = strchr(owner,'@')) != nil){
*s++ = 0;
strncpy(secstore, s, (sizeof secstore)-1);
}
fd = open("#c/hostowner", OWRITE);
if(fd >= 0){
if(fprint(fd, "%s", owner) < 0)
fprint(2, "factotum: setting #c/hostowner to %q: %r\n",
owner);
close(fd);
}
}
int
attrnamefmt(Fmt *fmt)
{
char *b, buf[1024], *ebuf;
Attr *a;
ebuf = buf+sizeof buf;
b = buf;
strcpy(buf, " ");
for(a=va_arg(fmt->args, Attr*); a; a=a->next){
if(a->name == nil)
continue;
b = seprint(b, ebuf, " %q?", a->name);
}
return fmtstrcpy(fmt, buf+1);
}
void
disablekey(Key *k)
{
Attr *a;
if(sflag) /* not on servers */
return;
for(a=k->attr; a; a=a->next){
if(a->type==AttrNameval && strcmp(a->name, "disabled") == 0)
return;
if(a->next == nil)
break;
}
if(a)
a->next = _mkattr(AttrNameval, "disabled", "by.factotum", nil);
else
k->attr = _mkattr(AttrNameval, "disabled", "by.factotum", nil); /* not reached: always a proto attribute */
}
|