xref: /titanic_52/usr/src/cmd/rexd/rpc.rexd.c (revision ac88567a7a5bb7f01cf22cf366bc9d6203e24d7a)
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