xref: /plan9/sys/src/cmd/auth/authsrv.c (revision f54edc786b9c49b2c7ab1c0695cdc8c698b11f4d)
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
main(int argc,char * argv[])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
ticketrequest(Ticketreq * tr)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
challengebox(Ticketreq * tr)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 	snprint(buf+1, sizeof 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
changepasswd(Ticketreq * tr)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
http(Ticketreq * tr)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*
domainname(void)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
h2b(char c)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
apop(Ticketreq * tr,int type)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
vnc(Ticketreq * tr)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 	snprint((char*)chal+1, sizeof 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
chap(Ticketreq * tr)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
printresp(uchar resp[MSresplen])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
mschap(Ticketreq * tr)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 dupe, 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/%s(%s)",
668 			tr->uid, tr->hostid, raddr);
669 		logfail(tr->uid);
670 		exits(0);
671 	}
672 
673 	lmhash(hash, secret);
674 	mschalresp(resp, hash, chal);
675 	lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
676 	nthash(hash, secret);
677 	mschalresp(resp, hash, chal);
678 	ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
679 	dupe = memcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
680 
681 	/*
682 	 * It is valid to send the same response in both the LM and NTLM
683 	 * fields provided one of them is correct, if neither matches,
684 	 * or the two fields are different and either fails to match,
685 	 * the whole sha-bang fails.
686 	 *
687 	 * This is an improvement in security as it allows clients who
688 	 * wish to do NTLM auth (which is insecure) not to send
689 	 * LM tokens (which is very insecure).
690 	 *
691 	 * Windows servers supports clients doing this also though
692 	 * windows clients don't seem to use the feature.
693 	 */
694 	if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
695 		replyerror("mschap-fail bad response %s/%s(%s) %d,%d,%d",
696 			tr->uid, tr->hostid, raddr, dupe, lmok, ntok);
697 		logfail(tr->uid);
698 		exits(0);
699 	}
700 
701 	succeed(tr->uid);
702 
703 	/*
704 	 *  reply with ticket & authenticator
705 	 */
706 	if(tickauthreply(tr, hkey) < 0)
707 		exits(0);
708 
709 	if(debug)
710 		replyerror("mschap-ok %s/%s(%s) %ux",
711 			tr->uid, tr->hostid, raddr);
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
nthash(uchar hash[MShashlen],char * passwd)724 nthash(uchar hash[MShashlen], char *passwd)
725 {
726 	uchar buf[512];
727 	int i;
728 
729 	for (i = 0; *passwd && i + 1 < sizeof(buf);) {
730 		Rune r;
731 		passwd += chartorune(&r, passwd);
732 		buf[i++] = r;
733 		buf[i++] = r >> 8;
734 	}
735 
736 	memset(hash, 0, 16);
737 
738 	md4(buf, i, hash, 0);
739 }
740 
741 void
lmhash(uchar hash[MShashlen],char * passwd)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
mschalresp(uchar resp[MSresplen],uchar hash[MShashlen],uchar chal[MSchallen])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
desencrypt(uchar data[8],uchar key[7])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
speaksfor(char * speaker,char * user)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 				ok = 0;
814 				break;
815 			}
816 			if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
817 				ok = 1;
818 		}
819 	ndbfree(tp);
820 	return ok;
821 }
822 
823 /*
824  *  return an error reply
825  */
826 void
replyerror(char * fmt,...)827 replyerror(char *fmt, ...)
828 {
829 	char buf[AERRLEN+1];
830 	va_list arg;
831 
832 	memset(buf, 0, sizeof(buf));
833 	va_start(arg, fmt);
834 	vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
835 	va_end(arg);
836 	buf[AERRLEN] = 0;
837 	buf[0] = AuthErr;
838 	write(1, buf, AERRLEN+1);
839 	syslog(0, AUTHLOG, buf+1);
840 }
841 
842 void
getraddr(char * dir)843 getraddr(char *dir)
844 {
845 	int n;
846 	char *cp;
847 	char file[Maxpath];
848 
849 	raddr[0] = 0;
850 	snprint(file, sizeof(file), "%s/remote", dir);
851 	n = readfile(file, raddr, sizeof(raddr)-1);
852 	if(n < 0)
853 		return;
854 	raddr[n] = 0;
855 
856 	cp = strchr(raddr, '\n');
857 	if(cp)
858 		*cp = 0;
859 	cp = strchr(raddr, '!');
860 	if(cp)
861 		*cp = 0;
862 }
863 
864 void
mkkey(char * k)865 mkkey(char *k)
866 {
867 	randombytes((uchar*)k, DESKEYLEN);
868 }
869 
870 void
randombytes(uchar * buf,int len)871 randombytes(uchar *buf, int len)
872 {
873 	int i;
874 
875 	if(readfile("/dev/random", (char*)buf, len) >= 0)
876 		return;
877 
878 	for(i = 0; i < len; i++)
879 		buf[i] = rand();
880 }
881 
882 /*
883  *  reply with ticket and authenticator
884  */
885 int
tickauthreply(Ticketreq * tr,char * hkey)886 tickauthreply(Ticketreq *tr, char *hkey)
887 {
888 	Ticket t;
889 	Authenticator a;
890 	char buf[TICKETLEN+AUTHENTLEN+1];
891 
892 	memset(&t, 0, sizeof(t));
893 	memmove(t.chal, tr->chal, CHALLEN);
894 	safecpy(t.cuid, tr->uid, sizeof t.cuid);
895 	safecpy(t.suid, tr->uid, sizeof t.suid);
896 	mkkey(t.key);
897 	buf[0] = AuthOK;
898 	t.num = AuthTs;
899 	convT2M(&t, buf+1, hkey);
900 	memmove(a.chal, t.chal, CHALLEN);
901 	a.num = AuthAc;
902 	a.id = 0;
903 	convA2M(&a, buf+TICKETLEN+1, t.key);
904 	if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0)
905 		return -1;
906 	return 0;
907 }
908 
909 void
safecpy(char * to,char * from,int len)910 safecpy(char *to, char *from, int len)
911 {
912 	strncpy(to, from, len);
913 	to[len-1] = 0;
914 }
915