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