plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

p9any.c (4954B)


      1 #include "std.h"
      2 #include "dat.h"
      3 
      4 /*
      5  * p9any - protocol negotiator
      6  *
      7  * Protocol:
      8  *	S->C: v.2 proto@dom proto@dom proto@dom... NUL
      9  *	C->S: proto dom NUL
     10  *	[negotiated proto continues]
     11  */
     12 
     13 extern Proto p9sk1, p9sk2, p9cr;
     14 
     15 static Proto* okproto[] =
     16 {
     17 	&p9sk1,
     18 	nil
     19 };
     20 
     21 static int
     22 rolecall(Role *r, char *name, Conv *c)
     23 {
     24 	for(; r->name; r++)
     25 		if(strcmp(r->name, name) == 0)
     26 			return (*r->fn)(c);
     27 	werrstr("unknown role");
     28 	return -1;
     29 }
     30 
     31 static int
     32 hasnul(void *v, int n)
     33 {
     34 	char *c;
     35 
     36 	c = v;
     37 	if(n > 0 && c[n-1] == '\0')
     38 		return n;
     39 	else
     40 		return AuthRpcMax;
     41 }
     42 
     43 static int
     44 p9anyserver(Conv *c)
     45 {
     46 	char *s, *dom;
     47 	int i, j, n, m, ret;
     48 	char *tok[3];
     49 	Attr *attr;
     50 	Key *k;
     51 
     52 	ret = -1;
     53 	s = estrdup("v.2");
     54 	n = 0;
     55 	attr = delattr(copyattr(c->attr), "proto");
     56 
     57 	for(i=0; i<ring.nkey; i++){
     58 		k = ring.key[i];
     59 		for(j=0; okproto[j]; j++)
     60 			if(k->proto == okproto[j]
     61 			&& (dom = strfindattr(k->attr, "dom")) != nil
     62 			&& matchattr(attr, k->attr, k->privattr)){
     63 				s = estrappend(s, " %s@%s", k->proto->name, dom);
     64 				n++;
     65 			}
     66 	}
     67 
     68 	if(n == 0){
     69 		werrstr("no valid keys");
     70 		goto out;
     71 	}
     72 
     73 	c->state = "write offer";
     74 	if(convwrite(c, s, strlen(s)+1) < 0)
     75 		goto out;
     76 	free(s);
     77 	s = nil;
     78 
     79 	c->state = "read choice";
     80 	if(convreadfn(c, hasnul, &s) < 0)
     81 		goto out;
     82 
     83 	m = tokenize(s, tok, nelem(tok));
     84 	if(m != 2){
     85 		werrstr("bad protocol message");
     86 		goto out;
     87 	}
     88 
     89 	for(i=0; okproto[i]; i++)
     90 		if(strcmp(okproto[i]->name, tok[0]) == 0)
     91 			break;
     92 	if(!okproto[i]){
     93 		werrstr("bad chosen protocol %q", tok[0]);
     94 		goto out;
     95 	}
     96 
     97 	c->state = "write ok";
     98 	if(convwrite(c, "OK\0", 3) < 0)
     99 		goto out;
    100 
    101 	c->state = "start choice";
    102 	attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
    103 	free(c->attr);
    104 	c->attr = attr;
    105 	attr = nil;
    106 	c->proto = okproto[i];
    107 
    108 	if(rolecall(c->proto->roles, "server", c) < 0){
    109 		werrstr("%s: %r", tok[0]);
    110 		goto out;
    111 	}
    112 
    113 	ret = 0;
    114 
    115 out:
    116 	free(s);
    117 	freeattr(attr);
    118 	return ret;
    119 }
    120 
    121 static int
    122 p9anyclient(Conv *c)
    123 {
    124 	char *s, **f, *tok[20], ok[3], *q, *user, *dom, *choice;
    125 	int i, n, ret, version;
    126 	Key *k;
    127 	Attr *attr;
    128 	Proto *p;
    129 
    130 	ret = -1;
    131 	s = nil;
    132 	k = nil;
    133 
    134 	user = strfindattr(c->attr, "user");
    135 	dom = strfindattr(c->attr, "dom");
    136 
    137 	/*
    138 	 * if the user is the factotum owner, any key will do.
    139 	 * if not, then if we have a speakfor key,
    140 	 * we will only vouch for the user's local identity.
    141 	 *
    142 	 * this logic is duplicated in p9sk1.c
    143 	 */
    144 	attr = delattr(copyattr(c->attr), "role");
    145 	attr = delattr(attr, "proto");
    146 	if(strcmp(c->sysuser, owner) == 0)
    147 		attr = addattr(attr, "role=client");
    148 	else if(user==nil || strcmp(c->sysuser, user)==0){
    149 		attr = delattr(attr, "user");
    150 		attr = addattr(attr, "role=speakfor");
    151 	}else{
    152 		werrstr("will not authenticate for %q as %q", c->sysuser, user);
    153 		goto out;
    154 	}
    155 
    156 	c->state = "read offer";
    157 	if(convreadfn(c, hasnul, &s) < 0)
    158 		goto out;
    159 
    160 	c->state = "look for keys";
    161 	n = tokenize(s, tok, nelem(tok));
    162 	f = tok;
    163 	version = 1;
    164 	if(n > 0 && memcmp(f[0], "v.", 2) == 0){
    165 		version = atoi(f[0]+2);
    166 		if(version != 2){
    167 			werrstr("unknown p9any version: %s", f[0]);
    168 			goto out;
    169 		}
    170 		f++;
    171 		n--;
    172 	}
    173 
    174 	/* look for keys that don't need confirmation */
    175 	for(i=0; i<n; i++){
    176 		if((q = strchr(f[i], '@')) == nil)
    177 			continue;
    178 		if(dom && strcmp(q+1, dom) != 0)
    179 			continue;
    180 		*q++ = '\0';
    181 		if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
    182 		&& strfindattr(k->attr, "confirm") == nil)
    183 			goto found;
    184 		*--q = '@';
    185 	}
    186 
    187 	/* look for any keys at all */
    188 	for(i=0; i<n; i++){
    189 		if((q = strchr(f[i], '@')) == nil)
    190 			continue;
    191 		if(dom && strcmp(q+1, dom) != 0)
    192 			continue;
    193 		*q++ = '\0';
    194 		if(k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
    195 			goto found;
    196 		*--q = '@';
    197 	}
    198 
    199 	/* ask for new keys */
    200 	c->state = "ask for keys";
    201 	for(i=0; i<n; i++){
    202 		if((q = strchr(f[i], '@')) == nil)
    203 			continue;
    204 		if(dom && strcmp(q+1, dom) != 0)
    205 			continue;
    206 		*q++ = '\0';
    207 		p = protolookup(f[i]);
    208 		if(p == nil || p->keyprompt == nil){
    209 			*--q = '@';
    210 			continue;
    211 		}
    212 		if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
    213 			goto found;
    214 		*--q = '@';
    215 	}
    216 
    217 	/* nothing worked */
    218 	werrstr("unable to find common key");
    219 	goto out;
    220 
    221 found:
    222 	/* f[i] is the chosen protocol, q the chosen domain */
    223 	attr = addattr(attr, "proto=%q dom=%q", f[i], q);
    224 	c->state = "write choice";
    225 
    226 	/* have a key: go for it */
    227 	choice = estrappend(nil, "%q %q", f[i], q);
    228 	if(convwrite(c, choice, strlen(choice)+1) < 0){
    229 		free(choice);
    230 		goto out;
    231 	}
    232 	free(choice);
    233 
    234 	if(version == 2){
    235 		c->state = "read ok";
    236 		if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
    237 			goto out;
    238 	}
    239 
    240 	c->state = "start choice";
    241 	c->proto = protolookup(f[i]);
    242 	freeattr(c->attr);
    243 	c->attr = attr;
    244 	attr = nil;
    245 
    246 	if(rolecall(c->proto->roles, "client", c) < 0){
    247 		werrstr("%s: %r", c->proto->name);
    248 		goto out;
    249 	}
    250 
    251 	ret = 0;
    252 
    253 out:
    254 	keyclose(k);
    255 	freeattr(attr);
    256 	free(s);
    257 	return ret;
    258 }
    259 
    260 static Role
    261 p9anyroles[] =
    262 {
    263 	"client",	p9anyclient,
    264 	"server",	p9anyserver,
    265 	0
    266 };
    267 
    268 Proto p9any = {
    269 	"p9any",
    270 	p9anyroles
    271 };