xref: /plan9/sys/src/cmd/auth/factotum/util.c (revision d9306527b4a7229dcf0cf3c58aed36bb9da82854)
19a747e4fSDavid du Colombier #include "dat.h"
29a747e4fSDavid du Colombier 
39a747e4fSDavid du Colombier static char secstore[100];   /* server name */
49a747e4fSDavid du Colombier 
59a747e4fSDavid du Colombier /* bind in the default network and cs */
69a747e4fSDavid du Colombier static int
79a747e4fSDavid du Colombier bindnetcs(void)
89a747e4fSDavid du Colombier {
99a747e4fSDavid du Colombier 	int srvfd;
109a747e4fSDavid du Colombier 
11*d9306527SDavid du Colombier 	if(access("/net/tcp", AEXIST) < 0)
129a747e4fSDavid du Colombier 		bind("#I", "/net", MBEFORE);
139a747e4fSDavid du Colombier 
149a747e4fSDavid du Colombier 	if(access("/net/cs", AEXIST) < 0){
159a747e4fSDavid du Colombier 		if((srvfd = open("#s/cs", ORDWR)) >= 0){
169a747e4fSDavid du Colombier 			if(mount(srvfd, -1, "/net", MBEFORE, "") >= 0)
179a747e4fSDavid du Colombier 				return 0;
189a747e4fSDavid du Colombier 			close(srvfd);
199a747e4fSDavid du Colombier 		}
209a747e4fSDavid du Colombier 		return -1;
219a747e4fSDavid du Colombier 	}
229a747e4fSDavid du Colombier 	return 0;
239a747e4fSDavid du Colombier }
249a747e4fSDavid du Colombier 
259a747e4fSDavid du Colombier int
269a747e4fSDavid du Colombier _authdial(char *net, char *authdom)
279a747e4fSDavid du Colombier {
289a747e4fSDavid du Colombier 	int fd;
299a747e4fSDavid du Colombier 	int vanilla;
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	vanilla = net==nil || strcmp(net, "/net")==0;
329a747e4fSDavid du Colombier 
339a747e4fSDavid du Colombier 	if(!vanilla || bindnetcs()>=0)
349a747e4fSDavid du Colombier 		return authdial(net, authdom);
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier 	/* use the auth sever passed to us as an arg */
379a747e4fSDavid du Colombier 	if(authaddr == nil)
389a747e4fSDavid du Colombier 		return -1;
399a747e4fSDavid du Colombier 	fd = dial(netmkaddr(authaddr, "il", "566"), 0, 0, 0);
409a747e4fSDavid du Colombier 	if(fd >= 0)
419a747e4fSDavid du Colombier 		return fd;
429a747e4fSDavid du Colombier 	return dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0);
439a747e4fSDavid du Colombier }
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier int
469a747e4fSDavid du Colombier secdial(void)
479a747e4fSDavid du Colombier {
489a747e4fSDavid du Colombier 	char *p, buf[80], *f[3];
499a747e4fSDavid du Colombier 	int fd, nf;
509a747e4fSDavid du Colombier 
519a747e4fSDavid du Colombier 	p = secstore; /* take it from writehostowner, if set there */
529a747e4fSDavid du Colombier 	if(*p == 0)	  /* else use the authserver */
539a747e4fSDavid du Colombier 		p = "$auth";
549a747e4fSDavid du Colombier 
559a747e4fSDavid du Colombier 	if(bindnetcs() >= 0)
569a747e4fSDavid du Colombier 		return dial(netmkaddr(p, "net", "secstore"), 0, 0, 0);
579a747e4fSDavid du Colombier 
589a747e4fSDavid du Colombier 	/* translate $auth ourselves.
599a747e4fSDavid du Colombier 	 * authaddr is something like il!host!566 or tcp!host!567.
609a747e4fSDavid du Colombier 	 * extract host, accounting for a change of format to something
619a747e4fSDavid du Colombier 	 * like il!host or tcp!host or host.
629a747e4fSDavid du Colombier 	 */
639a747e4fSDavid du Colombier 	if(strcmp(p, "$auth")==0){
649a747e4fSDavid du Colombier 		if(authaddr == nil)
659a747e4fSDavid du Colombier 			return -1;
669a747e4fSDavid du Colombier 		safecpy(buf, authaddr, sizeof buf);
679a747e4fSDavid du Colombier 		nf = getfields(buf, f, nelem(f), 0, "!");
689a747e4fSDavid du Colombier 		switch(nf){
699a747e4fSDavid du Colombier 		default:
709a747e4fSDavid du Colombier 			return -1;
719a747e4fSDavid du Colombier 		case 1:
729a747e4fSDavid du Colombier 			p = f[0];
739a747e4fSDavid du Colombier 			break;
749a747e4fSDavid du Colombier 		case 2:
759a747e4fSDavid du Colombier 		case 3:
769a747e4fSDavid du Colombier 			p = f[1];
779a747e4fSDavid du Colombier 			break;
789a747e4fSDavid du Colombier 		}
799a747e4fSDavid du Colombier 	}
809a747e4fSDavid du Colombier 	fd = dial(netmkaddr(p, "tcp", "5356"), 0, 0, 0);
819a747e4fSDavid du Colombier 	if(fd >= 0)
829a747e4fSDavid du Colombier 		return fd;
839a747e4fSDavid du Colombier 	return -1;
849a747e4fSDavid du Colombier }
859a747e4fSDavid du Colombier /*
869a747e4fSDavid du Colombier  *  prompt user for a key.  don't care about memory leaks, runs standalone
879a747e4fSDavid du Colombier  */
889a747e4fSDavid du Colombier static Attr*
899a747e4fSDavid du Colombier promptforkey(char *params)
909a747e4fSDavid du Colombier {
919a747e4fSDavid du Colombier 	char *v;
929a747e4fSDavid du Colombier 	int fd;
939a747e4fSDavid du Colombier 	Attr *a, *attr;
949a747e4fSDavid du Colombier 	char *def;
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier 	fd = open("/dev/cons", ORDWR);
979a747e4fSDavid du Colombier 	if(fd < 0)
989a747e4fSDavid du Colombier 		sysfatal("opening /dev/cons: %r");
999a747e4fSDavid du Colombier 
1009a747e4fSDavid du Colombier 	attr = _parseattr(params);
1019a747e4fSDavid du Colombier 	fprint(fd, "\n!Adding key:");
1029a747e4fSDavid du Colombier 	for(a=attr; a; a=a->next)
1039a747e4fSDavid du Colombier 		if(a->type != AttrQuery && s_to_c(a->name)[0] != '!')
1049a747e4fSDavid du Colombier 			fprint(fd, " %q=%q", s_to_c(a->name), s_to_c(a->val));
1059a747e4fSDavid du Colombier 	fprint(fd, "\n");
1069a747e4fSDavid du Colombier 
1079a747e4fSDavid du Colombier 	for(a=attr; a; a=a->next){
1089a747e4fSDavid du Colombier 		v = s_to_c(a->name);
1099a747e4fSDavid du Colombier 		if(a->type != AttrQuery || v[0]=='!')
1109a747e4fSDavid du Colombier 			continue;
1119a747e4fSDavid du Colombier 		def = nil;
1129a747e4fSDavid du Colombier 		if(strcmp(v, "user") == 0)
1139a747e4fSDavid du Colombier 			def = getuser();
1149a747e4fSDavid du Colombier 		a->val = readcons(v, def, 0);
1159a747e4fSDavid du Colombier 		if(a->val == nil)
1169a747e4fSDavid du Colombier 			sysfatal("user terminated key input");
1179a747e4fSDavid du Colombier 		a->type = AttrNameval;
1189a747e4fSDavid du Colombier 	}
1199a747e4fSDavid du Colombier 	for(a=attr; a; a=a->next){
1209a747e4fSDavid du Colombier 		v = s_to_c(a->name);
1219a747e4fSDavid du Colombier 		if(a->type != AttrQuery || v[0]!='!')
1229a747e4fSDavid du Colombier 			continue;
1239a747e4fSDavid du Colombier 		def = nil;
1249a747e4fSDavid du Colombier 		if(strcmp(v+1, "user") == 0)
1259a747e4fSDavid du Colombier 			def = getuser();
1269a747e4fSDavid du Colombier 		a->val = readcons(v+1, def, 1);
1279a747e4fSDavid du Colombier 		if(a->val == nil)
1289a747e4fSDavid du Colombier 			sysfatal("user terminated key input");
1299a747e4fSDavid du Colombier 		a->type = AttrNameval;
1309a747e4fSDavid du Colombier 	}
1319a747e4fSDavid du Colombier 	fprint(fd, "!\n");
1329a747e4fSDavid du Colombier 	close(fd);
1339a747e4fSDavid du Colombier 	return attr;
1349a747e4fSDavid du Colombier }
1359a747e4fSDavid du Colombier 
1369a747e4fSDavid du Colombier /*
1379a747e4fSDavid du Colombier  *  send a key to the mounted factotum
1389a747e4fSDavid du Colombier  */
1399a747e4fSDavid du Colombier static int
1409a747e4fSDavid du Colombier sendkey(Attr *attr)
1419a747e4fSDavid du Colombier {
1429a747e4fSDavid du Colombier 	int fd, rv;
1439a747e4fSDavid du Colombier 	char buf[1024];
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier 	fd = open("/mnt/factotum/ctl", ORDWR);
1469a747e4fSDavid du Colombier 	if(fd < 0)
1479a747e4fSDavid du Colombier 		sysfatal("opening /mnt/factotum/ctl: %r");
1489a747e4fSDavid du Colombier 	rv = fprint(fd, "key %A\n", attr);
1499a747e4fSDavid du Colombier 	read(fd, buf, sizeof buf);
1509a747e4fSDavid du Colombier 	close(fd);
1519a747e4fSDavid du Colombier 	return rv;
1529a747e4fSDavid du Colombier }
1539a747e4fSDavid du Colombier 
1549a747e4fSDavid du Colombier /* askuser */
1559a747e4fSDavid du Colombier void
1569a747e4fSDavid du Colombier askuser(char *params)
1579a747e4fSDavid du Colombier {
1589a747e4fSDavid du Colombier 	Attr *attr;
1599a747e4fSDavid du Colombier 
1609a747e4fSDavid du Colombier 	attr = promptforkey(params);
1619a747e4fSDavid du Colombier 	if(attr == nil)
1629a747e4fSDavid du Colombier 		sysfatal("no key supplied");
1639a747e4fSDavid du Colombier 	if(sendkey(attr) < 0)
1649a747e4fSDavid du Colombier 		sysfatal("sending key to factotum: %r");
1659a747e4fSDavid du Colombier }
1669a747e4fSDavid du Colombier 
1679a747e4fSDavid du Colombier ulong conftaggen;
1689a747e4fSDavid du Colombier int
1699a747e4fSDavid du Colombier canusekey(Fsstate *fss, Key *k)
1709a747e4fSDavid du Colombier {
1719a747e4fSDavid du Colombier 	int i;
1729a747e4fSDavid du Colombier 
1739a747e4fSDavid du Colombier 	if(_str_findattr(k->attr, "confirm")){
1749a747e4fSDavid du Colombier 		for(i=0; i<fss->nconf; i++)
1759a747e4fSDavid du Colombier 			if(fss->conf[i].key == k)
1769a747e4fSDavid du Colombier 				return fss->conf[i].canuse;
1779a747e4fSDavid du Colombier 		if(fss->nconf%16 == 0)
1789a747e4fSDavid du Colombier 			fss->conf = erealloc(fss->conf, (fss->nconf+16)*(sizeof(fss->conf[0])));
1799a747e4fSDavid du Colombier 		fss->conf[fss->nconf].key = k;
1809a747e4fSDavid du Colombier 		k->ref++;
1819a747e4fSDavid du Colombier 		fss->conf[fss->nconf].canuse = -1;
1829a747e4fSDavid du Colombier 		fss->conf[fss->nconf].tag = conftaggen++;
1839a747e4fSDavid du Colombier 		fss->nconf++;
1849a747e4fSDavid du Colombier 		return -1;
1859a747e4fSDavid du Colombier 	}
1869a747e4fSDavid du Colombier 	return 1;
1879a747e4fSDavid du Colombier }
1889a747e4fSDavid du Colombier 
1899a747e4fSDavid du Colombier /* closekey */
1909a747e4fSDavid du Colombier void
1919a747e4fSDavid du Colombier closekey(Key *k)
1929a747e4fSDavid du Colombier {
1939a747e4fSDavid du Colombier 	if(k == nil)
1949a747e4fSDavid du Colombier 		return;
1959a747e4fSDavid du Colombier 	if(--k->ref != 0)
1969a747e4fSDavid du Colombier 		return;
197*d9306527SDavid du Colombier 	if(k->proto && k->proto->closekey)
198*d9306527SDavid du Colombier 		(*k->proto->closekey)(k);
1999a747e4fSDavid du Colombier 	_freeattr(k->attr);
2009a747e4fSDavid du Colombier 	_freeattr(k->privattr);
2019a747e4fSDavid du Colombier 	k->attr = (void*)~1;
2029a747e4fSDavid du Colombier 	k->privattr = (void*)~1;
2039a747e4fSDavid du Colombier 	k->proto = nil;
2049a747e4fSDavid du Colombier 	free(k);
2059a747e4fSDavid du Colombier }
2069a747e4fSDavid du Colombier 
2079a747e4fSDavid du Colombier static uchar*
2089a747e4fSDavid du Colombier pstring(uchar *p, uchar *e, char *s)
2099a747e4fSDavid du Colombier {
2109a747e4fSDavid du Colombier 	uint n;
2119a747e4fSDavid du Colombier 
2129a747e4fSDavid du Colombier 	if(p == nil)
2139a747e4fSDavid du Colombier 		return nil;
2149a747e4fSDavid du Colombier 	if(s == nil)
2159a747e4fSDavid du Colombier 		s = "";
2169a747e4fSDavid du Colombier 	n = strlen(s);
2179a747e4fSDavid du Colombier 	if(p+n+BIT16SZ >= e)
2189a747e4fSDavid du Colombier 		return nil;
2199a747e4fSDavid du Colombier 	PBIT16(p, n);
2209a747e4fSDavid du Colombier 	p += BIT16SZ;
2219a747e4fSDavid du Colombier 	memmove(p, s, n);
2229a747e4fSDavid du Colombier 	p += n;
2239a747e4fSDavid du Colombier 	return p;
2249a747e4fSDavid du Colombier }
2259a747e4fSDavid du Colombier 
2269a747e4fSDavid du Colombier static uchar*
2279a747e4fSDavid du Colombier pcarray(uchar *p, uchar *e, uchar *s, uint n)
2289a747e4fSDavid du Colombier {
2299a747e4fSDavid du Colombier 	if(p == nil)
2309a747e4fSDavid du Colombier 		return nil;
2319a747e4fSDavid du Colombier 	if(s == nil){
2329a747e4fSDavid du Colombier 		if(n > 0)
2339a747e4fSDavid du Colombier 			sysfatal("pcarray");
2349a747e4fSDavid du Colombier 		s = (uchar*)"";
2359a747e4fSDavid du Colombier 	}
2369a747e4fSDavid du Colombier 	if(p+n+BIT16SZ >= e)
2379a747e4fSDavid du Colombier 		return nil;
2389a747e4fSDavid du Colombier 	PBIT16(p, n);
2399a747e4fSDavid du Colombier 	p += BIT16SZ;
2409a747e4fSDavid du Colombier 	memmove(p, s, n);
2419a747e4fSDavid du Colombier 	p += n;
2429a747e4fSDavid du Colombier 	return p;
2439a747e4fSDavid du Colombier }
2449a747e4fSDavid du Colombier 
2459a747e4fSDavid du Colombier uchar*
2469a747e4fSDavid du Colombier convAI2M(AuthInfo *ai, uchar *p, int n)
2479a747e4fSDavid du Colombier {
2489a747e4fSDavid du Colombier 	uchar *e = p+n;
2499a747e4fSDavid du Colombier 
2509a747e4fSDavid du Colombier 	p = pstring(p, e, ai->cuid);
2519a747e4fSDavid du Colombier 	p = pstring(p, e, ai->suid);
2529a747e4fSDavid du Colombier 	p = pstring(p, e, ai->cap);
2539a747e4fSDavid du Colombier 	p = pcarray(p, e, ai->secret, ai->nsecret);
2549a747e4fSDavid du Colombier 	return p;
2559a747e4fSDavid du Colombier }
2569a747e4fSDavid du Colombier 
2579a747e4fSDavid du Colombier int
2589a747e4fSDavid du Colombier failure(Fsstate *s, char *fmt, ...)
2599a747e4fSDavid du Colombier {
2609a747e4fSDavid du Colombier 	char e[ERRMAX];
2619a747e4fSDavid du Colombier 	va_list arg;
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier 	if(fmt == nil)
2649a747e4fSDavid du Colombier 		rerrstr(s->err, sizeof(s->err));
2659a747e4fSDavid du Colombier 	else {
2669a747e4fSDavid du Colombier 		va_start(arg, fmt);
2679a747e4fSDavid du Colombier 		snprint(e, sizeof e, fmt, arg);
2689a747e4fSDavid du Colombier 		va_end(arg);
2699a747e4fSDavid du Colombier 		strecpy(s->err, s->err+sizeof(s->err), e);
2709a747e4fSDavid du Colombier 		errstr(e, sizeof e);
2719a747e4fSDavid du Colombier 	}
2729a747e4fSDavid du Colombier 	flog("%d: failure %s", s->seqnum, s->err);
2739a747e4fSDavid du Colombier 	return RpcFailure;
2749a747e4fSDavid du Colombier }
2759a747e4fSDavid du Colombier 
2769a747e4fSDavid du Colombier static int
2779a747e4fSDavid du Colombier hasqueries(Attr *a)
2789a747e4fSDavid du Colombier {
2799a747e4fSDavid du Colombier 	for(; a; a=a->next)
2809a747e4fSDavid du Colombier 		if(a->type == AttrQuery)
2819a747e4fSDavid du Colombier 			return 1;
2829a747e4fSDavid du Colombier 	return 0;
2839a747e4fSDavid du Colombier }
2849a747e4fSDavid du Colombier 
2859a747e4fSDavid du Colombier char *ignored[] = {
2869a747e4fSDavid du Colombier 	"role",
2879a747e4fSDavid du Colombier };
2889a747e4fSDavid du Colombier 
2899a747e4fSDavid du Colombier static int
2909a747e4fSDavid du Colombier ignoreattr(char *s)
2919a747e4fSDavid du Colombier {
2929a747e4fSDavid du Colombier 	int i;
2939a747e4fSDavid du Colombier 
2949a747e4fSDavid du Colombier 	for(i=0; i<nelem(ignored); i++)
2959a747e4fSDavid du Colombier 		if(strcmp(ignored[i], s)==0)
2969a747e4fSDavid du Colombier 			return 1;
2979a747e4fSDavid du Colombier 	return 0;
2989a747e4fSDavid du Colombier }
2999a747e4fSDavid du Colombier 
3009a747e4fSDavid du Colombier int
3019a747e4fSDavid du Colombier findkey(Key **ret, Fsstate *fss, int who, int skip, Attr *attr0, char *fmt, ...)
3029a747e4fSDavid du Colombier {
3039a747e4fSDavid du Colombier 	int i, s, nmatch;
3049a747e4fSDavid du Colombier 	char buf[1024], *p;
3059a747e4fSDavid du Colombier 	va_list arg;
3069a747e4fSDavid du Colombier 	Attr *a, *attr1, **l;
3079a747e4fSDavid du Colombier 	Key *k;
3089a747e4fSDavid du Colombier 
3099a747e4fSDavid du Colombier 	*ret = nil;
3109a747e4fSDavid du Colombier 	switch(who&Kwho){
3119a747e4fSDavid du Colombier 	default:
3129a747e4fSDavid du Colombier 		werrstr("bad who %d", who);
3139a747e4fSDavid du Colombier 		failure(fss, nil);
3149a747e4fSDavid du Colombier 		return failure(fss, nil);
3159a747e4fSDavid du Colombier 	case Kowner:
3169a747e4fSDavid du Colombier 		break;
3179a747e4fSDavid du Colombier 	case Kuser:
318*d9306527SDavid du Colombier 		if(strcmp(fss->sysuser, owner) != 0
319*d9306527SDavid du Colombier 		&& strcmp(fss->sysuser, invoker) != 0){
3209a747e4fSDavid du Colombier 			werrstr("%q can't use %q's keys", fss->sysuser, owner);
3219a747e4fSDavid du Colombier 			return failure(fss, nil);
3229a747e4fSDavid du Colombier 		}
3239a747e4fSDavid du Colombier 		break;
3249a747e4fSDavid du Colombier 	}
3259a747e4fSDavid du Colombier 
3269a747e4fSDavid du Colombier 	if(fmt){
3279a747e4fSDavid du Colombier 		va_start(arg, fmt);
3289a747e4fSDavid du Colombier 		vseprint(buf, buf+sizeof buf, fmt, arg);
3299a747e4fSDavid du Colombier 		va_end(arg);
3309a747e4fSDavid du Colombier 		attr1 = _parseattr(buf);
3319a747e4fSDavid du Colombier 	}else
3329a747e4fSDavid du Colombier 		attr1 = nil;
3339a747e4fSDavid du Colombier 
3349a747e4fSDavid du Colombier 	p = _str_findattr(attr0, "proto");
3359a747e4fSDavid du Colombier 	if(p == nil)
3369a747e4fSDavid du Colombier 		p = _str_findattr(attr1, "proto");
3379a747e4fSDavid du Colombier 	if(p && findproto(p) == nil){
3389a747e4fSDavid du Colombier 		werrstr("unknown protocol %s", p);
3399a747e4fSDavid du Colombier 		_freeattr(attr1);
3409a747e4fSDavid du Colombier 		return failure(fss, nil);
3419a747e4fSDavid du Colombier 	}
3429a747e4fSDavid du Colombier 
3439a747e4fSDavid du Colombier 	nmatch = 0;
3449a747e4fSDavid du Colombier 	for(i=0; i<ring->nkey; i++){
3459a747e4fSDavid du Colombier 		k = ring->key[i];
3469a747e4fSDavid du Colombier 		if(matchattr(attr0, k->attr, k->privattr) && matchattr(attr1, k->attr, k->privattr)){
3479a747e4fSDavid du Colombier 			if(nmatch++ < skip)
3489a747e4fSDavid du Colombier 				continue;
3499a747e4fSDavid du Colombier 			if(!(who&Knoconf)){
3509a747e4fSDavid du Colombier 				switch(canusekey(fss, k)){
3519a747e4fSDavid du Colombier 				case -1:
3529a747e4fSDavid du Colombier 					_freeattr(attr1);
3539a747e4fSDavid du Colombier 					return RpcConfirm;
3549a747e4fSDavid du Colombier 				case 0:
3559a747e4fSDavid du Colombier 					continue;
3569a747e4fSDavid du Colombier 				case 1:
3579a747e4fSDavid du Colombier 					break;
3589a747e4fSDavid du Colombier 				}
3599a747e4fSDavid du Colombier 			}
3609a747e4fSDavid du Colombier 			_freeattr(attr1);
3619a747e4fSDavid du Colombier 			k->ref++;
3629a747e4fSDavid du Colombier 			*ret = k;
3639a747e4fSDavid du Colombier 			return RpcOk;
3649a747e4fSDavid du Colombier 		}
3659a747e4fSDavid du Colombier 	}
3669a747e4fSDavid du Colombier 	flog("%d: no key matches %A %A", fss->seqnum, attr0, attr1);
3679a747e4fSDavid du Colombier 	werrstr("no key matches %A %A", attr0, attr1);
3689a747e4fSDavid du Colombier 	s = RpcFailure;
3699a747e4fSDavid du Colombier 	if(askforkeys && (hasqueries(attr0) || hasqueries(attr1))){
3709a747e4fSDavid du Colombier 		if(nmatch == 0){
3719a747e4fSDavid du Colombier 			attr0 = _copyattr(attr0);
3729a747e4fSDavid du Colombier 			for(l=&attr0; *l; l=&(*l)->next)
3739a747e4fSDavid du Colombier 				;
3749a747e4fSDavid du Colombier 			*l = attr1;
3759a747e4fSDavid du Colombier 			for(l=&attr0; *l; ){
3769a747e4fSDavid du Colombier 				if(ignoreattr(s_to_c((*l)->name))){
3779a747e4fSDavid du Colombier 					a = *l;
3789a747e4fSDavid du Colombier 					*l = (*l)->next;
3799a747e4fSDavid du Colombier 					a->next = nil;
3809a747e4fSDavid du Colombier 					_freeattr(a);
3819a747e4fSDavid du Colombier 				}else
3829a747e4fSDavid du Colombier 					l = &(*l)->next;
3839a747e4fSDavid du Colombier 			}
3849a747e4fSDavid du Colombier 			attr0 = sortattr(attr0);
3859a747e4fSDavid du Colombier 			snprint(fss->keyinfo, sizeof fss->keyinfo, "%A", attr0);
3869a747e4fSDavid du Colombier 			_freeattr(attr0);
3879a747e4fSDavid du Colombier 			attr1 = nil;	/* attr1 was linked to attr0 */
3889a747e4fSDavid du Colombier 		}else
3899a747e4fSDavid du Colombier 			fss->keyinfo[0] = '\0';
3909a747e4fSDavid du Colombier 		s = RpcNeedkey;
3919a747e4fSDavid du Colombier 	}
3929a747e4fSDavid du Colombier 	_freeattr(attr1);
3939a747e4fSDavid du Colombier 	if(s == RpcFailure)
3949a747e4fSDavid du Colombier 		return failure(fss, nil);	/* loads error string */
3959a747e4fSDavid du Colombier 	return s;
3969a747e4fSDavid du Colombier }
3979a747e4fSDavid du Colombier 
3989a747e4fSDavid du Colombier int
3999a747e4fSDavid du Colombier findp9authkey(Key **k, Fsstate *fss)
4009a747e4fSDavid du Colombier {
4019a747e4fSDavid du Colombier 	char *dom;
4029a747e4fSDavid du Colombier 
4039a747e4fSDavid du Colombier 	/*
4049a747e4fSDavid du Colombier 	 * We don't use fss->attr here because we don't
4059a747e4fSDavid du Colombier 	 * care about what the user name is set to, for instance.
4069a747e4fSDavid du Colombier 	 */
4079a747e4fSDavid du Colombier 	if(dom = _str_findattr(fss->attr, "dom"))
4089a747e4fSDavid du Colombier 		return findkey(k, fss, Kowner, 0, nil, "proto=p9sk1 dom=%q role=server user?", dom);
4099a747e4fSDavid du Colombier 	else
4109a747e4fSDavid du Colombier 		return findkey(k, fss, Kowner, 0, nil, "proto=p9sk1 role=server dom? user?");
4119a747e4fSDavid du Colombier }
4129a747e4fSDavid du Colombier 
4139a747e4fSDavid du Colombier Proto*
4149a747e4fSDavid du Colombier findproto(char *name)
4159a747e4fSDavid du Colombier {
4169a747e4fSDavid du Colombier 	int i;
4179a747e4fSDavid du Colombier 
4189a747e4fSDavid du Colombier 	for(i=0; prototab[i]; i++)
4199a747e4fSDavid du Colombier 		if(strcmp(name, prototab[i]->name) == 0)
4209a747e4fSDavid du Colombier 			return prototab[i];
4219a747e4fSDavid du Colombier 	return nil;
4229a747e4fSDavid du Colombier }
4239a747e4fSDavid du Colombier 
4249a747e4fSDavid du Colombier char*
4259a747e4fSDavid du Colombier getnvramkey(int flag, char **secstorepw)
4269a747e4fSDavid du Colombier {
4279a747e4fSDavid du Colombier 	char *s;
4289a747e4fSDavid du Colombier 	Nvrsafe safe;
4299a747e4fSDavid du Colombier 	char spw[CONFIGLEN+1];
4309a747e4fSDavid du Colombier 	int i;
4319a747e4fSDavid du Colombier 
4329a747e4fSDavid du Colombier 	memset(&safe, 0, sizeof safe);
4339a747e4fSDavid du Colombier 	/*
4349a747e4fSDavid du Colombier 	 * readnvram can return -1 meaning nvram wasn't written,
4359a747e4fSDavid du Colombier 	 * but safe still holds good data.
4369a747e4fSDavid du Colombier 	 */
4379a747e4fSDavid du Colombier 	if(readnvram(&safe, flag)<0 && safe.authid[0]=='0')
4389a747e4fSDavid du Colombier 		return nil;
4399a747e4fSDavid du Colombier 
4409a747e4fSDavid du Colombier 	/*
4419a747e4fSDavid du Colombier 	 *  we're using the config area to hold the secstore
4429a747e4fSDavid du Colombier 	 *  password.  if there's anything there, return it.
4439a747e4fSDavid du Colombier 	 */
4449a747e4fSDavid du Colombier 	memmove(spw, safe.config, CONFIGLEN);
4459a747e4fSDavid du Colombier 	spw[CONFIGLEN] = 0;
4469a747e4fSDavid du Colombier 	if(spw[0] != 0)
4479a747e4fSDavid du Colombier 		*secstorepw = estrdup(spw);
4489a747e4fSDavid du Colombier 
4499a747e4fSDavid du Colombier 	/*
4509a747e4fSDavid du Colombier 	 *  only use nvram key if it is non-zero
4519a747e4fSDavid du Colombier 	 */
4529a747e4fSDavid du Colombier 	for(i = 0; i < DESKEYLEN; i++)
4539a747e4fSDavid du Colombier 		if(safe.machkey[i] != 0)
4549a747e4fSDavid du Colombier 			break;
4559a747e4fSDavid du Colombier 	if(i == DESKEYLEN)
4569a747e4fSDavid du Colombier 		return nil;
4579a747e4fSDavid du Colombier 
4589a747e4fSDavid du Colombier 	s = emalloc(512);
4599a747e4fSDavid du Colombier 	fmtinstall('H', encodefmt);
4609a747e4fSDavid du Colombier 	sprint(s, "key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______",
4619a747e4fSDavid du Colombier 		safe.authid, safe.authdom, DESKEYLEN, safe.machkey);
4629a747e4fSDavid du Colombier 	writehostowner(safe.authid);
4639a747e4fSDavid du Colombier 
4649a747e4fSDavid du Colombier 	return s;
4659a747e4fSDavid du Colombier }
4669a747e4fSDavid du Colombier 
4679a747e4fSDavid du Colombier int
4689a747e4fSDavid du Colombier isclient(char *role)
4699a747e4fSDavid du Colombier {
4709a747e4fSDavid du Colombier 	if(role == nil){
4719a747e4fSDavid du Colombier 		werrstr("role not specified");
4729a747e4fSDavid du Colombier 		return -1;
4739a747e4fSDavid du Colombier 	}
4749a747e4fSDavid du Colombier 	if(strcmp(role, "server") == 0)
4759a747e4fSDavid du Colombier 		return 0;
4769a747e4fSDavid du Colombier 	if(strcmp(role, "client") == 0)
4779a747e4fSDavid du Colombier 		return 1;
4789a747e4fSDavid du Colombier 	werrstr("unknown role %q", role);
4799a747e4fSDavid du Colombier 	return -1;
4809a747e4fSDavid du Colombier }
4819a747e4fSDavid du Colombier 
4829a747e4fSDavid du Colombier static int
4839a747e4fSDavid du Colombier hasname(Attr *a0, Attr *a1, char *name)
4849a747e4fSDavid du Colombier {
4859a747e4fSDavid du Colombier 	return _findattr(a0, name) || _findattr(a1, name);
4869a747e4fSDavid du Colombier }
4879a747e4fSDavid du Colombier 
4889a747e4fSDavid du Colombier static int
4899a747e4fSDavid du Colombier hasnameval(Attr *a0, Attr *a1, char *name, char *val)
4909a747e4fSDavid du Colombier {
4919a747e4fSDavid du Colombier 	Attr *a;
4929a747e4fSDavid du Colombier 
4939a747e4fSDavid du Colombier 	for(a=_findattr(a0, name); a; a=_findattr(a->next, name))
4949a747e4fSDavid du Colombier 		if(strcmp(s_to_c(a->val), val) == 0)
4959a747e4fSDavid du Colombier 			return 1;
4969a747e4fSDavid du Colombier 	for(a=_findattr(a1, name); a; a=_findattr(a->next, name))
4979a747e4fSDavid du Colombier 		if(strcmp(s_to_c(a->val), val) == 0)
4989a747e4fSDavid du Colombier 			return 1;
4999a747e4fSDavid du Colombier 	return 0;
5009a747e4fSDavid du Colombier }
5019a747e4fSDavid du Colombier 
5029a747e4fSDavid du Colombier int
5039a747e4fSDavid du Colombier matchattr(Attr *pat, Attr *a0, Attr *a1)
5049a747e4fSDavid du Colombier {
5059a747e4fSDavid du Colombier 	int type;
5069a747e4fSDavid du Colombier 
5079a747e4fSDavid du Colombier 	for(; pat; pat=pat->next){
5089a747e4fSDavid du Colombier 		type = pat->type;
5099a747e4fSDavid du Colombier 		if(ignoreattr(s_to_c(pat->name)))
5109a747e4fSDavid du Colombier 			type = AttrDefault;
5119a747e4fSDavid du Colombier 		switch(type){
5129a747e4fSDavid du Colombier 		case AttrQuery:		/* name=something be present */
5139a747e4fSDavid du Colombier 			if(!hasname(a0, a1, s_to_c(pat->name)))
5149a747e4fSDavid du Colombier 				return 0;
5159a747e4fSDavid du Colombier 			break;
5169a747e4fSDavid du Colombier 		case AttrNameval:	/* name=val must be present */
5179a747e4fSDavid du Colombier 			if(!hasnameval(a0, a1, s_to_c(pat->name), s_to_c(pat->val)))
5189a747e4fSDavid du Colombier 				return 0;
5199a747e4fSDavid du Colombier 			break;
5209a747e4fSDavid du Colombier 		case AttrDefault:	/* name=val must be present if name=anything is present */
5219a747e4fSDavid du Colombier 			if(hasname(a0, a1, s_to_c(pat->name)) && !hasnameval(a0, a1, s_to_c(pat->name), s_to_c(pat->val)))
5229a747e4fSDavid du Colombier 				return 0;
5239a747e4fSDavid du Colombier 			break;
5249a747e4fSDavid du Colombier 		}
5259a747e4fSDavid du Colombier 	}
5269a747e4fSDavid du Colombier 	return 1;
5279a747e4fSDavid du Colombier }
5289a747e4fSDavid du Colombier 
5299a747e4fSDavid du Colombier void
5309a747e4fSDavid du Colombier memrandom(void *p, int n)
5319a747e4fSDavid du Colombier {
5329a747e4fSDavid du Colombier 	uchar *cp;
5339a747e4fSDavid du Colombier 
5349a747e4fSDavid du Colombier 	for(cp = (uchar*)p; n > 0; n--)
5359a747e4fSDavid du Colombier 		*cp++ = fastrand();
5369a747e4fSDavid du Colombier }
5379a747e4fSDavid du Colombier 
5389a747e4fSDavid du Colombier /*
5393ff48bf5SDavid du Colombier  *  keep caphash fd open since opens of it could be disabled
5403ff48bf5SDavid du Colombier  */
5413ff48bf5SDavid du Colombier static int caphashfd;
5423ff48bf5SDavid du Colombier 
5433ff48bf5SDavid du Colombier void
5443ff48bf5SDavid du Colombier initcap(void)
5453ff48bf5SDavid du Colombier {
5463ff48bf5SDavid du Colombier 	caphashfd = open("#¤/caphash", OWRITE);
547*d9306527SDavid du Colombier //	if(caphashfd < 0)
548*d9306527SDavid du Colombier //		fprint(2, "%s: opening #¤/caphash: %r\n", argv0);
5493ff48bf5SDavid du Colombier }
5503ff48bf5SDavid du Colombier 
5513ff48bf5SDavid du Colombier /*
5529a747e4fSDavid du Colombier  *  create a change uid capability
5539a747e4fSDavid du Colombier  */
5549a747e4fSDavid du Colombier char*
5553ff48bf5SDavid du Colombier mkcap(char *from, char *to)
5569a747e4fSDavid du Colombier {
5579a747e4fSDavid du Colombier 	uchar rand[20];
5589a747e4fSDavid du Colombier 	char *cap;
5599a747e4fSDavid du Colombier 	char *key;
5603ff48bf5SDavid du Colombier 	int nfrom, nto;
5619a747e4fSDavid du Colombier 	uchar hash[SHA1dlen];
5629a747e4fSDavid du Colombier 
5633ff48bf5SDavid du Colombier 	if(caphashfd < 0)
5649a747e4fSDavid du Colombier 		return nil;
5659a747e4fSDavid du Colombier 
5669a747e4fSDavid du Colombier 	/* create the capability */
5673ff48bf5SDavid du Colombier 	nto = strlen(to);
5683ff48bf5SDavid du Colombier 	nfrom = strlen(from);
5693ff48bf5SDavid du Colombier 	cap = emalloc(nfrom+1+nto+1+sizeof(rand)*3+1);
5703ff48bf5SDavid du Colombier 	sprint(cap, "%s@%s", from, to);
5719a747e4fSDavid du Colombier 	memrandom(rand, sizeof(rand));
5723ff48bf5SDavid du Colombier 	key = cap+nfrom+1+nto+1;
5739a747e4fSDavid du Colombier 	enc64(key, sizeof(rand)*3, rand, sizeof(rand));
5749a747e4fSDavid du Colombier 
5759a747e4fSDavid du Colombier 	/* hash the capability */
5763ff48bf5SDavid du Colombier 	hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
5779a747e4fSDavid du Colombier 
5789a747e4fSDavid du Colombier 	/* give the kernel the hash */
5793ff48bf5SDavid du Colombier 	key[-1] = '@';
5803ff48bf5SDavid du Colombier 	if(write(caphashfd, hash, SHA1dlen) < 0){
5819a747e4fSDavid du Colombier 		free(cap);
5829a747e4fSDavid du Colombier 		return nil;
5839a747e4fSDavid du Colombier 	}
5849a747e4fSDavid du Colombier 
5859a747e4fSDavid du Colombier 	return cap;
5869a747e4fSDavid du Colombier }
5879a747e4fSDavid du Colombier 
5889a747e4fSDavid du Colombier int
5899a747e4fSDavid du Colombier phaseerror(Fsstate *s, char *op)
5909a747e4fSDavid du Colombier {
5919a747e4fSDavid du Colombier 	char tmp[32];
5929a747e4fSDavid du Colombier 
5939a747e4fSDavid du Colombier 	werrstr("protocol phase error: %s in state %s", op, phasename(s, s->phase, tmp));
5949a747e4fSDavid du Colombier 	return RpcPhase;
5959a747e4fSDavid du Colombier }
5969a747e4fSDavid du Colombier 
5979a747e4fSDavid du Colombier char*
5989a747e4fSDavid du Colombier phasename(Fsstate *fss, int phase, char *tmp)
5999a747e4fSDavid du Colombier {
6009a747e4fSDavid du Colombier 	char *name;
6019a747e4fSDavid du Colombier 
6029a747e4fSDavid du Colombier 	if(fss->phase == Broken)
6039a747e4fSDavid du Colombier 		name = "Broken";
6049a747e4fSDavid du Colombier 	else if(phase == Established)
6059a747e4fSDavid du Colombier 		name = "Established";
6069a747e4fSDavid du Colombier 	else if(phase == Notstarted)
6079a747e4fSDavid du Colombier 		name = "Notstarted";
6089a747e4fSDavid du Colombier 	else if(phase < 0 || phase >= fss->maxphase
6099a747e4fSDavid du Colombier 	|| (name = fss->phasename[phase]) == nil){
6109a747e4fSDavid du Colombier 		sprint(tmp, "%d", phase);
6119a747e4fSDavid du Colombier 		name = tmp;
6129a747e4fSDavid du Colombier 	}
6139a747e4fSDavid du Colombier 	return name;
6149a747e4fSDavid du Colombier }
6159a747e4fSDavid du Colombier 
6169a747e4fSDavid du Colombier static int
6179a747e4fSDavid du Colombier outin(char *prompt, char *def, int len)
6189a747e4fSDavid du Colombier {
6199a747e4fSDavid du Colombier 	String *s;
6209a747e4fSDavid du Colombier 
6219a747e4fSDavid du Colombier 	s = readcons(prompt, def, 0);
6229a747e4fSDavid du Colombier 	if(s == nil)
6239a747e4fSDavid du Colombier 		return -1;
6249a747e4fSDavid du Colombier 	if(s_to_c(s) == nil)
6259a747e4fSDavid du Colombier 		sysfatal("s_to_c(s)==nil???");
6269a747e4fSDavid du Colombier 	strncpy(def, s_to_c(s), len);
6279a747e4fSDavid du Colombier 	def[len-1] = 0;
6289a747e4fSDavid du Colombier 	s_free(s);
6299a747e4fSDavid du Colombier 	return strlen(def);
6309a747e4fSDavid du Colombier }
6319a747e4fSDavid du Colombier 
6329a747e4fSDavid du Colombier /*
6339a747e4fSDavid du Colombier  *  get host owner and set it
6349a747e4fSDavid du Colombier  */
6359a747e4fSDavid du Colombier void
6369a747e4fSDavid du Colombier promptforhostowner(void)
6379a747e4fSDavid du Colombier {
6389a747e4fSDavid du Colombier 	char owner[64], *p;
6399a747e4fSDavid du Colombier 
6409a747e4fSDavid du Colombier 	/* hack for bitsy; can't prompt during boot */
6419a747e4fSDavid du Colombier 	if(p = getenv("user")){
6429a747e4fSDavid du Colombier 		writehostowner(p);
643*d9306527SDavid du Colombier 		free(p);
6449a747e4fSDavid du Colombier 		return;
6459a747e4fSDavid du Colombier 	}
646*d9306527SDavid du Colombier 	free(p);
6479a747e4fSDavid du Colombier 
6489a747e4fSDavid du Colombier 	strcpy(owner, "none");
6499a747e4fSDavid du Colombier 	do{
6509a747e4fSDavid du Colombier 		outin("user", owner, sizeof(owner));
6519a747e4fSDavid du Colombier 	} while(*owner == 0);
6529a747e4fSDavid du Colombier 	writehostowner(owner);
6539a747e4fSDavid du Colombier }
6549a747e4fSDavid du Colombier 
6559a747e4fSDavid du Colombier /*
6569a747e4fSDavid du Colombier  *  prompt for a string with a possible default response
6579a747e4fSDavid du Colombier  */
6589a747e4fSDavid du Colombier String*
6599a747e4fSDavid du Colombier readcons(char *prompt, char *def, int raw)
6609a747e4fSDavid du Colombier {
6619a747e4fSDavid du Colombier 	int fdin, fdout, ctl, n;
6629a747e4fSDavid du Colombier 	char line[10];
6639a747e4fSDavid du Colombier 	String *s = s_new();
6649a747e4fSDavid du Colombier 
6659a747e4fSDavid du Colombier 	fdin = open("/dev/cons", OREAD);
6669a747e4fSDavid du Colombier 	if(fdin < 0)
6679a747e4fSDavid du Colombier 		fdin = 0;
6689a747e4fSDavid du Colombier 	fdout = open("/dev/cons", OWRITE);
6699a747e4fSDavid du Colombier 	if(fdout < 0)
6709a747e4fSDavid du Colombier 		fdout = 1;
6719a747e4fSDavid du Colombier 	if(def != nil)
6729a747e4fSDavid du Colombier 		fprint(fdout, "%s[%s]: ", prompt, def);
6739a747e4fSDavid du Colombier 	else
6749a747e4fSDavid du Colombier 		fprint(fdout, "%s: ", prompt);
6759a747e4fSDavid du Colombier 	if(raw){
6769a747e4fSDavid du Colombier 		ctl = open("/dev/consctl", OWRITE);
6779a747e4fSDavid du Colombier 		if(ctl >= 0)
6789a747e4fSDavid du Colombier 			write(ctl, "rawon", 5);
6799a747e4fSDavid du Colombier 	} else
6809a747e4fSDavid du Colombier 		ctl = -1;
6819a747e4fSDavid du Colombier 	for(;;){
6829a747e4fSDavid du Colombier 		n = read(fdin, line, 1);
6839a747e4fSDavid du Colombier 		if(n == 0){
6849a747e4fSDavid du Colombier 		Error:
6859a747e4fSDavid du Colombier 			close(fdin);
6869a747e4fSDavid du Colombier 			close(fdout);
6879a747e4fSDavid du Colombier 			if(ctl >= 0)
6889a747e4fSDavid du Colombier 				close(ctl);
6899a747e4fSDavid du Colombier 			s_free(s);
6909a747e4fSDavid du Colombier 			return nil;
6919a747e4fSDavid du Colombier 		}
6929a747e4fSDavid du Colombier 		if(n < 0)
6939a747e4fSDavid du Colombier 			goto Error;
6949a747e4fSDavid du Colombier 		if(line[0] == 0x7f)
6959a747e4fSDavid du Colombier 			goto Error;
6969a747e4fSDavid du Colombier 		if(n == 0 || line[0] == '\n' || line[0] == '\r'){
6979a747e4fSDavid du Colombier 			if(raw){
6989a747e4fSDavid du Colombier 				write(ctl, "rawoff", 6);
6999a747e4fSDavid du Colombier 				write(fdout, "\n", 1);
7009a747e4fSDavid du Colombier 			}
7019a747e4fSDavid du Colombier 			close(ctl);
7029a747e4fSDavid du Colombier 			close(fdin);
7039a747e4fSDavid du Colombier 			close(fdout);
7049a747e4fSDavid du Colombier 			s_terminate(s);
7059a747e4fSDavid du Colombier 			if(*s_to_c(s) == 0 && def != nil)
7069a747e4fSDavid du Colombier 				s_append(s, def);
7079a747e4fSDavid du Colombier 			return s;
7089a747e4fSDavid du Colombier 		}
7099a747e4fSDavid du Colombier 		if(line[0] == '\b'){
7109a747e4fSDavid du Colombier 			if(s_len(s) > 0)
7119a747e4fSDavid du Colombier 				s->ptr--;
7129a747e4fSDavid du Colombier 		} else if(line[0] == 0x15) {	/* ^U: line kill */
7139a747e4fSDavid du Colombier 			s_reset(s);
7149a747e4fSDavid du Colombier 		} else {
7159a747e4fSDavid du Colombier 			s_putc(s, line[0]);
7169a747e4fSDavid du Colombier 		}
7179a747e4fSDavid du Colombier 	}
7189a747e4fSDavid du Colombier 	return s;	/* how does this happen */
7199a747e4fSDavid du Colombier }
7209a747e4fSDavid du Colombier 
7219a747e4fSDavid du Colombier /*
7229a747e4fSDavid du Colombier  * Insert a key into the keyring.
7239a747e4fSDavid du Colombier  * If the public attributes are identical to some other key, replace that one.
7249a747e4fSDavid du Colombier  */
7259a747e4fSDavid du Colombier int
7269a747e4fSDavid du Colombier replacekey(Key *kn)
7279a747e4fSDavid du Colombier {
7289a747e4fSDavid du Colombier 	int i;
7299a747e4fSDavid du Colombier 	Key *k;
7309a747e4fSDavid du Colombier 
7319a747e4fSDavid du Colombier 	for(i=0; i<ring->nkey; i++){
7329a747e4fSDavid du Colombier 		k = ring->key[i];
7339a747e4fSDavid du Colombier 		if(matchattr(kn->attr, k->attr, nil) && matchattr(k->attr, kn->attr, nil)){
7349a747e4fSDavid du Colombier 			closekey(k);
7359a747e4fSDavid du Colombier 			kn->ref++;
7369a747e4fSDavid du Colombier 			ring->key[i] = kn;
7379a747e4fSDavid du Colombier 			return 0;
7389a747e4fSDavid du Colombier 		}
7399a747e4fSDavid du Colombier 	}
7409a747e4fSDavid du Colombier 	if(ring->nkey%16 == 0)
7419a747e4fSDavid du Colombier 		ring->key = erealloc(ring->key, (ring->nkey+16)*sizeof(ring->key[0]));
7429a747e4fSDavid du Colombier 	kn->ref++;
7439a747e4fSDavid du Colombier 	ring->key[ring->nkey++] = kn;
7449a747e4fSDavid du Colombier 	return 0;
7459a747e4fSDavid du Colombier }
7469a747e4fSDavid du Colombier 
7479a747e4fSDavid du Colombier char*
7489a747e4fSDavid du Colombier safecpy(char *to, char *from, int n)
7499a747e4fSDavid du Colombier {
7509a747e4fSDavid du Colombier 	memset(to, 0, n);
7519a747e4fSDavid du Colombier 	if(n == 1)
7529a747e4fSDavid du Colombier 		return to;
7539a747e4fSDavid du Colombier 	if(from==nil)
7549a747e4fSDavid du Colombier 		sysfatal("safecpy called with from==nil, pc=%lux\n",
7559a747e4fSDavid du Colombier 			getcallerpc(&to));
7569a747e4fSDavid du Colombier 	strncpy(to, from, n-1);
7579a747e4fSDavid du Colombier 	return to;
7589a747e4fSDavid du Colombier }
7599a747e4fSDavid du Colombier 
7609a747e4fSDavid du Colombier Attr*
7619a747e4fSDavid du Colombier setattr(Attr *a, char *fmt, ...)
7629a747e4fSDavid du Colombier {
7639a747e4fSDavid du Colombier 	char buf[1024];
7649a747e4fSDavid du Colombier 	va_list arg;
7659a747e4fSDavid du Colombier 	Attr *b;
7669a747e4fSDavid du Colombier 
7679a747e4fSDavid du Colombier 	va_start(arg, fmt);
7689a747e4fSDavid du Colombier 	vseprint(buf, buf+sizeof buf, fmt, arg);
7699a747e4fSDavid du Colombier 	va_end(arg);
7709a747e4fSDavid du Colombier 	b = _parseattr(buf);
7719a747e4fSDavid du Colombier 	a = setattrs(a, b);
7729a747e4fSDavid du Colombier 	setmalloctag(a, getcallerpc(&a));
7739a747e4fSDavid du Colombier 	_freeattr(b);
7749a747e4fSDavid du Colombier 	return a;
7759a747e4fSDavid du Colombier }
7769a747e4fSDavid du Colombier 
7779a747e4fSDavid du Colombier /*
7789a747e4fSDavid du Colombier  *  add attributes in list b to list a.  If any attributes are in
7799a747e4fSDavid du Colombier  *  both lists, replace those in a by those in b.
7809a747e4fSDavid du Colombier  */
7819a747e4fSDavid du Colombier Attr*
7829a747e4fSDavid du Colombier setattrs(Attr *a, Attr *b)
7839a747e4fSDavid du Colombier {
7849a747e4fSDavid du Colombier 	int found;
7859a747e4fSDavid du Colombier 	Attr **l, *freea;
7869a747e4fSDavid du Colombier 
7879a747e4fSDavid du Colombier 	for(; b; b=b->next){
7889a747e4fSDavid du Colombier 		found = 0;
7899a747e4fSDavid du Colombier 		for(l=&a; *l; ){
7909a747e4fSDavid du Colombier 			if(strcmp(s_to_c(b->name), s_to_c((*l)->name)) == 0){
7919a747e4fSDavid du Colombier 				switch(b->type){
7929a747e4fSDavid du Colombier 				case AttrNameval:
7939a747e4fSDavid du Colombier 					if(!found){
7949a747e4fSDavid du Colombier 						found = 1;
7959a747e4fSDavid du Colombier 						s_free((*l)->val);
7969a747e4fSDavid du Colombier 						(*l)->val = s_incref(b->val);
7979a747e4fSDavid du Colombier 						(*l)->type = AttrNameval;
7989a747e4fSDavid du Colombier 						l = &(*l)->next;
7999a747e4fSDavid du Colombier 					}else{
8009a747e4fSDavid du Colombier 						freea = *l;
8019a747e4fSDavid du Colombier 						*l = (*l)->next;
8029a747e4fSDavid du Colombier 						freea->next = nil;
8039a747e4fSDavid du Colombier 						_freeattr(freea);
8049a747e4fSDavid du Colombier 					}
8059a747e4fSDavid du Colombier 					break;
8069a747e4fSDavid du Colombier 				case AttrQuery:
8079a747e4fSDavid du Colombier 					found++;
8089a747e4fSDavid du Colombier 					break;
8099a747e4fSDavid du Colombier 				}
8109a747e4fSDavid du Colombier 			}else
8119a747e4fSDavid du Colombier 				l = &(*l)->next;
8129a747e4fSDavid du Colombier 		}
8139a747e4fSDavid du Colombier 		if(found == 0){
8149a747e4fSDavid du Colombier 			*l = _mkattr(b->type, s_to_c(b->name), s_to_c(b->val), nil);
8159a747e4fSDavid du Colombier 			setmalloctag(*l, getcallerpc(&a));
8169a747e4fSDavid du Colombier 		}
8179a747e4fSDavid du Colombier 	}
8189a747e4fSDavid du Colombier 	return a;
8199a747e4fSDavid du Colombier }
8209a747e4fSDavid du Colombier 
8219a747e4fSDavid du Colombier void
8229a747e4fSDavid du Colombier setmalloctaghere(void *v)
8239a747e4fSDavid du Colombier {
8249a747e4fSDavid du Colombier 	setmalloctag(v, getcallerpc(&v));
8259a747e4fSDavid du Colombier }
8269a747e4fSDavid du Colombier 
8279a747e4fSDavid du Colombier Attr*
8289a747e4fSDavid du Colombier sortattr(Attr *a)
8299a747e4fSDavid du Colombier {
8309a747e4fSDavid du Colombier 	int i;
8319a747e4fSDavid du Colombier 	Attr *anext, *a0, *a1, **l;
8329a747e4fSDavid du Colombier 
8339a747e4fSDavid du Colombier 	if(a == nil || a->next == nil)
8349a747e4fSDavid du Colombier 		return a;
8359a747e4fSDavid du Colombier 
8369a747e4fSDavid du Colombier 	/* cut list in halves */
8379a747e4fSDavid du Colombier 	a0 = nil;
8389a747e4fSDavid du Colombier 	a1 = nil;
8399a747e4fSDavid du Colombier 	i = 0;
8409a747e4fSDavid du Colombier 	for(; a; a=anext){
8419a747e4fSDavid du Colombier 		anext = a->next;
8429a747e4fSDavid du Colombier 		if(i++%2){
8439a747e4fSDavid du Colombier 			a->next = a0;
8449a747e4fSDavid du Colombier 			a0 = a;
8459a747e4fSDavid du Colombier 		}else{
8469a747e4fSDavid du Colombier 			a->next = a1;
8479a747e4fSDavid du Colombier 			a1 = a;
8489a747e4fSDavid du Colombier 		}
8499a747e4fSDavid du Colombier 	}
8509a747e4fSDavid du Colombier 
8519a747e4fSDavid du Colombier 	/* sort */
8529a747e4fSDavid du Colombier 	a0 = sortattr(a0);
8539a747e4fSDavid du Colombier 	a1 = sortattr(a1);
8549a747e4fSDavid du Colombier 
8559a747e4fSDavid du Colombier 	/* merge */
8569a747e4fSDavid du Colombier 	l = &a;
8579a747e4fSDavid du Colombier 	while(a0 || a1){
8589a747e4fSDavid du Colombier 		if(a1==nil){
8599a747e4fSDavid du Colombier 			anext = a0;
8609a747e4fSDavid du Colombier 			a0 = a0->next;
8619a747e4fSDavid du Colombier 		}else if(a0==nil){
8629a747e4fSDavid du Colombier 			anext = a1;
8639a747e4fSDavid du Colombier 			a1 = a1->next;
8649a747e4fSDavid du Colombier 		}else if(strcmp(s_to_c(a0->name), s_to_c(a1->name)) < 0){
8659a747e4fSDavid du Colombier 			anext = a0;
8669a747e4fSDavid du Colombier 			a0 = a0->next;
8679a747e4fSDavid du Colombier 		}else{
8689a747e4fSDavid du Colombier 			anext = a1;
8699a747e4fSDavid du Colombier 			a1 = a1->next;
8709a747e4fSDavid du Colombier 		}
8719a747e4fSDavid du Colombier 		*l = anext;
8729a747e4fSDavid du Colombier 		l = &(*l)->next;
8739a747e4fSDavid du Colombier 	}
8749a747e4fSDavid du Colombier 	*l = nil;
8759a747e4fSDavid du Colombier 	return a;
8769a747e4fSDavid du Colombier }
8779a747e4fSDavid du Colombier 
8789a747e4fSDavid du Colombier int
8799a747e4fSDavid du Colombier toosmall(Fsstate *fss, uint n)
8809a747e4fSDavid du Colombier {
8819a747e4fSDavid du Colombier 	fss->rpc.nwant = n;
8829a747e4fSDavid du Colombier 	return RpcToosmall;
8839a747e4fSDavid du Colombier }
8849a747e4fSDavid du Colombier 
8859a747e4fSDavid du Colombier void
8869a747e4fSDavid du Colombier writehostowner(char *owner)
8879a747e4fSDavid du Colombier {
8889a747e4fSDavid du Colombier 	int fd;
8899a747e4fSDavid du Colombier 	char *s;
8909a747e4fSDavid du Colombier 
8919a747e4fSDavid du Colombier 	if((s = strchr(owner,'@')) != nil){
8929a747e4fSDavid du Colombier 		*s++ = 0;
8939a747e4fSDavid du Colombier 		strncpy(secstore, s, (sizeof secstore)-1);
8949a747e4fSDavid du Colombier 	}
8959a747e4fSDavid du Colombier 	fd = open("#c/hostowner", OWRITE);
8969a747e4fSDavid du Colombier 	if(fd >= 0){
8979a747e4fSDavid du Colombier 		if(fprint(fd, "%s", owner) < 0)
8989a747e4fSDavid du Colombier 			fprint(2, "setting #c/hostowner to %q: %r\n", owner);
8999a747e4fSDavid du Colombier 		close(fd);
9009a747e4fSDavid du Colombier 	}
9019a747e4fSDavid du Colombier }
9029a747e4fSDavid du Colombier 
9035d459b5aSDavid du Colombier int
9045d459b5aSDavid du Colombier attrnamefmt(Fmt *fmt)
9055d459b5aSDavid du Colombier {
9065d459b5aSDavid du Colombier 	char *b, buf[1024], *ebuf;
9075d459b5aSDavid du Colombier 	Attr *a;
9085d459b5aSDavid du Colombier 
9095d459b5aSDavid du Colombier 	ebuf = buf+sizeof buf;
9105d459b5aSDavid du Colombier 	b = buf;
9115d459b5aSDavid du Colombier 	strcpy(buf, " ");
9125d459b5aSDavid du Colombier 	for(a=va_arg(fmt->args, Attr*); a; a=a->next){
9135d459b5aSDavid du Colombier 		if(a->name == nil)
9145d459b5aSDavid du Colombier 			continue;
9155d459b5aSDavid du Colombier 		b = seprint(b, ebuf, " %q?", s_to_c(a->name));
9165d459b5aSDavid du Colombier 	}
9175d459b5aSDavid du Colombier 	return fmtstrcpy(fmt, buf+1);
9185d459b5aSDavid du Colombier }
919