Blob


1 #include "std.h"
2 #include "dat.h"
4 /*
5 * p9any - protocol negotiator
6 *
7 * Protocol:
8 * S->C: v.2 proto@dom proto@dom proto@dom... NUL
9 * C->S: proto dom NUL
10 * [negotiated proto continues]
11 */
13 static Proto* okproto[] =
14 {
15 &p9sk1,
16 nil,
17 };
19 static int
20 rolecall(Role *r, char *name, Conv *c)
21 {
22 for(; r->name; r++)
23 if(strcmp(r->name, name) == 0)
24 return (*r->fn)(c);
25 werrstr("unknown role");
26 return -1;
27 }
29 static int
30 hasnul(void *v, int n)
31 {
32 char *c;
34 c = v;
35 if(n > 0 && c[n-1] == '\0')
36 return n;
37 else
38 return n+1;
39 }
41 static int
42 p9anyserver(Conv *c)
43 {
44 char *s, *dom;
45 int i, j, n, m, ret;
46 char *tok[3];
47 Attr *attr;
48 Key *k;
50 ret = -1;
51 s = estrdup("v.2");
52 n = 0;
53 attr = delattr(copyattr(c->attr), "proto");
55 for(i=0; i<ring.nkey; i++){
56 k = ring.key[i];
57 for(j=0; okproto[j]; j++)
58 if(k->proto == okproto[j]
59 && (dom = strfindattr(k->attr, "dom")) != nil
60 && matchattr(attr, k->attr, k->privattr)){
61 s = estrappend(s, " %s@%s", k->proto->name, dom);
62 n++;
63 }
64 }
66 if(n == 0){
67 werrstr("no valid keys");
68 goto out;
69 }
71 c->state = "write offer";
72 if(convwrite(c, s, strlen(s)+1) < 0)
73 goto out;
74 free(s);
75 s = nil;
77 c->state = "read choice";
78 if(convreadfn(c, hasnul, &s) < 0)
79 goto out;
81 m = tokenize(s, tok, nelem(tok));
82 if(m != 2){
83 werrstr("bad protocol message");
84 goto out;
85 }
87 for(i=0; okproto[i]; i++)
88 if(strcmp(okproto[i]->name, tok[0]) == 0)
89 break;
90 if(!okproto[i]){
91 werrstr("bad chosen protocol %q", tok[0]);
92 goto out;
93 }
95 c->state = "write ok";
96 if(convwrite(c, "OK\0", 3) < 0)
97 goto out;
99 c->state = "start choice";
100 attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
101 free(c->attr);
102 c->attr = attr;
103 attr = nil;
104 c->proto = okproto[i];
106 if(rolecall(c->proto->roles, "server", c) < 0){
107 werrstr("%s: %r", tok[0]);
108 goto out;
111 ret = 0;
113 out:
114 free(s);
115 freeattr(attr);
116 return ret;
119 static int
120 p9anyclient(Conv *c)
122 char *s, **f, *tok[20], ok[3], *q, *user, *dom;
123 int i, n, ret, version;
124 Key *k;
125 Attr *attr;
126 Proto *p;
128 ret = -1;
129 s = nil;
130 k = nil;
132 user = strfindattr(c->attr, "user");
133 dom = strfindattr(c->attr, "dom");
135 /*
136 * if the user is the factotum owner, any key will do.
137 * if not, then if we have a speakfor key,
138 * we will only vouch for the user's local identity.
140 * this logic is duplicated in p9sk1.c
141 */
142 attr = delattr(copyattr(c->attr), "role");
143 attr = delattr(attr, "proto");
144 if(strcmp(c->sysuser, owner) == 0)
145 attr = addattr(attr, "role=client");
146 else if(user==nil || strcmp(c->sysuser, user)==0){
147 attr = delattr(attr, "user");
148 attr = addattr(attr, "role=speakfor");
149 }else{
150 werrstr("will not authenticate for %q as %q", c->sysuser, user);
151 goto out;
154 c->state = "read offer";
155 if(convreadfn(c, hasnul, &s) < 0)
156 goto out;
158 c->state = "look for keys";
159 n = tokenize(s, tok, nelem(tok));
160 f = tok;
161 version = 1;
162 if(n > 0 && memcmp(f[0], "v.", 2) == 0){
163 version = atoi(f[0]+2);
164 if(version != 2){
165 werrstr("unknown p9any version: %s", f[0]);
166 goto out;
168 f++;
169 n--;
172 /* look for keys that don't need confirmation */
173 for(i=0; i<n; i++){
174 if((q = strchr(f[i], '@')) == nil)
175 continue;
176 if(dom && strcmp(q+1, dom) != 0)
177 continue;
178 *q++ = '\0';
179 if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
180 && strfindattr(k->attr, "confirm") == nil)
181 goto found;
182 *--q = '@';
185 /* look for any keys at all */
186 for(i=0; i<n; i++){
187 if((q = strchr(f[i], '@')) == nil)
188 continue;
189 if(dom && strcmp(q+1, dom) != 0)
190 continue;
191 *q++ = '\0';
192 if(k = keyfetch(c, "%A proto=%q dom=%q", attr, f[i], q))
193 goto found;
194 *--q = '@';
197 /* ask for new keys */
198 c->state = "ask for keys";
199 for(i=0; i<n; i++){
200 if((q = strchr(f[i], '@')) == nil)
201 continue;
202 if(dom && strcmp(q+1, dom) != 0)
203 continue;
204 *q++ = '\0';
205 p = protolookup(f[i]);
206 if(p == nil || p->keyprompt == nil){
207 *--q = '@';
208 continue;
210 if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
211 goto found;
212 *--q = '@';
215 /* nothing worked */
216 werrstr("unable to find common key");
217 goto out;
219 found:
220 /* f[i] is the chosen protocol, q the chosen domain */
221 attr = addattr(attr, "proto=%q dom=%q", f[i], q);
222 c->state = "write choice";
223 /* have a key: go for it */
224 if(convprint(c, "%q %q", f[i], q) < 0
225 || convwrite(c, "\0", 1) < 0)
226 goto out;
228 if(version == 2){
229 c->state = "read ok";
230 if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
231 goto out;
234 c->state = "start choice";
235 c->proto = protolookup(f[i]);
236 freeattr(c->attr);
237 c->attr = attr;
238 attr = nil;
240 if(rolecall(c->proto->roles, "client", c) < 0){
241 werrstr("%s: %r", c->proto->name);
242 goto out;
245 ret = 0;
247 out:
248 keyclose(k);
249 freeattr(attr);
250 free(s);
251 return ret;
254 static Role
255 p9anyroles[] =
257 "client", p9anyclient,
258 "server", p9anyserver,
260 };
262 Proto p9any = {
263 .name= "p9any",
264 .roles= p9anyroles,
265 };