blob: ad894330731d63abd907b1ba722ff20d0fde152e [file] [log] [blame]
#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;
}