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 } 915