xref: /plan9/sys/src/cmd/auth/authsrv.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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 
574 	/*
575 	 *  Create a challenge and send it.
576 	 */
577 	randombytes((uchar*)chal, sizeof(chal));
578 	write(1, chal, sizeof(chal));
579 
580 	/*
581 	 *  get chap reply
582 	 */
583 	if(readn(0, &reply, sizeof(reply)) < 0)
584 		exits(0);
585 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
586 
587 	/*
588 	 * lookup
589 	 */
590 	secret = findsecret(KEYDB, tr->uid, sbuf);
591 	hkey = findkey(KEYDB, tr->hostid, hbuf);
592 	if(hkey == 0 || secret == 0){
593 		replyerror("chap-fail bad response %s", raddr);
594 		logfail(tr->uid);
595 		exits(0);
596 	}
597 
598 	/*
599 	 *  check for match
600 	 */
601 	s = md5(&reply.id, 1, 0, 0);
602 	md5((uchar*)secret, strlen(secret), 0, s);
603 	md5((uchar*)chal, sizeof(chal), digest, s);
604 
605 	if(memcmp(digest, reply.resp, MD5dlen) != 0){
606 		replyerror("chap-fail bad response %s", raddr);
607 		logfail(tr->uid);
608 		exits(0);
609 	}
610 
611 	succeed(tr->uid);
612 
613 	/*
614 	 *  reply with ticket & authenticator
615 	 */
616 	if(tickauthreply(tr, hkey) < 0)
617 		exits(0);
618 
619 	if(debug)
620 		syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
621 }
622 
623 void
624 printresp(uchar resp[MSresplen])
625 {
626 	char buf[200], *p;
627 	int i;
628 
629 	p = buf;
630 	for(i=0; i<MSresplen; i++)
631 		p += sprint(p, "%.2ux ", resp[i]);
632 	syslog(0, AUTHLOG, "resp = %s", buf);
633 }
634 
635 
636 void
637 mschap(Ticketreq *tr)
638 {
639 
640 	char *secret, *hkey;
641 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
642 	uchar chal[CHALLEN];
643 	uchar hash[MShashlen];
644 	uchar hash2[MShashlen];
645 	uchar resp[MSresplen];
646 	OMSchapreply reply;
647 	int lmok, ntok;
648 	DigestState *s;
649 	uchar digest[SHA1dlen];
650 
651 	/*
652 	 *  Create a challenge and send it.
653 	 */
654 	randombytes((uchar*)chal, sizeof(chal));
655 	write(1, chal, sizeof(chal));
656 
657 	/*
658 	 *  get chap reply
659 	 */
660 	if(readn(0, &reply, sizeof(reply)) < 0)
661 		exits(0);
662 
663 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
664 	/*
665 	 * lookup
666 	 */
667 	secret = findsecret(KEYDB, tr->uid, sbuf);
668 	hkey = findkey(KEYDB, tr->hostid, hbuf);
669 	if(hkey == 0 || secret == 0){
670 		replyerror("mschap-fail bad response %s", raddr);
671 		logfail(tr->uid);
672 		exits(0);
673 	}
674 
675 	/*
676 	 *  check for match on LM algorithm
677 	 */
678 	lmhash(hash, secret);
679 	mschalresp(resp, hash, chal);
680 	lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
681 
682 	nthash(hash, secret);
683 	mschalresp(resp, hash, chal);
684 	ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
685 
686 	if(!ntok){
687 		replyerror("mschap-fail bad response %s %ux", raddr, (lmok<<1)|ntok);
688 		logfail(tr->uid);
689 		exits(0);
690 	}
691 
692 	succeed(tr->uid);
693 
694 	/*
695 	 *  reply with ticket & authenticator
696 	 */
697 	if(tickauthreply(tr, hkey) < 0)
698 		exits(0);
699 
700 	if(debug)
701 		syslog(0, AUTHLOG, "mschap-ok %s %s %ux", tr->uid, raddr, (lmok<<1)|ntok);
702 
703 	nthash(hash, secret);
704 	md4(hash, 16, hash2, 0);
705 	s = sha1(hash2, 16, 0, 0);
706 	sha1(hash2, 16, 0, s);
707 	sha1(chal, 8, digest, s);
708 
709 	if(write(1, digest, 16) < 0)
710 		exits(0);
711 }
712 
713 void
714 nthash(uchar hash[MShashlen], char *passwd)
715 {
716 	uchar buf[512];
717 	int i;
718 
719 	for (i = 0; *passwd && i + 1 < sizeof(buf);) {
720 		Rune r;
721 		passwd += chartorune(&r, passwd);
722 		buf[i++] = r;
723 		buf[i++] = r >> 8;
724 	}
725 
726 	memset(hash, 0, 16);
727 
728 	md4(buf, i, hash, 0);
729 }
730 
731 void
732 lmhash(uchar hash[MShashlen], char *passwd)
733 {
734 	uchar buf[14];
735 	char *stdtext = "KGS!@#$%";
736 	int i;
737 
738 	strncpy((char*)buf, passwd, sizeof(buf));
739 	for(i=0; i<sizeof(buf); i++)
740 		if(buf[i] >= 'a' && buf[i] <= 'z')
741 			buf[i] += 'A' - 'a';
742 
743 	memset(hash, 0, 16);
744 	memcpy(hash, stdtext, 8);
745 	memcpy(hash+8, stdtext, 8);
746 
747 	desencrypt(hash, buf);
748 	desencrypt(hash+8, buf+7);
749 }
750 
751 void
752 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
753 {
754 	int i;
755 	uchar buf[21];
756 
757 	memset(buf, 0, sizeof(buf));
758 	memcpy(buf, hash, MShashlen);
759 
760 	for(i=0; i<3; i++) {
761 		memmove(resp+i*MSchallen, chal, MSchallen);
762 		desencrypt(resp+i*MSchallen, buf+i*7);
763 	}
764 }
765 
766 void
767 desencrypt(uchar data[8], uchar key[7])
768 {
769 	ulong ekey[32];
770 
771 	key_setup(key, ekey);
772 	block_cipher(ekey, data, 0);
773 }
774 
775 /*
776  *  return true of the speaker may speak for the user
777  *
778  *  a speaker may always speak for himself/herself
779  */
780 int
781 speaksfor(char *speaker, char *user)
782 {
783 	Ndbtuple *tp, *ntp;
784 	Ndbs s;
785 	int ok;
786 	char notuser[Maxpath];
787 
788 	if(strcmp(speaker, user) == 0)
789 		return 1;
790 
791 	if(db == 0)
792 		return 0;
793 
794 	tp = ndbsearch(db, &s, "hostid", speaker);
795 	if(tp == 0)
796 		return 0;
797 
798 	ok = 0;
799 	snprint(notuser, sizeof notuser, "!%s", user);
800 	for(ntp = tp; ntp; ntp = ntp->entry)
801 		if(strcmp(ntp->attr, "uid") == 0){
802 			if(strcmp(ntp->val, notuser) == 0)
803 				break;
804 			if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
805 				ok = 1;
806 		}
807 	ndbfree(tp);
808 	return ok;
809 }
810 
811 /*
812  *  return an error reply
813  */
814 void
815 replyerror(char *fmt, ...)
816 {
817 	char buf[AERRLEN+1];
818 	va_list arg;
819 
820 	memset(buf, 0, sizeof(buf));
821 	va_start(arg, fmt);
822 	vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
823 	va_end(arg);
824 	buf[AERRLEN] = 0;
825 	buf[0] = AuthErr;
826 	write(1, buf, AERRLEN+1);
827 	syslog(0, AUTHLOG, buf+1);
828 }
829 
830 void
831 getraddr(char *dir)
832 {
833 	int n;
834 	char *cp;
835 	char file[Maxpath];
836 
837 	raddr[0] = 0;
838 	snprint(file, sizeof(file), "%s/remote", dir);
839 	n = readfile(file, raddr, sizeof(raddr)-1);
840 	if(n < 0)
841 		return;
842 	raddr[n] = 0;
843 
844 	cp = strchr(raddr, '\n');
845 	if(cp)
846 		*cp = 0;
847 	cp = strchr(raddr, '!');
848 	if(cp)
849 		*cp = 0;
850 }
851 
852 void
853 mkkey(char *k)
854 {
855 	randombytes((uchar*)k, DESKEYLEN);
856 }
857 
858 void
859 randombytes(uchar *buf, int len)
860 {
861 	int i;
862 
863 	if(readfile("/dev/random", (char*)buf, len) >= 0)
864 		return;
865 
866 	for(i = 0; i < len; i++)
867 		buf[i] = rand();
868 }
869 
870 /*
871  *  reply with ticket and authenticator
872  */
873 int
874 tickauthreply(Ticketreq *tr, char *hkey)
875 {
876 	Ticket t;
877 	Authenticator a;
878 	char buf[TICKETLEN+AUTHENTLEN+1];
879 
880 	memset(&t, 0, sizeof(t));
881 	memmove(t.chal, tr->chal, CHALLEN);
882 	safecpy(t.cuid, tr->uid, sizeof t.cuid);
883 	safecpy(t.suid, tr->uid, sizeof t.suid);
884 	mkkey(t.key);
885 	buf[0] = AuthOK;
886 	t.num = AuthTs;
887 	convT2M(&t, buf+1, hkey);
888 	memmove(a.chal, t.chal, CHALLEN);
889 	a.num = AuthAc;
890 	a.id = 0;
891 	convA2M(&a, buf+TICKETLEN+1, t.key);
892 	if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0)
893 		return -1;
894 	return 0;
895 }
896 
897 void
898 safecpy(char *to, char *from, int len)
899 {
900 	strncpy(to, from, len);
901 	to[len-1] = 0;
902 }
903