aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/auth/factotum/rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/auth/factotum/rpc.c')
-rw-r--r--src/cmd/auth/factotum/rpc.c315
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;
+}
+