4 * The client does not authenticate the server, hence no CAI
8 * S -> C: random 8-byte challenge
9 * C -> S: user in UTF-8
10 * C -> S: Chapreply or MSchapreply structure
11 * S -> C: ok or 'bad why'
13 * The chap protocol requires the client to give it id=%d, the id of
14 * the PPP message containing the challenge, which is used
15 * as part of the response. Because the client protocol is message-id
16 * specific, there is no point in looping to try multiple keys.
18 * The MS chap protocol actually uses two different hashes, an
19 * older insecure one called the LM (Lan Manager) hash, and a newer
20 * more secure one called the NT hash. By default we send back only
21 * the NT hash, because the LM hash can help an eavesdropper run
22 * a brute force attack. If the key has an lm attribute, then we send only the
29 extern Proto chap, mschap;
42 if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
43 werrstr("need user and !password attributes");
50 nthash(uchar hash[MShashlen], char *passwd)
55 for(i=0; *passwd && i<sizeof(buf); passwd++) {
66 desencrypt(uchar data[8], uchar key[7])
71 block_cipher(ekey, data, 0);
75 lmhash(uchar hash[MShashlen], char *passwd)
78 char *stdtext = "KGS!@#$%";
81 strncpy((char*)buf, passwd, sizeof(buf));
82 for(i=0; i<sizeof(buf); i++)
83 if(buf[i] >= 'a' && buf[i] <= 'z')
87 memcpy(hash, stdtext, 8);
88 memcpy(hash+8, stdtext, 8);
90 desencrypt(hash, buf);
91 desencrypt(hash+8, buf+7);
95 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
100 memset(buf, 0, sizeof(buf));
101 memcpy(buf, hash, MShashlen);
104 memmove(resp+i*MSchallen, chal, MSchallen);
105 desencrypt(resp+i*MSchallen, buf+i*7);
112 int id, astype, nchal, npw, ret;
114 char *s, *pw, *user, *res;
126 if(c->proto == &chap){
128 s = strfindattr(attr, "id");
129 if(s == nil || *s == 0){
130 werrstr("need id=n attr in start message");
133 id = strtol(s, &s, 10);
134 if(*s != 0 || id < 0 || id >= 256){
135 werrstr("bad id=n attr in start message");
139 }else if(c->proto == &mschap)
142 werrstr("bad proto");
146 c->state = "find key";
147 k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
151 c->attr = addattrs(copyattr(attr), k->attr);
153 c->state = "read challenge";
154 if((nchal = convreadm(c, (char**)(void*)&chal)) < 0)
156 if(astype == AuthMSchap && nchal != MSchallen)
157 c->state = "write user";
158 if((user = strfindattr(k->attr, "user")) == nil){
159 werrstr("key has no user (cannot happen?)");
162 if(convprint(c, "%s", user) < 0)
165 c->state = "write response";
166 if((pw = strfindattr(k->privattr, "!password")) == nil){
167 werrstr("key has no password (cannot happen?)");
172 if(astype == AuthChap){
173 ds = md5(&cr.id, 1, 0, 0);
174 md5((uchar*)pw, npw, 0, ds);
175 md5(chal, nchal, (uchar*)cr.resp, ds);
176 if(convwrite(c, &cr, sizeof cr) < 0)
179 uchar hash[MShashlen];
181 memset(&mscr, 0, sizeof mscr);
182 if(strfindattr(k->attr, "lm")){
184 mschalresp((uchar*)mscr.LMresp, hash, chal);
187 mschalresp((uchar*)mscr.NTresp, hash, chal);
189 if(convwrite(c, &mscr, sizeof mscr) < 0)
193 c->state = "read result";
194 if(convreadm(c, &res) < 0)
196 if(strcmp(res, "ok") == 0){
198 werrstr("succeeded");
201 if(strncmp(res, "bad ", 4) != 0){
202 werrstr("bad result: %s", res);
206 c->state = "replace key";
207 keyevict(c, k, "%s", res+4);
208 werrstr("%s", res+4);
219 /* shared with auth dialing routines */
220 typedef struct ServerState ServerState;
231 static int chapchal(ServerState*, int, char[ChapChallen]);
232 static int chapresp(ServerState*, char*, char*);
237 char chal[ChapChallen], *user, *resp;
245 memset(&s, 0, sizeof s);
248 if(c->proto == &chap)
250 else if(c->proto == &mschap)
253 werrstr("bad proto");
257 c->state = "find key";
258 if((s.k = plan9authkey(c->attr)) == nil)
261 a = copyattr(s.k->attr);
262 a = delattr(a, "proto");
263 c->attr = addattrs(c->attr, a);
266 c->state = "authdial";
267 s.hostid = strfindattr(s.k->attr, "user");
268 s.dom = strfindattr(s.k->attr, "dom");
269 if((s.asfd = xioauthdial(nil, s.dom)) < 0){
270 werrstr("authdial %s: %r", s.dom);
274 c->state = "authchal";
275 if(chapchal(&s, astype, chal) < 0)
278 c->state = "write challenge";
279 if(convprint(c, "%s", chal) < 0)
282 c->state = "read user";
283 if(convreadm(c, &user) < 0)
286 c->state = "read response";
287 if(convreadm(c, &resp) < 0)
290 c->state = "authwrite";
291 switch(chapresp(&s, user, resp)){
293 fprint(2, "factotum: bad result from chapresp\n");
298 c->state = "write status";
299 if(convprint(c, "bad authentication failed") < 0)
304 c->state = "write status";
305 if(convprint(c, "ok") < 0)
312 c->attr = addcap(c->attr, c->sysuser, &s.t);
318 /* xioclose(s.asfd); */
323 chapchal(ServerState *s, int astype, char chal[ChapChallen])
325 char trbuf[TICKREQLEN];
328 memset(&tr, 0, sizeof tr);
332 if(strlen(s->hostid) >= sizeof tr.hostid){
333 werrstr("hostid too long");
336 strcpy(tr.hostid, s->hostid);
338 if(strlen(s->dom) >= sizeof tr.authdom){
339 werrstr("domain too long");
342 strcpy(tr.authdom, s->dom);
344 convTR2M(&tr, trbuf);
345 if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
348 if(xioasrdresp(s->asfd, chal, ChapChallen) <= 5)
356 chapresp(ServerState *s, char *user, char *resp)
358 char tabuf[TICKETLEN+AUTHENTLEN];
359 char trbuf[TICKREQLEN];
366 if(memrandom(tr.chal, CHALLEN) < 0)
369 if(strlen(user) >= sizeof tr.uid){
370 werrstr("uid too long");
373 strcpy(tr.uid, user);
375 convTR2M(&tr, trbuf);
376 if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
380 if(xiowrite(s->asfd, resp, len) != len)
383 if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
386 convM2T(tabuf, &t, s->k->priv);
388 || memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
389 werrstr("key mismatch with auth server");
393 convM2A(tabuf+TICKETLEN, &a, t.key);
395 || memcmp(a.chal, tr.chal, sizeof a.chal) != 0
397 werrstr("key2 mismatch with auth server");
408 "client", chapclient,
409 "server", chapserver,