xref: /plan9/sys/src/cmd/auth/authsrv.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
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 	/* not reached */
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;
343 	int n;
344 
345 	if(domain)
346 		return domain;
347 	if(*sysname)
348 		return sysname;
349 
350 	domain = csgetvalue(0, "sys", sysname, "dom", nil);
351 	if(domain)
352 		return domain;
353 
354 	n = readfile("/dev/sysname", sysname, sizeof(sysname)-1);
355 	if(n < 0){
356 		strcpy(sysname, "kremvax");
357 		return sysname;
358 	}
359 	sysname[n] = 0;
360 
361 	return sysname;
362 }
363 
364 static int
365 h2b(char c)
366 {
367 	if(c >= '0' && c <= '9')
368 		return c - '0';
369 	if(c >= 'A' && c <= 'F')
370 		return c - 'A' + 10;
371 	if(c >= 'a' && c <= 'f')
372 		return c - 'a' + 10;
373 	return 0;
374 }
375 
376 void
377 apop(Ticketreq *tr, int type)
378 {
379 	int challen, i, tries;
380 	char *secret, *hkey, *p;
381 	Ticketreq treq;
382 	DigestState *s;
383 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
384 	char tbuf[TICKREQLEN];
385 	char buf[MD5dlen*2];
386 	uchar digest[MD5dlen], resp[MD5dlen];
387 	ulong rb[4];
388 	char chal[256];
389 
390 	USED(tr);
391 
392 	/*
393 	 *  Create a challenge and send it.
394 	 */
395 	randombytes((uchar*)rb, sizeof(rb));
396 	p = chal;
397 	p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>",
398 		rb[0], rb[1], rb[2], rb[3], domainname());
399 	challen = p - chal;
400 	print("%c%-5d%s", AuthOKvar, challen, chal);
401 
402 	/* give user a few attempts */
403 	for(tries = 0; ; tries++) {
404 		/*
405 		 *  get ticket request
406 		 */
407 		if(readn(0, tbuf, TICKREQLEN) < 0)
408 			exits(0);
409 		convM2TR(tbuf, &treq);
410 		tr = &treq;
411 		if(tr->type != type)
412 			exits(0);
413 
414 		/*
415 		 * read response
416 		 */
417 		if(readn(0, buf, MD5dlen*2) < 0)
418 			exits(0);
419 		for(i = 0; i < MD5dlen; i++)
420 			resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]);
421 
422 		/*
423 		 * lookup
424 		 */
425 		secret = findsecret(KEYDB, tr->uid, sbuf);
426 		hkey = findkey(KEYDB, tr->hostid, hbuf);
427 		if(hkey == 0 || secret == 0){
428 			replyerror("apop-fail bad response %s", raddr);
429 			logfail(tr->uid);
430 			if(tries > 5)
431 				return;
432 			continue;
433 		}
434 
435 		/*
436 		 *  check for match
437 		 */
438 		if(type == AuthCram){
439 			hmac_md5((uchar*)chal, challen,
440 				(uchar*)secret, strlen(secret),
441 				digest, nil);
442 		} else {
443 			s = md5((uchar*)chal, challen, 0, 0);
444 			md5((uchar*)secret, strlen(secret), digest, s);
445 		}
446 		if(memcmp(digest, resp, MD5dlen) != 0){
447 			replyerror("apop-fail bad response %s", raddr);
448 			logfail(tr->uid);
449 			if(tries > 5)
450 				return;
451 			continue;
452 		}
453 		break;
454 	}
455 
456 	succeed(tr->uid);
457 
458 	/*
459 	 *  reply with ticket & authenticator
460 	 */
461 	if(tickauthreply(tr, hkey) < 0)
462 		exits(0);
463 
464 	if(debug){
465 		if(type == AuthCram)
466 			syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
467 		else
468 			syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
469 	}
470 }
471 
472 enum {
473 	VNCchallen=	16,
474 };
475 
476 /* VNC reverses the bits of each byte before using as a des key */
477 uchar swizzletab[256] = {
478  0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
479  0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
480  0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
481  0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
482  0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
483  0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
484  0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
485  0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
486  0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
487  0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
488  0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
489  0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
490  0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
491  0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
492  0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
493  0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
494 };
495 
496 void
497 vnc(Ticketreq *tr)
498 {
499 	uchar chal[VNCchallen+6];
500 	uchar reply[VNCchallen];
501 	char *secret, *hkey;
502 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
503 	DESstate s;
504 	int i;
505 
506 	/*
507 	 *  Create a challenge and send it.
508 	 */
509 	randombytes(chal+6, VNCchallen);
510 	chal[0] = AuthOKvar;
511 	sprint((char*)chal+1, "%-5d", VNCchallen);
512 	if(write(1, chal, sizeof(chal)) != sizeof(chal))
513 		return;
514 
515 	/*
516 	 *  lookup keys (and swizzle bits)
517 	 */
518 	memset(sbuf, 0, sizeof(sbuf));
519 	secret = findsecret(KEYDB, tr->uid, sbuf);
520 	if(secret == 0){
521 		randombytes((uchar*)sbuf, sizeof(sbuf));
522 		secret = sbuf;
523 	}
524 	for(i = 0; i < 8; i++)
525 		secret[i] = swizzletab[(uchar)secret[i]];
526 
527 	hkey = findkey(KEYDB, tr->hostid, hbuf);
528 	if(hkey == 0){
529 		randombytes((uchar*)hbuf, sizeof(hbuf));
530 		hkey = hbuf;
531 	}
532 
533 	/*
534 	 *  get response
535 	 */
536 	if(readn(0, reply, sizeof(reply)) != sizeof(reply))
537 		return;
538 
539 	/*
540 	 *  decrypt response and compare
541 	 */
542 	setupDESstate(&s, (uchar*)secret, nil);
543 	desECBdecrypt(reply, sizeof(reply), &s);
544 	if(memcmp(reply, chal+6, VNCchallen) != 0){
545 		replyerror("vnc-fail bad response %s", raddr);
546 		logfail(tr->uid);
547 		return;
548 	}
549 	succeed(tr->uid);
550 
551 	/*
552 	 *  reply with ticket & authenticator
553 	 */
554 	if(tickauthreply(tr, hkey) < 0)
555 		exits(0);
556 
557 	if(debug)
558 		syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
559 }
560 
561 void
562 chap(Ticketreq *tr)
563 {
564 	char *secret, *hkey;
565 	DigestState *s;
566 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
567 	uchar digest[MD5dlen];
568 	char chal[CHALLEN];
569 	OChapreply reply;
570 
571 	/*
572 	 *  Create a challenge and send it.
573 	 */
574 	randombytes((uchar*)chal, sizeof(chal));
575 	write(1, chal, sizeof(chal));
576 
577 	/*
578 	 *  get chap reply
579 	 */
580 	if(readn(0, &reply, sizeof(reply)) < 0)
581 		exits(0);
582 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
583 
584 	/*
585 	 * lookup
586 	 */
587 	secret = findsecret(KEYDB, tr->uid, sbuf);
588 	hkey = findkey(KEYDB, tr->hostid, hbuf);
589 	if(hkey == 0 || secret == 0){
590 		replyerror("chap-fail bad response %s", raddr);
591 		logfail(tr->uid);
592 		exits(0);
593 	}
594 
595 	/*
596 	 *  check for match
597 	 */
598 	s = md5(&reply.id, 1, 0, 0);
599 	md5((uchar*)secret, strlen(secret), 0, s);
600 	md5((uchar*)chal, sizeof(chal), digest, s);
601 
602 	if(memcmp(digest, reply.resp, MD5dlen) != 0){
603 		replyerror("chap-fail bad response %s", raddr);
604 		logfail(tr->uid);
605 		exits(0);
606 	}
607 
608 	succeed(tr->uid);
609 
610 	/*
611 	 *  reply with ticket & authenticator
612 	 */
613 	if(tickauthreply(tr, hkey) < 0)
614 		exits(0);
615 
616 	if(debug)
617 		syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
618 }
619 
620 void
621 printresp(uchar resp[MSresplen])
622 {
623 	char buf[200], *p;
624 	int i;
625 
626 	p = buf;
627 	for(i=0; i<MSresplen; i++)
628 		p += sprint(p, "%.2ux ", resp[i]);
629 	syslog(0, AUTHLOG, "resp = %s", buf);
630 }
631 
632 
633 void
634 mschap(Ticketreq *tr)
635 {
636 
637 	char *secret, *hkey;
638 	char sbuf[SECRETLEN], hbuf[DESKEYLEN];
639 	uchar chal[CHALLEN];
640 	uchar hash[MShashlen];
641 	uchar hash2[MShashlen];
642 	uchar resp[MSresplen];
643 	OMSchapreply reply;
644 	int lmok, ntok;
645 	DigestState *s;
646 	uchar digest[SHA1dlen];
647 
648 	/*
649 	 *  Create a challenge and send it.
650 	 */
651 	randombytes((uchar*)chal, sizeof(chal));
652 	write(1, chal, sizeof(chal));
653 
654 	/*
655 	 *  get chap reply
656 	 */
657 	if(readn(0, &reply, sizeof(reply)) < 0)
658 		exits(0);
659 
660 	safecpy(tr->uid, reply.uid, sizeof(tr->uid));
661 	/*
662 	 * lookup
663 	 */
664 	secret = findsecret(KEYDB, tr->uid, sbuf);
665 	hkey = findkey(KEYDB, tr->hostid, hbuf);
666 	if(hkey == 0 || secret == 0){
667 		replyerror("mschap-fail bad response %s", raddr);
668 		logfail(tr->uid);
669 		exits(0);
670 	}
671 
672 	/*
673 	 *  check for match on LM algorithm
674 	 */
675 	lmhash(hash, secret);
676 	mschalresp(resp, hash, chal);
677 	lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
678 
679 	nthash(hash, secret);
680 	mschalresp(resp, hash, chal);
681 	ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
682 
683 	if(!ntok){
684 		replyerror("mschap-fail bad response %s %ux", raddr, (lmok<<1)|ntok);
685 		logfail(tr->uid);
686 		exits(0);
687 	}
688 
689 	succeed(tr->uid);
690 
691 	/*
692 	 *  reply with ticket & authenticator
693 	 */
694 	if(tickauthreply(tr, hkey) < 0)
695 		exits(0);
696 
697 	if(debug)
698 		syslog(0, AUTHLOG, "mschap-ok %s %s %ux", tr->uid, raddr, (lmok<<1)|ntok);
699 
700 	nthash(hash, secret);
701 	md4(hash, 16, hash2, 0);
702 	s = sha1(hash2, 16, 0, 0);
703 	sha1(hash2, 16, 0, s);
704 	sha1(chal, 8, digest, s);
705 
706 	if(write(1, digest, 16) < 0)
707 		exits(0);
708 }
709 
710 void
711 nthash(uchar hash[MShashlen], char *passwd)
712 {
713 	uchar buf[512];
714 	int i;
715 
716 	for (i = 0; *passwd && i + 1 < sizeof(buf);) {
717 		Rune r;
718 		passwd += chartorune(&r, passwd);
719 		buf[i++] = r;
720 		buf[i++] = r >> 8;
721 	}
722 
723 	memset(hash, 0, 16);
724 
725 	md4(buf, i, hash, 0);
726 }
727 
728 void
729 lmhash(uchar hash[MShashlen], char *passwd)
730 {
731 	uchar buf[14];
732 	char *stdtext = "KGS!@#$%";
733 	int i;
734 
735 	strncpy((char*)buf, passwd, sizeof(buf));
736 	for(i=0; i<sizeof(buf); i++)
737 		if(buf[i] >= 'a' && buf[i] <= 'z')
738 			buf[i] += 'A' - 'a';
739 
740 	memset(hash, 0, 16);
741 	memcpy(hash, stdtext, 8);
742 	memcpy(hash+8, stdtext, 8);
743 
744 	desencrypt(hash, buf);
745 	desencrypt(hash+8, buf+7);
746 }
747 
748 void
749 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
750 {
751 	int i;
752 	uchar buf[21];
753 
754 	memset(buf, 0, sizeof(buf));
755 	memcpy(buf, hash, MShashlen);
756 
757 	for(i=0; i<3; i++) {
758 		memmove(resp+i*MSchallen, chal, MSchallen);
759 		desencrypt(resp+i*MSchallen, buf+i*7);
760 	}
761 }
762 
763 void
764 desencrypt(uchar data[8], uchar key[7])
765 {
766 	ulong ekey[32];
767 
768 	key_setup(key, ekey);
769 	block_cipher(ekey, data, 0);
770 }
771 
772 /*
773  *  return true of the speaker may speak for the user
774  *
775  *  a speaker may always speak for himself/herself
776  */
777 int
778 speaksfor(char *speaker, char *user)
779 {
780 	Ndbtuple *tp, *ntp;
781 	Ndbs s;
782 	int ok;
783 	char notuser[Maxpath];
784 
785 	if(strcmp(speaker, user) == 0)
786 		return 1;
787 
788 	if(db == 0)
789 		return 0;
790 
791 	tp = ndbsearch(db, &s, "hostid", speaker);
792 	if(tp == 0)
793 		return 0;
794 
795 	ok = 0;
796 	snprint(notuser, sizeof notuser, "!%s", user);
797 	for(ntp = tp; ntp; ntp = ntp->entry)
798 		if(strcmp(ntp->attr, "uid") == 0){
799 			if(strcmp(ntp->val, notuser) == 0)
800 				break;
801 			if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
802 				ok = 1;
803 		}
804 	ndbfree(tp);
805 	return ok;
806 }
807 
808 /*
809  *  return an error reply
810  */
811 void
812 replyerror(char *fmt, ...)
813 {
814 	char buf[AERRLEN+1];
815 	va_list arg;
816 
817 	memset(buf, 0, sizeof(buf));
818 	va_start(arg, fmt);
819 	vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
820 	va_end(arg);
821 	buf[AERRLEN] = 0;
822 	buf[0] = AuthErr;
823 	write(1, buf, AERRLEN+1);
824 	syslog(0, AUTHLOG, buf+1);
825 }
826 
827 void
828 getraddr(char *dir)
829 {
830 	int n;
831 	char *cp;
832 	char file[Maxpath];
833 
834 	raddr[0] = 0;
835 	snprint(file, sizeof(file), "%s/remote", dir);
836 	n = readfile(file, raddr, sizeof(raddr)-1);
837 	if(n < 0)
838 		return;
839 	raddr[n] = 0;
840 
841 	cp = strchr(raddr, '\n');
842 	if(cp)
843 		*cp = 0;
844 	cp = strchr(raddr, '!');
845 	if(cp)
846 		*cp = 0;
847 }
848 
849 void
850 mkkey(char *k)
851 {
852 	randombytes((uchar*)k, DESKEYLEN);
853 }
854 
855 void
856 randombytes(uchar *buf, int len)
857 {
858 	int i;
859 
860 	if(readfile("/dev/random", (char*)buf, len) >= 0)
861 		return;
862 
863 	for(i = 0; i < len; i++)
864 		buf[i] = rand();
865 }
866 
867 /*
868  *  reply with ticket and authenticator
869  */
870 int
871 tickauthreply(Ticketreq *tr, char *hkey)
872 {
873 	Ticket t;
874 	Authenticator a;
875 	char buf[TICKETLEN+AUTHENTLEN+1];
876 
877 	memset(&t, 0, sizeof(t));
878 	memmove(t.chal, tr->chal, CHALLEN);
879 	safecpy(t.cuid, tr->uid, sizeof t.cuid);
880 	safecpy(t.suid, tr->uid, sizeof t.suid);
881 	mkkey(t.key);
882 	buf[0] = AuthOK;
883 	t.num = AuthTs;
884 	convT2M(&t, buf+1, hkey);
885 	memmove(a.chal, t.chal, CHALLEN);
886 	a.num = AuthAc;
887 	a.id = 0;
888 	convA2M(&a, buf+TICKETLEN+1, t.key);
889 	if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0)
890 		return -1;
891 	return 0;
892 }
893 
894 void
895 safecpy(char *to, char *from, int len)
896 {
897 	strncpy(to, from, len);
898 	to[len-1] = 0;
899 }
900