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