1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <bio.h> 6 #include "authsrv.h" 7 8 char authkey[DESKEYLEN]; 9 10 typedef struct Fid Fid; 11 typedef struct User User; 12 13 enum{ 14 Qroot, 15 Quser, 16 Qkey, 17 Qlog, 18 Qstatus, 19 Qexpire, 20 Qwarnings, 21 Qmax, 22 23 Nuser = 512, 24 MAXBAD = 50, /* max number of bad attempts before diabling the account */ 25 }; 26 27 enum{ 28 Sok, 29 Sdisabled, 30 Smax, 31 }; 32 33 struct Fid{ 34 int fid; 35 ulong qtype; 36 User *user; 37 int busy; 38 Fid *next; 39 }; 40 41 struct User{ 42 char name[NAMELEN]; 43 char key[DESKEYLEN]; 44 ulong expire; /* 0 == never */ 45 uchar status; 46 uchar bad; /* number of consecutive bad authentication attempts */ 47 int ref; 48 char removed; 49 uchar warnings; 50 ulong uniq; 51 User *link; 52 }; 53 54 char *qinfo[Qmax] = { 55 [Qroot] "keys", 56 [Quser] ".", 57 [Qkey] "key", 58 [Qlog] "log", 59 [Qexpire] "expire", 60 [Qstatus] "status", 61 [Qwarnings] "warnings", 62 }; 63 64 char *status[Smax] = { 65 [Sok] "ok", 66 [Sdisabled] "disabled", 67 }; 68 69 Fid *fids; 70 User *users[Nuser]; 71 char *userkeys; 72 int nuser; 73 ulong uniq = 1; 74 Fcall rhdr, 75 thdr; 76 int usepass; 77 char *warnarg; 78 79 int readusers(void); 80 ulong hash(char*); 81 Fid *findfid(int); 82 User *finduser(char*); 83 User *installuser(char*); 84 int removeuser(User*); 85 void insertuser(User*); 86 void writeusers(void); 87 void io(int, int); 88 void *emalloc(ulong); 89 Qid mkqid(User*, ulong); 90 void dostat(User*, ulong, void*); 91 int newkeys(void); 92 void warning(void); 93 94 char *Auth(Fid*), *Attach(Fid*), *Nop(Fid*), *Session(Fid*), 95 *Flush(Fid*), *Clone(Fid*), *Walk(Fid*), 96 *Clwalk(Fid *), *Open(Fid*), *Create(Fid*), 97 *Read(Fid *), *Write(Fid*), *Clunk(Fid*), 98 *Remove(Fid *), *Stat(Fid*), *Wstat(Fid*); 99 char *(*fcalls[])(Fid*) = { 100 [Tattach] Attach, 101 [Tclone] Clone, 102 [Tclunk] Clunk, 103 [Tclwalk] Clwalk, 104 [Tcreate] Create, 105 [Tflush] Flush, 106 [Tnop] Nop, 107 [Topen] Open, 108 [Tread] Read, 109 [Tremove] Remove, 110 [Tsession] Session, 111 [Tstat] Stat, 112 [Twalk] Walk, 113 [Twrite] Write, 114 [Twstat] Wstat, 115 }; 116 117 void 118 main(int argc, char *argv[]) 119 { 120 char *mntpt; 121 int p[2]; 122 123 mntpt = "/mnt/keys"; 124 ARGBEGIN{ 125 case 'm': 126 mntpt = ARGF(); 127 break; 128 case 'w': 129 warnarg = ARGF(); 130 break; 131 case 'p': 132 usepass = 1; 133 break; 134 }ARGEND 135 argv0 = "keyfs"; 136 137 userkeys = "/adm/keys"; 138 if(argc > 0) 139 userkeys = argv[0]; 140 141 if(pipe(p) < 0) 142 error("can't make pipe: %r"); 143 144 switch(fork()){ 145 case 0: 146 io(p[1], p[1]); 147 exits(0); 148 case -1: 149 error("fork"); 150 default: 151 if(mount(p[0], mntpt, MREPL|MCREATE, "") < 0) 152 error("can't mount: %r"); 153 exits(0); 154 } 155 } 156 157 char * 158 Nop(Fid *f) 159 { 160 USED(f); 161 return 0; 162 } 163 164 char * 165 Session(Fid *f) 166 { 167 USED(f); 168 return 0; 169 } 170 171 char * 172 Flush(Fid *f) 173 { 174 USED(f); 175 return 0; 176 } 177 178 char * 179 Auth(Fid *f) 180 { 181 if(f->busy) 182 Clunk(f); 183 f->user = 0; 184 f->qtype = Qroot; 185 f->busy = 1; 186 strcpy(thdr.chal, "none"); 187 return 0; 188 } 189 190 char * 191 Attach(Fid *f) 192 { 193 if(f->busy) 194 Clunk(f); 195 f->user = 0; 196 f->qtype = Qroot; 197 f->busy = 1; 198 thdr.qid = mkqid(f->user, f->qtype); 199 return 0; 200 } 201 202 char * 203 Clone(Fid *f) 204 { 205 Fid *nf; 206 207 if(!f->busy) 208 return "permission denied"; 209 nf = findfid(rhdr.newfid); 210 if(nf->busy) 211 Clunk(nf); 212 thdr.fid = rhdr.newfid; 213 nf->busy = 1; 214 nf->qtype = f->qtype; 215 if(nf->user = f->user) 216 nf->user->ref++; 217 return 0; 218 } 219 220 char * 221 Walk(Fid *f) 222 { 223 char *name; 224 int i, max; 225 226 if(!f->busy) 227 return "permission denied"; 228 name = rhdr.name; 229 switch(f->qtype){ 230 case Qroot: 231 f->user = finduser(name); 232 if(!f->user) 233 return "file does not exist"; 234 f->user->ref++; 235 f->qtype = Quser; 236 break; 237 case Quser: 238 max = Qmax; 239 for(i = Quser + 1; i < Qmax; i++) 240 if(strcmp(name, qinfo[i]) == 0){ 241 f->qtype = i; 242 break; 243 } 244 if(i == max) 245 return "file not found"; 246 break; 247 default: 248 return "file is not a directory"; 249 } 250 thdr.qid = mkqid(f->user, f->qtype); 251 return 0; 252 } 253 254 char * 255 Clunk(Fid *f) 256 { 257 f->busy = 0; 258 if(f->user && --f->user->ref == 0 && f->user->removed) 259 free(f->user); 260 f->user = 0; 261 return 0; 262 } 263 264 char * 265 Clwalk(Fid *f) 266 { 267 Fid *nf; 268 char *err; 269 270 if(!f->busy) 271 return "permission denied"; 272 nf = findfid(rhdr.newfid); 273 thdr.fid = rhdr.newfid; 274 if(nf->busy) 275 Clunk(nf); 276 nf->busy = 1; 277 nf->qtype = f->qtype; 278 if(nf->user = f->user) 279 nf->user->ref++; 280 if(err = Walk(nf)) 281 Clunk(nf); 282 return err; 283 } 284 285 char * 286 Open(Fid *f) 287 { 288 int mode; 289 290 if(!f->busy) 291 return "permission denied"; 292 mode = rhdr.mode; 293 if(f->qtype == Quser && (mode & (OWRITE|OTRUNC))) 294 return "user already exists"; 295 thdr.qid = mkqid(f->user, f->qtype); 296 return 0; 297 } 298 299 char * 300 Create(Fid *f) 301 { 302 char *name; 303 long perm; 304 305 if(!f->busy) 306 return "permission denied"; 307 name = rhdr.name; 308 if(f->user){ 309 return "permission denied"; 310 }else{ 311 perm = rhdr.perm; 312 if(!(perm & CHDIR)) 313 return "permission denied"; 314 if(!memchr(name, '\0', NAMELEN)) 315 return "bad file name"; 316 if(finduser(name)) 317 return "user already exists"; 318 f->user = installuser(name); 319 f->user->ref++; 320 f->qtype = Quser; 321 } 322 thdr.qid = mkqid(f->user, f->qtype); 323 writeusers(); 324 return 0; 325 } 326 327 char * 328 Read(Fid *f) 329 { 330 User *u; 331 char *data; 332 ulong off, n; 333 int i, j, max; 334 335 if(!f->busy) 336 return "permission denied"; 337 n = rhdr.count; 338 off = rhdr.offset; 339 thdr.count = 0; 340 data = thdr.data; 341 switch(f->qtype){ 342 case Qroot: 343 if(off % DIRLEN || n % DIRLEN) 344 return "unaligned directory read"; 345 off /= DIRLEN; 346 n /= DIRLEN; 347 j = 0; 348 for(i = 0; i < Nuser; i++) 349 for(u = users[i]; u; j++, u = u->link){ 350 if(j < off) 351 continue; 352 if(j - off >= n) 353 break; 354 dostat(u, Quser, data); 355 data += DIRLEN; 356 } 357 thdr.count = data - thdr.data; 358 return 0; 359 case Quser: 360 if(off % DIRLEN || n % DIRLEN) 361 return "unaligned directory read"; 362 off /= DIRLEN; 363 n /= DIRLEN; 364 max = Qmax; 365 max -= Quser + 1; 366 for(i = 0; i < max; i++){ 367 if(i < off) 368 continue; 369 if(i - off >= n) 370 break; 371 dostat(f->user, i - off + Quser + 1, data); 372 data += DIRLEN; 373 } 374 thdr.count = data - thdr.data; 375 return 0; 376 case Qkey: 377 if(f->user->status != Sok) 378 return "user disabled"; 379 if(f->user->expire != 0 && f->user->expire < time(0)) 380 return "user expired"; 381 if(off != 0) 382 return 0; 383 if(n > DESKEYLEN) 384 n = DESKEYLEN; 385 memmove(thdr.data, f->user->key, n); 386 thdr.count = n; 387 return 0; 388 case Qstatus: 389 if(off != 0){ 390 thdr.count = 0; 391 return 0; 392 } 393 if(f->user->status == Sok && f->user->expire && f->user->expire < time(0)) 394 sprint(thdr.data, "expired\n"); 395 else 396 sprint(thdr.data, "%s\n", status[f->user->status]); 397 thdr.count = strlen(thdr.data); 398 return 0; 399 case Qexpire: 400 if(off != 0){ 401 thdr.count = 0; 402 return 0; 403 } 404 if(!f->user->expire) 405 strcpy(data, "never\n"); 406 else 407 sprint(data, "%lud\n", f->user->expire); 408 if(n > strlen(data)) 409 n = strlen(data); 410 thdr.count = n; 411 return 0; 412 case Qlog: 413 if(off != 0){ 414 thdr.count = 0; 415 return 0; 416 } 417 sprint(data, "%d\n", f->user->bad); 418 if(n > strlen(data)) 419 n = strlen(data); 420 thdr.count = n; 421 return 0; 422 case Qwarnings: 423 if(off != 0){ 424 thdr.count = 0; 425 return 0; 426 } 427 sprint(data, "%lud\n", f->user->warnings); 428 if(n > strlen(data)) 429 n = strlen(data); 430 thdr.count = n; 431 return 0; 432 default: 433 return "permission denied"; 434 } 435 } 436 437 char * 438 Write(Fid *f) 439 { 440 char *data, *p; 441 ulong n, expire; 442 int i; 443 444 if(!f->busy) 445 return "permission denied"; 446 n = rhdr.count; 447 data = rhdr.data; 448 switch(f->qtype){ 449 case Qkey: 450 if(n != DESKEYLEN) 451 return "garbled write data"; 452 memmove(f->user->key, data, DESKEYLEN); 453 thdr.count = DESKEYLEN; 454 break; 455 case Qstatus: 456 data[n] = '\0'; 457 if(p = strchr(data, '\n')) 458 *p = '\0'; 459 for(i = 0; i < Smax; i++) 460 if(strcmp(data, status[i]) == 0){ 461 f->user->status = i; 462 break; 463 } 464 if(i == Smax) 465 return "unknown status"; 466 f->user->bad = 0; 467 thdr.count = n; 468 break; 469 case Qexpire: 470 data[n] = '\0'; 471 if(p = strchr(data, '\n')) 472 *p = '\0'; 473 else 474 p = &data[n]; 475 if(strcmp(data, "never") == 0) 476 expire = 0; 477 else{ 478 expire = strtoul(data, &data, 10); 479 if(data != p) 480 return "bad expiration date"; 481 } 482 f->user->expire = expire; 483 f->user->warnings = 0; 484 thdr.count = n; 485 break; 486 case Qlog: 487 data[n] = '\0'; 488 if(strcmp(data, "good") == 0) 489 f->user->bad = 0; 490 else 491 f->user->bad++; 492 if(f->user->bad >= MAXBAD){ 493 f->user->status = Sdisabled; 494 break; 495 } 496 return 0; 497 case Qwarnings: 498 data[n] = '\0'; 499 f->user->warnings = strtoul(data, 0, 10); 500 thdr.count = n; 501 break; 502 case Qroot: 503 case Quser: 504 default: 505 return "permission denied"; 506 } 507 writeusers(); 508 return 0; 509 } 510 511 char * 512 Remove(Fid *f) 513 { 514 if(!f->busy) 515 return "permission denied"; 516 if(f->qtype == Qwarnings) 517 f->user->warnings = 0; 518 else if(f->qtype == Quser) 519 removeuser(f->user); 520 else 521 return "permission denied"; 522 Clunk(f); 523 writeusers(); 524 return 0; 525 } 526 527 char * 528 Stat(Fid *f) 529 { 530 if(!f->busy) 531 return "stat on unattached fid"; 532 dostat(f->user, f->qtype, thdr.stat); 533 return 0; 534 } 535 536 char * 537 Wstat(Fid *f) 538 { 539 Dir d; 540 541 if(!f->busy || f->qtype != Quser) 542 return "permission denied"; 543 if(convM2D(rhdr.stat, &d) == 0) 544 return "bad stat buffer"; 545 if(!memchr(d.name, '\0', NAMELEN)) 546 return "bad user name"; 547 if(finduser(d.name)) 548 return "user already exists"; 549 if(!removeuser(f->user)) 550 return "user previously removed"; 551 strncpy(f->user->name, d.name, NAMELEN); 552 insertuser(f->user); 553 writeusers(); 554 return 0; 555 } 556 557 Qid 558 mkqid(User *u, ulong qtype) 559 { 560 Qid q; 561 562 q.vers = 0; 563 q.path = qtype; 564 if(u) 565 q.path |= u->uniq * 0x100; 566 if(qtype == Quser || qtype == Qroot) 567 q.path |= CHDIR; 568 return q; 569 } 570 571 void 572 dostat(User *user, ulong qtype, void *p) 573 { 574 Dir d; 575 576 if(qtype == Quser) 577 strncpy(d.name, user->name, NAMELEN); 578 else 579 strncpy(d.name, qinfo[qtype], NAMELEN); 580 strncpy(d.uid, "auth", NAMELEN); 581 strncpy(d.gid, "auth", NAMELEN); 582 d.qid = mkqid(user, qtype); 583 if(d.qid.path & CHDIR) 584 d.mode = 0777|CHDIR; 585 else 586 d.mode = 0666; 587 d.atime = d.mtime = time(0); 588 d.length = d.hlength = 0; 589 convD2M(&d, p); 590 } 591 592 int 593 passline(Biobuf *b, void *vbuf) 594 { 595 char *buf = vbuf; 596 597 if(Bread(b, buf, KEYDBLEN) != KEYDBLEN) 598 return 0; 599 decrypt(authkey, buf, KEYDBLEN); 600 buf[NAMELEN-1] = '\0'; 601 return 1; 602 } 603 604 void 605 writeusers(void) 606 { 607 User *u; 608 Biobuf *b; 609 char buf[KEYDBLEN], *p; 610 ulong expire; 611 int i; 612 613 b = Bopen(userkeys, OWRITE); 614 if(!b){ 615 fprint(2, "keyfs: can't write keys file\n"); 616 return; 617 } 618 for(i = 0; i < Nuser; i++) 619 for(u = users[i]; u; u = u->link){ 620 strncpy(buf, u->name, NAMELEN); 621 memmove(buf+NAMELEN, u->key, DESKEYLEN); 622 p = buf + NAMELEN + DESKEYLEN; 623 *p++ = u->status; 624 *p++ = u->warnings; 625 expire = u->expire; 626 *p++ = expire; 627 *p++ = expire >> 8; 628 *p++ = expire >> 16; 629 *p = expire >> 24; 630 encrypt(authkey, buf, KEYDBLEN); 631 Bwrite(b, buf, sizeof buf); 632 } 633 Bterm(b); 634 } 635 636 int 637 readusers(void) 638 { 639 Biobuf *b; 640 User *u; 641 char buf[KEYDBLEN]; 642 uchar *p; 643 int nu; 644 645 if(usepass) 646 getpass(authkey, 0); 647 else 648 if(!getauthkey(authkey)) 649 print("keyfs: warning: can't read /dev/key\n"); 650 651 nu = 0; 652 b = Bopen(userkeys, OREAD); 653 if(b){ 654 while(passline(b, buf)){ 655 u = finduser(buf); 656 if(u == 0) 657 u = installuser(buf); 658 memmove(u->key, buf+NAMELEN, DESKEYLEN); 659 p = (uchar*)buf + NAMELEN + DESKEYLEN; 660 u->status = *p++; 661 u->warnings = *p++; 662 if(u->status >= Smax) 663 fprint(2, "keyfs: warning: bad status in key file\n"); 664 u->expire = p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24); 665 nu++; 666 } 667 Bterm(b); 668 } 669 print("%d keys read\n", nu); 670 return b != 0; 671 } 672 673 User * 674 installuser(char *name) 675 { 676 User *u; 677 int h; 678 679 h = hash(name); 680 u = emalloc(sizeof *u); 681 strncpy(u->name, name, NAMELEN); 682 u->removed = 0; 683 u->ref = 0; 684 u->expire = 0; 685 u->status = Sok; 686 u->bad = 0; 687 u->warnings = 0; 688 u->uniq = uniq++; 689 u->link = users[h]; 690 users[h] = u; 691 return u; 692 } 693 694 User * 695 finduser(char *name) 696 { 697 User *u; 698 699 for(u = users[hash(name)]; u; u = u->link) 700 if(strcmp(name, u->name) == 0) 701 return u; 702 return 0; 703 } 704 705 int 706 removeuser(User *user) 707 { 708 User *u, **last; 709 char *name; 710 711 user->removed = 1; 712 name = user->name; 713 last = &users[hash(name)]; 714 for(u = *last; u; u = *last){ 715 if(strcmp(name, u->name) == 0){ 716 *last = u->link; 717 return 1; 718 } 719 last = &u->link; 720 } 721 return 0; 722 } 723 724 void 725 insertuser(User *user) 726 { 727 int h; 728 729 user->removed = 0; 730 h = hash(user->name); 731 user->link = users[h]; 732 users[h] = user; 733 } 734 735 ulong 736 hash(char *s) 737 { 738 ulong h; 739 740 h = 0; 741 while(*s) 742 h = (h << 1) ^ *s++; 743 return h % Nuser; 744 } 745 746 Fid * 747 findfid(int fid) 748 { 749 Fid *f, *ff; 750 751 ff = 0; 752 for(f = fids; f; f = f->next) 753 if(f->fid == fid) 754 return f; 755 else if(!ff && !f->busy) 756 ff = f; 757 if(ff){ 758 ff->fid = fid; 759 return ff; 760 } 761 f = emalloc(sizeof *f); 762 f->fid = fid; 763 f->busy = 0; 764 f->user = 0; 765 f->next = fids; 766 fids = f; 767 return f; 768 } 769 770 void 771 io(int in, int out) 772 { 773 char mdata[MAXFDATA + MAXMSG], *err; 774 int n; 775 static long lastwarning; 776 777 for(;;){ 778 n = read(in, mdata, sizeof mdata); 779 if(n == 0) 780 continue; 781 if(n < 0) 782 error("mount read %d", n); 783 if(convM2S(mdata, &rhdr, n) == 0) 784 continue; 785 786 if(newkeys()) 787 readusers(); 788 789 thdr.data = mdata + MAXMSG; 790 thdr.fid = rhdr.fid; 791 if(!fcalls[rhdr.type]) 792 err = "fcall request"; 793 else 794 err = (*fcalls[rhdr.type])(findfid(rhdr.fid)); 795 thdr.tag = rhdr.tag; 796 thdr.type = rhdr.type+1; 797 if(err){ 798 thdr.type = Rerror; 799 strncpy(thdr.ename, err, ERRLEN); 800 } 801 n = convS2M(&thdr, mdata); 802 if(write(out, mdata, n) != n) 803 error("mount write"); 804 805 if(warnarg && time(0) - lastwarning > 24*60*60){ 806 warning(); 807 lastwarning = time(0); 808 } 809 } 810 } 811 812 int 813 newkeys(void) 814 { 815 Dir d; 816 static long ftime; 817 818 if(dirstat(userkeys, &d) < 0) 819 return 0; 820 if(d.mtime > ftime) { 821 ftime = d.mtime; 822 return 1; 823 } 824 return 0; 825 } 826 827 void * 828 emalloc(ulong n) 829 { 830 void *p; 831 832 if(p = malloc(n)) 833 return p; 834 error("out of memory"); 835 return 0; /* not reached */ 836 } 837 838 void 839 warning(void) 840 { 841 char buf[64]; 842 843 snprint(buf, sizeof buf, "-%s", warnarg); 844 switch(rfork(RFPROC|RFNOTEG|RFNOWAIT|RFNAMEG|RFENVG|RFFDG)){ 845 case 0: 846 execl("/bin/auth/warning", "warning", warnarg, 0); 847 fprint(2, "keyfs: can't exec warning\n"); 848 exits(0); 849 } 850 } 851