#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= 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) { char *role, *proto; Conv *c; Attr *a; 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"); /* * defeat russ' fix to keys being used as server keys * when ment only as client auth so as not to break * most keys on the system. */ if(role==nil && 1 /* accept traditional keys */) if(strcmp(proto, "pass") == 0) role = "client"; 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 %#p", 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; }