1053 lines
19 KiB
C
1053 lines
19 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) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
|
|
* See /doc/license/gpl-2.0.txt for details about the licensing.
|
|
*/
|
|
|
|
#include <envvars.h>
|
|
#include "dat.h"
|
|
|
|
//static char secstore[100]; /* server name */ // NOT USED
|
|
static uint8_t zeros[16];
|
|
|
|
/* bind in the default network and cs */
|
|
static int
|
|
bindnetcs(void)
|
|
{
|
|
int srvfd;
|
|
|
|
if(access("/net/tcp", AEXIST) < 0)
|
|
sys_bind("#I", "/net", MBEFORE);
|
|
|
|
if(access("/net/cs", AEXIST) < 0){
|
|
if((srvfd = sys_open("#s/cs", ORDWR)) >= 0){
|
|
if(sys_mount(srvfd, -1, "/net", MBEFORE, "", '9') >= 0)
|
|
return 0;
|
|
sys_close(srvfd);
|
|
}
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* get all auth= attribute values from /net/ndb */
|
|
static void
|
|
netndbauthaddr(void)
|
|
{
|
|
enum { CHUNK = 1024 };
|
|
char *b, *p, *e;
|
|
int fd, n, m, i;
|
|
|
|
if((fd = sys_open("/net/ndb", OREAD)) < 0)
|
|
return;
|
|
m = 0;
|
|
b = nil;
|
|
for(;;){
|
|
if((p = realloc(b, m+CHUNK+1)) == nil)
|
|
break;
|
|
b = p;
|
|
if((n = jehanne_read(fd, b+m, CHUNK)) <= 0)
|
|
break;
|
|
m += n;
|
|
}
|
|
sys_close(fd);
|
|
if(b == nil)
|
|
return;
|
|
b[m] = '\0';
|
|
|
|
i = 0;
|
|
e = b;
|
|
while((p = strstr(e, "auth=")) != nil){
|
|
if(p > e && strchr("\n\t ", p[-1]) == nil){
|
|
e = p + strlen("auth=");
|
|
continue;
|
|
}
|
|
p += strlen("auth=");
|
|
for(e = p; *e != '\0'; e++)
|
|
if(strchr("\n\t ", *e) != nil)
|
|
break;
|
|
if(*e == '\0')
|
|
break;
|
|
*e++ = '\0';
|
|
if(*p == '\0')
|
|
continue;
|
|
authaddr[i++] = estrdup(p);
|
|
if(i >= nelem(authaddr)-1)
|
|
break;
|
|
}
|
|
authaddr[i] = nil;
|
|
free(b);
|
|
}
|
|
|
|
int
|
|
_authdial(char *authdom)
|
|
{
|
|
int i, fd;
|
|
|
|
sys_alarm(30*1000);
|
|
if(bindnetcs()>=0)
|
|
fd = authdial(nil, authdom);
|
|
else {
|
|
/*
|
|
* 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 or
|
|
* look for auth= attributes in /net/ndb.
|
|
* 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.
|
|
*/
|
|
fd = -1;
|
|
if(authaddr[0] == nil)
|
|
netndbauthaddr();
|
|
for(i = 0; fd < 0 && authaddr[i] != nil; i++){
|
|
fd = dial(netmkaddr(authaddr[i], "tcp", "567"), 0, 0, 0);
|
|
if(fd < 0)
|
|
fd = dial(netmkaddr(authaddr[i], "il", "566"), 0, 0, 0);
|
|
}
|
|
}
|
|
sys_alarm(0);
|
|
return fd;
|
|
}
|
|
|
|
int
|
|
_authreq(Ticketreq *tr, Authkey *k)
|
|
{
|
|
int fd;
|
|
|
|
fd = _authdial(tr->authdom);
|
|
if(fd < 0)
|
|
return -1;
|
|
sys_alarm(30*1000);
|
|
if(tsmemcmp(k->aes, zeros, AESKEYLEN) != 0){
|
|
if(_asgetpakkey(fd, tr, k) < 0){
|
|
sys_alarm(0);
|
|
sys_close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
if(_asrequest(fd, tr) < 0){
|
|
sys_alarm(0);
|
|
sys_close(fd);
|
|
return -1;
|
|
}
|
|
sys_alarm(0);
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
* 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 = sys_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");
|
|
sys_close(fd);
|
|
return attr;
|
|
}
|
|
|
|
/*
|
|
* send a key to the mounted factotum
|
|
*/
|
|
static int
|
|
sendkey(Attr *attr)
|
|
{
|
|
int fd, rv;
|
|
char buf[1024];
|
|
|
|
fd = sys_open("/mnt/factotum/ctl", ORDWR);
|
|
if(fd < 0)
|
|
sysfatal("opening /mnt/factotum/ctl: %r");
|
|
rv = fprint(fd, "key %A\n", attr);
|
|
jehanne_read(fd, buf, sizeof buf);
|
|
sys_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");
|
|
}
|
|
|
|
uint32_t 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])));
|
|
incref(k);
|
|
fss->conf[fss->nconf].key = k;
|
|
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(decref(k))
|
|
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 uint8_t*
|
|
pstring(uint8_t *p, uint8_t *e, char *s)
|
|
{
|
|
uint32_t 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 uint8_t*
|
|
pcarray(uint8_t *p, uint8_t *e, uint8_t *s, uint32_t n)
|
|
{
|
|
if(p == nil)
|
|
return nil;
|
|
if(s == nil){
|
|
if(n > 0)
|
|
sysfatal("pcarray");
|
|
s = (uint8_t*)"";
|
|
}
|
|
if(p+n+BIT16SZ >= e)
|
|
return nil;
|
|
PBIT16(p, n);
|
|
p += BIT16SZ;
|
|
memmove(p, s, n);
|
|
p += n;
|
|
return p;
|
|
}
|
|
|
|
uint8_t*
|
|
convAI2M(AuthInfo *ai, uint8_t *p, int n)
|
|
{
|
|
uint8_t *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);
|
|
sys_errstr(e, sizeof 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);
|
|
}
|
|
|
|
qlock(ring);
|
|
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:
|
|
qunlock(ring);
|
|
_freeattr(attr1);
|
|
return RpcConfirm;
|
|
case 0:
|
|
continue;
|
|
case 1:
|
|
break;
|
|
}
|
|
}
|
|
_freeattr(attr1);
|
|
_freeattr(attr2);
|
|
_freeattr(attr3);
|
|
incref(k);
|
|
*ret = k;
|
|
qunlock(ring);
|
|
return RpcOk;
|
|
}
|
|
}
|
|
qunlock(ring);
|
|
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;
|
|
int rv;
|
|
|
|
/*
|
|
* 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;
|
|
dom = _strfindattr(fss->attr, "dom");
|
|
if(dom != nil)
|
|
rv = findkey(k, &ki, "proto=dp9ik dom=%q role=server user?", dom);
|
|
else
|
|
rv = findkey(k, &ki, "proto=dp9ik role=server dom? user?");
|
|
if(rv != RpcOk){
|
|
if(dom != nil)
|
|
rv = findkey(k, &ki, "proto=p9sk1 dom=%q role=server user?", dom);
|
|
else
|
|
rv = findkey(k, &ki, "proto=p9sk1 role=server dom? user?");
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
Proto*
|
|
findproto(char *name)
|
|
{
|
|
int i;
|
|
|
|
for(i=0; prototab[i]; i++)
|
|
if(strcmp(name, prototab[i]->name) == 0)
|
|
return prototab[i];
|
|
return nil;
|
|
}
|
|
|
|
int
|
|
getnvramkey(int flag)
|
|
{
|
|
Nvrsafe safe;
|
|
char *s;
|
|
|
|
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 -1;
|
|
|
|
if(tsmemcmp(safe.machkey, zeros, DESKEYLEN) != 0){
|
|
s = smprint("key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______",
|
|
safe.authid, safe.authdom, DESKEYLEN, safe.machkey);
|
|
if(s != nil){
|
|
ctlwrite(s, 0);
|
|
memset(s, 0, strlen(s));
|
|
free(s);
|
|
}
|
|
}
|
|
if(tsmemcmp(safe.aesmachkey, zeros, AESKEYLEN) != 0){
|
|
s = smprint("key proto=dp9ik user=%q dom=%q !hex=%.*H !password=______",
|
|
safe.authid, safe.authdom, AESKEYLEN, safe.aesmachkey);
|
|
if(s != nil){
|
|
ctlwrite(s, 0);
|
|
memset(s, 0, strlen(s));
|
|
free(s);
|
|
}
|
|
}
|
|
writehostowner(safe.authid);
|
|
memset(&safe, 0, sizeof safe);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* keep caphash fd open since opens of it could be disabled
|
|
*/
|
|
static int caphashfd;
|
|
|
|
void
|
|
initcap(void)
|
|
{
|
|
caphashfd = sys_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)
|
|
{
|
|
uint8_t rand[20];
|
|
char *cap;
|
|
char *key;
|
|
int nfrom, nto;
|
|
uint8_t 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);
|
|
genrandom(rand, sizeof(rand));
|
|
key = cap+nfrom+1+nto+1;
|
|
enc64(key, sizeof(rand)*3, rand, sizeof(rand));
|
|
|
|
/* hash the capability */
|
|
hmac_sha1((uint8_t*)cap, strlen(cap), (uint8_t*)key, strlen(key), hash, nil);
|
|
|
|
/* give the kernel the hash */
|
|
key[-1] = '@';
|
|
if(jehanne_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(ENV_USER)){
|
|
writehostowner(p);
|
|
free(p);
|
|
return;
|
|
}
|
|
free(p);
|
|
|
|
strcpy(owner, "glenda");
|
|
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 = sys_open("/dev/cons", OREAD);
|
|
if(fdin < 0)
|
|
fdin = 0;
|
|
fdout = sys_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 = sys_open("/dev/consctl", OWRITE);
|
|
if(ctl >= 0)
|
|
jehanne_write(ctl, "rawon", 5);
|
|
} else
|
|
ctl = -1;
|
|
s = estrdup("");
|
|
for(;;){
|
|
n = jehanne_read(fdin, line, 1);
|
|
if(n == 0){
|
|
Error:
|
|
sys_close(fdin);
|
|
sys_close(fdout);
|
|
if(ctl >= 0)
|
|
sys_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){
|
|
jehanne_write(ctl, "rawoff", 6);
|
|
jehanne_write(fdout, "\n", 1);
|
|
}
|
|
sys_close(ctl);
|
|
sys_close(fdin);
|
|
sys_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;
|
|
|
|
qlock(ring);
|
|
incref(kn);
|
|
for(i=0; i<ring->nkey; i++){
|
|
k = ring->key[i];
|
|
if(matchattr(kn->attr, k->attr, nil) && matchattr(k->attr, kn->attr, nil)){
|
|
ring->key[i] = kn;
|
|
qunlock(ring);
|
|
closekey(k);
|
|
return 0;
|
|
}
|
|
}
|
|
if(ring->nkey%16 == 0)
|
|
ring->key = erealloc(ring->key, (ring->nkey+16)*sizeof(ring->key[0]));
|
|
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;
|
|
qunlock(ring);
|
|
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());
|
|
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());
|
|
_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());
|
|
}
|
|
continue2:;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
void
|
|
setmalloctaghere(void *v)
|
|
{
|
|
setmalloctag(v, getcallerpc());
|
|
}
|
|
|
|
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, uint32_t n)
|
|
{
|
|
fss->rpc.nwant = n;
|
|
return RpcToosmall;
|
|
}
|
|
|
|
void
|
|
writehostowner(char *owner)
|
|
{
|
|
int fd;
|
|
char *s;
|
|
|
|
if((s = strchr(owner,'@')) != nil)
|
|
*s = 0;
|
|
fd = sys_open("#c/hostowner", OWRITE);
|
|
if(fd >= 0){
|
|
if(fprint(fd, "%s", owner) < 0)
|
|
fprint(2, "factotum: setting #c/hostowner to %q: %r\n",
|
|
owner);
|
|
sys_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 */
|
|
}
|