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 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 int n; 574 575 /* 576 * Create a challenge and send it. 577 */ 578 randombytes((uchar*)chal, sizeof(chal)); 579 write(1, chal, sizeof(chal)); 580 581 /* 582 * get chap reply 583 */ 584 if(readn(0, &reply, sizeof(reply)) < 0) 585 exits(0); 586 safecpy(tr->uid, reply.uid, sizeof(tr->uid)); 587 588 /* 589 * lookup 590 */ 591 secret = findsecret(KEYDB, tr->uid, sbuf); 592 hkey = findkey(KEYDB, tr->hostid, hbuf); 593 if(hkey == 0 || secret == 0){ 594 replyerror("chap-fail bad response %s", raddr); 595 logfail(tr->uid); 596 exits(0); 597 } 598 599 /* 600 * check for match 601 */ 602 s = md5(&reply.id, 1, 0, 0); 603 md5((uchar*)secret, strlen(secret), 0, s); 604 md5((uchar*)chal, sizeof(chal), digest, s); 605 606 if(memcmp(digest, reply.resp, MD5dlen) != 0){ 607 replyerror("chap-fail bad response %s", raddr); 608 logfail(tr->uid); 609 exits(0); 610 } 611 612 succeed(tr->uid); 613 614 /* 615 * reply with ticket & authenticator 616 */ 617 if(tickauthreply(tr, hkey) < 0) 618 exits(0); 619 620 if(debug) 621 syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr); 622 } 623 624 void 625 printresp(uchar resp[MSresplen]) 626 { 627 char buf[200], *p; 628 int i; 629 630 p = buf; 631 for(i=0; i<MSresplen; i++) 632 p += sprint(p, "%.2ux ", resp[i]); 633 syslog(0, AUTHLOG, "resp = %s", buf); 634 } 635 636 637 void 638 mschap(Ticketreq *tr) 639 { 640 641 char *secret, *hkey; 642 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 643 uchar chal[CHALLEN]; 644 uchar hash[MShashlen]; 645 uchar hash2[MShashlen]; 646 uchar resp[MSresplen]; 647 OMSchapreply reply; 648 int lmok, ntok; 649 DigestState *s; 650 uchar digest[SHA1dlen]; 651 652 /* 653 * Create a challenge and send it. 654 */ 655 randombytes((uchar*)chal, sizeof(chal)); 656 write(1, chal, sizeof(chal)); 657 658 /* 659 * get chap reply 660 */ 661 if(readn(0, &reply, sizeof(reply)) < 0) 662 exits(0); 663 664 safecpy(tr->uid, reply.uid, sizeof(tr->uid)); 665 /* 666 * lookup 667 */ 668 secret = findsecret(KEYDB, tr->uid, sbuf); 669 hkey = findkey(KEYDB, tr->hostid, hbuf); 670 if(hkey == 0 || secret == 0){ 671 replyerror("mschap-fail bad response %s", raddr); 672 logfail(tr->uid); 673 exits(0); 674 } 675 676 /* 677 * check for match on LM algorithm 678 */ 679 lmhash(hash, secret); 680 mschalresp(resp, hash, chal); 681 lmok = memcmp(resp, reply.LMresp, MSresplen) == 0; 682 683 nthash(hash, secret); 684 mschalresp(resp, hash, chal); 685 ntok = memcmp(resp, reply.NTresp, MSresplen) == 0; 686 687 if(!ntok){ 688 replyerror("mschap-fail bad response %s %ux", raddr, (lmok<<1)|ntok); 689 logfail(tr->uid); 690 exits(0); 691 } 692 693 succeed(tr->uid); 694 695 /* 696 * reply with ticket & authenticator 697 */ 698 if(tickauthreply(tr, hkey) < 0) 699 exits(0); 700 701 if(debug) 702 syslog(0, AUTHLOG, "mschap-ok %s %s %ux", tr->uid, raddr, (lmok<<1)|ntok); 703 704 nthash(hash, secret); 705 md4(hash, 16, hash2, 0); 706 s = sha1(hash2, 16, 0, 0); 707 sha1(hash2, 16, 0, s); 708 sha1(chal, 8, digest, s); 709 710 if(write(1, digest, 16) < 0) 711 exits(0); 712 } 713 714 void 715 nthash(uchar hash[MShashlen], char *passwd) 716 { 717 uchar buf[512]; 718 int i; 719 720 for(i=0; *passwd && i<sizeof(buf); passwd++) { 721 buf[i++] = *passwd; 722 buf[i++] = 0; 723 } 724 725 memset(hash, 0, 16); 726 727 md4(buf, i, hash, 0); 728 } 729 730 void 731 lmhash(uchar hash[MShashlen], char *passwd) 732 { 733 uchar buf[14]; 734 char *stdtext = "KGS!@#$%"; 735 int i; 736 737 strncpy((char*)buf, passwd, sizeof(buf)); 738 for(i=0; i<sizeof(buf); i++) 739 if(buf[i] >= 'a' && buf[i] <= 'z') 740 buf[i] += 'A' - 'a'; 741 742 memset(hash, 0, 16); 743 memcpy(hash, stdtext, 8); 744 memcpy(hash+8, stdtext, 8); 745 746 desencrypt(hash, buf); 747 desencrypt(hash+8, buf+7); 748 } 749 750 void 751 mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]) 752 { 753 int i; 754 uchar buf[21]; 755 756 memset(buf, 0, sizeof(buf)); 757 memcpy(buf, hash, MShashlen); 758 759 for(i=0; i<3; i++) { 760 memmove(resp+i*MSchallen, chal, MSchallen); 761 desencrypt(resp+i*MSchallen, buf+i*7); 762 } 763 } 764 765 void 766 desencrypt(uchar data[8], uchar key[7]) 767 { 768 ulong ekey[32]; 769 770 key_setup(key, ekey); 771 block_cipher(ekey, data, 0); 772 } 773 774 /* 775 * return true of the speaker may speak for the user 776 * 777 * a speaker may always speak for himself/herself 778 */ 779 int 780 speaksfor(char *speaker, char *user) 781 { 782 Ndbtuple *tp, *ntp; 783 Ndbs s; 784 int ok; 785 char notuser[Maxpath]; 786 787 if(strcmp(speaker, user) == 0) 788 return 1; 789 790 if(db == 0) 791 return 0; 792 793 tp = ndbsearch(db, &s, "hostid", speaker); 794 if(tp == 0) 795 return 0; 796 797 ok = 0; 798 snprint(notuser, sizeof notuser, "!%s", user); 799 for(ntp = tp; ntp; ntp = ntp->entry) 800 if(strcmp(ntp->attr, "uid") == 0){ 801 if(strcmp(ntp->val, notuser) == 0) 802 break; 803 if(*ntp->val == '*' || strcmp(ntp->val, user) == 0) 804 ok = 1; 805 } 806 ndbfree(tp); 807 return ok; 808 } 809 810 /* 811 * return an error reply 812 */ 813 void 814 replyerror(char *fmt, ...) 815 { 816 char buf[AERRLEN+1]; 817 va_list arg; 818 819 memset(buf, 0, sizeof(buf)); 820 va_start(arg, fmt); 821 vseprint(buf + 1, buf + sizeof(buf), fmt, arg); 822 va_end(arg); 823 buf[AERRLEN] = 0; 824 buf[0] = AuthErr; 825 write(1, buf, AERRLEN+1); 826 syslog(0, AUTHLOG, buf+1); 827 } 828 829 void 830 getraddr(char *dir) 831 { 832 int n; 833 char *cp; 834 char file[Maxpath]; 835 836 raddr[0] = 0; 837 snprint(file, sizeof(file), "%s/remote", dir); 838 n = readfile(file, raddr, sizeof(raddr)-1); 839 if(n < 0) 840 return; 841 raddr[n] = 0; 842 843 cp = strchr(raddr, '\n'); 844 if(cp) 845 *cp = 0; 846 cp = strchr(raddr, '!'); 847 if(cp) 848 *cp = 0; 849 } 850 851 void 852 mkkey(char *k) 853 { 854 randombytes((uchar*)k, DESKEYLEN); 855 } 856 857 void 858 randombytes(uchar *buf, int len) 859 { 860 int i; 861 862 if(readfile("/dev/random", (char*)buf, len) >= 0) 863 return; 864 865 for(i = 0; i < len; i++) 866 buf[i] = rand(); 867 } 868 869 /* 870 * reply with ticket and authenticator 871 */ 872 int 873 tickauthreply(Ticketreq *tr, char *hkey) 874 { 875 Ticket t; 876 Authenticator a; 877 char buf[TICKETLEN+AUTHENTLEN+1]; 878 879 memset(&t, 0, sizeof(t)); 880 memmove(t.chal, tr->chal, CHALLEN); 881 safecpy(t.cuid, tr->uid, sizeof t.cuid); 882 safecpy(t.suid, tr->uid, sizeof t.suid); 883 mkkey(t.key); 884 buf[0] = AuthOK; 885 t.num = AuthTs; 886 convT2M(&t, buf+1, hkey); 887 memmove(a.chal, t.chal, CHALLEN); 888 a.num = AuthAc; 889 a.id = 0; 890 convA2M(&a, buf+TICKETLEN+1, t.key); 891 if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0) 892 return -1; 893 return 0; 894 } 895 896 void 897 safecpy(char *to, char *from, int len) 898 { 899 strncpy(to, from, len); 900 to[len-1] = 0; 901 } 902