diff options
Diffstat (limited to 'src/cmd/auth/factotum/rpc.c')
-rw-r--r-- | src/cmd/auth/factotum/rpc.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/cmd/auth/factotum/rpc.c b/src/cmd/auth/factotum/rpc.c new file mode 100644 index 00000000..e9c163aa --- /dev/null +++ b/src/cmd/auth/factotum/rpc.c @@ -0,0 +1,315 @@ +#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", +}; + +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"); + role = strfindattr(a, "role"); + + if(proto == nil){ + werrstr("no proto in attrs"); + goto out; + } + if(role == nil){ + werrstr("no role in attrs"); + goto out; + } + + p = protolookup(proto); + if(p == nil){ + werrstr("unknown proto %s", proto); + goto out; + } + + c->proto = p; + 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; + + switch(c->rpc.op){ + case RpcRead: + if(c->rpc.count > 0){ + rpcrespond(c, "error read takes no parameters"); + break; + } + /* fall through */ + 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; + + if(c->hangup) + return; + + if(strlen(verb)+1+count > sizeof c->reply){ + print("RPC response too large; caller %#lux", getcallerpc(&c)); + return; + } + + strcpy(c->reply, verb); + p = c->reply + strlen(c->reply); + *p++ = ' '; + memmove(p, data, count); + c->nreply = count + (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; +} + |