Blob


1 #include "std.h"
2 #include "dat.h"
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 */
35 char *rpcname[] =
36 {
37 "unknown",
38 "authinfo",
39 "attr",
40 "read",
41 "start",
42 "write",
43 "readhex",
44 "writehex",
45 };
47 static int
48 classify(char *s)
49 {
50 int i;
52 for(i=1; i<nelem(rpcname); i++)
53 if(strcmp(s, rpcname[i]) == 0)
54 return i;
55 return RpcUnknown;
56 }
58 int
59 rpcwrite(Conv *c, void *data, int count)
60 {
61 int op;
62 uchar *p;
64 if(count >= MaxRpc){
65 werrstr("rpc too large");
66 return -1;
67 }
69 /* cancel any current rpc */
70 c->rpc.op = RpcUnknown;
71 c->nreply = 0;
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 }
90 c->rpc.op = op;
91 return 0;
92 }
94 void
95 convthread(void *v)
96 {
97 Conv *c;
98 Attr *a;
99 char *role, *proto;
100 Proto *p;
101 Role *r;
103 c = v;
104 a = parseattr(c->rpc.data);
105 if(a == nil){
106 werrstr("empty attr");
107 goto out;
109 c->attr = a;
110 proto = strfindattr(a, "proto");
111 role = strfindattr(a, "role");
113 if(proto == nil){
114 werrstr("no proto in attrs");
115 goto out;
117 if(role == nil){
118 werrstr("no role in attrs");
119 goto out;
122 p = protolookup(proto);
123 if(p == nil){
124 werrstr("unknown proto %s", proto);
125 goto out;
128 c->proto = p;
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;
141 werrstr("unknown role");
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);
151 static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex);
153 void
154 rpcexec(Conv *c)
156 uchar *p;
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;
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;
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;
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));
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;
221 void
222 rpcrespond(Conv *c, char *fmt, ...)
224 va_list arg;
226 if(c->hangup)
227 return;
229 if(fmt == nil)
230 fmt = "";
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;
239 void
240 rpcrespondn(Conv *c, char *verb, void *data, int count)
242 char *p;
243 int need, hex;
245 if(c->hangup)
246 return;
248 need = strlen(verb)+1+count;
249 hex = 0;
250 if(c->rpc.hex && strcmp(verb, "ok") == 0){
251 need += count;
252 hex = 1;
254 if(need > sizeof c->reply){
255 print("RPC response too large; caller %#lux", getcallerpc(&c));
256 return;
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;
269 c->nreply = p - c->reply;
270 (*c->kickreply)(c);
271 c->rpc.op = RpcUnknown;
274 /* deprecated */
275 static uchar*
276 pstring(uchar *p, uchar *e, char *s)
278 uint n;
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;
294 static uchar*
295 pcarray(uchar *p, uchar *e, uchar *s, uint n)
297 if(p == nil)
298 return nil;
299 if(s == nil){
300 if(n > 0)
301 sysfatal("pcarray");
302 s = (uchar*)"";
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;
313 static uchar*
314 convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex)
316 uchar *e = p+n;
317 uchar *secret;
318 int nsecret;
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;
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;