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