1 /* 2 * cpu.c - Make a connection to a cpu server 3 * 4 * Invoked by listen as 'cpu -R | -N service net netdir' 5 * by users as 'cpu [-h system] [-c cmd args ...]' 6 */ 7 8 #include <u.h> 9 #include <libc.h> 10 #include <bio.h> 11 #include <auth.h> 12 #include <fcall.h> 13 14 void remoteside(void); 15 void fatal(int, char*, ...); 16 void lclnoteproc(int); 17 void rmtnoteproc(void); 18 void catcher(void*, char*); 19 void usage(void); 20 void writestr(int, char*, char*, int); 21 int readstr(int, char*, int); 22 char *rexcall(int*, char*, char*); 23 int filter(int); 24 25 int notechan; 26 char system[32]; 27 int cflag; 28 int hflag; 29 int fflag; 30 int dbg; 31 32 char *srvname = "cpu"; 33 char *exportfs = "/bin/exportfs"; 34 35 /* authentication mechanisms */ 36 static int netkeyauth(int); 37 static int netkeysrvauth(int, char*); 38 39 typedef struct AuthMethod AuthMethod; 40 struct AuthMethod { 41 char *name; /* name of method */ 42 int (*cf)(int); /* client side authentication */ 43 int (*sf)(int, char*); /* server side authentication */ 44 } authmethod[] = 45 { 46 { "p9", auth, srvauth}, 47 { "netkey", netkeyauth, netkeysrvauth}, 48 { nil, nil} 49 }; 50 AuthMethod *am = authmethod; /* default is p9 */ 51 52 int setam(char*); 53 54 void 55 usage(void) 56 { 57 fprint(2, "usage: cpu [-h system] [-a authmethod] [-c cmd args ...]\n"); 58 exits("usage"); 59 } 60 int fdd; 61 62 void 63 main(int argc, char **argv) 64 { 65 char dat[128], buf[128], cmd[128], *p, *err; 66 int data; 67 68 ARGBEGIN{ 69 case 'a': 70 p = ARGF(); 71 if(p==0) 72 usage(); 73 if(setam(p) < 0) 74 fatal(0, "unknown auth method %s\n", p); 75 break; 76 case 'd': 77 dbg++; 78 break; 79 case 'f': 80 fflag++; 81 break; 82 case 'R': /* From listen */ 83 remoteside(); 84 break; 85 case 'h': 86 hflag++; 87 p = ARGF(); 88 if(p==0) 89 usage(); 90 strcpy(system, p); 91 break; 92 case 'c': 93 cflag++; 94 cmd[0] = '!'; 95 cmd[1] = '\0'; 96 while(p = ARGF()) { 97 strcat(cmd, " "); 98 strcat(cmd, p); 99 } 100 break; 101 }ARGEND; 102 103 if(argv); 104 if(argc != 0) 105 usage(); 106 107 if(hflag == 0) { 108 p = getenv("cpu"); 109 if(p == 0) 110 fatal(0, "set $cpu"); 111 strcpy(system, p); 112 } 113 114 if(err = rexcall(&data, system, srvname)) 115 fatal(1, "%s: %s", err, system); 116 117 /* Tell the remote side the command to execute and where our working directory is */ 118 if(cflag) 119 writestr(data, cmd, "command", 0); 120 if(getwd(dat, sizeof(dat)) == 0) 121 writestr(data, "NO", "dir", 0); 122 else 123 writestr(data, dat, "dir", 0); 124 125 /* start up a process to pass along notes */ 126 lclnoteproc(data); 127 128 /* Wait for the other end to execute and start our file service 129 * of /mnt/term */ 130 if(readstr(data, buf, sizeof(buf)) < 0) 131 fatal(1, "waiting for FS"); 132 if(strncmp("FS", buf, 2) != 0) { 133 print("remote cpu: %s", buf); 134 exits(buf); 135 } 136 137 if(fflag) 138 data = filter(data); 139 140 /* Begin serving the gnot namespace */ 141 close(0); 142 dup(data, 0); 143 close(data); 144 if(dbg) 145 execl(exportfs, exportfs, "-d", 0); 146 else 147 execl(exportfs, exportfs, 0); 148 fatal(1, "starting exportfs"); 149 } 150 151 void 152 fatal(int syserr, char *fmt, ...) 153 { 154 char buf[ERRLEN]; 155 va_list arg; 156 157 va_start(arg, fmt); 158 doprint(buf, buf+sizeof(buf), fmt, arg); 159 va_end(arg); 160 if(syserr) 161 fprint(2, "cpu: %s: %r\n", buf); 162 else 163 fprint(2, "cpu: %s\n", buf); 164 exits(buf); 165 } 166 167 char *negstr = "negotiating authentication method"; 168 169 /* Invoked with stdin, stdout and stderr connected to the network connection */ 170 void 171 remoteside(void) 172 { 173 char user[NAMELEN], home[128], buf[128], xdir[128], cmd[128]; 174 int i, n, fd, badchdir, gotcmd; 175 176 /* negotiate authentication mechanism */ 177 n = readstr(0, cmd, sizeof(cmd)); 178 if(n < 0) 179 fatal(1, "authenticating"); 180 if(setam(cmd) < 0){ 181 writestr(1, "unsupported auth method", nil, 0); 182 fatal(1, "bad auth method %s", cmd); 183 } else 184 writestr(1, "", "", 1); 185 186 if((*am->sf)(0, user) < 0) 187 fatal(1, "srvauth"); 188 if(newns(user, 0) < 0) 189 fatal(1, "newns"); 190 191 /* Set environment values for the user */ 192 putenv("user", user); 193 sprint(home, "/usr/%s", user); 194 putenv("home", home); 195 196 /* Now collect invoking cpus current directory or possibly a command */ 197 gotcmd = 0; 198 if(readstr(0, xdir, sizeof(xdir)) < 0) 199 fatal(1, "dir/cmd"); 200 if(xdir[0] == '!') { 201 strcpy(cmd, &xdir[1]); 202 gotcmd = 1; 203 if(readstr(0, xdir, sizeof(xdir)) < 0) 204 fatal(1, "dir"); 205 } 206 207 208 /* Establish the new process at the current working directory of the 209 * gnot */ 210 badchdir = 0; 211 if(strcmp(xdir, "NO") == 0) 212 chdir(home); 213 else if(chdir(xdir) < 0) { 214 badchdir = 1; 215 chdir(home); 216 } 217 218 /* Start the gnot serving its namespace */ 219 writestr(1, "FS", "FS", 0); 220 writestr(1, "/", "exportfs dir", 0); 221 222 n = read(1, buf, sizeof(buf)); 223 if(n != 2 || buf[0] != 'O' || buf[1] != 'K') 224 exits("remote tree"); 225 226 fd = dup(1, -1); 227 228 /* push fcall and note proc */ 229 if(fflag) 230 fd = filter(fd); 231 if(amount(fd, "/mnt/term", MCREATE|MREPL, "") < 0) 232 exits("mount failed"); 233 close(fd); 234 235 /* the remote noteproc uses the mount so it must follow it */ 236 rmtnoteproc(); 237 238 for(i = 0; i < 3; i++) 239 close(i); 240 241 if(open("/mnt/term/dev/cons", OREAD) != 0){ 242 exits("open stdin"); 243 } 244 if(open("/mnt/term/dev/cons", OWRITE) != 1){ 245 exits("open stdout"); 246 } 247 dup(1, 2); 248 249 if(badchdir) 250 print("cpu: failed to chdir to '%s'\n", xdir); 251 252 if(gotcmd) 253 execl("/bin/rc", "rc", "-lc", cmd, 0); 254 else 255 execl("/bin/rc", "rc", "-li", 0); 256 fatal(1, "exec shell"); 257 } 258 259 char* 260 rexcall(int *fd, char *host, char *service) 261 { 262 char *na; 263 char dir[4*NAMELEN]; 264 char err[ERRLEN]; 265 int n; 266 267 na = netmkaddr(host, 0, service); 268 if((*fd = dial(na, 0, dir, 0)) < 0) 269 return "can't dial"; 270 271 /* negotiate authentication mechanism */ 272 writestr(*fd, am->name, negstr, 0); 273 n = readstr(*fd, err, ERRLEN); 274 if(n < 0) 275 return negstr; 276 if(*err){ 277 werrstr(err); 278 return negstr; 279 } 280 281 /* authenticate */ 282 if((*am->cf)(*fd) < 0) 283 return "can't authenticate"; 284 if(strstr(dir, "tcp")) 285 fflag = 1; 286 return 0; 287 } 288 289 void 290 writestr(int fd, char *str, char *thing, int ignore) 291 { 292 int l, n; 293 294 l = strlen(str); 295 n = write(fd, str, l+1); 296 if(!ignore && n < 0) 297 fatal(1, "writing network: %s", thing); 298 } 299 300 int 301 readstr(int fd, char *str, int len) 302 { 303 int n; 304 305 while(len) { 306 n = read(fd, str, 1); 307 if(n < 0) 308 return -1; 309 if(*str == '\0') 310 return 0; 311 str++; 312 len--; 313 } 314 return -1; 315 } 316 317 static int 318 readln(char *buf, int n) 319 { 320 char *p = buf; 321 322 n--; 323 while(n > 0){ 324 if(read(0, p, 1) != 1) 325 break; 326 if(*p == '\n' || *p == '\r'){ 327 *p = 0; 328 return p-buf; 329 } 330 p++; 331 } 332 *p = 0; 333 return p-buf; 334 } 335 336 static int 337 netkeyauth(int fd) 338 { 339 char chall[NAMELEN]; 340 char resp[NAMELEN]; 341 342 strcpy(chall, getuser()); 343 print("user[%s]: ", chall); 344 if(readln(resp, sizeof(resp)) < 0) 345 return -1; 346 if(*resp != 0) 347 strcpy(chall, resp); 348 writestr(fd, chall, "challenge/response", 1); 349 350 for(;;){ 351 if(readstr(fd, chall, sizeof chall) < 0) 352 break; 353 if(*chall == 0) 354 return 0; 355 print("challenge: %s\nresponse: ", chall); 356 if(readln(resp, sizeof(resp)) < 0) 357 break; 358 writestr(fd, resp, "challenge/response", 1); 359 } 360 return -1; 361 } 362 363 static int 364 netkeysrvauth(int fd, char *user) 365 { 366 char response[NAMELEN]; 367 Chalstate ch; 368 int tries; 369 370 if(readstr(fd, user, NAMELEN) < 0) 371 return -1; 372 373 for(tries = 0; tries < 10; tries++){ 374 if(getchal(&ch, user) < 0) 375 return -1; 376 writestr(fd, ch.chal, "challenge", 1); 377 if(readstr(fd, response, sizeof response) < 0) 378 return -1; 379 if(chalreply(&ch, response) >= 0) 380 break; 381 } 382 if(tries >= 10) 383 return -1; 384 writestr(fd, "", "challenge", 1); 385 return 0; 386 } 387 388 /* Network on fd1, mount driver on fd0 */ 389 int 390 filter(int fd) 391 { 392 int p[2]; 393 394 if(pipe(p) < 0) 395 fatal(1, "pipe"); 396 397 switch(rfork(RFNOWAIT|RFPROC|RFFDG)) { 398 case -1: 399 fatal(1, "rfork record module"); 400 case 0: 401 dup(fd, 1); 402 close(fd); 403 dup(p[0], 0); 404 close(p[0]); 405 close(p[1]); 406 execl("/bin/aux/fcall", "fcall", 0); 407 fatal(1, "exec record module"); 408 default: 409 close(fd); 410 close(p[0]); 411 } 412 return p[1]; 413 } 414 415 int 416 setam(char *name) 417 { 418 for(am = authmethod; am->name != nil; am++) 419 if(strcmp(am->name, name) == 0) 420 return 0; 421 am = authmethod; 422 return -1; 423 } 424 425 char *rmtnotefile = "/mnt/term/dev/cpunote"; 426 427 /* 428 * loop reading /mnt/term/dev/note looking for notes. 429 * The child returns to start the shell. 430 */ 431 void 432 rmtnoteproc(void) 433 { 434 int n, fd, pid, notepid; 435 char buf[256]; 436 Waitmsg w; 437 438 /* new proc returns to start shell */ 439 pid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM); 440 switch(pid){ 441 case -1: 442 syslog(0, "cpu", "cpu -R: can't start noteproc: %r"); 443 return; 444 case 0: 445 return; 446 } 447 448 /* new proc reads notes from other side and posts them to shell */ 449 switch(notepid = rfork(RFPROC|RFFDG|RFMEM)){ 450 case -1: 451 syslog(0, "cpu", "cpu -R: can't start wait proc: %r"); 452 _exits(0); 453 case 0: 454 fd = open(rmtnotefile, OREAD); 455 if(fd < 0){ 456 syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile); 457 _exits(0); 458 } 459 460 for(;;){ 461 n = read(fd, buf, sizeof(buf)-1); 462 if(n <= 0){ 463 postnote(PNGROUP, pid, "hangup"); 464 _exits(0); 465 } 466 buf[n] = 0; 467 postnote(PNGROUP, pid, buf); 468 } 469 break; 470 } 471 472 /* original proc waits for shell proc to die and kills note proc */ 473 for(;;){ 474 n = wait(&w); 475 if(n < 0 || n == pid) 476 break; 477 } 478 postnote(PNPROC, notepid, "kill"); 479 _exits(0); 480 } 481 482 enum 483 { 484 Qdir, 485 Qcpunote, 486 487 Nfid = 32, 488 }; 489 490 struct { 491 char *name; 492 Qid qid; 493 ulong perm; 494 } fstab[] = 495 { 496 [Qdir] { ".", {CHDIR|Qdir, 0}, CHDIR|0555 }, 497 [Qcpunote] { "cpunote", {Qcpunote, 0}, 0444 }, 498 }; 499 500 typedef struct Note Note; 501 struct Note 502 { 503 Note *next; 504 char msg[ERRLEN]; 505 }; 506 507 typedef struct Request Request; 508 struct Request 509 { 510 Request *next; 511 Fcall f; 512 }; 513 514 typedef struct Fid Fid; 515 struct Fid 516 { 517 int fid; 518 int file; 519 }; 520 Fid fids[Nfid]; 521 522 struct { 523 Lock; 524 Note *nfirst, *nlast; 525 Request *rfirst, *rlast; 526 } nfs; 527 528 int 529 fsreply(int fd, Fcall *f) 530 { 531 char buf[ERRLEN+MAXMSG]; 532 int n; 533 534 if(f->type == Rerror) 535 f->count = strlen(f->data); 536 if(dbg) 537 fprint(2, "<-%F\n", f); 538 n = convS2M(f, buf); 539 if(n > 0){ 540 if(write(fd, buf, n) != n){ 541 close(fd); 542 return -1; 543 } 544 } 545 return 0; 546 } 547 548 /* match a note read request with a note, reply to the request */ 549 int 550 kick(int fd) 551 { 552 Request *rp; 553 Note *np; 554 int rv; 555 556 for(;;){ 557 lock(&nfs); 558 rp = nfs.rfirst; 559 np = nfs.nfirst; 560 if(rp == nil || np == nil){ 561 unlock(&nfs); 562 break; 563 } 564 nfs.rfirst = rp->next; 565 nfs.nfirst = np->next; 566 unlock(&nfs); 567 568 rp->f.type = Rread; 569 rp->f.count = strlen(np->msg); 570 rp->f.data = np->msg; 571 rv = fsreply(fd, &rp->f); 572 free(rp); 573 free(np); 574 if(rv < 0) 575 return -1; 576 } 577 return 0; 578 } 579 580 void 581 flushreq(int tag) 582 { 583 Request **l, *rp; 584 585 lock(&nfs); 586 for(l = &nfs.rfirst; *l != nil; l = &(*l)->next){ 587 rp = *l; 588 if(rp->f.tag == tag){ 589 *l = rp->next; 590 unlock(&nfs); 591 free(rp); 592 return; 593 } 594 } 595 unlock(&nfs); 596 } 597 598 Fid* 599 getfid(int fid) 600 { 601 int i, freefid; 602 603 freefid = -1; 604 for(i = 0; i < Nfid; i++){ 605 if(freefid < 0 && fids[i].file < 0) 606 freefid = i; 607 if(fids[i].fid == fid) 608 return &fids[i]; 609 } 610 if(freefid >= 0){ 611 fids[freefid].fid = fid; 612 return &fids[freefid]; 613 } 614 return nil; 615 } 616 617 int 618 fsstat(int fd, Fid *fid, Fcall *f) 619 { 620 Dir d; 621 622 memset(&d, 0, sizeof(d)); 623 strcpy(d.name, fstab[fid->file].name); 624 d.qid = fstab[fid->file].qid; 625 d.mode = fstab[fid->file].perm; 626 d.atime = d.mtime = time(0); 627 convD2M(&d, f->stat); 628 return fsreply(fd, f); 629 } 630 631 int 632 fsread(int fd, Fid *fid, Fcall *f) 633 { 634 Dir d; 635 char buf[DIRLEN]; 636 Request *rp; 637 638 switch(fid->file){ 639 default: 640 return -1; 641 case Qdir: 642 if(f->offset == 0 && f->count == DIRLEN){ 643 memset(&d, 0, sizeof(d)); 644 d.qid = fstab[Qcpunote].qid; 645 d.mode = fstab[Qcpunote].perm; 646 d.atime = d.mtime = time(0); 647 convD2M(&d, buf); 648 f->data = buf; 649 f->count = 0; 650 } else 651 f->count = 0; 652 return fsreply(fd, f); 653 case Qcpunote: 654 rp = mallocz(sizeof(*rp), 1); 655 if(rp == nil) 656 return -1; 657 rp->f = *f; 658 lock(&nfs); 659 if(nfs.rfirst == nil) 660 nfs.rfirst = rp; 661 else 662 nfs.rlast->next = rp; 663 nfs.rlast = rp; 664 unlock(&nfs); 665 return kick(fd);; 666 } 667 } 668 669 char *Eperm = "permission denied"; 670 char *Enofile = "out of files"; 671 char *Enotexist = "file does not exist"; 672 char *Enotdir = "not a directory"; 673 674 void 675 notefs(int fd) 676 { 677 char buf[MAXFDATA+MAXMSG]; 678 int n; 679 Fcall f; 680 Fid *fid, *nfid; 681 int doreply; 682 683 rfork(RFNOTEG); 684 fmtinstall('F', fcallconv); 685 686 for(n = 0; n < Nfid; n++) 687 fids[n].file = -1; 688 689 for(;;){ 690 n = read9p(fd, buf, sizeof(buf)); 691 if(n <= 0) 692 break; 693 if(convM2S(buf, &f, n) < 0) 694 break; 695 if(dbg) 696 fprint(2, "->%F\n", &f); 697 doreply = 1; 698 fid = getfid(f.fid); 699 if(fid == nil){ 700 nofids: 701 f.type = Rerror; 702 f.data = Enofile; 703 fsreply(fd, &f); 704 continue; 705 } 706 switch(f.type++){ 707 default: 708 f.type = Rerror; 709 f.data = "unknown type"; 710 break; 711 case Tflush: 712 flushreq(f.oldtag); 713 break; 714 case Tsession: 715 memset(f.authid, 0, sizeof(f.authid)); 716 memset(f.authdom, 0, sizeof(f.authdom)); 717 memset(f.chal, 0, sizeof(f.chal)); 718 break; 719 case Tnop: 720 break; 721 case Tattach: 722 f.qid = fstab[Qdir].qid; 723 fid->file = Qdir; 724 break; 725 case Tclone: 726 nfid = getfid(f.newfid); 727 if(nfid == nil) 728 goto nofids; 729 nfid->file = fid->file; 730 break; 731 case Tclwalk: 732 nfid = getfid(f.newfid); 733 if(nfid == nil) 734 goto nofids; 735 nfid->file = fid->file; 736 fid = nfid; 737 /* fall through */ 738 case Twalk: 739 if(fid->file != Qdir){ 740 f.type = Rerror; 741 f.data = Enotdir; 742 } else if(strcmp(f.name, "cpunote") == 0){ 743 fid->file = Qcpunote; 744 f.qid = fstab[Qcpunote].qid; 745 } else { 746 f.type = Rerror; 747 f.data = Enotexist; 748 } 749 break; 750 case Topen: 751 if(f.mode != OREAD){ 752 f.type = Rerror; 753 f.data = Eperm; 754 } 755 break; 756 case Tcreate: 757 f.type = Rerror; 758 f.data = Eperm; 759 break; 760 case Tread: 761 if(fsread(fd, fid, &f) < 0) 762 goto err; 763 doreply = 0; 764 break; 765 case Twrite: 766 f.type = Rerror; 767 f.data = Eperm; 768 break; 769 case Tclunk: 770 fid->file = -1; 771 break; 772 case Tremove: 773 f.type = Rerror; 774 f.data = Eperm; 775 break; 776 case Tstat: 777 if(fsstat(fd, fid, &f) < 0) 778 goto err; 779 doreply = 0; 780 break; 781 case Twstat: 782 f.type = Rerror; 783 f.data = Eperm; 784 break; 785 } 786 if(doreply) 787 if(fsreply(fd, &f) < 0) 788 break; 789 } 790 err: 791 close(fd); 792 } 793 794 char notebuf[ERRLEN]; 795 796 void 797 catcher(void*, char *text) 798 { 799 strncpy(notebuf, text, ERRLEN); 800 notebuf[ERRLEN-1] = 0; 801 noted(NCONT); 802 } 803 804 /* 805 * mount in /dev a note file for the remote side to read. 806 */ 807 void 808 lclnoteproc(int netfd) 809 { 810 int exportfspid, n; 811 Waitmsg w; 812 Note *np; 813 int pfd[2]; 814 815 if(pipe(pfd) < 0){ 816 fprint(2, "cpu: can't start note proc: %r\n"); 817 return; 818 } 819 820 /* new proc mounts and returns to start exportfs */ 821 switch(exportfspid = rfork(RFPROC|RFNAMEG|RFFDG|RFMEM)){ 822 case -1: 823 fprint(2, "cpu: can't start note proc: %r\n"); 824 return; 825 case 0: 826 close(pfd[0]); 827 amount(pfd[1], "/dev", MBEFORE, ""); 828 close(pfd[1]); 829 return; 830 } 831 832 close(netfd); 833 close(pfd[1]); 834 835 /* new proc listens for note file system rpc's */ 836 switch(rfork(RFPROC|RFNAMEG|RFMEM)){ 837 case -1: 838 fprint(2, "cpu: can't start note proc: %r\n"); 839 _exits(0); 840 case 0: 841 notefs(pfd[0]); 842 _exits(0); 843 } 844 845 /* original proc waits for notes */ 846 notify(catcher); 847 for(;;) { 848 *notebuf = 0; 849 n = wait(&w); 850 if(n < 0) { 851 if(*notebuf == 0) 852 break; 853 np = mallocz(sizeof(Note), 1); 854 if(np != nil){ 855 strcpy(np->msg, notebuf); 856 lock(&nfs); 857 if(nfs.nfirst == nil) 858 nfs.nfirst = np; 859 else 860 nfs.nlast->next = np; 861 nfs.nlast = np; 862 unlock(&nfs); 863 kick(pfd[0]); 864 } 865 unlock(&nfs); 866 } else if(n == exportfspid) 867 break; 868 } 869 870 exits(w.msg); 871 } 872