xref: /plan9-contrib/sys/src/cmd/auth/authsrv.c (revision a6a9e07217f318acf170f99684a55fba5200524f)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ndb.h>
5 #include <regexp.h>
6 #include <mp.h>
7 #include <libsec.h>
8 #include <authsrv.h>
9 #include "authcmdlib.h"
10 
11 int debug;
12 Ndb *db;
13 char raddr[128];
14 
15 /* Microsoft auth constants */
16 enum {
17 	MShashlen = 16,
18 	MSchallen = 8,
19 	MSresplen = 24,
20 };
21 
22 int	ticketrequest(Ticketreq*);
23 void	challengebox(Ticketreq*);
24 void	changepasswd(Ticketreq*);
25 void	apop(Ticketreq*, int);
26 void	chap(Ticketreq*);
27 void	mschap(Ticketreq*);
28 void	http(Ticketreq*);
29 void	vnc(Ticketreq*);
30 int	speaksfor(char*, char*);
31 void	replyerror(char*, ...);
32 void	getraddr(char*);
33 void	mkkey(char*);
34 void	randombytes(uchar*, int);
35 void	nthash(uchar hash[MShashlen], char *passwd);
36 void	lmhash(uchar hash[MShashlen], char *passwd);
37 void	mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]);
38 void	desencrypt(uchar data[8], uchar key[7]);
39 int	tickauthreply(Ticketreq*, char*);
40 void	safecpy(char*, char*, int);
41 
42 
43 void
44 main(int argc, char *argv[])
45 {
46 	char buf[TICKREQLEN];
47 	Ticketreq tr;
48 
49 	ARGBEGIN{
50 	case 'd':
51 		debug++;
52 	}ARGEND
53 
54 	strcpy(raddr, "unknown");
55 	if(argc >= 1)
56 		getraddr(argv[argc-1]);
57 
58 	alarm(10*60*1000);	/* kill a connection after 10 minutes */
59 
60 	db = ndbopen("/lib/ndb/auth");
61 	if(db == 0)
62 		syslog(0, AUTHLOG, "no /lib/ndb/auth");
63 
64 	srand(time(0)*getpid());
65 	for(;;){
66 		if(readn(0, buf, TICKREQLEN) <= 0)
67 			exits(0);
68 
69 		convM2TR(buf, &tr);
70 		switch(buf[0]){
71 		case AuthTreq:
72 			ticketrequest(&tr);
73 			break;
74 		case AuthChal:
75 			challengebox(&tr);
76 			break;
77 		case AuthPass:
78 			changepasswd(&tr);
79 			break;
80 		case AuthApop:
81 			apop(&tr, AuthApop);
82 			break;
83 		case AuthChap:
84 			chap(&tr);
85 			break;
86 		case AuthMSchap:
87 			mschap(&tr);
88 			break;
89 		case AuthCram:
90 			apop(&tr, AuthCram);
91 			break;
92 		case AuthHttp:
93 			http(&tr);
94 			break;
95 		case AuthVNC:
96 			vnc(&tr);
97 			break;
98 		default:
99 			syslog(0, AUTHLOG, "unknown ticket request type: %d", buf[0]);
100 			exits(0);
101 		}
102 	}
103 	exits(0);
104 }
105 
106 int
107 ticketrequest(Ticketreq *tr)
108 {
109 	char akey[DESKEYLEN];
110 	char hkey[DESKEYLEN];
111 	Ticket t;
112 	char tbuf[2*TICKETLEN+1];
113 
114 	if(findkey(KEYDB, tr->authid, akey) == 0){
115 		/* make one up so caller doesn't know it was wrong */
116 		mkkey(akey);
117 		if(debug)
118 			syslog(0, AUTHLOG, "tr-fail authid %s", raddr);
119 	}
120 	if(findkey(KEYDB, tr->hostid, hkey) == 0){
121 		/* make one up so caller doesn't know it was wrong */
122 		mkkey(hkey);
123 		if(debug)
124 			syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr);
125 	}
126 
127 	memset(&t, 0, sizeof(t));
128 	memmove(t.chal, tr->chal, CHALLEN);
129 	strcpy(t.cuid, tr->uid);
130 	if(speaksfor(tr->hostid, tr->uid))
131 		strcpy(t.suid, tr->uid);
132 	else {
133 		mkkey(akey);
134 		mkkey(hkey);
135 		if(debug)
136 			syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for",
137 				tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
138 	}
139 
140 	mkkey(t.key);
141 
142 	tbuf[0] = AuthOK;
143 	t.num = AuthTc;
144 	convT2M(&t, tbuf+1, hkey);
145 	t.num = AuthTs;
146 	convT2M(&t, tbuf+1+TICKETLEN, akey);
147 	if(write(1, tbuf, 2*TICKETLEN+1) < 0){
148 		if(debug)
149 			syslog(0, AUTHLOG, "tr-fail %s@%s(%s): hangup",
150 				tr->uid, tr->hostid, raddr);
151 		exits(0);
152 	}
153 	if(debug)
154 		syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s",
155 			tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
156 
157 	return 0;
158 }
159 
160 void
161 challengebox(Ticketreq *tr)
162 {
163 	long chal;
164 	char *key, *netkey;
165 	char kbuf[DESKEYLEN], nkbuf[DESKEYLEN], hkey[DESKEYLEN];
166 	char buf[NETCHLEN+1];
167 	char *err;
168 
169 	key = findkey(KEYDB, tr->uid, kbuf);
170 	netkey = findkey(NETKEYDB, tr->uid, nkbuf);
171 	if(key == 0 && netkey == 0){
172 		/* make one up so caller doesn't know it was wrong */
173 		mkkey(nkbuf);
174 		netkey = nkbuf;
175 		if(debug)
176 			syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr);
177 	}
178 	if(findkey(KEYDB, tr->hostid, hkey) == 0){
179 		/* make one up so caller doesn't know it was wrong */
180 		mkkey(hkey);
181 		if(debug)
182 			syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid,
183 				tr->uid, raddr);
184 	}
185 
186 	/*
187 	 * challenge-response
188 	 */
189 	memset(buf, 0, sizeof(buf));
190 	buf[0] = AuthOK;
191 	chal = lnrand(MAXNETCHAL);
192 	sprint(buf+1, "%lud", chal);
193 	if(write(1, buf, NETCHLEN+1) < 0)
194 		exits(0);
195 	if(readn(0, buf, NETCHLEN) < 0)
196 		exits(0);
197 	if(!(key && netcheck(key, chal, buf))
198 	&& !(netkey && netcheck(netkey, chal, buf))
199 	&& (err = secureidcheck(tr->uid, buf)) != nil){
200 		replyerror("cr-fail %s %s %s", err, tr->uid, raddr);
201 		logfail(tr->uid);
202 		if(debug)
203 			syslog(0, AUTHLOG, "cr-fail %s@%s(%s): bad resp",
204 				tr->uid, tr->hostid, raddr);
205 		return;
206 	}
207 	succeed(tr->uid);
208 
209 	/*
210 	 *  reply with ticket & authenticator
211 	 */
212 	if(tickauthreply(tr, hkey) < 0){
213 		if(debug)
214 			syslog(0, AUTHLOG, "cr-fail %s@%s(%s): hangup",
215 				tr->uid, tr->hostid, raddr);
216 		exits(0);
217 	}
218 
219 	if(debug)
220 		syslog(0, AUTHLOG, "cr-ok %s@%s(%s)",
221 			tr->uid, tr->hostid, raddr);
222 }
223 
224 void
225 changepasswd(Ticketreq *tr)
226 {
227 	Ticket t;
228 	char tbuf[TICKETLEN+1];
229 	char prbuf[PASSREQLEN];
230 	Passwordreq pr;
231 	char okey[DESKEYLEN], nkey[DESKEYLEN];
232 	char *err;
233 
234 	if(findkey(KEYDB, tr->uid, okey) == 0){
235 		/* make one up so caller doesn't know it was wrong */
236 		mkkey(okey);
237 		syslog(0, AUTHLOG, "cp-fail uid %s", raddr);
238 	}
239 
240 	/* send back a ticket with a new key */
241 	memmove(t.chal, tr->chal, CHALLEN);
242 	mkkey(t.key);
243 	tbuf[0] = AuthOK;
244 	t.num = AuthTp;
245 	safecpy(t.cuid, tr->uid, sizeof(t.cuid));
246 	safecpy(t.suid, tr->uid, sizeof(t.suid));
247 	convT2M(&t, tbuf+1, okey);
248 	write(1, tbuf, sizeof(tbuf));
249 
250 	/* loop trying passwords out */
251 	for(;;){
252 		if(readn(0, prbuf, PASSREQLEN) < 0)
253 			exits(0);
254 		convM2PR(prbuf, &pr, t.key);
255 		if(pr.num != AuthPass){
256 			replyerror("protocol botch1: %s", raddr);
257 			exits(0);
258 		}
259 		passtokey(nkey, pr.old);
260 		if(memcmp(nkey, okey, DESKEYLEN)){
261 			replyerror("protocol botch2: %s", raddr);
262 			continue;
263 		}
264 		if(*pr.new){
265 			err = okpasswd(pr.new);
266 			if(err){
267 				replyerror("%s %s", err, raddr);
268 				continue;
269 			}
270 			passtokey(nkey, pr.new);
271 		}
272 		if(pr.changesecret && setsecret(KEYDB, tr->uid, pr.secret) == 0){
273 			replyerror("can't write secret %s", raddr);
274 			continue;
275 		}
276 		if(*pr.new && setkey(KEYDB, tr->uid, nkey) == 0){
277 			replyerror("can't write key %s", raddr);
278 			continue;
279 		}
280 		break;
281 	}
282 
283 	prbuf[0] = AuthOK;
284 	write(1, prbuf, 1);
285 	succeed(tr->uid);
286 	return;
287 }
288 
289 void
290 http(Ticketreq *tr)
291 {
292 	Ticket t;
293 	char tbuf[TICKETLEN+1];
294 	char key[DESKEYLEN];
295 	char *p;
296 	Biobuf *b;
297 	int n;
298 
299 	n = strlen(tr->uid);
300 	b = Bopen("/sys/lib/httppasswords", OREAD);
301 	if(b == nil){
302 		replyerror("no password file", raddr);
303 		return;
304 	}
305 
306 	/* find key */
307 	for(;;){
308 		p = Brdline(b, '\n');
309 		if(p == nil)
310 			break;
311 		p[Blinelen(b)-1] = 0;
312 		if(strncmp(p, tr->uid, n) == 0)
313 		if(p[n] == ' ' || p[n] == '\t'){
314 			p += n;
315 			break;
316 		}
317 	}
318 	Bterm(b);
319 	if(p == nil) {
320 		randombytes((uchar*)key, DESKEYLEN);
321 	} else {
322 		while(*p == ' ' || *p == '\t')
323 			p++;
324 		passtokey(key, p);
325 	}
326 
327 	/* send back a ticket encrypted with the key */
328 	randombytes((uchar*)t.chal, CHALLEN);
329 	mkkey(t.key);
330 	tbuf[0] = AuthOK;
331 	t.num = AuthHr;
332 	safecpy(t.cuid, tr->uid, sizeof(t.cuid));
333 	safecpy(t.suid, tr->uid, sizeof(t.suid));
334 	convT2M(&t, tbuf+1, key);
335 	write(1, tbuf, sizeof(tbuf));
336 }
337 
338 static char*
339 domainname(void)
340 {
341 	static char sysname[Maxpath];
342 	static char domain[Ndbvlen];
343 	Ndbtuple *t;
344 	int n;
345 
346 	if(*domain)
347 		return domain;
348 
349 	if(*sysname)
350 		return sysname;
351 
352 	n = readfile("/dev/sysname", sysname, sizeof(sysname)-1);
353 	if(n < 0){
354 		strcpy(sysname, "kremvax");
355 		return sysname;
356 	}
357 	sysname[n] = 0;
358 
359 	t = csgetval(0, "sys", sysname, "dom", domain);
360 	if(t == 0)
361 		return sysname;
362 
363 	ndbfree(t);
364 	return domain;
365 }
366 
367 static int
368 h2b(char c)
369 {
370 	if(c >= '0' && c <= '9')
371 		return c - '0';
372 	if(c >= 'A' && c <= 'F')
373 		return c - 'A' + 10;
374 	if(c >= 'a' && c <= 'f')
375 		return c - 'a' + 10;
376 	return 0;
377 }
378 
379 void
380 apop(Ticketreq *tr, int type)
381 {
382 	int challen, i, tries;
383 	char *secret, *hkey, *p;
384 	Ticketreq treq;
385 	DigestState *s;
386 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
387 	char tbuf[TICKREQLEN];
388 	char buf[MD5dlen*2];
389 	uchar digest[MD5dlen], resp[MD5dlen];
390 	ulong rb[4];
391 	char chal[256];
392 
393 	USED(tr);
394 
395 	/*
396 	 *  Create a challenge and send it.
397 	 */
398 	randombytes((uchar*)rb, sizeof(rb));
399 	p = chal;
400 	p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>",
401 		rb[0], rb[1], rb[2], rb[3], domainname());
402 	challen = p - chal;
403 	print("%c%-5d%s", AuthOKvar, challen, chal);
404 
405 	/* give user a few attempts */
406 	for(tries = 0; ; tries++) {
407 		/*
408 		 *  get ticket request
409 		 */
410 		if(readn(0, tbuf, TICKREQLEN) < 0)
411 			exits(0);
412 		convM2TR(tbuf, &treq);
413 		tr = &treq;
414 		if(tr->type != type)
415 			exits(0);
416 
417 		/*
418 		 * read response
419 		 */
420 		if(readn(0, buf, MD5dlen*2) < 0)
421 			exits(0);
422 		for(i = 0; i < MD5dlen; i++)
423 			resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]);
424 
425 		/*
426 		 * lookup
427 		 */
428 		secret = findsecret(KEYDB, tr->uid, sbuf);
429 		hkey = findkey(KEYDB, tr->hostid, hbuf);
430 		if(hkey == 0 || secret == 0){
431 			replyerror("apop-fail bad response %s", raddr);
432 			logfail(tr->uid);
433 			if(tries > 5)
434 				return;
435 			continue;
436 		}
437 
438 		/*
439 		 *  check for match
440 		 */
441 		if(type == AuthCram){
442 			hmac_md5((uchar*)chal, challen,
443 				(uchar*)secret, strlen(secret),
444 				digest, nil);
445 		} else {
446 			s = md5((uchar*)chal, challen, 0, 0);
447 			md5((uchar*)secret, strlen(secret), digest, s);
448 		}
449 		if(memcmp(digest, resp, MD5dlen) != 0){
450 			replyerror("apop-fail bad response %s", raddr);
451 			logfail(tr->uid);
452 			if(tries > 5)
453 				return;
454 			continue;
455 		}
456 		break;
457 	}
458 
459 	succeed(tr->uid);
460 
461 	/*
462 	 *  reply with ticket & authenticator
463 	 */
464 	if(tickauthreply(tr, hkey) < 0)
465 		exits(0);
466 
467 	if(debug){
468 		if(type == AuthCram)
469 			syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
470 		else
471 			syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
472 	}
473 }
474 
475 enum {
476 	VNCchallen=	16,
477 };
478 
479 /* VNC reverses the bits of each byte before using as a des key */
480 uchar swizzletab[256] = {
481  0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
482  0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
483  0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
484  0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
485  0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
486  0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
487  0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
488  0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
489  0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
490  0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
491  0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
492  0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
493  0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
494  0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
495  0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
496  0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
497 };
498 
499 void
500 vnc(Ticketreq *tr)
501 {
502 	uchar chal[VNCchallen+6];
503 	uchar reply[VNCchallen];
504 	char *secret, *hkey;
505 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
506 	DESstate s;
507 	int i;
508 
509 	/*
510 	 *  Create a challenge and send it.
511 	 */
512 	randombytes(chal+6, VNCchallen);
513 	chal[0] = AuthOKvar;
514 	sprint((char*)chal+1, "%-5d", VNCchallen);
515 	if(write(1, chal, sizeof(chal)) != sizeof(chal))
516 		return;
517 
518 	/*
519 	 *  lookup keys (and swizzle bits)
520 	 */
521 	memset(sbuf, 0, sizeof(sbuf));
522 	secret = findsecret(KEYDB, tr->uid, sbuf);
523 	if(secret == 0){
524 		randombytes((uchar*)sbuf, sizeof(sbuf));
525 		secret = sbuf;
526 	}
527 	for(i = 0; i < 8; i++)
528 		secret[i] = swizzletab[(uchar)secret[i]];
529 
530 	hkey = findkey(KEYDB, tr->hostid, hbuf);
531 	if(hkey == 0){
532 		randombytes((uchar*)hbuf, sizeof(hbuf));
533 		hkey = hbuf;
534 	}
535 
536 	/*
537 	 *  get response
538 	 */
539 	if(readn(0, reply, sizeof(reply)) != sizeof(reply))
540 		return;
541 
542 	/*
543 	 *  decrypt response and compare
544 	 */
545 	setupDESstate(&s, (uchar*)secret, nil);
546 	desECBdecrypt(reply, sizeof(reply), &s);
547 	if(memcmp(reply, chal+6, VNCchallen) != 0){
548 		replyerror("vnc-fail bad response %s", raddr);
549 		logfail(tr->uid);
550 		return;
551 	}
552 	succeed(tr->uid);
553 
554 	/*
555 	 *  reply with ticket & authenticator
556 	 */
557 	if(tickauthreply(tr, hkey) < 0)
558 		exits(0);
559 
560 	if(debug)
561 		syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
562 }
563 
564 void
565 chap(Ticketreq *tr)
566 {
567 	char *secret, *hkey;
568 	DigestState *s;
569 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
570 	uchar digest[MD5dlen];
571 	char chal[CHALLEN];
572 	OChapreply reply;
573 	int n;
574 
575 	/*
576 	 *  Create a challenge and send it.
577 	 */
578 	randombytes((uchar*)chal, sizeof(chal));
579 	write(1, chal, sizeof(chal));
580 
581 	/*
582 	 *  get chap reply
583 	 */
584 	if(readn(0, &reply, sizeof(reply)) < 0)
585 		exits(0);
586 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
587 
588 	/*
589 	 * lookup
590 	 */
591 	secret = findsecret(KEYDB, tr->uid, sbuf);
592 	hkey = findkey(KEYDB, tr->hostid, hbuf);
593 	if(hkey == 0 || secret == 0){
594 		replyerror("chap-fail bad response %s", raddr);
595 		logfail(tr->uid);
596 		exits(0);
597 	}
598 
599 	/*
600 	 *  check for match
601 	 */
602 	s = md5(&reply.id, 1, 0, 0);
603 	md5((uchar*)secret, strlen(secret), 0, s);
604 	md5((uchar*)chal, sizeof(chal), digest, s);
605 
606 	if(memcmp(digest, reply.resp, MD5dlen) != 0){
607 		replyerror("chap-fail bad response %s", raddr);
608 		logfail(tr->uid);
609 		exits(0);
610 	}
611 
612 	succeed(tr->uid);
613 
614 	/*
615 	 *  reply with ticket & authenticator
616 	 */
617 	if(tickauthreply(tr, hkey) < 0)
618 		exits(0);
619 
620 	if(debug)
621 		syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
622 }
623 
624 void
625 printresp(uchar resp[MSresplen])
626 {
627 	char buf[200], *p;
628 	int i;
629 
630 	p = buf;
631 	for(i=0; i<MSresplen; i++)
632 		p += sprint(p, "%.2ux ", resp[i]);
633 	syslog(0, AUTHLOG, "resp = %s", buf);
634 }
635 
636 
637 void
638 mschap(Ticketreq *tr)
639 {
640 
641 	char *secret, *hkey;
642 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
643 	uchar chal[CHALLEN];
644 	uchar hash[MShashlen];
645 	uchar hash2[MShashlen];
646 	uchar resp[MSresplen];
647 	OMSchapreply reply;
648 	int lmok, ntok;
649 	DigestState *s;
650 	uchar digest[SHA1dlen];
651 
652 	/*
653 	 *  Create a challenge and send it.
654 	 */
655 	randombytes((uchar*)chal, sizeof(chal));
656 	write(1, chal, sizeof(chal));
657 
658 	/*
659 	 *  get chap reply
660 	 */
661 	if(readn(0, &reply, sizeof(reply)) < 0)
662 		exits(0);
663 
664 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
665 	/*
666 	 * lookup
667 	 */
668 	secret = findsecret(KEYDB, tr->uid, sbuf);
669 	hkey = findkey(KEYDB, tr->hostid, hbuf);
670 	if(hkey == 0 || secret == 0){
671 		replyerror("mschap-fail bad response %s", raddr);
672 		logfail(tr->uid);
673 		exits(0);
674 	}
675 
676 	/*
677 	 *  check for match on LM algorithm
678 	 */
679 	lmhash(hash, secret);
680 	mschalresp(resp, hash, chal);
681 	lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
682 
683 	nthash(hash, secret);
684 	mschalresp(resp, hash, chal);
685 	ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
686 
687 	if(!ntok){
688 		replyerror("mschap-fail bad response %s %ux", raddr, (lmok<<1)|ntok);
689 		logfail(tr->uid);
690 		exits(0);
691 	}
692 
693 	succeed(tr->uid);
694 
695 	/*
696 	 *  reply with ticket & authenticator
697 	 */
698 	if(tickauthreply(tr, hkey) < 0)
699 		exits(0);
700 
701 	if(debug)
702 		syslog(0, AUTHLOG, "mschap-ok %s %s %ux", tr->uid, raddr, (lmok<<1)|ntok);
703 
704 	nthash(hash, secret);
705 	md4(hash, 16, hash2, 0);
706 	s = sha1(hash2, 16, 0, 0);
707 	sha1(hash2, 16, 0, s);
708 	sha1(chal, 8, digest, s);
709 
710 	if(write(1, digest, 16) < 0)
711 		exits(0);
712 }
713 
714 void
715 nthash(uchar hash[MShashlen], char *passwd)
716 {
717 	uchar buf[512];
718 	int i;
719 
720 	for(i=0; *passwd && i<sizeof(buf); passwd++) {
721 		buf[i++] = *passwd;
722 		buf[i++] = 0;
723 	}
724 
725 	memset(hash, 0, 16);
726 
727 	md4(buf, i, hash, 0);
728 }
729 
730 void
731 lmhash(uchar hash[MShashlen], char *passwd)
732 {
733 	uchar buf[14];
734 	char *stdtext = "KGS!@#$%";
735 	int i;
736 
737 	strncpy((char*)buf, passwd, sizeof(buf));
738 	for(i=0; i<sizeof(buf); i++)
739 		if(buf[i] >= 'a' && buf[i] <= 'z')
740 			buf[i] += 'A' - 'a';
741 
742 	memset(hash, 0, 16);
743 	memcpy(hash, stdtext, 8);
744 	memcpy(hash+8, stdtext, 8);
745 
746 	desencrypt(hash, buf);
747 	desencrypt(hash+8, buf+7);
748 }
749 
750 void
751 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
752 {
753 	int i;
754 	uchar buf[21];
755 
756 	memset(buf, 0, sizeof(buf));
757 	memcpy(buf, hash, MShashlen);
758 
759 	for(i=0; i<3; i++) {
760 		memmove(resp+i*MSchallen, chal, MSchallen);
761 		desencrypt(resp+i*MSchallen, buf+i*7);
762 	}
763 }
764 
765 void
766 desencrypt(uchar data[8], uchar key[7])
767 {
768 	ulong ekey[32];
769 
770 	key_setup(key, ekey);
771 	block_cipher(ekey, data, 0);
772 }
773 
774 /*
775  *  return true of the speaker may speak for the user
776  *
777  *  a speaker may always speak for himself/herself
778  */
779 int
780 speaksfor(char *speaker, char *user)
781 {
782 	Ndbtuple *tp, *ntp;
783 	Ndbs s;
784 	int ok;
785 	char notuser[Maxpath];
786 
787 	if(strcmp(speaker, user) == 0)
788 		return 1;
789 
790 	if(db == 0)
791 		return 0;
792 
793 	tp = ndbsearch(db, &s, "hostid", speaker);
794 	if(tp == 0)
795 		return 0;
796 
797 	ok = 0;
798 	snprint(notuser, sizeof notuser, "!%s", user);
799 	for(ntp = tp; ntp; ntp = ntp->entry)
800 		if(strcmp(ntp->attr, "uid") == 0){
801 			if(strcmp(ntp->val, notuser) == 0)
802 				break;
803 			if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
804 				ok = 1;
805 		}
806 	ndbfree(tp);
807 	return ok;
808 }
809 
810 /*
811  *  return an error reply
812  */
813 void
814 replyerror(char *fmt, ...)
815 {
816 	char buf[AERRLEN+1];
817 	va_list arg;
818 
819 	memset(buf, 0, sizeof(buf));
820 	va_start(arg, fmt);
821 	vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
822 	va_end(arg);
823 	buf[AERRLEN] = 0;
824 	buf[0] = AuthErr;
825 	write(1, buf, AERRLEN+1);
826 	syslog(0, AUTHLOG, buf+1);
827 }
828 
829 void
830 getraddr(char *dir)
831 {
832 	int n;
833 	char *cp;
834 	char file[Maxpath];
835 
836 	raddr[0] = 0;
837 	snprint(file, sizeof(file), "%s/remote", dir);
838 	n = readfile(file, raddr, sizeof(raddr)-1);
839 	if(n < 0)
840 		return;
841 	raddr[n] = 0;
842 
843 	cp = strchr(raddr, '\n');
844 	if(cp)
845 		*cp = 0;
846 	cp = strchr(raddr, '!');
847 	if(cp)
848 		*cp = 0;
849 }
850 
851 void
852 mkkey(char *k)
853 {
854 	randombytes((uchar*)k, DESKEYLEN);
855 }
856 
857 void
858 randombytes(uchar *buf, int len)
859 {
860 	int i;
861 
862 	if(readfile("/dev/random", (char*)buf, len) >= 0)
863 		return;
864 
865 	for(i = 0; i < len; i++)
866 		buf[i] = rand();
867 }
868 
869 /*
870  *  reply with ticket and authenticator
871  */
872 int
873 tickauthreply(Ticketreq *tr, char *hkey)
874 {
875 	Ticket t;
876 	Authenticator a;
877 	char buf[TICKETLEN+AUTHENTLEN+1];
878 
879 	memset(&t, 0, sizeof(t));
880 	memmove(t.chal, tr->chal, CHALLEN);
881 	safecpy(t.cuid, tr->uid, sizeof t.cuid);
882 	safecpy(t.suid, tr->uid, sizeof t.suid);
883 	mkkey(t.key);
884 	buf[0] = AuthOK;
885 	t.num = AuthTs;
886 	convT2M(&t, buf+1, hkey);
887 	memmove(a.chal, t.chal, CHALLEN);
888 	a.num = AuthAc;
889 	a.id = 0;
890 	convA2M(&a, buf+TICKETLEN+1, t.key);
891 	if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0)
892 		return -1;
893 	return 0;
894 }
895 
896 void
897 safecpy(char *to, char *from, int len)
898 {
899 	strncpy(to, from, len);
900 	to[len-1] = 0;
901 }
902