diff options
Diffstat (limited to 'src/cmd/auth/factotum/p9any.c')
-rw-r--r-- | src/cmd/auth/factotum/p9any.c | 272 |
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, +}; + |