Blob


1 /*
2 * p9cr - one-sided challenge/response authentication
3 *
4 * Protocol:
5 *
6 * C -> S: user
7 * S -> C: challenge
8 * C -> S: response
9 * S -> C: ok or bad
10 *
11 * Note that this is the protocol between factotum and the local
12 * program, not between the two factotums. The information
13 * exchanged here is wrapped in other protocols by the local
14 * programs.
15 */
17 #include "std.h"
18 #include "dat.h"
20 /* shared with auth dialing routines */
21 typedef struct ServerState ServerState;
22 struct ServerState
23 {
24 int asfd;
25 Key *k;
26 Ticketreq tr;
27 Ticket t;
28 char *dom;
29 char *hostid;
30 };
32 enum
33 {
34 MAXCHAL = 64,
35 MAXRESP = 64,
36 };
38 extern Proto p9cr, vnc;
39 static int p9response(char*, uchar*, uchar*);
40 // static int vncresponse(char*, uchar*, uchar*);
41 static int p9crchal(ServerState *s, int, char*, uchar*, int);
42 static int p9crresp(ServerState*, uchar*, int);
44 static int
45 p9crcheck(Key *k)
46 {
47 if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
48 werrstr("need user and !password attributes");
49 return -1;
50 }
51 return 0;
52 }
54 static int
55 p9crclient(Conv *c)
56 {
57 char *pw, *res, *user;
58 int astype, challen, resplen, ntry, ret;
59 Attr *attr;
60 Key *k;
61 uchar chal[MAXCHAL+1], resp[MAXRESP];
62 int (*response)(char*, uchar*, uchar*);
64 k = nil;
65 res = nil;
66 ret = -1;
67 attr = c->attr;
69 if(c->proto == &p9cr){
70 astype = AuthChal;
71 challen = NETCHLEN;
72 response = p9response;
73 attr = _mkattr(AttrNameval, "proto", "p9sk1", _delattr(_copyattr(attr), "proto"));
74 }else if(c->proto == &vnc){
75 astype = AuthVNC;
76 challen = MAXCHAL;
77 // response = vncresponse;
78 werrstr("no vnc");
79 goto out;
80 }else{
81 werrstr("bad proto");
82 goto out;
83 }
85 c->state = "find key";
86 k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
87 if(k == nil)
88 goto out;
90 for(ntry=1;; ntry++){
91 if(c->attr != attr)
92 freeattr(c->attr);
93 c->attr = addattrs(copyattr(attr), k->attr);
95 if((pw = strfindattr(k->privattr, "!password")) == nil){
96 werrstr("key has no !password (cannot happen)");
97 goto out;
98 }
99 if((user = strfindattr(k->attr, "user")) == nil){
100 werrstr("key has no user (cannot happen)");
101 goto out;
104 if(convprint(c, "%s", user) < 0)
105 goto out;
107 if(convread(c, chal, challen) < 0)
108 goto out;
109 chal[challen] = 0;
111 if((resplen = (*response)(pw, chal, resp)) < 0)
112 goto out;
114 if(convwrite(c, resp, resplen) < 0)
115 goto out;
117 if(convreadm(c, &res) < 0)
118 goto out;
120 if(strcmp(res, "ok") == 0)
121 break;
123 if((k = keyreplace(c, k, "%s", res)) == nil){
124 c->state = "auth failed";
125 werrstr("%s", res);
126 goto out;
130 werrstr("succeeded");
131 ret = 0;
133 out:
134 keyclose(k);
135 if(c->attr != attr)
136 freeattr(attr);
137 return ret;
140 static int
141 p9crserver(Conv *c)
143 uchar chal[MAXCHAL], *resp, *resp1;
144 char *user;
145 ServerState s;
146 int astype, ret, challen, resplen;
147 Attr *a;
149 ret = -1;
150 user = nil;
151 resp = nil;
152 memset(&s, 0, sizeof s);
153 s.asfd = -1;
155 if(c->proto == &p9cr){
156 astype = AuthChal;
157 challen = NETCHLEN;
158 }else if(c->proto == &vnc){
159 astype = AuthVNC;
160 challen = MAXCHAL;
161 }else{
162 werrstr("bad proto");
163 goto out;
166 c->state = "find key";
167 if((s.k = plan9authkey(c->attr)) == nil)
168 goto out;
170 a = copyattr(s.k->attr);
171 a = delattr(a, "proto");
172 c->attr = addattrs(c->attr, a);
173 freeattr(a);
175 c->state = "authdial";
176 s.hostid = strfindattr(s.k->attr, "user");
177 s.dom = strfindattr(s.k->attr, "dom");
178 if((s.asfd = xioauthdial(nil, s.dom)) < 0){
179 werrstr("authdial %s: %r", s.dom);
180 goto out;
183 for(;;){
184 c->state = "read user";
185 if(convreadm(c, &user) < 0)
186 goto out;
188 c->state = "authchal";
189 if(p9crchal(&s, astype, user, chal, challen) < 0)
190 goto out;
192 c->state = "write challenge";
193 if(convwrite(c, chal, challen) < 0)
194 goto out;
196 c->state = "read response";
197 if((resplen = convreadm(c, (char**)(void*)&resp)) < 0)
198 goto out;
199 if(c->proto == &p9cr){
200 if(resplen > NETCHLEN){
201 convprint(c, "bad response too long");
202 goto out;
204 resp1 = emalloc(NETCHLEN);
205 memset(resp1, 0, NETCHLEN);
206 memmove(resp1, resp, resplen);
207 free(resp);
208 resp = resp1;
209 resplen = NETCHLEN;
212 c->state = "authwrite";
213 switch(p9crresp(&s, resp, resplen)){
214 case -1:
215 fprint(2, "p9crresp: %r\n");
216 goto out;
217 case 0:
218 c->state = "write status";
219 if(convprint(c, "bad authentication failed") < 0)
220 goto out;
221 break;
222 case 1:
223 c->state = "write status";
224 if(convprint(c, "ok") < 0)
225 goto out;
226 goto ok;
228 free(user);
229 free(resp);
230 user = nil;
231 resp = nil;
234 ok:
235 ret = 0;
236 c->attr = addcap(c->attr, c->sysuser, &s.t);
238 out:
239 keyclose(s.k);
240 free(user);
241 free(resp);
242 xioclose(s.asfd);
243 return ret;
246 static int
247 p9crchal(ServerState *s, int astype, char *user, uchar *chal, int challen)
249 char trbuf[TICKREQLEN];
250 Ticketreq tr;
251 int n;
253 memset(&tr, 0, sizeof tr);
255 tr.type = astype;
257 if(strlen(s->hostid) >= sizeof tr.hostid){
258 werrstr("hostid too long");
259 return -1;
261 strcpy(tr.hostid, s->hostid);
263 if(strlen(s->dom) >= sizeof tr.authdom){
264 werrstr("domain too long");
265 return -1;
267 strcpy(tr.authdom, s->dom);
269 if(strlen(user) >= sizeof tr.uid){
270 werrstr("user name too long");
271 return -1;
273 strcpy(tr.uid, user);
274 convTR2M(&tr, trbuf);
276 if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
277 return -1;
279 if((n=xioasrdresp(s->asfd, chal, challen)) <= 0)
280 return -1;
281 return n;
284 static int
285 p9crresp(ServerState *s, uchar *resp, int resplen)
287 char tabuf[TICKETLEN+AUTHENTLEN];
288 Authenticator a;
289 Ticket t;
290 Ticketreq tr;
292 if(xiowrite(s->asfd, resp, resplen) != resplen)
293 return -1;
295 if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
296 return 0;
298 convM2T(tabuf, &t, s->k->priv);
299 if(t.num != AuthTs
300 || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
301 werrstr("key mismatch with auth server");
302 return -1;
305 convM2A(tabuf+TICKETLEN, &a, t.key);
306 if(a.num != AuthAc
307 || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
308 || a.id != 0){
309 werrstr("key2 mismatch with auth server");
310 return -1;
313 s->t = t;
314 return 1;
317 static int
318 p9response(char *pw, uchar *chal, uchar *resp)
320 char key[DESKEYLEN];
321 uchar buf[8];
322 ulong x;
324 passtokey(key, pw);
325 memset(buf, 0, 8);
326 snprint((char*)buf, sizeof buf, "%d", atoi((char*)chal));
327 if(encrypt(key, buf, 8) < 0){
328 werrstr("can't encrypt response");
329 return -1;
331 x = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
332 return snprint((char*)resp, MAXRESP, "%.8lux", x);
335 /*
336 static int
337 vncresponse(char *pw, uchar *chal, uchar *resp)
339 DESstate des;
341 memmove(resp, chal, MAXCHAL);
342 setupDESstate(&des, 0, nil); // XXX put key in for 0
343 desECBencrypt(resp, MAXCHAL, &des);
344 return MAXCHAL;
346 */
348 static Role
349 p9crroles[] =
351 "client", p9crclient,
352 "server", p9crserver,
354 };
356 Proto p9cr = {
357 "p9cr",
358 p9crroles,
359 "user? !password?",
360 p9crcheck,
361 nil
362 };
364 /* still need to implement vnc key generator */
365 Proto vnc = {
366 "vnc",
367 p9crroles,
368 "user? !password?",
369 p9crcheck,
370 nil
371 };