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;
68 astype = -1;
70 if(c->proto == &p9cr){
71 astype = AuthChal;
72 challen = NETCHLEN;
73 response = p9response;
74 attr = _mkattr(AttrNameval, "proto", "p9sk1", _delattr(_copyattr(attr), "proto"));
75 }else if(c->proto == &vnc){
76 astype = AuthVNC;
77 challen = MAXCHAL;
78 // response = vncresponse;
79 werrstr("no vnc");
80 goto out;
81 }else{
82 werrstr("bad proto");
83 goto out;
84 }
86 c->state = "find key";
87 k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
88 if(k == nil)
89 goto out;
91 for(ntry=1;; ntry++){
92 if(c->attr != attr)
93 freeattr(c->attr);
94 c->attr = addattrs(copyattr(attr), k->attr);
96 if((pw = strfindattr(k->privattr, "!password")) == nil){
97 werrstr("key has no !password (cannot happen)");
98 goto out;
99 }
100 if((user = strfindattr(k->attr, "user")) == nil){
101 werrstr("key has no user (cannot happen)");
102 goto out;
105 if(convprint(c, "%s", user) < 0)
106 goto out;
108 if(convread(c, chal, challen) < 0)
109 goto out;
110 chal[challen] = 0;
112 if((resplen = (*response)(pw, chal, resp)) < 0)
113 goto out;
115 if(convwrite(c, resp, resplen) < 0)
116 goto out;
118 if(convreadm(c, &res) < 0)
119 goto out;
121 if(strcmp(res, "ok") == 0)
122 break;
124 if((k = keyreplace(c, k, "%s", res)) == nil){
125 c->state = "auth failed";
126 werrstr("%s", res);
127 goto out;
131 werrstr("succeeded");
132 ret = 0;
134 out:
135 USED(astype);
136 keyclose(k);
137 if(c->attr != attr)
138 freeattr(attr);
139 return ret;
142 static int
143 p9crserver(Conv *c)
145 uchar chal[MAXCHAL], *resp, *resp1;
146 char *user;
147 ServerState s;
148 int astype, ret, challen, resplen;
149 Attr *a;
151 ret = -1;
152 user = nil;
153 resp = nil;
154 memset(&s, 0, sizeof s);
155 s.asfd = -1;
157 if(c->proto == &p9cr){
158 astype = AuthChal;
159 challen = NETCHLEN;
160 }else if(c->proto == &vnc){
161 challen = MAXCHAL;
162 }else{
163 werrstr("bad proto");
164 goto out;
167 c->state = "find key";
168 if((s.k = plan9authkey(c->attr)) == nil)
169 goto out;
171 a = copyattr(s.k->attr);
172 a = delattr(a, "proto");
173 c->attr = addattrs(c->attr, a);
174 freeattr(a);
176 c->state = "authdial";
177 s.hostid = strfindattr(s.k->attr, "user");
178 s.dom = strfindattr(s.k->attr, "dom");
179 if((s.asfd = xioauthdial(nil, s.dom)) < 0){
180 werrstr("authdial %s: %r", s.dom);
181 goto out;
184 for(;;){
185 c->state = "read user";
186 if(convreadm(c, &user) < 0)
187 goto out;
189 c->state = "authchal";
190 if(p9crchal(&s, astype, user, chal, challen) < 0)
191 goto out;
193 c->state = "write challenge";
194 if(convwrite(c, chal, challen) < 0)
195 goto out;
197 c->state = "read response";
198 if((resplen = convreadm(c, (char**)(void*)&resp)) < 0)
199 goto out;
200 if(c->proto == &p9cr){
201 if(resplen > NETCHLEN){
202 convprint(c, "bad response too long");
203 goto out;
205 resp1 = emalloc(NETCHLEN);
206 memset(resp1, 0, NETCHLEN);
207 memmove(resp1, resp, resplen);
208 free(resp);
209 resp = resp1;
210 resplen = NETCHLEN;
213 c->state = "authwrite";
214 switch(p9crresp(&s, resp, resplen)){
215 case -1:
216 fprint(2, "p9crresp: %r\n");
217 goto out;
218 case 0:
219 c->state = "write status";
220 if(convprint(c, "bad authentication failed") < 0)
221 goto out;
222 break;
223 case 1:
224 c->state = "write status";
225 if(convprint(c, "ok") < 0)
226 goto out;
227 goto ok;
229 free(user);
230 free(resp);
231 user = nil;
232 resp = nil;
235 ok:
236 ret = 0;
237 c->attr = addcap(c->attr, c->sysuser, &s.t);
239 out:
240 keyclose(s.k);
241 free(user);
242 free(resp);
243 xioclose(s.asfd);
244 return ret;
247 static int
248 p9crchal(ServerState *s, int astype, char *user, uchar *chal, int challen)
250 char trbuf[TICKREQLEN];
251 Ticketreq tr;
252 int n;
254 memset(&tr, 0, sizeof tr);
256 tr.type = astype;
258 if(strlen(s->hostid) >= sizeof tr.hostid){
259 werrstr("hostid too long");
260 return -1;
262 strcpy(tr.hostid, s->hostid);
264 if(strlen(s->dom) >= sizeof tr.authdom){
265 werrstr("domain too long");
266 return -1;
268 strcpy(tr.authdom, s->dom);
270 if(strlen(user) >= sizeof tr.uid){
271 werrstr("user name too long");
272 return -1;
274 strcpy(tr.uid, user);
275 convTR2M(&tr, trbuf);
277 if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
278 return -1;
280 if((n=xioasrdresp(s->asfd, chal, challen)) <= 0)
281 return -1;
282 return n;
285 static int
286 p9crresp(ServerState *s, uchar *resp, int resplen)
288 char tabuf[TICKETLEN+AUTHENTLEN];
289 Authenticator a;
290 Ticket t;
291 Ticketreq tr;
293 memset(&tr, 0, sizeof tr); // TODO: what should tr be initialized to?
295 if(xiowrite(s->asfd, resp, resplen) != resplen)
296 return -1;
298 if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
299 return 0;
301 convM2T(tabuf, &t, s->k->priv);
302 if(t.num != AuthTs
303 || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
304 werrstr("key mismatch with auth server");
305 return -1;
308 convM2A(tabuf+TICKETLEN, &a, t.key);
309 if(a.num != AuthAc
310 || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
311 || a.id != 0){
312 werrstr("key2 mismatch with auth server");
313 return -1;
316 s->t = t;
317 return 1;
320 static int
321 p9response(char *pw, uchar *chal, uchar *resp)
323 char key[DESKEYLEN];
324 uchar buf[8];
325 ulong x;
327 passtokey(key, pw);
328 memset(buf, 0, 8);
329 snprint((char*)buf, sizeof buf, "%d", atoi((char*)chal));
330 if(encrypt(key, buf, 8) < 0){
331 werrstr("can't encrypt response");
332 return -1;
334 x = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
335 return snprint((char*)resp, MAXRESP, "%.8lux", x);
338 /*
339 static int
340 vncresponse(char *pw, uchar *chal, uchar *resp)
342 DESstate des;
344 memmove(resp, chal, MAXCHAL);
345 setupDESstate(&des, 0, nil); // XXX put key in for 0
346 desECBencrypt(resp, MAXCHAL, &des);
347 return MAXCHAL;
349 */
351 static Role
352 p9crroles[] =
354 "client", p9crclient,
355 "server", p9crserver,
357 };
359 Proto p9cr = {
360 "p9cr",
361 p9crroles,
362 "user? !password?",
363 p9crcheck,
364 nil
365 };
367 /* still need to implement vnc key generator */
368 Proto vnc = {
369 "vnc",
370 p9crroles,
371 "user? !password?",
372 p9crcheck,
373 nil
374 };