| #include "std.h" |
| #include "dat.h" |
| |
| /* |
| * Factotum RPC |
| * |
| * Must be paired write/read cycles on /mnt/factotum/rpc. |
| * The format of a request is verb, single space, data. |
| * Data format is verb-dependent; in particular, it can be binary. |
| * The format of a response is the same. The write only sets up |
| * the RPC. The read tries to execute it. If the /mnt/factotum/key |
| * file is open, we ask for new keys using that instead of returning |
| * an error in the RPC. This means the read blocks. |
| * Textual arguments are parsed with tokenize, so rc-style quoting |
| * rules apply. |
| * |
| * Only authentication protocol messages go here. Configuration |
| * is still via ctl (below). |
| * |
| * Request RPCs are: |
| * start attrs - initializes protocol for authentication, can fail. |
| * returns "ok read" or "ok write" on success. |
| * read - execute protocol read |
| * write - execute protocol write |
| * authinfo - if the protocol is finished, return the AI if any |
| * attr - return protocol information |
| * Return values are: |
| * error message - an error happened. |
| * ok [data] - success, possible data is request dependent. |
| * needkey attrs - request aborted, get me this key and try again |
| * badkey attrs - request aborted, this key might be bad |
| * done [haveai] - authentication is done [haveai: you can get an ai with authinfo] |
| */ |
| |
| char *rpcname[] = |
| { |
| "unknown", |
| "authinfo", |
| "attr", |
| "read", |
| "start", |
| "write", |
| "readhex", |
| "writehex" |
| }; |
| |
| static int |
| classify(char *s) |
| { |
| int i; |
| |
| for(i=1; i<nelem(rpcname); i++) |
| if(strcmp(s, rpcname[i]) == 0) |
| return i; |
| return RpcUnknown; |
| } |
| |
| int |
| rpcwrite(Conv *c, void *data, int count) |
| { |
| int op; |
| uchar *p; |
| |
| if(count >= MaxRpc){ |
| werrstr("rpc too large"); |
| return -1; |
| } |
| |
| /* cancel any current rpc */ |
| c->rpc.op = RpcUnknown; |
| c->nreply = 0; |
| |
| /* parse new rpc */ |
| memmove(c->rpcbuf, data, count); |
| c->rpcbuf[count] = 0; |
| if(p = (uchar*)strchr((char*)c->rpcbuf, ' ')){ |
| *p++ = '\0'; |
| c->rpc.data = p; |
| c->rpc.count = count - (p - (uchar*)c->rpcbuf); |
| }else{ |
| c->rpc.data = ""; |
| c->rpc.count = 0; |
| } |
| op = classify(c->rpcbuf); |
| if(op == RpcUnknown){ |
| werrstr("bad rpc verb: %s", c->rpcbuf); |
| return -1; |
| } |
| |
| c->rpc.op = op; |
| return 0; |
| } |
| |
| void |
| convthread(void *v) |
| { |
| Conv *c; |
| Attr *a; |
| char *role, *proto; |
| Proto *p; |
| Role *r; |
| |
| c = v; |
| a = parseattr(c->rpc.data); |
| if(a == nil){ |
| werrstr("empty attr"); |
| goto out; |
| } |
| c->attr = a; |
| proto = strfindattr(a, "proto"); |
| if(proto == nil){ |
| werrstr("no proto in attrs"); |
| goto out; |
| } |
| |
| p = protolookup(proto); |
| if(p == nil){ |
| werrstr("unknown proto %s", proto); |
| goto out; |
| } |
| c->proto = p; |
| |
| role = strfindattr(a, "role"); |
| if(role == nil){ |
| werrstr("no role in attrs"); |
| goto out; |
| } |
| |
| for(r=p->roles; r->name; r++){ |
| if(strcmp(r->name, role) != 0) |
| continue; |
| rpcrespond(c, "ok"); |
| c->active = 1; |
| if((*r->fn)(c) == 0){ |
| c->done = 1; |
| werrstr("protocol finished"); |
| }else |
| werrstr("%s %s %s: %r", p->name, r->name, c->state); |
| goto out; |
| } |
| werrstr("unknown role"); |
| |
| out: |
| c->active = 0; |
| c->state = 0; |
| rerrstr(c->err, sizeof c->err); |
| rpcrespond(c, "error %r"); |
| convclose(c); |
| } |
| |
| static uchar* convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex); |
| |
| void |
| rpcexec(Conv *c) |
| { |
| uchar *p; |
| |
| c->rpc.hex = 0; |
| switch(c->rpc.op){ |
| case RpcWriteHex: |
| c->rpc.op = RpcWrite; |
| if(dec16(c->rpc.data, c->rpc.count, c->rpc.data, c->rpc.count) != c->rpc.count/2){ |
| rpcrespond(c, "bad hex"); |
| break; |
| } |
| c->rpc.count /= 2; |
| goto Default; |
| case RpcReadHex: |
| c->rpc.hex = 1; |
| c->rpc.op = RpcRead; |
| /* fall through */ |
| case RpcRead: |
| if(c->rpc.count > 0){ |
| rpcrespond(c, "error read takes no parameters"); |
| break; |
| } |
| /* fall through */ |
| default: |
| Default: |
| if(!c->active){ |
| if(c->done) |
| rpcrespond(c, "done"); |
| else |
| rpcrespond(c, "error %s", c->err); |
| break; |
| } |
| nbsendp(c->rpcwait, 0); |
| break; |
| case RpcUnknown: |
| break; |
| case RpcAuthinfo: |
| /* deprecated */ |
| if(c->active) |
| rpcrespond(c, "error conversation still active"); |
| else if(!c->done) |
| rpcrespond(c, "error conversation not successful"); |
| else{ |
| /* make up an auth info using the attr */ |
| p = convAI2M((uchar*)c->reply+3, sizeof c->reply-3, |
| strfindattr(c->attr, "cuid"), |
| strfindattr(c->attr, "suid"), |
| strfindattr(c->attr, "cap"), |
| strfindattr(c->attr, "secret")); |
| if(p == nil) |
| rpcrespond(c, "error %r"); |
| else |
| rpcrespondn(c, "ok", c->reply+3, p-(uchar*)(c->reply+3)); |
| } |
| break; |
| case RpcAttr: |
| rpcrespond(c, "ok %A", c->attr); |
| break; |
| case RpcStart: |
| convreset(c); |
| c->ref++; |
| threadcreate(convthread, c, STACK); |
| break; |
| } |
| } |
| |
| void |
| rpcrespond(Conv *c, char *fmt, ...) |
| { |
| va_list arg; |
| |
| if(c->hangup) |
| return; |
| |
| if(fmt == nil) |
| fmt = ""; |
| |
| va_start(arg, fmt); |
| c->nreply = vsnprint(c->reply, sizeof c->reply, fmt, arg); |
| va_end(arg); |
| (*c->kickreply)(c); |
| c->rpc.op = RpcUnknown; |
| } |
| |
| void |
| rpcrespondn(Conv *c, char *verb, void *data, int count) |
| { |
| char *p; |
| int need, hex; |
| |
| if(c->hangup) |
| return; |
| |
| need = strlen(verb)+1+count; |
| hex = 0; |
| if(c->rpc.hex && strcmp(verb, "ok") == 0){ |
| need += count; |
| hex = 1; |
| } |
| if(need > sizeof c->reply){ |
| print("RPC response too large; caller %#lux", getcallerpc(&c)); |
| return; |
| } |
| |
| strcpy(c->reply, verb); |
| p = c->reply + strlen(c->reply); |
| *p++ = ' '; |
| if(hex){ |
| enc16(p, 2*count+1, data, count); |
| p += 2*count; |
| }else{ |
| memmove(p, data, count); |
| p += count; |
| } |
| c->nreply = p - c->reply; |
| (*c->kickreply)(c); |
| c->rpc.op = RpcUnknown; |
| } |
| |
| /* deprecated */ |
| static uchar* |
| pstring(uchar *p, uchar *e, char *s) |
| { |
| uint n; |
| |
| if(p == nil) |
| return nil; |
| if(s == nil) |
| s = ""; |
| n = strlen(s); |
| if(p+n+BIT16SZ >= e) |
| return nil; |
| PBIT16(p, n); |
| p += BIT16SZ; |
| memmove(p, s, n); |
| p += n; |
| return p; |
| } |
| |
| static uchar* |
| pcarray(uchar *p, uchar *e, uchar *s, uint n) |
| { |
| if(p == nil) |
| return nil; |
| if(s == nil){ |
| if(n > 0) |
| sysfatal("pcarray"); |
| s = (uchar*)""; |
| } |
| if(p+n+BIT16SZ >= e) |
| return nil; |
| PBIT16(p, n); |
| p += BIT16SZ; |
| memmove(p, s, n); |
| p += n; |
| return p; |
| } |
| |
| static uchar* |
| convAI2M(uchar *p, int n, char *cuid, char *suid, char *cap, char *hex) |
| { |
| uchar *e = p+n; |
| uchar *secret; |
| int nsecret; |
| |
| if(cuid == nil) |
| cuid = ""; |
| if(suid == nil) |
| suid = ""; |
| if(cap == nil) |
| cap = ""; |
| if(hex == nil) |
| hex = ""; |
| nsecret = strlen(hex)/2; |
| secret = emalloc(nsecret); |
| if(hexparse(hex, secret, nsecret) < 0){ |
| werrstr("hexparse %s failed", hex); /* can't happen */ |
| free(secret); |
| return nil; |
| } |
| p = pstring(p, e, cuid); |
| p = pstring(p, e, suid); |
| p = pstring(p, e, cap); |
| p = pcarray(p, e, secret, nsecret); |
| free(secret); |
| if(p == nil) |
| werrstr("authinfo too big"); |
| return p; |
| } |
| |