xref: /plan9-contrib/sys/src/cmd/auth/factotum/p9any.c (revision 260f7b65f27bafa63b361ec761042c0d76ce7e36)
1 /*
2  * p9any - protocol negotiator.
3  *
4  * Protocol:
5  *	Server->Client: list of proto@domain, tokenize separated, nul terminated
6  *	Client->Server: proto domain, tokenize separated (not proto@domain), nul terminated
7  *
8  * Server protocol:
9  * 	read list of protocols.
10  *	write null-terminated
11  */
12 
13 #include "dat.h"
14 
15 static Proto *negotiable[] = {
16 	&p9sk1,
17 };
18 
19 struct State
20 {
21 	Fsstate subfss;
22 	State *substate;	/* be very careful; this is not one of our States */
23 	Proto *subproto;
24 	int keyasked;
25 	String *subdom;
26 	int version;
27 };
28 
29 enum
30 {
31 	CNeedProtos,
32 	CHaveProto,
33 	CNeedOK,
34 	CRelay,
35 
36 	SHaveProtos,
37 	SNeedProto,
38 	SHaveOK,
39 	SRelay,
40 
41 	Maxphase,
42 };
43 
44 static char *phasenames[Maxphase] =
45 {
46 [CNeedProtos]	"CNeedProtos",
47 [CHaveProto]	"CHaveProto",
48 [CNeedOK]	"CNeedOK",
49 [CRelay]	"CRelay",
50 [SHaveProtos]	"SHaveProtos",
51 [SNeedProto]	"SNeedProto",
52 [SHaveOK]	"SHaveOK",
53 [SRelay]	"SRelay",
54 };
55 
56 static int
p9anyinit(Proto *,Fsstate * fss)57 p9anyinit(Proto*, Fsstate *fss)
58 {
59 	int iscli;
60 	State *s;
61 
62 	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
63 		return failure(fss, nil);
64 
65 	s = emalloc(sizeof *s);
66 	fss = fss;
67 	fss->phasename = phasenames;
68 	fss->maxphase = Maxphase;
69 	if(iscli)
70 		fss->phase = CNeedProtos;
71 	else
72 		fss->phase = SHaveProtos;
73 	s->version = 1;
74 	fss->ps = s;
75 	return RpcOk;
76 }
77 
78 static void
p9anyclose(Fsstate * fss)79 p9anyclose(Fsstate *fss)
80 {
81 	State *s;
82 
83 	s = fss->ps;
84 	if(s->subproto && s->subfss.ps && s->subproto->close)
85 		(*s->subproto->close)(&s->subfss);
86 	s->subproto = nil;
87 	s->substate = nil;
88 	s_free(s->subdom);
89 	s->subdom = nil;
90 	s->keyasked = 0;
91 	memset(&s->subfss, 0, sizeof s->subfss);
92 	free(s);
93 }
94 
95 static void
setupfss(Fsstate * fss,State * s,Key * k)96 setupfss(Fsstate *fss, State *s, Key *k)
97 {
98 	fss->attr = setattr(fss->attr, "proto=%q", s->subproto->name);
99 	fss->attr = setattr(fss->attr, "dom=%q", _strfindattr(k->attr, "dom"));
100 	s->subfss.attr = fss->attr;
101 	s->subfss.phase = Notstarted;
102 	s->subfss.sysuser = fss->sysuser;
103 	s->subfss.seqnum = fss->seqnum;
104 	s->subfss.conf = fss->conf;
105 	s->subfss.nconf = fss->nconf;
106 }
107 
108 static int
passret(Fsstate * fss,State * s,int ret)109 passret(Fsstate *fss, State *s, int ret)
110 {
111 	switch(ret){
112 	default:
113 		return ret;
114 	case RpcFailure:
115 		if(s->subfss.phase == Broken)
116 			fss->phase = Broken;
117 		memmove(fss->err, s->subfss.err, sizeof fss->err);
118 		return ret;
119 	case RpcNeedkey:
120 		memmove(fss->keyinfo, s->subfss.keyinfo, sizeof fss->keyinfo);
121 		return ret;
122 	case RpcOk:
123 		if(s->subfss.haveai){
124 			fss->haveai = 1;
125 			fss->ai = s->subfss.ai;
126 			s->subfss.haveai = 0;
127 		}
128 		if(s->subfss.phase == Established)
129 			fss->phase = Established;
130 		return ret;
131 	case RpcToosmall:
132 		fss->rpc.nwant = s->subfss.rpc.nwant;
133 		return ret;
134 	case RpcConfirm:
135 		fss->conf = s->subfss.conf;
136 		fss->nconf = s->subfss.nconf;
137 		return ret;
138 	}
139 }
140 
141 static int
p9anyread(Fsstate * fss,void * a,uint * n)142 p9anyread(Fsstate *fss, void *a, uint *n)
143 {
144 	int i, m, ophase, ret;
145 	Attr *anew;
146 	Key *k;
147 	Keyinfo ki;
148 	String *negstr;
149 	State *s;
150 
151 	s = fss->ps;
152 	switch(fss->phase){
153 	default:
154 		return phaseerror(fss, "read");
155 
156 	case SHaveProtos:
157 		m = 0;
158 		negstr = s_new();
159 		mkkeyinfo(&ki, fss, nil);
160 		ki.attr = nil;
161 		ki.noconf = 1;
162 		ki.user = nil;
163 		for(i=0; i<nelem(negotiable); i++){
164 			anew = setattr(_copyattr(fss->attr), "proto=%q dom?", negotiable[i]->name);
165 			ki.attr = anew;
166 			for(ki.skip=0; findkey(&k, &ki, nil)==RpcOk; ki.skip++){
167 				if(m++)
168 					s_append(negstr, " ");
169 				s_append(negstr, negotiable[i]->name);
170 				s_append(negstr, "@");
171 				s_append(negstr, _strfindattr(k->attr, "dom"));
172 				closekey(k);
173 			}
174 			_freeattr(anew);
175 		}
176 		if(m == 0){
177 			s_free(negstr);
178 			return failure(fss, Enegotiation);
179 		}
180 		i = s_len(negstr)+1;
181 		if(*n < i){
182 			s_free(negstr);
183 			return toosmall(fss, i);
184 		}
185 		*n = i;
186 		memmove(a, s_to_c(negstr), i+1);
187 		fss->phase = SNeedProto;
188 		s_free(negstr);
189 		return RpcOk;
190 
191 	case CHaveProto:
192 		i = strlen(s->subproto->name)+1+s_len(s->subdom)+1;
193 		if(*n < i)
194 			return toosmall(fss, i);
195 		*n = i;
196 		strcpy(a, s->subproto->name);
197 		strcat(a, " ");
198 		strcat(a, s_to_c(s->subdom));
199 		if(s->version == 1)
200 			fss->phase = CRelay;
201 		else
202 			fss->phase = CNeedOK;
203 		return RpcOk;
204 
205 	case SHaveOK:
206 		i = 3;
207 		if(*n < i)
208 			return toosmall(fss, i);
209 		*n = i;
210 		strcpy(a, "OK");
211 		fss->phase = SRelay;
212 		return RpcOk;
213 
214 	case CRelay:
215 	case SRelay:
216 		ophase = s->subfss.phase;
217 		ret = (*s->subproto->read)(&s->subfss, a, n);
218 		rpcrdwrlog(&s->subfss, "read", *n, ophase, ret);
219 		return passret(fss, s, ret);
220 	}
221 }
222 
223 static char*
getdom(char * p)224 getdom(char *p)
225 {
226 	p = strchr(p, '@');
227 	if(p == nil)
228 		return "";
229 	return p+1;
230 }
231 
232 static Proto*
findneg(char * name)233 findneg(char *name)
234 {
235 	int i, len;
236 	char *p;
237 
238 	if(p = strchr(name, '@'))
239 		len = p-name;
240 	else
241 		len = strlen(name);
242 
243 	for(i=0; i<nelem(negotiable); i++)
244 		if(strncmp(negotiable[i]->name, name, len) == 0 && negotiable[i]->name[len] == 0)
245 			return negotiable[i];
246 	return nil;
247 }
248 
249 static int
p9anywrite(Fsstate * fss,void * va,uint n)250 p9anywrite(Fsstate *fss, void *va, uint n)
251 {
252 	char *a, *dom, *user, *token[20];
253 	int asking, i, m, ophase, ret;
254 	Attr *anew, *anewsf, *attr;
255 	Key *k;
256 	Keyinfo ki;
257 	Proto *p;
258 	State *s;
259 
260 	s = fss->ps;
261 	a = va;
262 	switch(fss->phase){
263 	default:
264 		return phaseerror(fss, "write");
265 
266 	case CNeedProtos:
267 		if(n==0 || a[n-1] != '\0')
268 			return toosmall(fss, 2048);
269 		a = estrdup(a);
270 		m = tokenize(a, token, nelem(token));
271 		if(m > 0 && strncmp(token[0], "v.", 2) == 0){
272 			s->version = atoi(token[0]+2);
273 			if(s->version != 2){
274 				free(a);
275 				return failure(fss, "unknown version of p9any");
276 			}
277 		}
278 
279 		/*
280 		 * look for a key
281 		 */
282 		anew = _delattr(_delattr(_copyattr(fss->attr), "proto"), "role");
283 		anewsf = _delattr(_copyattr(anew), "user");
284 		user = _strfindattr(anew, "user");
285 		k = nil;
286 		p = nil;
287 		dom = nil;
288 		for(i=(s->version==1?0:1); i<m; i++){
289 			p = findneg(token[i]);
290 			if(p == nil)
291 				continue;
292 			dom = getdom(token[i]);
293 			ret = RpcFailure;
294 			mkkeyinfo(&ki, fss, nil);
295 			if(user==nil || strcmp(user, fss->sysuser)==0){
296 				ki.attr = anewsf;
297 				ki.user = nil;
298 				ret = findkey(&k, &ki, "proto=%q dom=%q role=speakfor %s",
299 						p->name, dom, p->keyprompt);
300 			}
301 			if(ret == RpcFailure){
302 				ki.attr = anew;
303 				ki.user = fss->sysuser;
304 				ret = findkey(&k, &ki,
305 					"proto=%q dom=%q role=client %s",
306 					p->name, dom, p->keyprompt);
307 			}
308 			if(ret == RpcConfirm){
309 				free(a);
310 				return ret;
311 			}
312 			if(ret == RpcOk)
313 				break;
314 		}
315 		_freeattr(anewsf);
316 
317 		/*
318 		 * no acceptable key, go through the proto@domains one at a time.
319 		 */
320 		asking = 0;
321 		if(k == nil){
322 			while(!asking && s->keyasked < m){
323 				p = findneg(token[s->keyasked]);
324 				if(p == nil){
325 					s->keyasked++;
326 					continue;
327 				}
328 				dom = getdom(token[s->keyasked]);
329 				mkkeyinfo(&ki, fss, nil);
330 				ki.attr = anew;
331 				ret = findkey(&k, &ki,
332 					"proto=%q dom=%q role=client %s",
333 					p->name, dom, p->keyprompt);
334 				s->keyasked++;
335 				if(ret == RpcNeedkey){
336 					asking = 1;
337 					break;
338 				}
339 			}
340 		}
341 		if(k == nil){
342 			free(a);
343 			_freeattr(anew);
344 			if(asking)
345 				return RpcNeedkey;
346 			else if(s->keyasked)
347 				return failure(fss, nil);
348 			else
349 				return failure(fss, Enegotiation);
350 		}
351 		s->subdom = s_copy(dom);
352 		s->subproto = p;
353 		free(a);
354 		_freeattr(anew);
355 		setupfss(fss, s, k);
356 		closekey(k);
357 		ret = (*s->subproto->init)(p, &s->subfss);
358 		rpcstartlog(s->subfss.attr, &s->subfss, ret);
359 		if(ret == RpcOk)
360 			fss->phase = CHaveProto;
361 		return passret(fss, s, ret);
362 
363 	case SNeedProto:
364 		if(n==0 || a[n-1] != '\0')
365 			return toosmall(fss, n+1);
366 		a = estrdup(a);
367 		m = tokenize(a, token, nelem(token));
368 		if(m != 2){
369 			free(a);
370 			return failure(fss, Ebadarg);
371 		}
372 		p = findneg(token[0]);
373 		if(p == nil){
374 			free(a);
375 			return failure(fss, Enegotiation);
376 		}
377 		attr = _delattr(_copyattr(fss->attr), "proto");
378 		mkkeyinfo(&ki, fss, nil);
379 		ki.attr = attr;
380 		ki.user = nil;
381 		ret = findkey(&k, &ki, "proto=%q dom=%q role=server", token[0], token[1]);
382 		free(a);
383 		_freeattr(attr);
384 		if(ret == RpcConfirm)
385 			return ret;
386 		if(ret != RpcOk)
387 			return failure(fss, Enegotiation);
388 		s->subproto = p;
389 		setupfss(fss, s, k);
390 		closekey(k);
391 		ret = (*s->subproto->init)(p, &s->subfss);
392 		if(ret == RpcOk){
393 			if(s->version == 1)
394 				fss->phase = SRelay;
395 			else
396 				fss->phase = SHaveOK;
397 		}
398 		return passret(fss, s, ret);
399 
400 	case CNeedOK:
401 		if(n < 3)
402 			return toosmall(fss, 3);
403 		if(strcmp("OK", a) != 0)
404 			return failure(fss, "server gave up");
405 		fss->phase = CRelay;
406 		return RpcOk;
407 
408 	case CRelay:
409 	case SRelay:
410 		ophase = s->subfss.phase;
411 		ret = (*s->subproto->write)(&s->subfss, va, n);
412 		rpcrdwrlog(&s->subfss, "write", n, ophase, ret);
413 		return passret(fss, s, ret);
414 	}
415 }
416 
417 Proto p9any =
418 {
419 .name=	"p9any",
420 .init=		p9anyinit,
421 .write=	p9anywrite,
422 .read=	p9anyread,
423 .close=	p9anyclose,
424 };
425