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 translate(char*, char*, int); 32 void replyerror(char*, ...); 33 void getraddr(char*); 34 void mkkey(char*); 35 void randombytes(uchar*, int); 36 void nthash(uchar hash[MShashlen], char *passwd); 37 void lmhash(uchar hash[MShashlen], char *passwd); 38 void mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]); 39 void desencrypt(uchar data[8], uchar key[7]); 40 int tickauthreply(Ticketreq*, char*); 41 void safecpy(char*, char*, int); 42 43 44 void 45 main(int argc, char *argv[]) 46 { 47 char buf[TICKREQLEN]; 48 Ticketreq tr; 49 Ndb *db2; 50 51 ARGBEGIN{ 52 case 'd': 53 debug++; 54 }ARGEND 55 56 strcpy(raddr, "unknown"); 57 if(argc >= 1) 58 getraddr(argv[argc-1]); 59 60 alarm(10*60*1000); /* kill a connection after 10 minutes */ 61 62 db = ndbopen("/lib/ndb/auth"); 63 if(db == 0) 64 syslog(0, AUTHLOG, "no /lib/ndb/auth"); 65 db2 = ndbopen(0); 66 if(db2 == 0) 67 syslog(0, AUTHLOG, "no /lib/ndb/local"); 68 db = ndbcat(db, db2); 69 70 srand(time(0)*getpid()); 71 for(;;){ 72 if(readn(0, buf, TICKREQLEN) <= 0) 73 exits(0); 74 75 convM2TR(buf, &tr); 76 switch(buf[0]){ 77 case AuthTreq: 78 ticketrequest(&tr); 79 break; 80 case AuthChal: 81 challengebox(&tr); 82 break; 83 case AuthPass: 84 changepasswd(&tr); 85 break; 86 case AuthApop: 87 apop(&tr, AuthApop); 88 break; 89 case AuthChap: 90 chap(&tr); 91 break; 92 case AuthMSchap: 93 mschap(&tr); 94 break; 95 case AuthCram: 96 apop(&tr, AuthCram); 97 break; 98 case AuthHttp: 99 http(&tr); 100 break; 101 case AuthVNC: 102 vnc(&tr); 103 break; 104 default: 105 syslog(0, AUTHLOG, "unknown ticket request type: %d", buf[0]); 106 break; 107 } 108 } 109 exits(0); 110 } 111 112 int 113 ticketrequest(Ticketreq *tr) 114 { 115 char akey[DESKEYLEN]; 116 char hkey[DESKEYLEN]; 117 Ticket t; 118 char tbuf[2*TICKETLEN+1]; 119 120 if(findkey(KEYDB, tr->authid, akey) == 0){ 121 /* make one up so caller doesn't know it was wrong */ 122 mkkey(akey); 123 if(debug) 124 syslog(0, AUTHLOG, "tr-fail authid %s", raddr); 125 } 126 if(findkey(KEYDB, tr->hostid, hkey) == 0){ 127 /* make one up so caller doesn't know it was wrong */ 128 mkkey(hkey); 129 if(debug) 130 syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr); 131 } 132 133 memset(&t, 0, sizeof(t)); 134 memmove(t.chal, tr->chal, CHALLEN); 135 strcpy(t.cuid, tr->uid); 136 if(speaksfor(tr->hostid, tr->uid)) 137 strcpy(t.suid, tr->uid); 138 else 139 strcpy(t.suid, "none"); 140 translate(tr->authdom, t.suid, sizeof t.suid); 141 142 mkkey(t.key); 143 144 tbuf[0] = AuthOK; 145 t.num = AuthTc; 146 convT2M(&t, tbuf+1, hkey); 147 t.num = AuthTs; 148 convT2M(&t, tbuf+1+TICKETLEN, akey); 149 if(write(1, tbuf, 2*TICKETLEN+1) < 0){ 150 if(debug) 151 syslog(0, AUTHLOG, "tr-fail %s@%s(%s): hangup", 152 tr->uid, tr->hostid, raddr); 153 exits(0); 154 } 155 if(debug) 156 syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s", 157 tr->uid, tr->hostid, raddr, tr->uid, tr->authid); 158 159 return 0; 160 } 161 162 void 163 challengebox(Ticketreq *tr) 164 { 165 long chal; 166 char *key, *netkey; 167 char kbuf[DESKEYLEN], nkbuf[DESKEYLEN], hkey[DESKEYLEN]; 168 char buf[NETCHLEN+1]; 169 char *err; 170 171 key = findkey(KEYDB, tr->uid, kbuf); 172 netkey = findkey(NETKEYDB, tr->uid, nkbuf); 173 if(key == 0 && netkey == 0){ 174 /* make one up so caller doesn't know it was wrong */ 175 mkkey(nkbuf); 176 netkey = nkbuf; 177 if(debug) 178 syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr); 179 } 180 if(findkey(KEYDB, tr->hostid, hkey) == 0){ 181 /* make one up so caller doesn't know it was wrong */ 182 mkkey(hkey); 183 if(debug) 184 syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid, 185 tr->uid, raddr); 186 } 187 188 /* 189 * challenge-response 190 */ 191 memset(buf, 0, sizeof(buf)); 192 buf[0] = AuthOK; 193 chal = lnrand(MAXNETCHAL); 194 sprint(buf+1, "%lud", chal); 195 if(write(1, buf, NETCHLEN+1) < 0) 196 exits(0); 197 if(readn(0, buf, NETCHLEN) < 0) 198 exits(0); 199 if(!(key && netcheck(key, chal, buf)) 200 && !(netkey && netcheck(netkey, chal, buf)) 201 && (err = secureidcheck(tr->uid, buf)) != nil){ 202 replyerror("cr-fail %s %s %s", err, tr->uid, raddr); 203 logfail(tr->uid); 204 if(debug) 205 syslog(0, AUTHLOG, "cr-fail %s@%s(%s): bad resp", 206 tr->uid, tr->hostid, raddr); 207 return; 208 } 209 succeed(tr->uid); 210 211 /* 212 * reply with ticket & authenticator 213 */ 214 if(tickauthreply(tr, hkey) < 0){ 215 if(debug) 216 syslog(0, AUTHLOG, "cr-fail %s@%s(%s): hangup", 217 tr->uid, tr->hostid, raddr); 218 exits(0); 219 } 220 221 if(debug) 222 syslog(0, AUTHLOG, "cr-ok %s@%s(%s)", 223 tr->uid, tr->hostid, raddr); 224 } 225 226 void 227 changepasswd(Ticketreq *tr) 228 { 229 Ticket t; 230 char tbuf[TICKETLEN+1]; 231 char prbuf[PASSREQLEN]; 232 Passwordreq pr; 233 char okey[DESKEYLEN], nkey[DESKEYLEN]; 234 char *err; 235 236 if(findkey(KEYDB, tr->uid, okey) == 0){ 237 /* make one up so caller doesn't know it was wrong */ 238 mkkey(okey); 239 syslog(0, AUTHLOG, "cp-fail uid %s", raddr); 240 } 241 242 /* send back a ticket with a new key */ 243 memmove(t.chal, tr->chal, CHALLEN); 244 mkkey(t.key); 245 tbuf[0] = AuthOK; 246 t.num = AuthTp; 247 safecpy(t.cuid, tr->uid, sizeof(t.cuid)); 248 safecpy(t.suid, tr->uid, sizeof(t.suid)); 249 convT2M(&t, tbuf+1, okey); 250 write(1, tbuf, sizeof(tbuf)); 251 252 /* loop trying passwords out */ 253 for(;;){ 254 if(readn(0, prbuf, PASSREQLEN) < 0) 255 exits(0); 256 convM2PR(prbuf, &pr, t.key); 257 if(pr.num != AuthPass){ 258 replyerror("protocol botch1: %s", raddr); 259 exits(0); 260 } 261 passtokey(nkey, pr.old); 262 if(memcmp(nkey, okey, DESKEYLEN)){ 263 replyerror("protocol botch2: %s", raddr); 264 continue; 265 } 266 if(*pr.new){ 267 err = okpasswd(pr.new); 268 if(err){ 269 replyerror("%s %s", err, raddr); 270 continue; 271 } 272 passtokey(nkey, pr.new); 273 } 274 if(pr.changesecret && setsecret(KEYDB, tr->uid, pr.secret) == 0){ 275 replyerror("can't write secret %s", raddr); 276 continue; 277 } 278 if(*pr.new && setkey(KEYDB, tr->uid, nkey) == 0){ 279 replyerror("can't write key %s", raddr); 280 continue; 281 } 282 break; 283 } 284 285 prbuf[0] = AuthOK; 286 write(1, prbuf, 1); 287 succeed(tr->uid); 288 return; 289 } 290 291 void 292 http(Ticketreq *tr) 293 { 294 Ticket t; 295 char tbuf[TICKETLEN+1]; 296 char key[DESKEYLEN]; 297 char *p; 298 Biobuf *b; 299 int n; 300 301 n = strlen(tr->uid); 302 b = Bopen("/sys/lib/httppasswords", OREAD); 303 if(b == nil){ 304 replyerror("no password file", raddr); 305 return; 306 } 307 308 /* find key */ 309 for(;;){ 310 p = Brdline(b, '\n'); 311 if(p == nil) 312 break; 313 p[Blinelen(b)-1] = 0; 314 if(strncmp(p, tr->uid, n) == 0) 315 if(p[n] == ' ' || p[n] == '\t'){ 316 p += n; 317 break; 318 } 319 } 320 Bterm(b); 321 if(p == nil) { 322 randombytes((uchar*)key, DESKEYLEN); 323 } else { 324 while(*p == ' ' || *p == '\t') 325 p++; 326 passtokey(key, p); 327 } 328 329 /* send back a ticket encrypted with the key */ 330 randombytes((uchar*)t.chal, CHALLEN); 331 mkkey(t.key); 332 tbuf[0] = AuthOK; 333 t.num = AuthHr; 334 safecpy(t.cuid, tr->uid, sizeof(t.cuid)); 335 safecpy(t.suid, tr->uid, sizeof(t.suid)); 336 convT2M(&t, tbuf+1, key); 337 write(1, tbuf, sizeof(tbuf)); 338 } 339 340 static char* 341 domainname(void) 342 { 343 static char sysname[Maxpath]; 344 static char domain[Ndbvlen]; 345 Ndbtuple *t; 346 int n; 347 348 if(*domain) 349 return domain; 350 351 if(*sysname) 352 return sysname; 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 t = csgetval(0, "sys", sysname, "dom", domain); 362 if(t == 0) 363 return sysname; 364 365 ndbfree(t); 366 return domain; 367 } 368 369 static int 370 h2b(char c) 371 { 372 if(c >= '0' && c <= '9') 373 return c - '0'; 374 if(c >= 'A' && c <= 'F') 375 return c - 'A' + 10; 376 if(c >= 'a' && c <= 'f') 377 return c - 'a' + 10; 378 return 0; 379 } 380 381 void 382 apop(Ticketreq *tr, int type) 383 { 384 int challen, i, tries; 385 char *secret, *hkey, *p; 386 Ticketreq treq; 387 DigestState *s; 388 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 389 char tbuf[TICKREQLEN]; 390 char buf[MD5dlen*2]; 391 uchar digest[MD5dlen], resp[MD5dlen]; 392 ulong rb[4]; 393 char chal[256]; 394 395 USED(tr); 396 397 /* 398 * Create a challenge and send it. 399 */ 400 randombytes((uchar*)rb, sizeof(rb)); 401 p = chal; 402 p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>", 403 rb[0], rb[1], rb[2], rb[3], domainname()); 404 challen = p - chal; 405 print("%c%-5d%s", AuthOKvar, challen, chal); 406 407 /* give user a few attempts */ 408 for(tries = 0; ; tries++) { 409 /* 410 * get ticket request 411 */ 412 if(readn(0, tbuf, TICKREQLEN) < 0) 413 exits(0); 414 convM2TR(tbuf, &treq); 415 tr = &treq; 416 if(tr->type != type) 417 exits(0); 418 419 /* 420 * read response 421 */ 422 if(readn(0, buf, MD5dlen*2) < 0) 423 exits(0); 424 for(i = 0; i < MD5dlen; i++) 425 resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]); 426 427 /* 428 * lookup 429 */ 430 secret = findsecret(KEYDB, tr->uid, sbuf); 431 hkey = findkey(KEYDB, tr->hostid, hbuf); 432 if(hkey == 0 || secret == 0){ 433 replyerror("apop-fail bad response %s", raddr); 434 logfail(tr->uid); 435 if(tries > 5) 436 return; 437 continue; 438 } 439 440 /* 441 * check for match 442 */ 443 if(type == AuthCram){ 444 hmac_md5((uchar*)chal, challen, 445 (uchar*)secret, strlen(secret), 446 digest, nil); 447 } else { 448 s = md5((uchar*)chal, challen, 0, 0); 449 md5((uchar*)secret, strlen(secret), digest, s); 450 } 451 if(memcmp(digest, resp, MD5dlen) != 0){ 452 replyerror("apop-fail bad response %s", raddr); 453 logfail(tr->uid); 454 if(tries > 5) 455 return; 456 continue; 457 } 458 break; 459 } 460 461 succeed(tr->uid); 462 463 /* 464 * reply with ticket & authenticator 465 */ 466 if(tickauthreply(tr, hkey) < 0) 467 exits(0); 468 469 if(debug){ 470 if(type == AuthCram) 471 syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr); 472 else 473 syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr); 474 } 475 } 476 477 enum { 478 VNCchallen= 16, 479 }; 480 481 /* VNC reverses the bits of each byte before using as a des key */ 482 uchar swizzletab[256] = { 483 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 484 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 485 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 486 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 487 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 488 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 489 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 490 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 491 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 492 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 493 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 494 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 495 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 496 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 497 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 498 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, 499 }; 500 501 void 502 vnc(Ticketreq *tr) 503 { 504 uchar chal[VNCchallen+6]; 505 uchar reply[VNCchallen]; 506 char *secret, *hkey; 507 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 508 DESstate s; 509 int i; 510 511 /* 512 * Create a challenge and send it. 513 */ 514 randombytes(chal+6, VNCchallen); 515 chal[0] = AuthOKvar; 516 sprint((char*)chal+1, "%-5d", VNCchallen); 517 if(write(1, chal, sizeof(chal)) != sizeof(chal)) 518 return; 519 520 /* 521 * lookup keys (and swizzle bits) 522 */ 523 memset(sbuf, 0, sizeof(sbuf)); 524 secret = findsecret(KEYDB, tr->uid, sbuf); 525 if(secret == 0){ 526 randombytes((uchar*)sbuf, sizeof(sbuf)); 527 secret = sbuf; 528 } 529 for(i = 0; i < 8; i++) 530 secret[i] = swizzletab[(uchar)secret[i]]; 531 532 hkey = findkey(KEYDB, tr->hostid, hbuf); 533 if(hkey == 0){ 534 randombytes((uchar*)hbuf, sizeof(hbuf)); 535 hkey = hbuf; 536 } 537 538 /* 539 * get response 540 */ 541 if(readn(0, reply, sizeof(reply)) != sizeof(reply)) 542 return; 543 544 /* 545 * decrypt response and compare 546 */ 547 setupDESstate(&s, (uchar*)secret, nil); 548 desECBdecrypt(reply, sizeof(reply), &s); 549 if(memcmp(reply, chal+6, VNCchallen) != 0){ 550 replyerror("vnc-fail bad response %s", raddr); 551 logfail(tr->uid); 552 return; 553 } 554 succeed(tr->uid); 555 556 /* 557 * reply with ticket & authenticator 558 */ 559 if(tickauthreply(tr, hkey) < 0) 560 exits(0); 561 562 if(debug) 563 syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr); 564 } 565 566 void 567 chap(Ticketreq *tr) 568 { 569 char *secret, *hkey; 570 DigestState *s; 571 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 572 uchar digest[MD5dlen]; 573 char chal[CHALLEN]; 574 OChapreply reply; 575 uchar md5buf[512]; 576 int n; 577 578 /* 579 * Create a challenge and send it. 580 */ 581 randombytes((uchar*)chal, sizeof(chal)); 582 write(1, chal, sizeof(chal)); 583 584 /* 585 * get chap reply 586 */ 587 if(readn(0, &reply, sizeof(reply)) < 0) 588 exits(0); 589 safecpy(tr->uid, reply.uid, sizeof(tr->uid)); 590 591 /* 592 * lookup 593 */ 594 secret = findsecret(KEYDB, tr->uid, sbuf); 595 hkey = findkey(KEYDB, tr->hostid, hbuf); 596 if(hkey == 0 || secret == 0){ 597 replyerror("chap-fail bad response %s", raddr); 598 logfail(tr->uid); 599 exits(0); 600 } 601 602 /* 603 * check for match 604 */ 605 s = md5(&reply.id, 1, 0, 0); 606 md5((uchar*)secret, strlen(secret), 0, s); 607 md5((uchar*)chal, sizeof(chal), digest, s); 608 609 md5buf[0] = reply.id; 610 n = 1; 611 memmove(md5buf+n, secret, strlen(secret)); 612 n += strlen(secret); 613 memmove(md5buf+n, chal, sizeof(chal)); 614 n += sizeof(chal); 615 md5(md5buf, n, digest, 0); 616 617 if(memcmp(digest, reply.resp, MD5dlen) != 0){ 618 replyerror("chap-fail bad response %s", raddr); 619 logfail(tr->uid); 620 exits(0); 621 } 622 623 succeed(tr->uid); 624 625 /* 626 * reply with ticket & authenticator 627 */ 628 if(tickauthreply(tr, hkey) < 0) 629 exits(0); 630 631 if(debug) 632 syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr); 633 } 634 635 void 636 printresp(uchar resp[MSresplen]) 637 { 638 char buf[200], *p; 639 int i; 640 641 p = buf; 642 for(i=0; i<MSresplen; i++) 643 p += sprint(p, "%.2ux ", resp[i]); 644 syslog(0, AUTHLOG, "resp = %s", buf); 645 } 646 647 648 void 649 mschap(Ticketreq *tr) 650 { 651 652 char *secret, *hkey; 653 char sbuf[SECRETLEN], hbuf[DESKEYLEN]; 654 uchar chal[CHALLEN]; 655 uchar hash[MShashlen]; 656 uchar hash2[MShashlen]; 657 uchar resp[MSresplen]; 658 OMSchapreply reply; 659 int lmok, ntok; 660 DigestState *s; 661 uchar digest[SHA1dlen]; 662 663 /* 664 * Create a challenge and send it. 665 */ 666 randombytes((uchar*)chal, sizeof(chal)); 667 write(1, chal, sizeof(chal)); 668 669 /* 670 * get chap reply 671 */ 672 if(readn(0, &reply, sizeof(reply)) < 0) 673 exits(0); 674 675 safecpy(tr->uid, reply.uid, sizeof(tr->uid)); 676 /* 677 * lookup 678 */ 679 secret = findsecret(KEYDB, tr->uid, sbuf); 680 hkey = findkey(KEYDB, tr->hostid, hbuf); 681 if(hkey == 0 || secret == 0){ 682 replyerror("mschap-fail bad response %s", raddr); 683 logfail(tr->uid); 684 exits(0); 685 } 686 687 /* 688 * check for match on LM algorithm 689 */ 690 lmhash(hash, secret); 691 mschalresp(resp, hash, chal); 692 lmok = memcmp(resp, reply.LMresp, MSresplen) == 0; 693 694 nthash(hash, secret); 695 mschalresp(resp, hash, chal); 696 ntok = memcmp(resp, reply.NTresp, MSresplen) == 0; 697 698 if(!ntok){ 699 replyerror("mschap-fail bad response %s %ux", raddr, (lmok<<1)|ntok); 700 logfail(tr->uid); 701 exits(0); 702 } 703 704 succeed(tr->uid); 705 706 /* 707 * reply with ticket & authenticator 708 */ 709 if(tickauthreply(tr, hkey) < 0) 710 exits(0); 711 712 if(debug) 713 syslog(0, AUTHLOG, "mschap-ok %s %s %ux", tr->uid, raddr, (lmok<<1)|ntok); 714 715 nthash(hash, secret); 716 md4(hash, 16, hash2, 0); 717 s = sha1(hash2, 16, 0, 0); 718 sha1(hash2, 16, 0, s); 719 sha1(chal, 8, digest, s); 720 721 if(write(1, digest, 16) < 0) 722 exits(0); 723 } 724 725 void 726 nthash(uchar hash[MShashlen], char *passwd) 727 { 728 uchar buf[512]; 729 int i; 730 731 for(i=0; *passwd && i<sizeof(buf); passwd++) { 732 buf[i++] = *passwd; 733 buf[i++] = 0; 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 break; 814 if(*ntp->val == '*' || strcmp(ntp->val, user) == 0) 815 ok = 1; 816 } 817 ndbfree(tp); 818 return ok; 819 } 820 821 /* 822 * translate a userid for the destination domain 823 */ 824 void 825 translate(char *domain, char *user, int len) 826 { 827 Ndbtuple *tp, *ntp, *mtp; 828 Ndbs s; 829 830 if(*domain == 0) 831 return; 832 833 if(db == 0) 834 return; 835 836 tp = ndbsearch(db, &s, "authdom", domain); 837 if(tp == 0) 838 return; 839 840 for(ntp = tp; ntp; ntp = ntp->entry) 841 if(strcmp(ntp->attr, "cuid") == 0 && strcmp(ntp->val, user) == 0) 842 break; 843 844 if(ntp == 0){ 845 ndbfree(tp); 846 return; 847 } 848 849 for(mtp = ntp->line; mtp != ntp; mtp = mtp->line) 850 if(strcmp(mtp->attr, "suid") == 0){ 851 memset(user, 0, len); 852 strncpy(user, mtp->val, len-1); 853 break; 854 } 855 ndbfree(tp); 856 } 857 858 /* 859 * return an error reply 860 */ 861 void 862 replyerror(char *fmt, ...) 863 { 864 char buf[AERRLEN+1]; 865 va_list arg; 866 867 memset(buf, 0, sizeof(buf)); 868 va_start(arg, fmt); 869 vseprint(buf + 1, buf + sizeof(buf), fmt, arg); 870 va_end(arg); 871 buf[AERRLEN] = 0; 872 buf[0] = AuthErr; 873 write(1, buf, AERRLEN+1); 874 syslog(0, AUTHLOG, buf+1); 875 } 876 877 void 878 getraddr(char *dir) 879 { 880 int n; 881 char *cp; 882 char file[Maxpath]; 883 884 raddr[0] = 0; 885 snprint(file, sizeof(file), "%s/remote", dir); 886 n = readfile(file, raddr, sizeof(raddr)-1); 887 if(n < 0) 888 return; 889 raddr[n] = 0; 890 891 cp = strchr(raddr, '\n'); 892 if(cp) 893 *cp = 0; 894 cp = strchr(raddr, '!'); 895 if(cp) 896 *cp = 0; 897 } 898 899 void 900 mkkey(char *k) 901 { 902 randombytes((uchar*)k, DESKEYLEN); 903 } 904 905 void 906 randombytes(uchar *buf, int len) 907 { 908 int i; 909 910 if(readfile("/dev/random", (char*)buf, len) >= 0) 911 return; 912 913 for(i = 0; i < len; i++) 914 buf[i] = rand(); 915 } 916 917 /* 918 * reply with ticket and authenticator 919 */ 920 int 921 tickauthreply(Ticketreq *tr, char *hkey) 922 { 923 Ticket t; 924 Authenticator a; 925 char buf[TICKETLEN+AUTHENTLEN+1]; 926 927 memset(&t, 0, sizeof(t)); 928 memmove(t.chal, tr->chal, CHALLEN); 929 safecpy(t.cuid, tr->uid, sizeof t.cuid); 930 safecpy(t.suid, tr->uid, sizeof t.suid); 931 mkkey(t.key); 932 buf[0] = AuthOK; 933 t.num = AuthTs; 934 convT2M(&t, buf+1, hkey); 935 memmove(a.chal, t.chal, CHALLEN); 936 a.num = AuthAc; 937 a.id = 0; 938 convA2M(&a, buf+TICKETLEN+1, t.key); 939 if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0) 940 return -1; 941 return 0; 942 } 943 944 void 945 safecpy(char *to, char *from, int len) 946 { 947 strncpy(to, from, len); 948 to[len-1] = 0; 949 } 950