aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/auth/factotum/conv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/auth/factotum/conv.c')
-rw-r--r--src/cmd/auth/factotum/conv.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/src/cmd/auth/factotum/conv.c b/src/cmd/auth/factotum/conv.c
new file mode 100644
index 00000000..862993f9
--- /dev/null
+++ b/src/cmd/auth/factotum/conv.c
@@ -0,0 +1,254 @@
+#include "std.h"
+#include "dat.h"
+
+Conv *conv;
+
+ulong taggen = 1;
+
+Conv*
+convalloc(char *sysuser)
+{
+ Conv *c;
+
+ c = mallocz(sizeof(Conv), 1);
+ if(c == nil)
+ return nil;
+ c->ref = 1;
+ c->tag = taggen++;
+ c->next = conv;
+ c->sysuser = estrdup(sysuser);
+ c->state = "nascent";
+ c->rpcwait = chancreate(sizeof(void*), 0);
+ c->keywait = chancreate(sizeof(void*), 0);
+ strcpy(c->err, "protocol has not started");
+ conv = c;
+ convreset(c);
+ return c;
+}
+
+void
+convreset(Conv *c)
+{
+ if(c->ref != 1){
+ c->hangup = 1;
+ nbsendp(c->rpcwait, 0);
+ while(c->ref > 1)
+ yield();
+ c->hangup = 0;
+ }
+ c->state = "nascent";
+ c->err[0] = '\0';
+ freeattr(c->attr);
+ c->attr = nil;
+ c->proto = nil;
+ c->rpc.op = 0;
+ c->active = 0;
+ c->done = 0;
+ c->hangup = 0;
+}
+
+void
+convhangup(Conv *c)
+{
+ c->hangup = 1;
+ c->rpc.op = 0;
+ (*c->kickreply)(c);
+ nbsendp(c->rpcwait, 0);
+}
+
+void
+convclose(Conv *c)
+{
+ Conv *p;
+
+ if(c == nil)
+ return;
+
+ if(--c->ref > 0)
+ return;
+
+ if(c == conv){
+ conv = c->next;
+ goto free;
+ }
+ for(p=conv; p && p->next!=c; p=p->next)
+ ;
+ if(p == nil){
+ print("cannot find conv in list\n");
+ return;
+ }
+ p->next = c->next;
+
+free:
+ c->next = nil;
+ free(c);
+}
+
+static Rpc*
+convgetrpc(Conv *c, int want)
+{
+ for(;;){
+ if(c->hangup){
+ werrstr("hangup");
+ return nil;
+ }
+ if(c->rpc.op == RpcUnknown){
+ recvp(c->rpcwait);
+ if(c->hangup){
+ werrstr("hangup");
+ return nil;
+ }
+ if(c->rpc.op == RpcUnknown)
+ continue;
+ }
+ if(want < 0 || c->rpc.op == want)
+ return &c->rpc;
+ rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]);
+ }
+ return nil; /* not reached */
+}
+
+/* read until the done function tells us that's enough */
+int
+convreadfn(Conv *c, int (*done)(void*, int), char **ps)
+{
+ int n;
+ Rpc *r;
+ char *s;
+
+ for(;;){
+ r = convgetrpc(c, RpcWrite);
+ if(r == nil)
+ return -1;
+ n = (*done)(r->data, r->count);
+ if(n == r->count)
+ break;
+ rpcrespond(c, "toosmall %d", n);
+ }
+
+ s = emalloc(r->count+1);
+ memmove(s, r->data, r->count);
+ s[r->count] = 0;
+ *ps = s;
+ rpcrespond(c, "ok");
+ return r->count;
+}
+
+/*
+ * read until we get a non-zero write. assumes remote side
+ * knows something about the protocol (is not auth_proxy).
+ * the remote side typically won't bother with the zero-length
+ * write to find out the length -- the loop is there only so the
+ * test program can call auth_proxy on both sides of a pipe
+ * to play a conversation.
+ */
+int
+convreadm(Conv *c, char **ps)
+{
+ char *s;
+ Rpc *r;
+
+ for(;;){
+ r = convgetrpc(c, RpcWrite);
+ if(r == nil)
+ return -1;
+ if(r->count > 0)
+ break;
+ rpcrespond(c, "toosmall %d", AuthRpcMax);
+ }
+ s = emalloc(r->count+1);
+ memmove(s, r->data, r->count);
+ s[r->count] = 0;
+ *ps = s;
+ rpcrespond(c, "ok");
+ return r->count;
+}
+
+/* read exactly count bytes */
+int
+convread(Conv *c, void *data, int count)
+{
+ Rpc *r;
+
+ for(;;){
+ r = convgetrpc(c, RpcWrite);
+ if(r == nil)
+ return -1;
+ if(r->count == count)
+ break;
+ if(r->count < count)
+ rpcrespond(c, "toosmall %d", count);
+ else
+ rpcrespond(c, "error too much data; want %d got %d", count, r->count);
+ }
+ memmove(data, r->data, count);
+ rpcrespond(c, "ok");
+ return 0;
+}
+
+/* write exactly count bytes */
+int
+convwrite(Conv *c, void *data, int count)
+{
+ Rpc *r;
+
+ for(;;){
+ r = convgetrpc(c, RpcRead);
+ if(r == nil)
+ return -1;
+ break;
+ }
+ rpcrespondn(c, "ok", data, count);
+ return 0;
+}
+
+/* print to the conversation */
+int
+convprint(Conv *c, char *fmt, ...)
+{
+ char *s;
+ va_list arg;
+ int ret;
+
+ va_start(arg, fmt);
+ s = vsmprint(fmt, arg);
+ va_end(arg);
+ if(s == nil)
+ return -1;
+ ret = convwrite(c, s, strlen(s));
+ free(s);
+ return ret;
+}
+
+/* ask for a key */
+int
+convneedkey(Conv *c, Attr *a)
+{
+ /*
+ * Piggyback key requests in the usual RPC channel.
+ * Wait for the next RPC and then send a key request
+ * in response. The keys get added out-of-band (via the
+ * ctl file), so assume the key has been added when the
+ * next request comes in.
+ */
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ rpcrespond(c, "needkey %A", a);
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ return 0;
+}
+
+/* ask for a replacement for a bad key*/
+int
+convbadkey(Conv *c, Key *k, char *msg, Attr *a)
+{
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ rpcrespond(c, "badkey %A %N\n%s\n%A",
+ k->attr, k->privattr, msg, a);
+ if(convgetrpc(c, -1) == nil)
+ return -1;
+ return 0;
+}
+