aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/auth/factotum/p9any.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/auth/factotum/p9any.c')
-rw-r--r--src/cmd/auth/factotum/p9any.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/cmd/auth/factotum/p9any.c b/src/cmd/auth/factotum/p9any.c
new file mode 100644
index 00000000..694d4cbc
--- /dev/null
+++ b/src/cmd/auth/factotum/p9any.c
@@ -0,0 +1,272 @@
+#include "std.h"
+#include "dat.h"
+
+/*
+ * p9any - protocol negotiator
+ *
+ * Protocol:
+ * S->C: v.2 proto@dom proto@dom proto@dom... NUL
+ * C->S: proto dom NUL
+ * [negotiated proto continues]
+ */
+
+extern Proto p9sk1, p9sk2, p9cr;
+
+static Proto* okproto[] =
+{
+ &p9sk1,
+ nil,
+};
+
+static int
+rolecall(Role *r, char *name, Conv *c)
+{
+ for(; r->name; r++)
+ if(strcmp(r->name, name) == 0)
+ return (*r->fn)(c);
+ werrstr("unknown role");
+ return -1;
+}
+
+static int
+hasnul(void *v, int n)
+{
+ char *c;
+
+ c = v;
+ if(n > 0 && c[n-1] == '\0')
+ return n;
+ else
+ return AuthRpcMax;
+}
+
+static int
+p9anyserver(Conv *c)
+{
+ char *s, *dom;
+ int i, j, n, m, ret;
+ char *tok[3];
+ Attr *attr;
+ Key *k;
+
+ ret = -1;
+ s = estrdup("v.2");
+ n = 0;
+ attr = delattr(copyattr(c->attr), "proto");
+
+ for(i=0; i<ring.nkey; i++){
+ k = ring.key[i];
+ for(j=0; okproto[j]; j++)
+ if(k->proto == okproto[j]
+ && (dom = strfindattr(k->attr, "dom")) != nil
+ && matchattr(attr, k->attr, k->privattr)){
+ s = estrappend(s, " %s@%s", k->proto->name, dom);
+ n++;
+ }
+ }
+
+ if(n == 0){
+ werrstr("no valid keys");
+ goto out;
+ }
+
+ c->state = "write offer";
+ if(convwrite(c, s, strlen(s)+1) < 0)
+ goto out;
+ free(s);
+ s = nil;
+
+ c->state = "read choice";
+ if(convreadfn(c, hasnul, &s) < 0)
+ goto out;
+
+ m = tokenize(s, tok, nelem(tok));
+ if(m != 2){
+ werrstr("bad protocol message");
+ goto out;
+ }
+
+ for(i=0; okproto[i]; i++)
+ if(strcmp(okproto[i]->name, tok[0]) == 0)
+ break;
+ if(!okproto[i]){
+ werrstr("bad chosen protocol %q", tok[0]);
+ goto out;
+ }
+
+ c->state = "write ok";
+ if(convwrite(c, "OK\0", 3) < 0)
+ goto out;
+
+ c->state = "start choice";
+ attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
+ free(c->attr);
+ c->attr = attr;
+ attr = nil;
+ c->proto = okproto[i];
+
+ if(rolecall(c->proto->roles, "server", c) < 0){
+ werrstr("%s: %r", tok[0]);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ free(s);
+ freeattr(attr);
+ return ret;
+}
+
+static int
+p9anyclient(Conv *c)
+{
+ char *s, **f, *tok[20], ok[3], *q, *user, *dom, *choice;
+ int i, n, ret, version;
+ Key *k;
+ Attr *attr;
+ Proto *p;
+
+ ret = -1;
+ s = nil;
+ k = nil;
+
+ user = strfindattr(c->attr, "user");
+ dom = strfindattr(c->attr, "dom");
+
+ /*
+ * if the user is the factotum owner, any key will do.
+ * if not, then if we have a speakfor key,
+ * we will only vouch for the user's local identity.
+ *
+ * this logic is duplicated in p9sk1.c
+ */
+ attr = delattr(copyattr(c->attr), "role");
+ attr = delattr(attr, "proto");
+ if(strcmp(c->sysuser, owner) == 0)
+ attr = addattr(attr, "role=client");
+ else if(user==nil || strcmp(c->sysuser, user)==0){
+ attr = delattr(attr, "user");
+ attr = addattr(attr, "role=speakfor");
+ }else{
+ werrstr("will not authenticate for %q as %q", c->sysuser, user);
+ goto out;
+ }
+
+ c->state = "read offer";
+ if(convreadfn(c, hasnul, &s) < 0)
+ goto out;
+
+ c->state = "look for keys";
+ n = tokenize(s, tok, nelem(tok));
+ f = tok;
+ version = 1;
+ if(n > 0 && memcmp(f[0], "v.", 2) == 0){
+ version = atoi(f[0]+2);
+ if(version != 2){
+ werrstr("unknown p9any version: %s", f[0]);
+ goto out;
+ }
+ f++;
+ n--;
+ }
+
+ /* look for keys that don't need confirmation */
+ for(i=0; i<n; i++){
+ if((q = strchr(f[i], '@')) == nil)
+ continue;
+ if(dom && strcmp(q+1, dom) != 0)
+ continue;
+ *q++ = '\0';
+ if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
+ && strfindattr(k->attr, "confirm") == nil)
+ goto found;
+ *--q = '@';
+ }
+
+ /* look for any keys at all */
+ for(i=0; i<n; i++){
+ if((q = strchr(f[i], '@')) == nil)
+ continue;
+ if(dom && strcmp(q+1, dom) != 0)
+ continue;
+ *q++ = '\0';
+ if(k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
+ goto found;
+ *--q = '@';
+ }
+
+ /* ask for new keys */
+ c->state = "ask for keys";
+ for(i=0; i<n; i++){
+ if((q = strchr(f[i], '@')) == nil)
+ continue;
+ if(dom && strcmp(q+1, dom) != 0)
+ continue;
+ *q++ = '\0';
+ p = protolookup(f[i]);
+ if(p == nil || p->keyprompt == nil){
+ *--q = '@';
+ continue;
+ }
+ if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
+ goto found;
+ *--q = '@';
+ }
+
+ /* nothing worked */
+ werrstr("unable to find common key");
+ goto out;
+
+found:
+ /* f[i] is the chosen protocol, q the chosen domain */
+ attr = addattr(attr, "proto=%q dom=%q", f[i], q);
+ c->state = "write choice";
+
+ /* have a key: go for it */
+ choice = estrappend(nil, "%q %q", f[i], q);
+ if(convwrite(c, choice, strlen(choice)+1) < 0){
+ free(choice);
+ goto out;
+ }
+ free(choice);
+
+ if(version == 2){
+ c->state = "read ok";
+ if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
+ goto out;
+ }
+
+ c->state = "start choice";
+ c->proto = protolookup(f[i]);
+ freeattr(c->attr);
+ c->attr = attr;
+ attr = nil;
+
+ if(rolecall(c->proto->roles, "client", c) < 0){
+ werrstr("%s: %r", c->proto->name);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ keyclose(k);
+ freeattr(attr);
+ free(s);
+ return ret;
+}
+
+static Role
+p9anyroles[] =
+{
+ "client", p9anyclient,
+ "server", p9anyserver,
+ 0
+};
+
+Proto p9any = {
+.name= "p9any",
+.roles= p9anyroles,
+};
+