plan9port

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

rpc.c (6591B)


      1 #include "std.h"
      2 #include "dat.h"
      3 
      4 /*
      5  * Factotum RPC
      6  *
      7  * Must be paired write/read cycles on /mnt/factotum/rpc.
      8  * The format of a request is verb, single space, data.
      9  * Data format is verb-dependent; in particular, it can be binary.
     10  * The format of a response is the same.  The write only sets up
     11  * the RPC.  The read tries to execute it.  If the /mnt/factotum/key
     12  * file is open, we ask for new keys using that instead of returning
     13  * an error in the RPC.  This means the read blocks.
     14  * Textual arguments are parsed with tokenize, so rc-style quoting
     15  * rules apply.
     16  *
     17  * Only authentication protocol messages go here.  Configuration
     18  * is still via ctl (below).
     19  *
     20  * Request RPCs are:
     21  *	start attrs - initializes protocol for authentication, can fail.
     22  *		returns "ok read" or "ok write" on success.
     23  *	read - execute protocol read
     24  *	write - execute protocol write
     25  *	authinfo - if the protocol is finished, return the AI if any
     26  *	attr - return protocol information
     27  * Return values are:
     28  *	error message - an error happened.
     29  *	ok [data] - success, possible data is request dependent.
     30  *	needkey attrs - request aborted, get me this key and try again
     31  *	badkey attrs - request aborted, this key might be bad
     32  *	done [haveai] - authentication is done [haveai: you can get an ai with authinfo]
     33  */
     34 
     35 char *rpcname[] =
     36 {
     37 	"unknown",
     38 	"authinfo",
     39 	"attr",
     40 	"read",
     41 	"start",
     42 	"write",
     43 	"readhex",
     44 	"writehex"
     45 };
     46 
     47 static int
     48 classify(char *s)
     49 {
     50 	int i;
     51 
     52 	for(i=1; i<nelem(rpcname); i++)
     53 		if(strcmp(s, rpcname[i]) == 0)
     54 			return i;
     55 	return RpcUnknown;
     56 }
     57 
     58 int
     59 rpcwrite(Conv *c, void *data, int count)
     60 {
     61 	int op;
     62 	uchar *p;
     63 
     64 	if(count >= MaxRpc){
     65 		werrstr("rpc too large");
     66 		return -1;
     67 	}
     68 
     69 	/* cancel any current rpc */
     70 	c->rpc.op = RpcUnknown;
     71 	c->nreply = 0;
     72 
     73 	/* parse new rpc */
     74 	memmove(c->rpcbuf, data, count);
     75 	c->rpcbuf[count] = 0;
     76 	if(p = (uchar*)strchr((char*)c->rpcbuf, ' ')){
     77 		*p++ = '\0';
     78 		c->rpc.data = p;
     79 		c->rpc.count = count - (p - (uchar*)c->rpcbuf);
     80 	}else{
     81 		c->rpc.data = "";
     82 		c->rpc.count = 0;
     83 	}
     84 	op = classify(c->rpcbuf);
     85 	if(op == RpcUnknown){
     86 		werrstr("bad rpc verb: %s", c->rpcbuf);
     87 		return -1;
     88 	}
     89 
     90 	c->rpc.op = op;
     91 	return 0;
     92 }
     93 
     94 void
     95 convthread(void *v)
     96 {
     97 	Conv *c;
     98 	Attr *a;
     99 	char *role, *proto;
    100 	Proto *p;
    101 	Role *r;
    102 
    103 	c = v;
    104 	a = parseattr(c->rpc.data);
    105 	if(a == nil){
    106 		werrstr("empty attr");
    107 		goto out;
    108 	}
    109 	c->attr = a;
    110 	proto = strfindattr(a, "proto");
    111 	if(proto == nil){
    112 		werrstr("no proto in attrs");
    113 		goto out;
    114 	}
    115 
    116 	p = protolookup(proto);
    117 	if(p == nil){
    118 		werrstr("unknown proto %s", proto);
    119 		goto out;
    120 	}
    121 	c->proto = p;
    122 
    123 	role = strfindattr(a, "role");
    124 	if(role == nil){
    125 		werrstr("no role in attrs");
    126 		goto out;
    127 	}
    128 
    129 	for(r=p->roles; r->name; r++){
    130 		if(strcmp(r->name, role) != 0)
    131 			continue;
    132 		rpcrespond(c, "ok");
    133 		c->active = 1;
    134 		if((*r->fn)(c) == 0){
    135 			c->done = 1;
    136 			werrstr("protocol finished");
    137 		}else
    138 			werrstr("%s %s %s: %r", p->name, r->name, c->state);
    139 		goto out;
    140 	}
    141 	werrstr("unknown role");
    142 
    143 out:
    144 	c->active = 0;
    145 	c->state = 0;
    146 	rerrstr(c->err, sizeof c->err);
    147 	rpcrespond(c, "error %r");
    148 	convclose(c);
    149 }
    150 
    151 static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex);
    152 
    153 void
    154 rpcexec(Conv *c)
    155 {
    156 	uchar *p;
    157 
    158 	c->rpc.hex = 0;
    159 	switch(c->rpc.op){
    160 	case RpcWriteHex:
    161 		c->rpc.op = RpcWrite;
    162 		if(dec16(c->rpc.data, c->rpc.count, c->rpc.data, c->rpc.count) != c->rpc.count/2){
    163 			rpcrespond(c, "bad hex");
    164 			break;
    165 		}
    166 		c->rpc.count /= 2;
    167 		goto Default;
    168 	case RpcReadHex:
    169 		c->rpc.hex = 1;
    170 		c->rpc.op = RpcRead;
    171 		/* fall through */
    172 	case RpcRead:
    173 		if(c->rpc.count > 0){
    174 			rpcrespond(c, "error read takes no parameters");
    175 			break;
    176 		}
    177 		/* fall through */
    178 	default:
    179 	Default:
    180 		if(!c->active){
    181 			if(c->done)
    182 				rpcrespond(c, "done");
    183 			else
    184 				rpcrespond(c, "error %s", c->err);
    185 			break;
    186 		}
    187 		nbsendp(c->rpcwait, 0);
    188 		break;
    189 	case RpcUnknown:
    190 		break;
    191 	case RpcAuthinfo:
    192 		/* deprecated */
    193 		if(c->active)
    194 			rpcrespond(c, "error conversation still active");
    195 		else if(!c->done)
    196 			rpcrespond(c, "error conversation not successful");
    197 		else{
    198 			/* make up an auth info using the attr */
    199 			p = convAI2M((uchar*)c->reply+3, sizeof c->reply-3,
    200 				strfindattr(c->attr, "cuid"),
    201 				strfindattr(c->attr, "suid"),
    202 				strfindattr(c->attr, "cap"),
    203 				strfindattr(c->attr, "secret"));
    204 			if(p == nil)
    205 				rpcrespond(c, "error %r");
    206 			else
    207 				rpcrespondn(c, "ok", c->reply+3, p-(uchar*)(c->reply+3));
    208 		}
    209 		break;
    210 	case RpcAttr:
    211 		rpcrespond(c, "ok %A", c->attr);
    212 		break;
    213 	case RpcStart:
    214 		convreset(c);
    215 		c->ref++;
    216 		threadcreate(convthread, c, STACK);
    217 		break;
    218 	}
    219 }
    220 
    221 void
    222 rpcrespond(Conv *c, char *fmt, ...)
    223 {
    224 	va_list arg;
    225 
    226 	if(c->hangup)
    227 		return;
    228 
    229 	if(fmt == nil)
    230 		fmt = "";
    231 
    232 	va_start(arg, fmt);
    233 	c->nreply = vsnprint(c->reply, sizeof c->reply, fmt, arg);
    234 	va_end(arg);
    235 	(*c->kickreply)(c);
    236 	c->rpc.op = RpcUnknown;
    237 }
    238 
    239 void
    240 rpcrespondn(Conv *c, char *verb, void *data, int count)
    241 {
    242 	char *p;
    243 	int need, hex;
    244 
    245 	if(c->hangup)
    246 		return;
    247 
    248 	need = strlen(verb)+1+count;
    249 	hex = 0;
    250 	if(c->rpc.hex && strcmp(verb, "ok") == 0){
    251 		need += count;
    252 		hex = 1;
    253 	}
    254 	if(need > sizeof c->reply){
    255 		print("RPC response too large; caller %#lux", getcallerpc(&c));
    256 		return;
    257 	}
    258 
    259 	strcpy(c->reply, verb);
    260 	p = c->reply + strlen(c->reply);
    261 	*p++ = ' ';
    262 	if(hex){
    263 		enc16(p, 2*count+1, data, count);
    264 		p += 2*count;
    265 	}else{
    266 		memmove(p, data, count);
    267 		p += count;
    268 	}
    269 	c->nreply = p - c->reply;
    270 	(*c->kickreply)(c);
    271 	c->rpc.op = RpcUnknown;
    272 }
    273 
    274 /* deprecated */
    275 static uchar*
    276 pstring(uchar *p, uchar *e, char *s)
    277 {
    278 	uint n;
    279 
    280 	if(p == nil)
    281 		return nil;
    282 	if(s == nil)
    283 		s = "";
    284 	n = strlen(s);
    285 	if(p+n+BIT16SZ >= e)
    286 		return nil;
    287 	PBIT16(p, n);
    288 	p += BIT16SZ;
    289 	memmove(p, s, n);
    290 	p += n;
    291 	return p;
    292 }
    293 
    294 static uchar*
    295 pcarray(uchar *p, uchar *e, uchar *s, uint n)
    296 {
    297 	if(p == nil)
    298 		return nil;
    299 	if(s == nil){
    300 		if(n > 0)
    301 			sysfatal("pcarray");
    302 		s = (uchar*)"";
    303 	}
    304 	if(p+n+BIT16SZ >= e)
    305 		return nil;
    306 	PBIT16(p, n);
    307 	p += BIT16SZ;
    308 	memmove(p, s, n);
    309 	p += n;
    310 	return p;
    311 }
    312 
    313 static uchar*
    314 convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex)
    315 {
    316 	uchar *e = p+n;
    317 	uchar *secret;
    318 	int nsecret;
    319 
    320 	if(cuid == nil)
    321 		cuid = "";
    322 	if(suid == nil)
    323 		suid = "";
    324 	if(cap == nil)
    325 		cap = "";
    326 	if(hex == nil)
    327 		hex = "";
    328 	nsecret = strlen(hex)/2;
    329 	secret = emalloc(nsecret);
    330 	if(hexparse(hex, secret, nsecret) < 0){
    331 		werrstr("hexparse %s failed", hex);	/* can't happen */
    332 		free(secret);
    333 		return nil;
    334 	}
    335 	p = pstring(p, e, cuid);
    336 	p = pstring(p, e, suid);
    337 	p = pstring(p, e, cap);
    338 	p = pcarray(p, e, secret, nsecret);
    339 	free(secret);
    340 	if(p == nil)
    341 		werrstr("authinfo too big");
    342 	return p;
    343 }