xref: /plan9/sys/src/cmd/auth/factotum/rpc.c (revision 70b8e0103a6fc04254076ea6a1a33497e2b04a62)
1 /*
2  * Factotum RPC
3  *
4  * Must be paired write/read cycles on /mnt/factotum/rpc.
5  * The format of a request is verb, single space, data.
6  * Data format is verb-dependent; in particular, it can be binary.
7  * The format of a response is the same.  The write only sets up
8  * the RPC.  The read tries to execute it.  If the /mnt/factotum/key
9  * file is open, we ask for new keys using that instead of returning
10  * an error in the RPC.  This means the read blocks.
11  * Textual arguments are parsed with tokenize, so rc-style quoting
12  * rules apply.
13  *
14  * Only authentication protocol messages go here.  Configuration
15  * is still via ctl (below).
16  *
17  * Return values are:
18  *	error message - an error happened.
19  *	ok [data] - success, possible data is request dependent.
20  *	needkey proto domain user - request aborted, get me this key and try again
21  *	badkey proto domain user - request aborted, this key might be bad
22  *	done [haveai] - authentication is done [haveai: you can get an ai with authinfo]
23  * Request RPCs are:
24  *	start attrs - initializes protocol for authentication, can fail.
25  *		returns "ok read" or "ok write" on success.
26  *	read - execute protocol read
27  *	write - execute protocol write
28  *	authinfo - if the protocol is finished, return the AI if any
29  *	attr - return protocol information
30  */
31 
32 #include "dat.h"
33 
34 Req *rpcwait;
35 
36 typedef struct Verb Verb;
37 struct Verb {
38 	char *verb;
39 	int iverb;
40 };
41 
42 enum {
43 	Vunknown = -1,
44 	Vauthinfo = 0,
45 	Vread,
46 	Vstart,
47 	Vwrite,
48 	Vattr,
49 };
50 
51 Verb rpctab[] = {
52 	"authinfo",	Vauthinfo,
53 	"read",		Vread,
54 	"start",		Vstart,
55 	"write",		Vwrite,
56 	"attr",		Vattr,
57 };
58 
59 static int
classify(char * s,Verb * verbtab,int nverbtab)60 classify(char *s, Verb *verbtab, int nverbtab)
61 {
62 	int i;
63 
64 	for(i=0; i<nverbtab; i++)
65 		if(strcmp(s, verbtab[i].verb) == 0)
66 			return verbtab[i].iverb;
67 	return Vunknown;
68 }
69 
70 void
rpcwrite(Req * r)71 rpcwrite(Req *r)
72 {
73 	Fsstate *fss;
74 
75 	if(r->ifcall.count >= Maxrpc){
76 		respond(r, Etoolarge);
77 		return;
78 	}
79 	fss = r->fid->aux;
80 	if(fss->pending){
81 		respond(r, "rpc already pending; read to clear");
82 		return;
83 	}
84 	memmove(fss->rpc.buf, r->ifcall.data, r->ifcall.count);
85 	fss->rpc.buf[r->ifcall.count] = '\0';
86 	fss->rpc.verb = fss->rpc.buf;
87 	if(fss->rpc.arg = strchr(fss->rpc.buf, ' ')){
88 		*fss->rpc.arg++ = '\0';
89 		fss->rpc.narg = r->ifcall.count - (fss->rpc.arg - fss->rpc.buf);
90 	}else{
91 		fss->rpc.arg = "";
92 		fss->rpc.narg = 0;
93 	}
94 	fss->rpc.iverb = classify(fss->rpc.verb, rpctab, nelem(rpctab));
95 	r->ofcall.count = r->ifcall.count;
96 	fss->pending = 1;
97 	respond(r, nil);
98 }
99 
100 static void
retstring(Req * r,Fsstate * fss,char * s)101 retstring(Req *r, Fsstate *fss, char *s)
102 {
103 	int n;
104 
105 	n = strlen(s);
106 	if(n > r->ifcall.count)
107 		n = r->ifcall.count;
108 	memmove(r->ofcall.data, s, n);
109 	r->ofcall.count = n;
110 	fss->pending = 0;
111 	respond(r, nil);
112 	return;
113 }
114 
115 static void
retrpc(Req * r,int ret,Fsstate * fss)116 retrpc(Req *r, int ret, Fsstate *fss)
117 {
118 	switch(ret){
119 	default:
120 		snprint(fss->rpc.buf, Maxrpc, "internal error %d", ret);
121 		retstring(r, fss, fss->rpc.buf);
122 		return;
123 	case RpcErrstr:
124 		snprint(fss->rpc.buf, Maxrpc, "error %r");
125 		retstring(r, fss, fss->rpc.buf);
126 		return;
127 	case RpcFailure:
128 		snprint(fss->rpc.buf, Maxrpc, "error %s", fss->err);
129 		retstring(r, fss, fss->rpc.buf);
130 		return;
131 	case RpcNeedkey:
132 		if(needkeyqueue(r, fss) < 0){
133 			snprint(fss->rpc.buf, Maxrpc, "needkey %s", fss->keyinfo);
134 			retstring(r, fss, fss->rpc.buf);
135 		}
136 		return;
137 	case RpcOk:
138 		retstring(r, fss, "ok");
139 		return;
140 	case RpcToosmall:
141 		snprint(fss->rpc.buf, Maxrpc, "toosmall %d", fss->rpc.nwant);
142 		retstring(r, fss, fss->rpc.buf);
143 		return;
144 	case RpcPhase:
145 		snprint(fss->rpc.buf, Maxrpc, "phase %r");
146 		retstring(r, fss, fss->rpc.buf);
147 		return;
148 	case RpcConfirm:
149 		confirmqueue(r, fss);
150 		return;
151 	}
152 }
153 
154 int
rdwrcheck(Req * r,Fsstate * fss)155 rdwrcheck(Req *r, Fsstate *fss)
156 {
157 	if(fss->ps == nil){
158 		retstring(r, fss, "error no current protocol");
159 		return -1;
160 	}
161 	if(fss->phase == Notstarted){
162 		retstring(r, fss, "protocol not started");
163 		return -1;
164 	}
165 	if(fss->phase == Broken){
166 		snprint(fss->rpc.buf, Maxrpc, "error %s", fss->err);
167 		retstring(r, fss, fss->rpc.buf);
168 		return -1;
169 	}
170 	if(fss->phase == Established){
171 		if(fss->haveai)
172 			retstring(r, fss, "done haveai");
173 		else
174 			retstring(r, fss, "done");
175 		return -1;
176 	}
177 	return 0;
178 }
179 
180 static void
logret(char * pre,Fsstate * fss,int ret)181 logret(char *pre, Fsstate *fss, int ret)
182 {
183 	switch(ret){
184 	default:
185 		flog("%s: code %d", pre, ret);
186 		break;
187 	case RpcErrstr:
188 		flog("%s: error %r", pre);
189 		break;
190 	case RpcFailure:
191 		flog("%s: failure %s", pre, fss->err);
192 		break;
193 	case RpcNeedkey:
194 		flog("%s: needkey %s", pre, fss->keyinfo);
195 		break;
196 	case RpcOk:
197 		flog("%s: ok", pre);
198 		break;
199 	case RpcToosmall:
200 		flog("%s: toosmall %d", pre, fss->rpc.nwant);
201 		break;
202 	case RpcPhase:
203 		flog("%s: phase: %r", pre);
204 		break;
205 	case RpcConfirm:
206 		flog("%s: waiting for confirm", pre);
207 		break;
208 	}
209 }
210 
211 void
rpcrdwrlog(Fsstate * fss,char * rdwr,uint n,int ophase,int ret)212 rpcrdwrlog(Fsstate *fss, char *rdwr, uint n, int ophase, int ret)
213 {
214 	char buf0[40], buf1[40], pre[300];
215 
216 	if(!debug)
217 		return;
218 	snprint(pre, sizeof pre, "%d: %s %ud in phase %s yields phase %s",
219 		fss->seqnum, rdwr, n, phasename(fss, ophase, buf0), phasename(fss, fss->phase, buf1));
220 	logret(pre, fss, ret);
221 }
222 
223 void
rpcstartlog(Attr * attr,Fsstate * fss,int ret)224 rpcstartlog(Attr *attr, Fsstate *fss, int ret)
225 {
226 	char pre[300], tmp[40];
227 
228 	if(!debug)
229 		return;
230 	snprint(pre, sizeof pre, "%d: start %A yields phase %s", fss->seqnum,
231 		attr, phasename(fss, fss->phase, tmp));
232 	logret(pre, fss, ret);
233 }
234 
235 int seqnum;
236 
237 void
rpcread(Req * r)238 rpcread(Req *r)
239 {
240 	Attr *attr;
241 	char *p;
242 	int ophase, ret;
243 	uchar *e;
244 	uint count;
245 	Fsstate *fss;
246 	Proto *proto;
247 
248 	if(r->ifcall.count < 64){
249 		respond(r, "rpc read too small");
250 		return;
251 	}
252 	fss = r->fid->aux;
253 	if(!fss->pending){
254 		respond(r, "no rpc pending");
255 		return;
256 	}
257 	switch(fss->rpc.iverb){
258 	default:
259 	case Vunknown:
260 		retstring(r, fss, "error unknown verb");
261 		break;
262 
263 	case Vstart:
264 		if(fss->phase != Notstarted){
265 			flog("%d: implicit close due to second start; old attr '%A'", fss->seqnum, fss->attr);
266 			if(fss->proto && fss->ps)
267 				(*fss->proto->close)(fss);
268 			fss->ps = nil;
269 			fss->proto = nil;
270 			_freeattr(fss->attr);
271 			fss->attr = nil;
272 			fss->phase = Notstarted;
273 		}
274 		attr = _parseattr(fss->rpc.arg);
275 		if((p = _strfindattr(attr, "proto")) == nil){
276 			retstring(r, fss, "error did not specify proto");
277 			_freeattr(attr);
278 			break;
279 		}
280 		if((proto = findproto(p)) == nil){
281 			snprint(fss->rpc.buf, Maxrpc, "error unknown protocol %q", p);
282 			retstring(r, fss, fss->rpc.buf);
283 			_freeattr(attr);
284 			break;
285 		}
286 		fss->attr = attr;
287 		fss->proto = proto;
288 		fss->seqnum = ++seqnum;
289 		ret = (*proto->init)(proto, fss);
290 		rpcstartlog(attr, fss, ret);
291 		if(ret != RpcOk){
292 			_freeattr(fss->attr);
293 			fss->attr = nil;
294 			fss->phase = Notstarted;
295 		}
296 		retrpc(r, ret, fss);
297 		break;
298 
299 	case Vread:
300 		if(fss->rpc.arg && fss->rpc.arg[0]){
301 			retstring(r, fss, "error read needs no parameters");
302 			break;
303 		}
304 		if(rdwrcheck(r, fss) < 0)
305 			break;
306 		count = r->ifcall.count - 3;
307 		ophase = fss->phase;
308 		ret = fss->proto->read(fss, (uchar*)r->ofcall.data+3, &count);
309 		rpcrdwrlog(fss, "read", count, ophase, ret);
310 		if(ret == RpcOk){
311 			memmove(r->ofcall.data, "ok ", 3);
312 			if(count == 0)
313 				r->ofcall.count = 2;
314 			else
315 				r->ofcall.count = 3+count;
316 			fss->pending = 0;
317 			respond(r, nil);
318 		}else
319 			retrpc(r, ret, fss);
320 		break;
321 
322 	case Vwrite:
323 		if(rdwrcheck(r, fss) < 0)
324 			break;
325 		ophase = fss->phase;
326 		ret = fss->proto->write(fss, fss->rpc.arg, fss->rpc.narg);
327 		rpcrdwrlog(fss, "write", fss->rpc.narg, ophase, ret);
328 		retrpc(r, ret, fss);
329 		break;
330 
331 	case Vauthinfo:
332 		if(fss->phase != Established){
333 			retstring(r, fss, "error authentication unfinished");
334 			break;
335 		}
336 		if(!fss->haveai){
337 			retstring(r, fss, "error no authinfo available");
338 			break;
339 		}
340 		memmove(r->ofcall.data, "ok ", 3);
341 		fss->ai.cap = mkcap(r->fid->uid, fss->ai.suid);
342 		e = convAI2M(&fss->ai, (uchar*)r->ofcall.data+3, r->ifcall.count-3);
343 		free(fss->ai.cap);
344 		fss->ai.cap = nil;
345 		if(e == nil){
346 			retstring(r, fss, "error read too small");
347 			break;
348 		}
349 		r->ofcall.count = e - (uchar*)r->ofcall.data;
350 		fss->pending = 0;
351 		respond(r, nil);
352 		break;
353 
354 	case Vattr:
355 		snprint(fss->rpc.buf, Maxrpc, "ok %A", fss->attr);
356 		retstring(r, fss, fss->rpc.buf);
357 		break;
358 	}
359 }
360 
361 enum {
362 	Vdelkey,
363 	Vaddkey,
364 	Vdebug,
365 };
366 
367 Verb ctltab[] = {
368 	"delkey",		Vdelkey,
369 	"key",		Vaddkey,
370 	"debug",	Vdebug,
371 };
372 
373 /*
374  *	key attr=val... - add a key
375  *		the attr=val pairs are protocol-specific.
376  *		for example, both of these are valid:
377  *			key p9sk1 gre cs.bell-labs.com mysecret
378  *			key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex
379  *	delkey ... - delete a key
380  *		if given, the attr=val pairs are used to narrow the search
381  *		[maybe should require a password?]
382  */
383 
384 int
ctlwrite(char * a,int atzero)385 ctlwrite(char *a, int atzero)
386 {
387 	char *p;
388 	int i, nmatch, ret;
389 	Attr *attr, **l, **lpriv, **lprotos, *pa, *priv, *protos;
390 	Key *k;
391 	Proto *proto;
392 
393 	if(a[0] == '#' || a[0] == '\0')
394 		return 0;
395 
396 	/*
397 	 * it would be nice to emit a warning of some sort here.
398 	 * we ignore all but the first line of the write.  this helps
399 	 * both with things like "echo delkey >/mnt/factotum/ctl"
400 	 * and writes that (incorrectly) contain multiple key lines.
401 	 */
402 	if(p = strchr(a, '\n')){
403 		if(p[1] != '\0'){
404 			werrstr("multiline write not allowed");
405 			return -1;
406 		}
407 		*p = '\0';
408 	}
409 
410 	if((p = strchr(a, ' ')) == nil)
411 		p = "";
412 	else
413 		*p++ = '\0';
414 	switch(classify(a, ctltab, nelem(ctltab))){
415 	default:
416 	case Vunknown:
417 		werrstr("unknown verb");
418 		return -1;
419 	case Vdebug:
420 		debug ^= 1;
421 		return 0;
422 	case Vdelkey:
423 		nmatch = 0;
424 		attr = _parseattr(p);
425 		for(pa=attr; pa; pa=pa->next){
426 			if(pa->type != AttrQuery && pa->name[0]=='!'){
427 				werrstr("only !private? patterns are allowed for private fields");
428 				_freeattr(attr);
429 				return -1;
430 			}
431 		}
432 		for(i=0; i<ring->nkey; ){
433 			if(matchattr(attr, ring->key[i]->attr, ring->key[i]->privattr)){
434 				nmatch++;
435 				closekey(ring->key[i]);
436 				ring->nkey--;
437 				memmove(&ring->key[i], &ring->key[i+1], (ring->nkey-i)*sizeof(ring->key[0]));
438 			}else
439 				i++;
440 		}
441 		_freeattr(attr);
442 		if(nmatch == 0){
443 			werrstr("found no keys to delete");
444 			return -1;
445 		}
446 		return 0;
447 	case Vaddkey:
448 		attr = _parseattr(p);
449 		/* separate out proto= attributes */
450 		lprotos = &protos;
451 		for(l=&attr; (*l); ){
452 			if(strcmp((*l)->name, "proto") == 0){
453 				*lprotos = *l;
454 				lprotos = &(*l)->next;
455 				*l = (*l)->next;
456 			}else
457 				l = &(*l)->next;
458 		}
459 		*lprotos = nil;
460 		if(protos == nil){
461 			werrstr("key without protos");
462 			_freeattr(attr);
463 			return -1;
464 		}
465 
466 		/* separate out private attributes */
467 		lpriv = &priv;
468 		for(l=&attr; (*l); ){
469 			if((*l)->name[0] == '!'){
470 				*lpriv = *l;
471 				lpriv = &(*l)->next;
472 				*l = (*l)->next;
473 			}else
474 				l = &(*l)->next;
475 		}
476 		*lpriv = nil;
477 
478 		/* add keys */
479 		ret = 0;
480 		for(pa=protos; pa; pa=pa->next){
481 			if((proto = findproto(pa->val)) == nil){
482 				werrstr("unknown proto %s", pa->val);
483 				ret = -1;
484 				continue;
485 			}
486 			if(proto->addkey == nil){
487 				werrstr("proto %s doesn't take keys", proto->name);
488 				ret = -1;
489 				continue;
490 			}
491 			k = emalloc(sizeof(Key));
492 			k->attr = _mkattr(AttrNameval, "proto", proto->name, _copyattr(attr));
493 			k->privattr = _copyattr(priv);
494 			k->ref = 1;
495 			k->proto = proto;
496 			if(proto->addkey(k, atzero) < 0){
497 				ret = -1;
498 				closekey(k);
499 				continue;
500 			}
501 			closekey(k);
502 		}
503 		_freeattr(attr);
504 		_freeattr(priv);
505 		_freeattr(protos);
506 		return ret;
507 	}
508 }
509