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