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 extern Proto p9sk1, p9sk2, p9cr;
15 static Proto* okproto[] =
16 {
17 &p9sk1,
18 nil
19 };
21 static int
22 rolecall(Role *r, char *name, Conv *c)
23 {
24 for(; r->name; r++)
25 if(strcmp(r->name, name) == 0)
26 return (*r->fn)(c);
27 werrstr("unknown role");
28 return -1;
29 }
31 static int
32 hasnul(void *v, int n)
33 {
34 char *c;
36 c = v;
37 if(n > 0 && c[n-1] == '\0')
38 return n;
39 else
40 return AuthRpcMax;
41 }
43 static int
44 p9anyserver(Conv *c)
45 {
46 char *s, *dom;
47 int i, j, n, m, ret;
48 char *tok[3];
49 Attr *attr;
50 Key *k;
52 ret = -1;
53 s = estrdup("v.2");
54 n = 0;
55 attr = delattr(copyattr(c->attr), "proto");
57 for(i=0; i<ring.nkey; i++){
58 k = ring.key[i];
59 for(j=0; okproto[j]; j++)
60 if(k->proto == okproto[j]
61 && (dom = strfindattr(k->attr, "dom")) != nil
62 && matchattr(attr, k->attr, k->privattr)){
63 s = estrappend(s, " %s@%s", k->proto->name, dom);
64 n++;
65 }
66 }
68 if(n == 0){
69 werrstr("no valid keys");
70 goto out;
71 }
73 c->state = "write offer";
74 if(convwrite(c, s, strlen(s)+1) < 0)
75 goto out;
76 free(s);
77 s = nil;
79 c->state = "read choice";
80 if(convreadfn(c, hasnul, &s) < 0)
81 goto out;
83 m = tokenize(s, tok, nelem(tok));
84 if(m != 2){
85 werrstr("bad protocol message");
86 goto out;
87 }
89 for(i=0; okproto[i]; i++)
90 if(strcmp(okproto[i]->name, tok[0]) == 0)
91 break;
92 if(!okproto[i]){
93 werrstr("bad chosen protocol %q", tok[0]);
94 goto out;
95 }
97 c->state = "write ok";
98 if(convwrite(c, "OK\0", 3) < 0)
99 goto out;
101 c->state = "start choice";
102 attr = addattr(attr, "proto=%q dom=%q", tok[0], tok[1]);
103 free(c->attr);
104 c->attr = attr;
105 attr = nil;
106 c->proto = okproto[i];
108 if(rolecall(c->proto->roles, "server", c) < 0){
109 werrstr("%s: %r", tok[0]);
110 goto out;
113 ret = 0;
115 out:
116 free(s);
117 freeattr(attr);
118 return ret;
121 static int
122 p9anyclient(Conv *c)
124 char *s, **f, *tok[20], ok[3], *q, *user, *dom, *choice;
125 int i, n, ret, version;
126 Key *k;
127 Attr *attr;
128 Proto *p;
130 ret = -1;
131 s = nil;
132 k = nil;
134 user = strfindattr(c->attr, "user");
135 dom = strfindattr(c->attr, "dom");
137 /*
138 * if the user is the factotum owner, any key will do.
139 * if not, then if we have a speakfor key,
140 * we will only vouch for the user's local identity.
142 * this logic is duplicated in p9sk1.c
143 */
144 attr = delattr(copyattr(c->attr), "role");
145 attr = delattr(attr, "proto");
146 if(strcmp(c->sysuser, owner) == 0)
147 attr = addattr(attr, "role=client");
148 else if(user==nil || strcmp(c->sysuser, user)==0){
149 attr = delattr(attr, "user");
150 attr = addattr(attr, "role=speakfor");
151 }else{
152 werrstr("will not authenticate for %q as %q", c->sysuser, user);
153 goto out;
156 c->state = "read offer";
157 if(convreadfn(c, hasnul, &s) < 0)
158 goto out;
160 c->state = "look for keys";
161 n = tokenize(s, tok, nelem(tok));
162 f = tok;
163 version = 1;
164 if(n > 0 && memcmp(f[0], "v.", 2) == 0){
165 version = atoi(f[0]+2);
166 if(version != 2){
167 werrstr("unknown p9any version: %s", f[0]);
168 goto out;
170 f++;
171 n--;
174 /* look for keys that don't need confirmation */
175 for(i=0; i<n; i++){
176 if((q = strchr(f[i], '@')) == nil)
177 continue;
178 if(dom && strcmp(q+1, dom) != 0)
179 continue;
180 *q++ = '\0';
181 if((k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
182 && strfindattr(k->attr, "confirm") == nil)
183 goto found;
184 *--q = '@';
187 /* look for any keys at all */
188 for(i=0; i<n; i++){
189 if((q = strchr(f[i], '@')) == nil)
190 continue;
191 if(dom && strcmp(q+1, dom) != 0)
192 continue;
193 *q++ = '\0';
194 if(k = keylookup("%A proto=%q dom=%q", attr, f[i], q))
195 goto found;
196 *--q = '@';
199 /* ask for new keys */
200 c->state = "ask for keys";
201 for(i=0; i<n; i++){
202 if((q = strchr(f[i], '@')) == nil)
203 continue;
204 if(dom && strcmp(q+1, dom) != 0)
205 continue;
206 *q++ = '\0';
207 p = protolookup(f[i]);
208 if(p == nil || p->keyprompt == nil){
209 *--q = '@';
210 continue;
212 if(k = keyfetch(c, "%A proto=%q dom=%q %s", attr, f[i], q, p->keyprompt))
213 goto found;
214 *--q = '@';
217 /* nothing worked */
218 werrstr("unable to find common key");
219 goto out;
221 found:
222 /* f[i] is the chosen protocol, q the chosen domain */
223 attr = addattr(attr, "proto=%q dom=%q", f[i], q);
224 c->state = "write choice";
226 /* have a key: go for it */
227 choice = estrappend(nil, "%q %q", f[i], q);
228 if(convwrite(c, choice, strlen(choice)+1) < 0){
229 free(choice);
230 goto out;
232 free(choice);
234 if(version == 2){
235 c->state = "read ok";
236 if(convread(c, ok, 3) < 0 || memcmp(ok, "OK\0", 3) != 0)
237 goto out;
240 c->state = "start choice";
241 c->proto = protolookup(f[i]);
242 freeattr(c->attr);
243 c->attr = attr;
244 attr = nil;
246 if(rolecall(c->proto->roles, "client", c) < 0){
247 werrstr("%s: %r", c->proto->name);
248 goto out;
251 ret = 0;
253 out:
254 keyclose(k);
255 freeattr(attr);
256 free(s);
257 return ret;
260 static Role
261 p9anyroles[] =
263 "client", p9anyclient,
264 "server", p9anyserver,
266 };
268 Proto p9any = {
269 "p9any",
270 p9anyroles
271 };