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