Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
	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
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
	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
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 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
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
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
				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
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
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
865
mkkey(char *k)
866
{
867
	randombytes((uchar*)k, DESKEYLEN);
868
}
869
 
870
void
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
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
910
safecpy(char *to, char *from, int len)
911
{
912
	strncpy(to, from, len);
913
	to[len-1] = 0;
914
}