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 break; 101 } 102 } 103 exits(0); 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 sprint(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[Ndbvlen]; 343 Ndbtuple *t; 344 int n; 345 346 if(*domain) 347 return domain; 348 349 if(*sysname) 350 return sysname; 351 352 n = readfile("/dev/sysname", sysname, sizeof(sysname)-1); 353 if(n < 0){ 354 strcpy(sysname, "kremvax"); 355 return sysname; 356 } 357 sysname[n] = 0; 358 359 t = csgetval(0, "sys", sysname, "dom", domain); 360 if(t == 0) 361 return sysname; 362 363 ndbfree(t); 364 return domain; 365 } 366 367 static int 368 h2b(char c) 369 { 370 if(c >= '0' && c <= '9') 371 return c - '0'; 372 if(c >= 'A' && c <= 'F') 373 return c - 'A' + 10; 374 if(c >= 'a' && c <= 'f') 375 return c - 'a' + 10; 376 return 0; 377 } 378 379 void 380 apop(Ticketreq *tr, int type) 381 { 382 int challen, i, tries; 383 char *secret, *hkey, *p; 384 Ticketreq treq; 385 DigestState *s; 386 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 387 char tbuf[TICKREQLEN]; 388 char buf[MD5dlen*2]; 389 uchar digest[MD5dlen], resp[MD5dlen]; 390 ulong rb[4]; 391 char chal[256]; 392 393 USED(tr); 394 395 /* 396 * Create a challenge and send it. 397 */ 398 randombytes((uchar*)rb, sizeof(rb)); 399 p = chal; 400 p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>", 401 rb[0], rb[1], rb[2], rb[3], domainname()); 402 challen = p - chal; 403 print("%c%-5d%s", AuthOKvar, challen, chal); 404 405 /* give user a few attempts */ 406 for(tries = 0; ; tries++) { 407 /* 408 * get ticket request 409 */ 410 if(readn(0, tbuf, TICKREQLEN) < 0) 411 exits(0); 412 convM2TR(tbuf, &treq); 413 tr = &treq; 414 if(tr->type != type) 415 exits(0); 416 417 /* 418 * read response 419 */ 420 if(readn(0, buf, MD5dlen*2) < 0) 421 exits(0); 422 for(i = 0; i < MD5dlen; i++) 423 resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]); 424 425 /* 426 * lookup 427 */ 428 secret = findsecret(KEYDB, tr->uid, sbuf); 429 hkey = findkey(KEYDB, tr->hostid, hbuf); 430 if(hkey == 0 || secret == 0){ 431 replyerror("apop-fail bad response %s", raddr); 432 logfail(tr->uid); 433 if(tries > 5) 434 return; 435 continue; 436 } 437 438 /* 439 * check for match 440 */ 441 if(type == AuthCram){ 442 hmac_md5((uchar*)chal, challen, 443 (uchar*)secret, strlen(secret), 444 digest, nil); 445 } else { 446 s = md5((uchar*)chal, challen, 0, 0); 447 md5((uchar*)secret, strlen(secret), digest, s); 448 } 449 if(memcmp(digest, resp, MD5dlen) != 0){ 450 replyerror("apop-fail bad response %s", raddr); 451 logfail(tr->uid); 452 if(tries > 5) 453 return; 454 continue; 455 } 456 break; 457 } 458 459 succeed(tr->uid); 460 461 /* 462 * reply with ticket & authenticator 463 */ 464 if(tickauthreply(tr, hkey) < 0) 465 exits(0); 466 467 if(debug){ 468 if(type == AuthCram) 469 syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr); 470 else 471 syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr); 472 } 473 } 474 475 enum { 476 VNCchallen= 16, 477 }; 478 479 /* VNC reverses the bits of each byte before using as a des key */ 480 uchar swizzletab[256] = { 481 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 482 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 483 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 484 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 485 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 486 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 487 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 488 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 489 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 490 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 491 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 492 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 493 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 494 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 495 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 496 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, 497 }; 498 499 void 500 vnc(Ticketreq *tr) 501 { 502 uchar chal[VNCchallen+6]; 503 uchar reply[VNCchallen]; 504 char *secret, *hkey; 505 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 506 DESstate s; 507 int i; 508 509 /* 510 * Create a challenge and send it. 511 */ 512 randombytes(chal+6, VNCchallen); 513 chal[0] = AuthOKvar; 514 sprint((char*)chal+1, "%-5d", VNCchallen); 515 if(write(1, chal, sizeof(chal)) != sizeof(chal)) 516 return; 517 518 /* 519 * lookup keys (and swizzle bits) 520 */ 521 memset(sbuf, 0, sizeof(sbuf)); 522 secret = findsecret(KEYDB, tr->uid, sbuf); 523 if(secret == 0){ 524 randombytes((uchar*)sbuf, sizeof(sbuf)); 525 secret = sbuf; 526 } 527 for(i = 0; i < 8; i++) 528 secret[i] = swizzletab[(uchar)secret[i]]; 529 530 hkey = findkey(KEYDB, tr->hostid, hbuf); 531 if(hkey == 0){ 532 randombytes((uchar*)hbuf, sizeof(hbuf)); 533 hkey = hbuf; 534 } 535 536 /* 537 * get response 538 */ 539 if(readn(0, reply, sizeof(reply)) != sizeof(reply)) 540 return; 541 542 /* 543 * decrypt response and compare 544 */ 545 setupDESstate(&s, (uchar*)secret, nil); 546 desECBdecrypt(reply, sizeof(reply), &s); 547 if(memcmp(reply, chal+6, VNCchallen) != 0){ 548 replyerror("vnc-fail bad response %s", raddr); 549 logfail(tr->uid); 550 return; 551 } 552 succeed(tr->uid); 553 554 /* 555 * reply with ticket & authenticator 556 */ 557 if(tickauthreply(tr, hkey) < 0) 558 exits(0); 559 560 if(debug) 561 syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr); 562 } 563 564 void 565 chap(Ticketreq *tr) 566 { 567 char *secret, *hkey; 568 DigestState *s; 569 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 570 uchar digest[MD5dlen]; 571 char chal[CHALLEN]; 572 OChapreply reply; 573 uchar md5buf[512]; 574 int n; 575 576 /* 577 * Create a challenge and send it. 578 */ 579 randombytes((uchar*)chal, sizeof(chal)); 580 write(1, chal, sizeof(chal)); 581 582 /* 583 * get chap reply 584 */ 585 if(readn(0, &reply, sizeof(reply)) < 0) 586 exits(0); 587 safecpy(tr->uid, reply.uid, sizeof(tr->uid)); 588 589 /* 590 * lookup 591 */ 592 secret = findsecret(KEYDB, tr->uid, sbuf); 593 hkey = findkey(KEYDB, tr->hostid, hbuf); 594 if(hkey == 0 || secret == 0){ 595 replyerror("chap-fail bad response %s", raddr); 596 logfail(tr->uid); 597 exits(0); 598 } 599 600 /* 601 * check for match 602 */ 603 s = md5(&reply.id, 1, 0, 0); 604 md5((uchar*)secret, strlen(secret), 0, s); 605 md5((uchar*)chal, sizeof(chal), digest, s); 606 607 md5buf[0] = reply.id; 608 n = 1; 609 memmove(md5buf+n, secret, strlen(secret)); 610 n += strlen(secret); 611 memmove(md5buf+n, chal, sizeof(chal)); 612 n += sizeof(chal); 613 md5(md5buf, n, digest, 0); 614 615 if(memcmp(digest, reply.resp, MD5dlen) != 0){ 616 replyerror("chap-fail bad response %s", raddr); 617 logfail(tr->uid); 618 exits(0); 619 } 620 621 succeed(tr->uid); 622 623 /* 624 * reply with ticket & authenticator 625 */ 626 if(tickauthreply(tr, hkey) < 0) 627 exits(0); 628 629 if(debug) 630 syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr); 631 } 632 633 void 634 printresp(uchar resp[MSresplen]) 635 { 636 char buf[200], *p; 637 int i; 638 639 p = buf; 640 for(i=0; i<MSresplen; i++) 641 p += sprint(p, "%.2ux ", resp[i]); 642 syslog(0, AUTHLOG, "resp = %s", buf); 643 } 644 645 646 void 647 mschap(Ticketreq *tr) 648 { 649 650 char *secret, *hkey; 651 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 652 uchar chal[CHALLEN]; 653 uchar hash[MShashlen]; 654 uchar hash2[MShashlen]; 655 uchar resp[MSresplen]; 656 OMSchapreply reply; 657 int lmok, ntok; 658 DigestState *s; 659 uchar digest[SHA1dlen]; 660 661 /* 662 * Create a challenge and send it. 663 */ 664 randombytes((uchar*)chal, sizeof(chal)); 665 write(1, chal, sizeof(chal)); 666 667 /* 668 * get chap reply 669 */ 670 if(readn(0, &reply, sizeof(reply)) < 0) 671 exits(0); 672 673 safecpy(tr->uid, reply.uid, sizeof(tr->uid)); 674 /* 675 * lookup 676 */ 677 secret = findsecret(KEYDB, tr->uid, sbuf); 678 hkey = findkey(KEYDB, tr->hostid, hbuf); 679 if(hkey == 0 || secret == 0){ 680 replyerror("mschap-fail bad response %s", raddr); 681 logfail(tr->uid); 682 exits(0); 683 } 684 685 /* 686 * check for match on LM algorithm 687 */ 688 lmhash(hash, secret); 689 mschalresp(resp, hash, chal); 690 lmok = memcmp(resp, reply.LMresp, MSresplen) == 0; 691 692 nthash(hash, secret); 693 mschalresp(resp, hash, chal); 694 ntok = memcmp(resp, reply.NTresp, MSresplen) == 0; 695 696 if(!ntok){ 697 replyerror("mschap-fail bad response %s %ux", raddr, (lmok<<1)|ntok); 698 logfail(tr->uid); 699 exits(0); 700 } 701 702 succeed(tr->uid); 703 704 /* 705 * reply with ticket & authenticator 706 */ 707 if(tickauthreply(tr, hkey) < 0) 708 exits(0); 709 710 if(debug) 711 syslog(0, AUTHLOG, "mschap-ok %s %s %ux", tr->uid, raddr, (lmok<<1)|ntok); 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<sizeof(buf); passwd++) { 730 buf[i++] = *passwd; 731 buf[i++] = 0; 732 } 733 734 memset(hash, 0, 16); 735 736 md4(buf, i, hash, 0); 737 } 738 739 void 740 lmhash(uchar hash[MShashlen], char *passwd) 741 { 742 uchar buf[14]; 743 char *stdtext = "KGS!@#$%"; 744 int i; 745 746 strncpy((char*)buf, passwd, sizeof(buf)); 747 for(i=0; i<sizeof(buf); i++) 748 if(buf[i] >= 'a' && buf[i] <= 'z') 749 buf[i] += 'A' - 'a'; 750 751 memset(hash, 0, 16); 752 memcpy(hash, stdtext, 8); 753 memcpy(hash+8, stdtext, 8); 754 755 desencrypt(hash, buf); 756 desencrypt(hash+8, buf+7); 757 } 758 759 void 760 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]) 761 { 762 int i; 763 uchar buf[21]; 764 765 memset(buf, 0, sizeof(buf)); 766 memcpy(buf, hash, MShashlen); 767 768 for(i=0; i<3; i++) { 769 memmove(resp+i*MSchallen, chal, MSchallen); 770 desencrypt(resp+i*MSchallen, buf+i*7); 771 } 772 } 773 774 void 775 desencrypt(uchar data[8], uchar key[7]) 776 { 777 ulong ekey[32]; 778 779 key_setup(key, ekey); 780 block_cipher(ekey, data, 0); 781 } 782 783 /* 784 * return true of the speaker may speak for the user 785 * 786 * a speaker may always speak for himself/herself 787 */ 788 int 789 speaksfor(char *speaker, char *user) 790 { 791 Ndbtuple *tp, *ntp; 792 Ndbs s; 793 int ok; 794 char notuser[Maxpath]; 795 796 if(strcmp(speaker, user) == 0) 797 return 1; 798 799 if(db == 0) 800 return 0; 801 802 tp = ndbsearch(db, &s, "hostid", speaker); 803 if(tp == 0) 804 return 0; 805 806 ok = 0; 807 snprint(notuser, sizeof notuser, "!%s", user); 808 for(ntp = tp; ntp; ntp = ntp->entry) 809 if(strcmp(ntp->attr, "uid") == 0){ 810 if(strcmp(ntp->val, notuser) == 0) 811 break; 812 if(*ntp->val == '*' || strcmp(ntp->val, user) == 0) 813 ok = 1; 814 } 815 ndbfree(tp); 816 return ok; 817 } 818 819 /* 820 * return an error reply 821 */ 822 void 823 replyerror(char *fmt, ...) 824 { 825 char buf[AERRLEN+1]; 826 va_list arg; 827 828 memset(buf, 0, sizeof(buf)); 829 va_start(arg, fmt); 830 vseprint(buf + 1, buf + sizeof(buf), fmt, arg); 831 va_end(arg); 832 buf[AERRLEN] = 0; 833 buf[0] = AuthErr; 834 write(1, buf, AERRLEN+1); 835 syslog(0, AUTHLOG, buf+1); 836 } 837 838 void 839 getraddr(char *dir) 840 { 841 int n; 842 char *cp; 843 char file[Maxpath]; 844 845 raddr[0] = 0; 846 snprint(file, sizeof(file), "%s/remote", dir); 847 n = readfile(file, raddr, sizeof(raddr)-1); 848 if(n < 0) 849 return; 850 raddr[n] = 0; 851 852 cp = strchr(raddr, '\n'); 853 if(cp) 854 *cp = 0; 855 cp = strchr(raddr, '!'); 856 if(cp) 857 *cp = 0; 858 } 859 860 void 861 mkkey(char *k) 862 { 863 randombytes((uchar*)k, DESKEYLEN); 864 } 865 866 void 867 randombytes(uchar *buf, int len) 868 { 869 int i; 870 871 if(readfile("/dev/random", (char*)buf, len) >= 0) 872 return; 873 874 for(i = 0; i < len; i++) 875 buf[i] = rand(); 876 } 877 878 /* 879 * reply with ticket and authenticator 880 */ 881 int 882 tickauthreply(Ticketreq *tr, char *hkey) 883 { 884 Ticket t; 885 Authenticator a; 886 char buf[TICKETLEN+AUTHENTLEN+1]; 887 888 memset(&t, 0, sizeof(t)); 889 memmove(t.chal, tr->chal, CHALLEN); 890 safecpy(t.cuid, tr->uid, sizeof t.cuid); 891 safecpy(t.suid, tr->uid, sizeof t.suid); 892 mkkey(t.key); 893 buf[0] = AuthOK; 894 t.num = AuthTs; 895 convT2M(&t, buf+1, hkey); 896 memmove(a.chal, t.chal, CHALLEN); 897 a.num = AuthAc; 898 a.id = 0; 899 convA2M(&a, buf+TICKETLEN+1, t.key); 900 if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0) 901 return -1; 902 return 0; 903 } 904 905 void 906 safecpy(char *to, char *from, int len) 907 { 908 strncpy(to, from, len); 909 to[len-1] = 0; 910 } 911