|  | #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 = { | 
|  | "p9any", | 
|  | p9anyroles | 
|  | }; | 
|  |  |