mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
316 lines
5.9 KiB
C
316 lines
5.9 KiB
C
|
#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;
|
||
|
}
|
||
|
|