1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * rexd - a remote execution daemon based on SUN Remote Procedure Calls 24 * 25 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <errno.h> 32 #include <netdb.h> 33 #include <signal.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include <netinet/in.h> 40 #include <rpc/rpc.h> 41 #include <rpc/svc_soc.h> 42 #include <rpc/key_prot.h> 43 #include <sys/fcntl.h> 44 #include <sys/ioctl.h> 45 #include <sys/param.h> 46 #include <sys/socket.h> 47 #include <sys/sockio.h> 48 #include <sys/mntent.h> 49 #include <sys/mnttab.h> 50 #include <sys/stat.h> 51 #include <sys/time.h> 52 #include <wait.h> 53 #include <sys/systeminfo.h> 54 55 #include <sys/ttold.h> 56 57 #include "rex.h" 58 59 #include <security/pam_appl.h> 60 #include <stropts.h> 61 #include <sys/stream.h> 62 /* #include <sys/termios.h> XXX */ 63 #include <sys/ttcompat.h> 64 65 #include <bsm/audit.h> 66 67 /* #define stderr stdout */ /* XXX */ 68 69 #define ListnerTimeout 300 /* seconds listner stays alive */ 70 #define WaitLimit 10 /* seconds to wait after io is closed */ 71 #define MOUNTED "/etc/mnttab" 72 #define TempDir "/tmp_rex" /* directory to hold temp mounts */ 73 static char TempName[] = "/tmp_rex/rexdXXXXXX"; 74 /* name template for temp mount points */ 75 #define TempMatch 13 /* unique prefix of above */ 76 77 SVCXPRT *ListnerTransp; /* non-null means still a listner */ 78 79 static char **Argv; /* saved argument vector (for ps) */ 80 static char *LastArgv; /* saved end-of-argument vector */ 81 int OutputSocket; /* socket for stop/cont notification */ 82 int MySocket; /* transport socket */ 83 int HasHelper = 0; /* must kill helpers (interactive mode) */ 84 int DesOnly = 0; /* unix credentials too weak */ 85 int confd; /* console fd */ 86 87 int Debug = 0; 88 89 pam_handle_t *pamh; /* PAM handle */ 90 91 time_t time_now; 92 93 extern int Master; /* half of the pty */ 94 extern char **environ; 95 96 int child = 0; /* pid of the executed process */ 97 int ChildStatus = 0; /* saved return status of child */ 98 int ChildDied = 0; /* true when above is valid */ 99 char nfsdir[MAXPATHLEN]; /* file system we mounted */ 100 char *tmpdir; /* where above is mounted, NULL if none */ 101 102 extern void rex_cleanup(void); 103 extern int ValidUser(char *host, uid_t uid, gid_t gid, 104 char *error, char *shell, 105 char *dir, struct rex_start *rst); 106 107 extern void audit_rexd_fail(char *, char *, char *, uid_t, gid_t, 108 char *, char **); 109 extern void audit_rexd_success(char *, char *, uid_t, gid_t, 110 char *, char **); 111 extern void audit_rexd_setup(); 112 113 extern int audit_settid(int); 114 115 /* process rex requests */ 116 void dorex(struct svc_req *rqstp, SVCXPRT *transp); 117 void ListnerTimer(int); /* destroy listener */ 118 void CatchChild(int); /* handle child signals */ 119 void oob(int); /* out of band signals */ 120 void sigwinch(int); /* window change signals -- dummy */ 121 FILE *setmntent(char *fname, char *flag); 122 extern void HelperRead(pollfd_t *fdp, int, int *); 123 124 int 125 main(int argc, char **argv) 126 { 127 /* 128 * the server is a typical RPC daemon, except that we only 129 * accept TCP connections. 130 */ 131 int pollretval; 132 int npollfds = 0; 133 pollfd_t *pollset = NULL; 134 struct sockaddr_in addr; 135 int maxrecsz = RPC_MAXDATASIZE; 136 137 audit_rexd_setup(); /* BSM */ 138 139 /* 140 * Remember the start and extent of argv for setproctitle(). 141 * Open the console for error printouts, but don't let it be 142 * our controlling terminal. 143 */ 144 if (argc > 1) { 145 if (strcmp("-s", argv[1]) == 0) 146 DesOnly = 1; 147 148 if (strcmp("-d", argv[1]) == 0) 149 Debug = 1; 150 } 151 152 if (argc > 2) { 153 if (strcmp("-s", argv[2]) == 0) 154 DesOnly = 1; 155 156 if (strcmp("-d", argv[2]) == 0) 157 Debug = 1; 158 } 159 160 /* 161 * argv start and extent for setproctitle() 162 */ 163 Argv = argv; 164 if (argc > 0) 165 LastArgv = argv[argc-1] + strlen(argv[argc-1]); 166 else 167 LastArgv = NULL; 168 169 /* 170 * console open for errors w/o being the controlling terminal 171 */ 172 173 if ((confd = open("/dev/console", 1)) > 0) { 174 close(1); 175 close(2); 176 confd = dup2(confd, 1); /* console fd copied to stdout */ 177 dup(1); /* console fd copied to stderr */ 178 } 179 180 setsid(); /* get rid of controlling terminal */ 181 182 /* 183 * setup signals 184 */ 185 sigset(SIGCHLD, CatchChild); 186 sigset(SIGPIPE, SIG_IGN); 187 sigset(SIGALRM, ListnerTimer); 188 189 /* 190 * Enable non-blocking mode and maximum record size checks for 191 * connection oriented transports. 192 */ 193 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) { 194 fprintf(stderr, "rexd: unable to set RPC max record size\n"); 195 } 196 197 /* 198 * determine how we started to see if we are already in the background 199 * and get appropriately registered with rpcbind (portmapper) 200 */ 201 202 if (isfrominetd(0)) { 203 /* 204 * Started from inetd: use fd 0 as socket 205 */ 206 if (Debug) 207 printf("Started from inetd\n"); 208 209 if ((ListnerTransp = svctcp_create(0, 0, 0)) == NULL) { 210 fprintf(stderr, "rexd: svctcp_create error\n"); 211 exit(1); 212 } 213 214 if (!svc_register(ListnerTransp, REXPROG, REXVERS, dorex, 0)) { 215 fprintf(stderr, "rexd: service register error\n"); 216 exit(1); 217 } 218 219 alarm(ListnerTimeout); 220 } else { 221 222 if (Debug) 223 printf("started from shell\n"); 224 if (!Debug) { 225 /* 226 * Started from shell, background 227 * thyself and run forever. 228 */ 229 230 int pid = fork(); 231 232 if (pid < 0) { /* fork error */ 233 perror("rpc.rexd: can't fork"); 234 exit(1); 235 } 236 237 if (pid) { /* parent terminates */ 238 exit(0); 239 } 240 } 241 242 /* 243 * child process continues to establish connections 244 */ 245 246 if (Debug) 247 printf("before svctcp_create() call\n"); 248 if ((ListnerTransp = svctcp_create(RPC_ANYSOCK, 0, 0)) 249 == NULL) { 250 fprintf(stderr, "rexd: svctcp_create: error\n"); 251 exit(1); 252 } 253 254 pmap_unset(REXPROG, REXVERS); 255 256 if (!svc_register(ListnerTransp, REXPROG, REXVERS, 257 dorex, IPPROTO_TCP)) { 258 fprintf(stderr, "rexd: service rpc register: error\n"); 259 exit(1); 260 } 261 } 262 263 /* 264 * Create a private temporary directory to hold rexd's mounts 265 */ 266 if (mkdir(TempDir, 0777) < 0) 267 if (errno != EEXIST) { 268 perror("rexd: mkdir"); 269 fprintf(stderr, 270 "rexd: can't create temp directory %s\n", 271 TempDir); 272 exit(1); 273 } 274 275 if (Debug) 276 printf("created temporary directory\n"); 277 278 279 /* 280 * normally we would call svc_run() at this point, but we need to be 281 * informed of when the RPC connection is broken, in case the other 282 * side crashes. 283 */ 284 while (TRUE) { 285 if (Debug) 286 printf("Entered While loop\n"); 287 288 if (MySocket) { 289 int i; 290 char *waste; 291 292 /* try to find MySocket in the pollfd set */ 293 for (i = 0; i < svc_max_pollfd; i++) 294 if (svc_pollfd[i].fd == MySocket) 295 break; 296 /* 297 * If we didn't find it, the connection died for 298 * some random reason, e.g. client crashed. 299 */ 300 if (i == svc_max_pollfd) { 301 if (Debug) 302 printf("Connection died\n"); 303 (void) rex_wait(&waste); 304 rex_cleanup(); 305 exit(1); 306 } 307 } 308 309 /* 310 * Get existing array of pollfd's, should really compress 311 * this but it shouldn't get very large (or sparse). 312 */ 313 if (npollfds != svc_max_pollfd) { 314 pollset = realloc(pollset, 315 sizeof (pollfd_t) * svc_max_pollfd); 316 npollfds = svc_max_pollfd; 317 } 318 319 if (npollfds == 0) 320 break; /* None waiting, hence return */ 321 322 (void) memcpy(pollset, svc_pollfd, 323 sizeof (pollfd_t) * svc_max_pollfd); 324 325 if (Debug) 326 printf("Before select readfds\n"); 327 switch (pollretval = poll(pollset, npollfds, -1)) { 328 case -1: 329 if (Debug) 330 printf("Poll failed\n"); 331 if (errno == EINTR) 332 continue; 333 perror("rexd: poll failed"); 334 exit(1); 335 336 case 0: 337 if (Debug) 338 printf("Poll returned zero\n"); 339 fprintf(stderr, "rexd: poll returned zero\r\n"); 340 continue; 341 342 default: 343 if (Debug) 344 printf("Before HelperRead\n"); 345 if (HasHelper) 346 HelperRead(pollset, npollfds, &pollretval); 347 if (Debug) 348 printf("After HelperRead\n"); 349 time_now = time((time_t *)0); 350 if (Debug) 351 printf("before svc_getreq_poll\n"); 352 svc_getreq_poll(pollset, pollretval); 353 } 354 if (Debug) 355 printf("After switch\n"); 356 } 357 return (0); 358 } 359 360 /* 361 * This function gets called after the listner has timed out waiting 362 * for any new connections coming in. 363 */ 364 void 365 ListnerTimer(int junk) 366 { 367 /* 368 * svc_destroy not done here due to problems with M_ERROR 369 * on stream head and inetd 370 */ 371 exit(0); 372 } 373 374 struct authunix_parms 375 *authdes_to_unix(des_cred) 376 struct authdes_cred *des_cred; 377 { 378 struct authunix_parms *unix_cred; 379 static struct authunix_parms au; 380 static uint_t stuff[32]; 381 char publickey[HEXKEYBYTES+1]; 382 383 384 unix_cred = &au; 385 386 unix_cred->aup_gids = (gid_t *)stuff; 387 388 unix_cred->aup_machname = ""; 389 if (getpublickey(des_cred->adc_fullname.name, publickey) == 0) 390 return (NULL); 391 392 if (netname2user(des_cred->adc_fullname.name, 393 &(unix_cred->aup_uid), 394 &(unix_cred->aup_gid), 395 (int *)&(unix_cred->aup_len), 396 unix_cred->aup_gids) == FALSE) 397 return (NULL); 398 else 399 return (unix_cred); 400 } 401 402 /* 403 * dorex - handle one of the rex procedure calls, dispatching to the 404 * correct function. 405 */ 406 void 407 dorex(rqstp, transp) 408 struct svc_req *rqstp; 409 SVCXPRT *transp; 410 { 411 struct rex_start *rst; 412 struct rex_result result; 413 struct authunix_parms *unix_cred; 414 struct sockaddr_in *calleraddr; 415 416 417 if (ListnerTransp) { 418 419 /* 420 * First call - fork a server for this connection 421 */ 422 int fd, pid, count; 423 424 for (count = 0; (pid = fork()) < 0; count++) { 425 if (count > 4) 426 { 427 perror("rexd: cannot fork"); 428 break; 429 } 430 sleep(5); 431 } 432 433 if (pid != 0) { 434 435 /* 436 * Parent - return to service loop to accept further 437 * connections. 438 */ 439 alarm(ListnerTimeout); 440 svc_destroy(transp); 441 return; 442 } 443 444 /* 445 * child - close listner transport to avoid confusion 446 * Also need to close all other service transports 447 * besides the one we are interested in. 448 * Save ours so that we know when it goes away. 449 */ 450 if (Debug) 451 printf("child server process\n"); 452 453 alarm(0); 454 455 456 457 if (transp != ListnerTransp) { 458 459 close(ListnerTransp->xp_sock); 460 xprt_unregister(ListnerTransp); 461 } 462 ListnerTransp = NULL; 463 464 MySocket = transp->xp_sock; 465 466 /* temp workaround to restore sanity in TLI state */ 467 if (transp->xp_sock != 0) 468 t_close(0); /* opened in parent possibly by inetd */ 469 470 /* 471 * XXX: svc_pollfd[] is a read-only structure. This 472 * appears to be dead code, which should be removed. 473 * However, until it can be clearly understood, leaving 474 * in. 475 */ 476 for (fd = 1; fd < svc_max_pollfd; fd++) { 477 if (fd != transp->xp_sock && svc_pollfd[fd].fd == fd) { 478 479 printf("close of fd %d\n", fd); 480 close(fd); 481 svc_pollfd[fd].fd = -1; 482 svc_pollfd[fd].events = 0; 483 svc_pollfd[fd].revents = 0; 484 } 485 } 486 } 487 488 /* 489 * execute the requested prodcedure 490 */ 491 switch (rqstp->rq_proc) { 492 case NULLPROC: 493 if (Debug) /* XXX */ 494 printf("dorex: call to NULLPROC\n"); 495 496 if (svc_sendreply(transp, xdr_void, 0) == FALSE) { 497 498 fprintf(stderr, "rexd: nullproc err"); 499 exit(1); 500 } 501 return; 502 503 case REXPROC_START: 504 if (Debug) /* XXX */ 505 printf("dorex: call to REXPROC_START\n"); 506 507 508 rst = (struct rex_start *)malloc(sizeof (struct rex_start)); 509 memset((char *)rst, '\0', sizeof (*rst)); 510 511 if (svc_getargs(transp, xdr_rex_start, (char *)rst) == FALSE) { 512 513 svcerr_decode(transp); 514 exit(1); 515 } 516 if (Debug) 517 printf("svc_getargs: suceeded\n"); 518 519 if (rqstp->rq_cred.oa_flavor == AUTH_DES) { 520 521 unix_cred = authdes_to_unix(rqstp->rq_clntcred); 522 523 } else if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) { 524 525 if (DesOnly) { 526 fprintf(stderr, 527 "Unix too weak auth(DesOnly)!\n"); 528 unix_cred = NULL; 529 } else 530 unix_cred = 531 (struct authunix_parms *)rqstp->rq_clntcred; 532 533 } else { 534 535 fprintf(stderr, "Unknown weak auth!\n"); 536 svcerr_weakauth(transp); 537 sleep(5); 538 exit(1); 539 } 540 541 if (unix_cred == NULL) { 542 543 svcerr_weakauth(transp); 544 sleep(5); 545 exit(1); 546 } 547 548 calleraddr = svc_getcaller(transp); 549 550 result.rlt_stat = (int)rex_startup(rst, 551 unix_cred, 552 (char **)&result.rlt_message, 553 calleraddr); 554 555 if (Debug) 556 printf("rex_startup: completed\n"); 557 558 if (svc_sendreply(transp, xdr_rex_result, (char *)&result) 559 == FALSE) { 560 fprintf(stderr, "rexd: reply failed\n"); 561 rex_cleanup(); 562 exit(1); 563 } 564 565 if (Debug) 566 printf("svc_sendreply: suceeded\n"); 567 568 if (result.rlt_stat) { 569 570 rex_cleanup(); 571 exit(0); 572 } 573 return; 574 575 case REXPROC_MODES: 576 { 577 struct rex_ttymode mode; 578 579 if (Debug) /* XXX */ 580 printf("dorex: call to REXPROC_MODES\n"); 581 582 if (svc_getargs(transp, xdr_rex_ttymode, 583 (char *)&mode) == FALSE) { 584 svcerr_decode(transp); 585 exit(1); 586 } 587 if (Debug) 588 printf("svc_getargs succ REXPROC_MODES call\n"); 589 590 SetPtyMode(&mode); /* XXX Fix? */ 591 592 if (svc_sendreply(transp, xdr_void, 0) == FALSE) { 593 594 fprintf(stderr, "rexd: mode reply failed"); 595 exit(1); 596 } 597 } 598 return; 599 600 case REXPROC_WINCH: /* XXX Fix? */ 601 { 602 struct rex_ttysize size; 603 604 if (Debug) /* XXX */ 605 printf("dorex: call to REXPROC_WINCH\n"); 606 607 if (svc_getargs(transp, xdr_rex_ttysize, (char *)&size) 608 == FALSE) { 609 svcerr_decode(transp); 610 exit(1); 611 } 612 613 SetPtySize(&size); 614 615 if (svc_sendreply(transp, xdr_void, 0) == FALSE) { 616 617 fprintf(stderr, 618 "rexd: window change reply failed"); 619 exit(1); 620 } 621 } 622 return; 623 624 case REXPROC_SIGNAL: 625 { 626 int sigNumber; 627 628 if (Debug) /* XXX */ 629 printf("dorex: call to REXPROC_SIGNAL\n"); 630 631 if (svc_getargs(transp, xdr_int, 632 (char *)&sigNumber) == FALSE) { 633 svcerr_decode(transp); 634 exit(1); 635 } 636 637 SendSignal(sigNumber); 638 639 if (svc_sendreply(transp, xdr_void, 0) == FALSE) { 640 fprintf(stderr, "rexd: signal reply failed"); 641 exit(1); 642 } 643 } 644 return; 645 646 case REXPROC_WAIT: 647 if (Debug) /* XXX */ 648 printf("dorex: call to REXPROC_WAIT\n"); 649 650 result.rlt_stat = rex_wait(&result.rlt_message); 651 652 if (svc_sendreply(transp, xdr_rex_result, (char *)&result) 653 == FALSE) { 654 fprintf(stderr, "rexd: reply failed\n"); 655 exit(1); 656 } 657 658 rex_cleanup(); 659 exit(0); 660 661 /* NOTREACHED */ 662 default: 663 if (Debug) 664 printf("dorex: call to bad process!\n"); 665 666 svcerr_noproc(transp); 667 exit(1); 668 } 669 } 670 671 /* 672 * signal handler for SIGCHLD - called when user process dies or is stopped 673 */ 674 void 675 CatchChild(int junk) 676 { 677 pid_t pid; 678 int status; 679 680 if (Debug) 681 printf("Enter Catchild\n"); 682 683 while ((pid = waitpid((pid_t)-1, &status, WNOHANG|WUNTRACED)) > 0) { 684 685 if (Debug) printf("After waitpid\n"); 686 if (pid == child) { 687 if (Debug) 688 printf("pid==child\n"); 689 if (WIFSTOPPED(status)) { 690 sigset_t nullsigset; 691 692 if (Debug) 693 printf("WIFSTOPPED\n"); 694 /* tell remote client to stop */ 695 send(OutputSocket, "", 1, MSG_OOB); 696 697 sigemptyset(&nullsigset); 698 /* port of BSD sigpause(0); */ 699 sigsuspend(&nullsigset); 700 /* restart child */ 701 /* killpg() of SunOS 4.1.1 */ 702 kill((-child), SIGCONT); 703 return; 704 } 705 706 /* 707 * XXX this probably does not cover all interesting 708 * exit cases hence reread the man page to determine 709 * if we need more data or more test cases 710 */ 711 712 ChildStatus = status; 713 ChildDied = 1; 714 715 if (HasHelper && svc_pollfd[Master].fd == -1) { 716 if (Debug) 717 printf("Within If HasHelper\n"); 718 KillHelper(child); 719 HasHelper = 0; 720 } 721 } 722 } 723 } 724 725 /* 726 * oob -- called when we should restart the stopped child. 727 */ 728 void 729 oob(int junk) 730 { 731 int atmark; 732 char waste[BUFSIZ], mark; 733 734 for (;;) { 735 736 if (ioctl(OutputSocket, SIOCATMARK, &atmark) < 0) { 737 perror("ioctl"); 738 break; 739 } 740 741 if (atmark) 742 break; 743 744 (void) read(OutputSocket, waste, sizeof (waste)); 745 } 746 747 (void) recv(OutputSocket, &mark, 1, MSG_OOB); 748 } 749 750 /* 751 * rex_wait - wait for command to finish, unmount the file system, 752 * and return the exit status. 753 * message gets an optional string error message. 754 */ 755 int 756 rex_wait(message) 757 char **message; 758 { 759 static char error[1024]; 760 int count; 761 762 *message = error; 763 strcpy(error, ""); 764 if (child == 0) { 765 errprintf(error, "No process to wait for!\n"); 766 rex_cleanup(); 767 return (1); 768 } 769 770 kill(child, SIGHUP); 771 772 for (count = 0; !ChildDied && count < WaitLimit; count++) 773 sleep(1); 774 775 if (ChildStatus & 0xFF) 776 return (ChildStatus); 777 778 return (ChildStatus >> 8); 779 } 780 781 782 /* 783 * cleanup - unmount and remove our temporary directory 784 */ 785 void 786 rex_cleanup() 787 { 788 789 if (tmpdir) { 790 791 if (child && !ChildDied) { 792 793 fprintf(stderr, 794 "rexd: child killed to unmount %s\r\n", 795 nfsdir); 796 kill(child, SIGKILL); 797 } 798 chdir("/"); 799 800 if (nfsdir[0] && umount_nfs(nfsdir, tmpdir)) 801 fprintf(stderr, "rexd: couldn't umount %s from %s\r\n", 802 nfsdir, 803 tmpdir); 804 if (rmdir(tmpdir) < 0) 805 if (errno != EBUSY) 806 perror("rmdir"); 807 tmpdir = NULL; 808 809 } 810 811 if (Debug) 812 printf("rex_cleaup: HasHelper=%d\n", HasHelper); 813 if (HasHelper) 814 KillHelper(child); 815 816 HasHelper = 0; 817 } 818 819 820 /* 821 * This function does the server work to get a command executed 822 * Returns 0 if OK, nonzero if error 823 */ 824 int 825 rex_startup(rst, ucred, message, calleraddr) 826 struct rex_start *rst; 827 struct authunix_parms *ucred; 828 char **message; 829 struct sockaddr_in *calleraddr; 830 { 831 char hostname[255]; 832 char *p, *wdhost, *fsname, *subdir; 833 char dirbuf[1024]; 834 static char error[1024]; 835 char defaultShell[1024]; /* command executed if none given */ 836 char defaultDir[1024]; /* directory used if none given */ 837 int len; 838 int fd0, fd1, fd2; 839 extern pam_handle_t *pamh; 840 char *user = NULL; 841 842 if (Debug) 843 printf("Beginning of Rex_Startup\n"); 844 845 if (child) { /* already started */ 846 if (Debug) 847 printf("Killing \"child\" process\n"); 848 kill((-child), SIGKILL); /* killpg() of SunOS 4.1.1 */ 849 return (1); 850 } 851 852 853 *message = error; 854 (void) strcpy(error, ""); 855 /* sigset(SIGCHLD, CatchChild); */ 856 857 858 if (ValidUser(ucred->aup_machname, 859 (uid_t)ucred->aup_uid, 860 (gid_t)ucred->aup_gid, 861 error, 862 defaultShell, defaultDir, rst)) 863 return (1); 864 865 if (rst->rst_fsname && strlen(rst->rst_fsname)) { 866 fsname = rst->rst_fsname; 867 subdir = rst->rst_dirwithin; 868 wdhost = rst->rst_host; 869 } else { 870 fsname = defaultDir; 871 subdir = ""; 872 wdhost = hostname; 873 } 874 875 sysinfo(SI_HOSTNAME, hostname, 255); 876 877 if (Debug) 878 printf("rexd: errno %d after gethostname\n", errno); 879 880 if (Debug) { 881 printf("rex_startup on host %s:\nrequests fsname=%s", 882 hostname, fsname); 883 printf("\t\tsubdir=%s\t\twdhost=%s\n", subdir, wdhost); 884 } 885 if (strcmp(wdhost, hostname) == 0) { 886 887 /* 888 * The requested directory is local to our machine, 889 * so just change to it. 890 */ 891 strcpy(dirbuf, fsname); 892 } else { 893 894 static char wanted[1024]; 895 static char mountedon[1024]; 896 897 strcpy(wanted, wdhost); 898 strcat(wanted, ":"); 899 strcat(wanted, fsname); 900 901 if (AlreadyMounted(wanted, mountedon)) { 902 903 if (Debug) 904 printf("AlreadyMounted (%d)\n", errno); 905 906 /* 907 * The requested directory is already mounted. If the 908 * mount is not by another rexd, just change to it. 909 * Otherwise, mount it again. If just changing to 910 * the mounted directy, be careful. It might be mounted 911 * in a different place. 912 * (dirbuf is modified in place!) 913 */ 914 if (strncmp(mountedon, TempName, TempMatch) == 0) { 915 tmpdir = mktemp(TempName); 916 /* 917 * XXX errno is set to ENOENT on success 918 * of mktemp because of accesss checks for file 919 */ 920 if (errno == ENOENT) 921 errno = 0; 922 923 if (mkdir(tmpdir, 0777)) { 924 perror("Already Mounted"); 925 if (pamh) { 926 pam_end(pamh, PAM_ABORT); 927 pamh = NULL; 928 } 929 return (1); 930 } 931 932 if (Debug) 933 printf("created %s (%d)\n", 934 tmpdir, errno); 935 936 strcpy(nfsdir, wanted); 937 938 if (mount_nfs(wanted, tmpdir, error)) { 939 if (Debug) 940 printf("mount_nfs:error return\n"); 941 if (pamh) { 942 pam_end(pamh, PAM_ABORT); 943 pamh = NULL; 944 } 945 return (1); 946 } 947 if (Debug) 948 printf("mount_nfs: success return\n"); 949 950 strcpy(dirbuf, tmpdir); 951 952 } else 953 strcpy(dirbuf, mountedon); 954 955 } else { 956 if (Debug) 957 printf("not AlreadyMounted (%d)\n", errno); 958 /* 959 * The requested directory is not mounted anywhere, 960 * so try to mount our own copy of it. We set nfsdir 961 * so that it gets unmounted later, and tmpdir so that 962 * it also gets removed when we are done. 963 */ 964 tmpdir = mktemp(TempName); 965 966 /* 967 * XXX errno is set to ENOENT on success of mktemp 968 * becuase of accesss checks for file 969 */ 970 if (errno == ENOENT) 971 errno = 0; 972 if (mkdir(tmpdir, 0777)) { 973 perror("Not Already Mounted"); 974 if (pamh) { 975 pam_end(pamh, PAM_ABORT); 976 pamh = NULL; 977 } 978 return (1); 979 } 980 981 if (Debug) 982 printf("created %s (%d)\n", tmpdir, errno); 983 984 strcpy(nfsdir, wanted); 985 986 if (mount_nfs(wanted, tmpdir, error)) { 987 if (Debug) 988 printf("mount_nfs:error return\n"); 989 if (pamh) { 990 pam_end(pamh, PAM_ABORT); 991 pamh = NULL; 992 } 993 return (1); 994 } 995 if (Debug) 996 printf("mount_nfs: success return\n"); 997 strcpy(dirbuf, tmpdir); 998 } 999 } 1000 1001 /* 1002 * "dirbuf" now contains the local mount point, so just tack on 1003 * the subdirectory to get the pathname to which we "chdir" 1004 */ 1005 strcat(dirbuf, subdir); 1006 1007 1008 fd0 = socket(AF_INET, SOCK_STREAM, 0); 1009 if (Debug) 1010 printf("Before doconnect\n"); 1011 fd0 = doconnect(calleraddr, rst->rst_port0, fd0); 1012 OutputSocket = fd0; 1013 1014 /* 1015 * Arrange for fd0 to send the SIGURG signal when out-of-band data 1016 * arrives, which indicates that we should send the stopped child a 1017 * SIGCONT signal so that we can resume work. 1018 */ 1019 (void) fcntl(fd0, F_SETOWN, getpid()); 1020 /* ioctl(fd0, SIOCSPGRP, ?X?); */ 1021 sigset(SIGURG, oob); 1022 1023 if (Debug) 1024 printf("Before \"use same port\"\n"); 1025 if (rst->rst_port0 == rst->rst_port1) { 1026 /* 1027 * use the same connection for both stdin and stdout 1028 */ 1029 fd1 = fd0; 1030 } 1031 1032 if (rst->rst_flags & REX_INTERACTIVE) { 1033 /* 1034 * allocate a pseudo-terminal if necessary 1035 */ 1036 if (Debug) 1037 printf("Before AllocatePty call\n"); 1038 1039 /* AllocatePty has grantpt() call which has bug */ 1040 /* Hence clear SIGCHLD handler setting */ 1041 sigset(SIGCHLD, SIG_DFL); 1042 if (AllocatePty(fd0, fd1)) { 1043 errprintf(error, "rexd: cannot allocate a pty\n"); 1044 if (pamh) { 1045 pam_end(pamh, PAM_ABORT); 1046 pamh = NULL; 1047 } 1048 return (1); 1049 } 1050 HasHelper = 1; 1051 } 1052 /* 1053 * this sigset()call moved to after AllocatePty() call 1054 * because a bug in waitpid() inside grantpt() 1055 * causes CatchChild() to be invoked. 1056 */ 1057 1058 sigset(SIGCHLD, CatchChild); 1059 1060 if (rst->rst_flags & REX_INTERACTIVE) { 1061 sigset(SIGWINCH, sigwinch); /* a dummy signal handler */ 1062 /* block the sigpause until signal in */ 1063 /* child releases the signal */ 1064 sighold(SIGWINCH); 1065 } 1066 1067 if (Debug) 1068 printf("Before a \"child\" fork\n"); 1069 1070 child = fork(); 1071 1072 if (child < 0) { 1073 errprintf(error, "rexd: can't fork\n"); 1074 if (pamh) { 1075 pam_end(pamh, PAM_ABORT); 1076 pamh = NULL; 1077 } 1078 return (1); 1079 } 1080 1081 if (child) { 1082 /* 1083 * parent rexd: close network connections if needed, 1084 * then return to the main loop. 1085 */ 1086 if ((rst->rst_flags & REX_INTERACTIVE) == 0) { 1087 close(fd0); 1088 close(fd1); 1089 } 1090 if (Debug) 1091 printf("Parent ret to main loop, child does startup\n"); 1092 if (pamh) { 1093 pam_end(pamh, PAM_SUCCESS); 1094 pamh = NULL; 1095 } 1096 return (0); 1097 } 1098 1099 /* child rexd */ 1100 1101 if (Debug) 1102 printf("Child rexd\n"); 1103 1104 /* setpgrp(0, 0) */ 1105 setsid(); /* make session leader */ 1106 1107 if (Debug) 1108 printf("After setsid\n"); 1109 1110 if (rst->rst_flags & REX_INTERACTIVE) { 1111 if (Debug) 1112 printf("Before OpenPtySlave\n"); 1113 /* reopen slave so that child has controlling tty */ 1114 OpenPtySlave(); 1115 if (Debug) 1116 printf("After OpenPtySlave\n"); 1117 } 1118 1119 if (rst->rst_port0 != rst->rst_port1) { 1120 1121 if (Debug) 1122 printf("rst_port0 != rst_port1\n"); /* XXX */ 1123 1124 fd1 = socket(AF_INET, SOCK_STREAM, 0); 1125 shutdown(fd0, 1); /* 1=>further sends disallowed */ 1126 fd1 = doconnect(calleraddr, rst->rst_port1, fd1); 1127 shutdown(fd1, 0); /* 0=>further receives disallowed */ 1128 } 1129 1130 if (rst->rst_port1 == rst->rst_port2) { 1131 if (Debug) 1132 printf("rst_port1 == rst_port2\n"); /* XXX */ 1133 1134 /* 1135 * Use the same connection for both stdout and stderr 1136 */ 1137 fd2 = fd1; 1138 } else { 1139 if (Debug) 1140 printf("rst_port1 != rst_port2\n"); /* XXX */ 1141 1142 fd2 = socket(AF_INET, SOCK_STREAM, 0); 1143 fd2 = doconnect(calleraddr, rst->rst_port2, fd2); 1144 shutdown(fd2, 0); /* 0=>further receives disallowed */ 1145 } 1146 1147 if (rst->rst_flags & REX_INTERACTIVE) { 1148 1149 /* 1150 * use ptys instead of sockets in interactive mode 1151 */ 1152 DoHelper(&fd0, &fd1, &fd2); 1153 LoginUser(); 1154 } 1155 1156 dup2(fd0, 0); 1157 dup2(fd1, 1); 1158 dup2(fd2, 2); 1159 1160 /* setup terminal ID (use read file descriptor) */ 1161 if (audit_settid(fd0) != 0) { 1162 errprintf("cannot set audit characteristics\n"); 1163 return (1); 1164 } 1165 1166 closefrom(3); 1167 1168 if (Debug) 1169 printf("After close-all-fds-loop-- errno=%d\n", errno); 1170 1171 environ = rst->rst_env; 1172 1173 if (pam_get_item(pamh, PAM_USER, (void **)&user) != PAM_SUCCESS) { 1174 audit_rexd_fail("user id is not valid", 1175 ucred->aup_machname, 1176 user, 1177 ucred->aup_uid, 1178 ucred->aup_gid, 1179 defaultShell, 1180 rst->rst_cmd); /* BSM */ 1181 fprintf(stderr, "rexd: invalid uid/gid.\n"); 1182 exit(1); 1183 } 1184 1185 /* set the real (and effective) GID */ 1186 if (setgid(ucred->aup_gid) == -1) { 1187 fprintf(stderr, "rexd: invalid gid.\n"); 1188 exit(1); 1189 } 1190 /* Set the supplementary group access list. */ 1191 if (setgroups(ucred->aup_len, (gid_t *)ucred->aup_gids) == -1) { 1192 fprintf(stderr, "rexd: invalid group list.\n"); 1193 exit(1); 1194 } 1195 1196 if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) { 1197 audit_rexd_fail("user id is not valid", 1198 ucred->aup_machname, 1199 user, 1200 ucred->aup_uid, 1201 ucred->aup_gid, 1202 defaultShell, 1203 rst->rst_cmd); /* BSM */ 1204 fprintf(stderr, "rexd: invalid uid/gid.\n"); 1205 exit(1); 1206 } 1207 1208 audit_rexd_success(ucred->aup_machname, 1209 user, 1210 ucred->aup_uid, 1211 ucred->aup_gid, 1212 defaultShell, 1213 rst->rst_cmd); /* BSM */ 1214 1215 /* set the real (and effective) UID */ 1216 if (setuid(ucred->aup_uid) == -1) { 1217 fprintf(stderr, "rexd: invalid uid.\n"); 1218 exit(1); 1219 } 1220 1221 if (pamh) { 1222 pam_end(pamh, PAM_SUCCESS); 1223 pamh = NULL; 1224 } 1225 1226 if (Debug) /* XXX */ 1227 fprintf(stderr, "uid %d gid %d (%d)\n", 1228 ucred->aup_uid, ucred->aup_gid, errno); 1229 1230 if (chdir(dirbuf)) { 1231 fprintf(stderr, "rexd: can't chdir to %s\n", dirbuf); 1232 exit(1); 1233 } 1234 1235 sigset(SIGINT, SIG_DFL); 1236 sigset(SIGHUP, SIG_DFL); 1237 sigset(SIGQUIT, SIG_DFL); 1238 1239 if (rst->rst_flags & REX_INTERACTIVE) { 1240 /* pause to sync with first SIGWINCH sent as part of */ 1241 sigpause(SIGWINCH); 1242 /* protocol and handled by parent doing other rex primitves */ 1243 sigrelse(SIGWINCH); 1244 sigset(SIGWINCH, SIG_DFL); 1245 } 1246 1247 if (rst->rst_cmd == (char **)NULL) { 1248 1249 /* 1250 * Null command means execute the default shell for this user 1251 */ 1252 char *args[2]; 1253 1254 args[0] = defaultShell; 1255 args[1] = NULL; 1256 1257 execvp(defaultShell, args); 1258 1259 fprintf(stderr, "rexd: can't exec shell %s\n", defaultShell); 1260 exit(1); 1261 } 1262 1263 if (Debug) 1264 for (len = 0; rst->rst_cmd[len] != (char *)NULL && 1265 *rst->rst_cmd[len] != NULL; len++) 1266 printf("cmds: %s (%d)\n", rst->rst_cmd[len], errno); 1267 1268 1269 /* XXX */ 1270 if (Debug) 1271 for (len = 0; rst->rst_env[len] != (char *)NULL && 1272 *rst->rst_env[len] != NULL; len++) 1273 printf("envs: %s\n", rst->rst_env[len]); 1274 1275 1276 execvp(rst->rst_cmd[0], rst->rst_cmd); 1277 1278 /* XXX get rid of errno in parens */ 1279 fprintf(stderr, "rexd: can't exec %s (%d)\n", *rst->rst_cmd, errno); 1280 exit(1); 1281 } 1282 1283 /* 1284 * Search the mount table to see if the given file system is already 1285 * mounted. If so, return the place that it is mounted on. 1286 */ 1287 int 1288 AlreadyMounted(fsname, mountedon) 1289 char *fsname; 1290 char *mountedon; 1291 { 1292 FILE *table; 1293 struct mnttab mt; 1294 1295 table = setmntent(MOUNTED, "r"); 1296 if (table == NULL) 1297 return (0); 1298 1299 while ((getmntent(table, &mt)) != (-1)) { 1300 1301 if (strcmp(mt.mnt_special, fsname) == 0) { 1302 strcpy(mountedon, mt.mnt_mountp); 1303 endmntent(table); 1304 return (1); 1305 } 1306 } 1307 endmntent(table); 1308 1309 return (0); 1310 } 1311 1312 1313 /* 1314 * connect to the indicated IP address/port, and return the 1315 * resulting file descriptor. 1316 */ 1317 int 1318 doconnect(sin, port, fd) 1319 struct sockaddr_in *sin; 1320 short port; 1321 int fd; 1322 { 1323 sin->sin_port = ntohs(port); 1324 1325 if (connect(fd, (struct sockaddr *)sin, sizeof (*sin))) { 1326 1327 perror("rexd: connect"); 1328 exit(1); 1329 } 1330 1331 return (fd); 1332 } 1333 1334 void 1335 sigwinch(int junk) 1336 { 1337 } 1338 1339 /* 1340 * SETPROCTITLE -- set the title of this process for "ps" 1341 * 1342 * Does nothing if there were not enough arguments on the command 1343 * line for the information. 1344 * 1345 * Side Effects: 1346 * Clobbers argv[] of our main procedure. 1347 */ 1348 void 1349 setproctitle(user, host) 1350 char *user, *host; 1351 { 1352 register char *tohere; 1353 1354 tohere = Argv[0]; 1355 if ((int)(LastArgv == NULL) || 1356 (int)(strlen(user)+strlen(host)+3) > 1357 (int)(LastArgv - tohere)) 1358 return; 1359 1360 *tohere++ = '-'; /* So ps prints (rpc.rexd) */ 1361 sprintf(tohere, "%s@%s", user, host); 1362 while (*tohere++) /* Skip to end of printf output */ 1363 ; 1364 while (tohere < LastArgv) /* Avoid confusing ps */ 1365 *tohere++ = ' '; 1366 } 1367 1368 1369 /* 1370 * Determine if started from inetd or not 1371 */ 1372 1373 int 1374 isfrominetd(fd) 1375 int fd; 1376 { 1377 /* 1378 * If fd looks like a TLI endpoint, we assume 1379 * that we were started by a port monitor. If 1380 * t_getstate fails with TBADF, this is not a 1381 * TLI endpoint. 1382 */ 1383 if (t_getstate(0) != -1 || t_errno != TBADF) 1384 return (1); 1385 return (0); 1386 } 1387