11592Srgrimes/*-
21592Srgrimes * Copyright (c) 1988, 1989, 1992, 1993, 1994
31592Srgrimes *	The Regents of the University of California.  All rights reserved.
496195Sdes * Copyright (c) 2002 Networks Associates Technology, Inc.
596195Sdes * All rights reserved.
61592Srgrimes *
796195Sdes * Portions of this software were developed for the FreeBSD Project by
896195Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network
996195Sdes * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
1096195Sdes * ("CBOSS"), as part of the DARPA CHATS research program.
1196195Sdes *
121592Srgrimes * Redistribution and use in source and binary forms, with or without
131592Srgrimes * modification, are permitted provided that the following conditions
141592Srgrimes * are met:
151592Srgrimes * 1. Redistributions of source code must retain the above copyright
161592Srgrimes *    notice, this list of conditions and the following disclaimer.
171592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
181592Srgrimes *    notice, this list of conditions and the following disclaimer in the
191592Srgrimes *    documentation and/or other materials provided with the distribution.
20262136Sbrueffer * 3. Neither the name of the University nor the names of its contributors
211592Srgrimes *    may be used to endorse or promote products derived from this software
221592Srgrimes *    without specific prior written permission.
231592Srgrimes *
241592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341592Srgrimes * SUCH DAMAGE.
351592Srgrimes */
361592Srgrimes
371592Srgrimes#ifndef lint
3829917Smarkmstatic const char copyright[] =
391592Srgrimes"@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\
401592Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411592Srgrimes#endif /* not lint */
421592Srgrimes
431592Srgrimes#ifndef lint
4431490Scharnier#if 0
4529917Smarkmstatic const char sccsid[] = "@(#)rshd.c	8.2 (Berkeley) 4/6/94";
4631490Scharnier#endif
471592Srgrimes#endif /* not lint */
481592Srgrimes
4998885Smarkm#include <sys/cdefs.h>
5098885Smarkm__FBSDID("$FreeBSD: stable/11/libexec/rshd/rshd.c 360673 2020-05-05 20:53:45Z dim $");
5198885Smarkm
521592Srgrimes/*
531592Srgrimes * remote shell server:
541592Srgrimes *	[port]\0
5596195Sdes *	ruser\0
5696195Sdes *	luser\0
571592Srgrimes *	command\0
581592Srgrimes *	data
591592Srgrimes */
601592Srgrimes#include <sys/param.h>
611592Srgrimes#include <sys/ioctl.h>
621592Srgrimes#include <sys/time.h>
631592Srgrimes#include <sys/socket.h>
641592Srgrimes
6522454Simp#include <netinet/in_systm.h>
661592Srgrimes#include <netinet/in.h>
6722454Simp#include <netinet/ip.h>
6841445Sdg#include <netinet/tcp.h>
691592Srgrimes#include <arpa/inet.h>
701592Srgrimes#include <netdb.h>
711592Srgrimes
7276094Smarkm#include <err.h>
731592Srgrimes#include <errno.h>
741592Srgrimes#include <fcntl.h>
7545393Sbrian#include <libutil.h>
761592Srgrimes#include <paths.h>
771592Srgrimes#include <pwd.h>
781592Srgrimes#include <signal.h>
7976134Smarkm#include <stdarg.h>
801592Srgrimes#include <stdio.h>
811592Srgrimes#include <stdlib.h>
821592Srgrimes#include <string.h>
831592Srgrimes#include <syslog.h>
841592Srgrimes#include <unistd.h>
8525099Sdavidn#include <login_cap.h>
861592Srgrimes
8774874Smarkm#include <security/pam_appl.h>
8896195Sdes#include <security/openpam.h>
8974874Smarkm#include <sys/wait.h>
9074874Smarkm
91301242Slidl#ifdef USE_BLACKLIST
92301242Slidl#include <blacklist.h>
93301242Slidl#endif
94301242Slidl
9596195Sdesstatic struct pam_conv pamc = { openpam_nullconv, NULL };
9674874Smarkmstatic pam_handle_t *pamh;
9796195Sdesstatic int pam_err;
9874874Smarkm
9974874Smarkm#define PAM_END { \
10096195Sdes	if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
10196195Sdes		syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", pam_strerror(pamh, pam_err)); \
10296195Sdes	if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
10396195Sdes		syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", pam_strerror(pamh, pam_err)); \
10496195Sdes	if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
10596195Sdes		syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", pam_strerror(pamh, pam_err)); \
10674874Smarkm}
10774874Smarkm
1081592Srgrimesint	keepalive = 1;
1091592Srgrimesint	log_success;		/* If TRUE, log all successful accesses */
1101592Srgrimesint	sent_null;
11141445Sdgint	no_delay;
1121592Srgrimes
11396195Sdesvoid	 doit(struct sockaddr *);
11490334Simpstatic void	 rshd_errx(int, const char *, ...) __printf0like(2, 3);
11590335Simpvoid	 getstr(char *, int, const char *);
11690334Simpint	 local_domain(char *);
11790334Simpchar	*topdomain(char *);
11890334Simpvoid	 usage(void);
1191592Srgrimes
12098885Smarkmchar	 slash[] = "/";
12198885Smarkmchar	 bshell[] = _PATH_BSHELL;
12298885Smarkm
123141589Sru#define	OPTIONS	"aDLln"
12451433Smarkm
1251592Srgrimesint
12690334Simpmain(int argc, char *argv[])
1271592Srgrimes{
1281592Srgrimes	extern int __check_rhosts_file;
1291592Srgrimes	struct linger linger;
130141918Sstefanf	socklen_t fromlen;
131141918Sstefanf	int ch, on = 1;
13256590Sshin	struct sockaddr_storage from;
1331592Srgrimes
134270111Sneel	openlog("rshd", LOG_PID, LOG_DAEMON);
1351592Srgrimes
1361592Srgrimes	opterr = 0;
13724349Simp	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
1381592Srgrimes		switch (ch) {
1391592Srgrimes		case 'a':
14072093Sasmodai			/* ignored for compatibility */
1411592Srgrimes			break;
1421592Srgrimes		case 'l':
1431592Srgrimes			__check_rhosts_file = 0;
1441592Srgrimes			break;
1451592Srgrimes		case 'n':
1461592Srgrimes			keepalive = 0;
1471592Srgrimes			break;
14841445Sdg		case 'D':
14941445Sdg			no_delay = 1;
15041445Sdg			break;
1511592Srgrimes		case 'L':
1521592Srgrimes			log_success = 1;
1531592Srgrimes			break;
1541592Srgrimes		case '?':
1551592Srgrimes		default:
1561592Srgrimes			usage();
1571592Srgrimes			break;
1581592Srgrimes		}
1591592Srgrimes
1601592Srgrimes	argc -= optind;
1611592Srgrimes	argv += optind;
1621592Srgrimes
1631592Srgrimes	fromlen = sizeof (from);
1641592Srgrimes	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
1651592Srgrimes		syslog(LOG_ERR, "getpeername: %m");
16635728Srnordier		exit(1);
1671592Srgrimes	}
1681592Srgrimes	if (keepalive &&
1691592Srgrimes	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
1701592Srgrimes	    sizeof(on)) < 0)
1711592Srgrimes		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
1721592Srgrimes	linger.l_onoff = 1;
1731592Srgrimes	linger.l_linger = 60;			/* XXX */
1741592Srgrimes	if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
1751592Srgrimes	    sizeof (linger)) < 0)
1761592Srgrimes		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
17741445Sdg	if (no_delay &&
17841445Sdg	    setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
17941445Sdg		syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
18096195Sdes	doit((struct sockaddr *)&from);
1811592Srgrimes	/* NOTREACHED */
18229917Smarkm	return(0);
1831592Srgrimes}
1841592Srgrimes
18596195Sdesextern char **environ;
18674874Smarkm
1871592Srgrimesvoid
18896195Sdesdoit(struct sockaddr *fromp)
1891592Srgrimes{
1901592Srgrimes	extern char *__rcmd_errstr;	/* syslog hook from libc/net/rcmd.c. */
1911592Srgrimes	struct passwd *pwd;
1921592Srgrimes	u_short port;
1931592Srgrimes	fd_set ready, readfrom;
194330322Seadler	int cc, nfd, pv[2], pid, s;
1951592Srgrimes	int one = 1;
19696195Sdes	const char *cp, *errorstr;
19796195Sdes	char sig, buf[BUFSIZ];
198143907Sdas	char *cmdbuf, luser[16], ruser[16];
19996195Sdes	char rhost[2 * MAXHOSTNAMELEN + 1];
20056590Sshin	char numericname[INET6_ADDRSTRLEN];
20198885Smarkm	int af, srcport;
202143907Sdas	int maxcmdlen;
20325099Sdavidn	login_cap_t *lc;
2041592Srgrimes
205143907Sdas	maxcmdlen = (int)sysconf(_SC_ARG_MAX);
206143907Sdas	if (maxcmdlen <= 0 || (cmdbuf = malloc(maxcmdlen)) == NULL)
207143907Sdas		exit(1);
208143907Sdas
2091592Srgrimes	(void) signal(SIGINT, SIG_DFL);
2101592Srgrimes	(void) signal(SIGQUIT, SIG_DFL);
2111592Srgrimes	(void) signal(SIGTERM, SIG_DFL);
21296195Sdes	af = fromp->sa_family;
21396195Sdes	srcport = ntohs(*((in_port_t *)&fromp->sa_data));
21496195Sdes	if (af == AF_INET) {
21596195Sdes		inet_ntop(af, &((struct sockaddr_in *)fromp)->sin_addr,
21696195Sdes		    numericname, sizeof numericname);
21796195Sdes	} else if (af == AF_INET6) {
21896195Sdes		inet_ntop(af, &((struct sockaddr_in6 *)fromp)->sin6_addr,
21996195Sdes		    numericname, sizeof numericname);
22096195Sdes	} else {
22174874Smarkm		syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
2221592Srgrimes		exit(1);
2231592Srgrimes	}
2241592Srgrimes#ifdef IP_OPTIONS
22596195Sdes	if (af == AF_INET) {
22696195Sdes		u_char optbuf[BUFSIZ/3];
227141918Sstefanf		socklen_t optsize = sizeof(optbuf), ipproto, i;
22896195Sdes		struct protoent *ip;
2291592Srgrimes
23096195Sdes		if ((ip = getprotobyname("ip")) != NULL)
23196195Sdes			ipproto = ip->p_proto;
23296195Sdes		else
23396195Sdes			ipproto = IPPROTO_IP;
23496195Sdes		if (!getsockopt(0, ipproto, IP_OPTIONS, optbuf, &optsize) &&
23596195Sdes		    optsize != 0) {
23696195Sdes			for (i = 0; i < optsize; ) {
23796195Sdes				u_char c = optbuf[i];
23896195Sdes				if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
23996195Sdes					syslog(LOG_NOTICE,
24096195Sdes					    "connection refused from %s with IP option %s",
24196195Sdes					    numericname,
24296195Sdes					    c == IPOPT_LSRR ? "LSRR" : "SSRR");
24396195Sdes					exit(1);
24496195Sdes				}
24596195Sdes				if (c == IPOPT_EOL)
24696195Sdes					break;
24796195Sdes				i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
24822454Simp			}
2491592Srgrimes		}
2501592Srgrimes	}
2511592Srgrimes#endif
2521592Srgrimes
25396195Sdes	if (srcport >= IPPORT_RESERVED ||
25496195Sdes	    srcport < IPPORT_RESERVED/2) {
25551433Smarkm		syslog(LOG_NOTICE|LOG_AUTH,
25651433Smarkm		    "connection from %s on illegal port %u",
25756590Sshin		    numericname,
25896195Sdes		    srcport);
259301242Slidl#ifdef USE_BLACKLIST
260301242Slidl		blacklist(1, STDIN_FILENO, "illegal port");
261301242Slidl#endif
26251433Smarkm		exit(1);
26351433Smarkm	}
2641592Srgrimes
2651592Srgrimes	(void) alarm(60);
2661592Srgrimes	port = 0;
26741860Speter	s = 0;		/* not set or used if port == 0 */
2681592Srgrimes	for (;;) {
2691592Srgrimes		char c;
2701592Srgrimes		if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
2711592Srgrimes			if (cc < 0)
2721592Srgrimes				syslog(LOG_NOTICE, "read: %m");
273146074Sjmallett			shutdown(0, SHUT_RDWR);
2741592Srgrimes			exit(1);
2751592Srgrimes		}
27641860Speter		if (c == 0)
2771592Srgrimes			break;
2781592Srgrimes		port = port * 10 + c - '0';
2791592Srgrimes	}
2801592Srgrimes
2811592Srgrimes	(void) alarm(0);
2821592Srgrimes	if (port != 0) {
2831592Srgrimes		int lport = IPPORT_RESERVED - 1;
28456590Sshin		s = rresvport_af(&lport, af);
2851592Srgrimes		if (s < 0) {
2861592Srgrimes			syslog(LOG_ERR, "can't get stderr port: %m");
2871592Srgrimes			exit(1);
2881592Srgrimes		}
28951433Smarkm		if (port >= IPPORT_RESERVED ||
29051433Smarkm		    port < IPPORT_RESERVED/2) {
29151433Smarkm			syslog(LOG_NOTICE|LOG_AUTH,
29251433Smarkm			    "2nd socket from %s on unreserved port %u",
29356590Sshin			    numericname,
29451433Smarkm			    port);
295301242Slidl#ifdef USE_BLACKLIST
296301242Slidl			blacklist(1, STDIN_FILENO, "unreserved port");
297301242Slidl#endif
29851433Smarkm			exit(1);
29951433Smarkm		}
30096195Sdes		*((in_port_t *)&fromp->sa_data) = htons(port);
30196195Sdes		if (connect(s, fromp, fromp->sa_len) < 0) {
3021592Srgrimes			syslog(LOG_INFO, "connect second port %d: %m", port);
3031592Srgrimes			exit(1);
3041592Srgrimes		}
3051592Srgrimes	}
3061592Srgrimes
3071592Srgrimes	errorstr = NULL;
30896195Sdes	realhostname_sa(rhost, sizeof(rhost) - 1, fromp, fromp->sa_len);
30996195Sdes	rhost[sizeof(rhost) - 1] = '\0';
31096195Sdes	/* XXX truncation! */
3111592Srgrimes
31296195Sdes	(void) alarm(60);
31396195Sdes	getstr(ruser, sizeof(ruser), "ruser");
31496195Sdes	getstr(luser, sizeof(luser), "luser");
315143907Sdas	getstr(cmdbuf, maxcmdlen, "command");
31696195Sdes	(void) alarm(0);
31774874Smarkm
31896195Sdes	pam_err = pam_start("rsh", luser, &pamc, &pamh);
31996195Sdes	if (pam_err != PAM_SUCCESS) {
32096195Sdes		syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s",
32196195Sdes		    pam_strerror(pamh, pam_err));
322301242Slidl#ifdef USE_BLACKLIST
323301242Slidl		blacklist(1, STDIN_FILENO, "login incorrect");
324301242Slidl#endif
32576134Smarkm		rshd_errx(1, "Login incorrect.");
32674874Smarkm	}
32774874Smarkm
32896195Sdes	if ((pam_err = pam_set_item(pamh, PAM_RUSER, ruser)) != PAM_SUCCESS ||
329226937Sbrueffer	    (pam_err = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) {
33096195Sdes		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s",
33196195Sdes		    pam_strerror(pamh, pam_err));
332301242Slidl#ifdef USE_BLACKLIST
333301242Slidl		blacklist(1, STDIN_FILENO, "login incorrect");
334301242Slidl#endif
33576134Smarkm		rshd_errx(1, "Login incorrect.");
33674874Smarkm	}
33774874Smarkm
33896195Sdes	pam_err = pam_authenticate(pamh, 0);
33996195Sdes	if (pam_err == PAM_SUCCESS) {
34096195Sdes		if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) {
341321068Sdelphij			strlcpy(luser, cp, sizeof(luser));
34296195Sdes			/* XXX truncation! */
34396195Sdes		}
34496195Sdes		pam_err = pam_acct_mgmt(pamh, 0);
34574874Smarkm	}
34696195Sdes	if (pam_err != PAM_SUCCESS) {
34796195Sdes		syslog(LOG_INFO|LOG_AUTH,
34896195Sdes		    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
34996195Sdes		    ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf);
350301242Slidl#ifdef USE_BLACKLIST
351301242Slidl		blacklist(1, STDIN_FILENO, "permission denied");
352301242Slidl#endif
35376134Smarkm		rshd_errx(1, "Login incorrect.");
35474874Smarkm	}
35574874Smarkm
3561592Srgrimes	setpwent();
35796195Sdes	pwd = getpwnam(luser);
3581592Srgrimes	if (pwd == NULL) {
3591592Srgrimes		syslog(LOG_INFO|LOG_AUTH,
3601592Srgrimes		    "%s@%s as %s: unknown login. cmd='%.80s'",
36196195Sdes		    ruser, rhost, luser, cmdbuf);
362301242Slidl#ifdef USE_BLACKLIST
363301242Slidl		blacklist(1, STDIN_FILENO, "unknown login");
364301242Slidl#endif
3651592Srgrimes		if (errorstr == NULL)
36676094Smarkm			errorstr = "Login incorrect.";
36796195Sdes		rshd_errx(1, errorstr, rhost);
3681592Srgrimes	}
36974874Smarkm
37025674Sdavidn	lc = login_getpwclass(pwd);
37174874Smarkm	if (pwd->pw_uid)
37274874Smarkm		auth_checknologin(lc);
37374874Smarkm
3741592Srgrimes	if (chdir(pwd->pw_dir) < 0) {
37525099Sdavidn		if (chdir("/") < 0 ||
37625099Sdavidn		    login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) {
37725099Sdavidn			syslog(LOG_INFO|LOG_AUTH,
37825099Sdavidn			"%s@%s as %s: no home directory. cmd='%.80s'",
37996195Sdes			ruser, rhost, luser, cmdbuf);
38076134Smarkm			rshd_errx(0, "No remote home directory.");
38125099Sdavidn		}
38298885Smarkm		pwd->pw_dir = slash;
3831592Srgrimes	}
3841592Srgrimes
38596195Sdes	if (lc != NULL && fromp->sa_family == AF_INET) {	/*XXX*/
38625099Sdavidn		char	remote_ip[MAXHOSTNAMELEN];
38725099Sdavidn
388321068Sdelphij		strlcpy(remote_ip, numericname, sizeof(remote_ip));
38996195Sdes		/* XXX truncation! */
39096195Sdes		if (!auth_hostok(lc, rhost, remote_ip)) {
39125099Sdavidn			syslog(LOG_INFO|LOG_AUTH,
39225099Sdavidn			    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
39396195Sdes			    ruser, rhost, luser, __rcmd_errstr,
39425099Sdavidn			    cmdbuf);
395301242Slidl#ifdef USE_BLACKLIST
396301242Slidl			blacklist(1, STDIN_FILENO, "permission denied");
397301242Slidl#endif
39876134Smarkm			rshd_errx(1, "Login incorrect.");
39925099Sdavidn		}
40076094Smarkm		if (!auth_timeok(lc, time(NULL)))
40176134Smarkm			rshd_errx(1, "Logins not available right now");
40225099Sdavidn	}
4031592Srgrimes
40474874Smarkm	/*
40574874Smarkm	 * PAM modules might add supplementary groups in
40674874Smarkm	 * pam_setcred(), so initialize them first.
40774874Smarkm	 * But we need to open the session as root.
40874874Smarkm	 */
40974874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
41096195Sdes		syslog(LOG_ERR, "setusercontext: %m");
41174874Smarkm		exit(1);
41274874Smarkm	}
41374874Smarkm
41496195Sdes	if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
41596195Sdes		syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err));
41696195Sdes	} else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
41796195Sdes		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err));
41874874Smarkm	}
41974874Smarkm
4201592Srgrimes	(void) write(STDERR_FILENO, "\0", 1);
4211592Srgrimes	sent_null = 1;
4221592Srgrimes
4231592Srgrimes	if (port) {
42476094Smarkm		if (pipe(pv) < 0)
42576134Smarkm			rshd_errx(1, "Can't make pipe.");
4261592Srgrimes		pid = fork();
42776094Smarkm		if (pid == -1)
42876134Smarkm			rshd_errx(1, "Can't fork; try again.");
4291592Srgrimes		if (pid) {
43098885Smarkm			(void) close(0);
43198885Smarkm			(void) close(1);
4321592Srgrimes			(void) close(2);
4331592Srgrimes			(void) close(pv[1]);
4341592Srgrimes
4351592Srgrimes			FD_ZERO(&readfrom);
4361592Srgrimes			FD_SET(s, &readfrom);
4371592Srgrimes			FD_SET(pv[0], &readfrom);
4381592Srgrimes			if (pv[0] > s)
4391592Srgrimes				nfd = pv[0];
4401592Srgrimes			else
4411592Srgrimes				nfd = s;
442360673Sdim			ioctl(pv[0], FIONBIO, (char *)&one);
4431592Srgrimes
4441592Srgrimes			/* should set s nbio! */
4451592Srgrimes			nfd++;
4461592Srgrimes			do {
4471592Srgrimes				ready = readfrom;
44898885Smarkm				if (select(nfd, &ready, (fd_set *)0,
44998885Smarkm				  (fd_set *)0, (struct timeval *)0) < 0)
45098885Smarkm					break;
4511592Srgrimes				if (FD_ISSET(s, &ready)) {
4521592Srgrimes					int	ret;
4531592Srgrimes						ret = read(s, &sig, 1);
45498885Smarkm				if (ret <= 0)
45598885Smarkm					FD_CLR(s, &readfrom);
45698885Smarkm				else
45798885Smarkm					killpg(pid, sig);
4581592Srgrimes				}
4591592Srgrimes				if (FD_ISSET(pv[0], &ready)) {
4601592Srgrimes					errno = 0;
4611592Srgrimes					cc = read(pv[0], buf, sizeof(buf));
4621592Srgrimes					if (cc <= 0) {
463146074Sjmallett						shutdown(s, SHUT_RDWR);
4641592Srgrimes						FD_CLR(pv[0], &readfrom);
4651592Srgrimes					} else {
46698885Smarkm						(void)write(s, buf, cc);
4671592Srgrimes					}
4681592Srgrimes				}
4691592Srgrimes
4701592Srgrimes			} while (FD_ISSET(s, &readfrom) ||
4711592Srgrimes			    FD_ISSET(pv[0], &readfrom));
47274874Smarkm			PAM_END;
4731592Srgrimes			exit(0);
4741592Srgrimes		}
4751592Srgrimes		(void) close(s);
4761592Srgrimes		(void) close(pv[0]);
4771592Srgrimes		dup2(pv[1], 2);
4781592Srgrimes		close(pv[1]);
4791592Srgrimes	}
48074874Smarkm	else {
48174874Smarkm		pid = fork();
48276094Smarkm		if (pid == -1)
48376134Smarkm			rshd_errx(1, "Can't fork; try again.");
48474874Smarkm		if (pid) {
48574874Smarkm			/* Parent. */
48696195Sdes			while (wait(NULL) > 0 || errno == EINTR)
48796195Sdes				/* nothing */ ;
48874874Smarkm			PAM_END;
48974874Smarkm			exit(0);
49074874Smarkm		}
49174874Smarkm	}
49274874Smarkm
493301242Slidl#ifdef USE_BLACKLIST
494301242Slidl	blacklist(0, STDIN_FILENO, "success");
495301242Slidl#endif
496330322Seadler	closefrom(3);
49796195Sdes	if (setsid() == -1)
49896195Sdes		syslog(LOG_ERR, "setsid() failed: %m");
49996195Sdes	if (setlogin(pwd->pw_name) < 0)
50096195Sdes		syslog(LOG_ERR, "setlogin() failed: %m");
50196195Sdes
5021592Srgrimes	if (*pwd->pw_shell == '\0')
50398885Smarkm		pwd->pw_shell = bshell;
50496195Sdes	(void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
50596195Sdes	(void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
50696195Sdes	(void) pam_setenv(pamh, "USER", pwd->pw_name, 1);
50796195Sdes	(void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
50896195Sdes	environ = pam_getenvlist(pamh);
50996195Sdes	(void) pam_end(pamh, pam_err);
5101592Srgrimes	cp = strrchr(pwd->pw_shell, '/');
5111592Srgrimes	if (cp)
5121592Srgrimes		cp++;
5131592Srgrimes	else
5141592Srgrimes		cp = pwd->pw_shell;
51574874Smarkm
51696195Sdes	if (setusercontext(lc, pwd, pwd->pw_uid,
51796195Sdes		LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) {
51896195Sdes		syslog(LOG_ERR, "setusercontext(): %m");
51925099Sdavidn		exit(1);
52025099Sdavidn	}
52125099Sdavidn	login_close(lc);
5221592Srgrimes	endpwent();
5231592Srgrimes	if (log_success || pwd->pw_uid == 0) {
5241592Srgrimes		    syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
52596195Sdes			ruser, rhost, luser, cmdbuf);
5261592Srgrimes	}
527127864Smux	execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL);
52896195Sdes	err(1, "%s", pwd->pw_shell);
5291592Srgrimes	exit(1);
5301592Srgrimes}
5311592Srgrimes
53276134Smarkm/*
53376134Smarkm * Report error to client.  Note: can't be used until second socket has
53476134Smarkm * connected to client, or older clients will hang waiting for that
53576134Smarkm * connection first.
53676134Smarkm */
53776134Smarkm
53876134Smarkmstatic void
53976134Smarkmrshd_errx(int errcode, const char *fmt, ...)
54076134Smarkm{
54176134Smarkm	va_list ap;
54276134Smarkm
54376134Smarkm	va_start(ap, fmt);
54476134Smarkm
54576134Smarkm	if (sent_null == 0)
54676134Smarkm		write(STDERR_FILENO, "\1", 1);
54776134Smarkm
54876134Smarkm	verrx(errcode, fmt, ap);
54976134Smarkm	/* NOTREACHED */
55076134Smarkm}
55176134Smarkm
5521592Srgrimesvoid
55390335Simpgetstr(char *buf, int cnt, const char *error)
5541592Srgrimes{
5551592Srgrimes	char c;
5561592Srgrimes
5571592Srgrimes	do {
5581592Srgrimes		if (read(STDIN_FILENO, &c, 1) != 1)
5591592Srgrimes			exit(1);
5601592Srgrimes		*buf++ = c;
561301242Slidl		if (--cnt == 0) {
562301242Slidl#ifdef USE_BLACKLIST
563301242Slidl			blacklist(1, STDIN_FILENO, "buffer overflow");
564301242Slidl#endif
56590335Simp			rshd_errx(1, "%s too long", error);
566301242Slidl		}
5671592Srgrimes	} while (c != 0);
5681592Srgrimes}
5691592Srgrimes
5701592Srgrimes/*
5711592Srgrimes * Check whether host h is in our local domain,
5721592Srgrimes * defined as sharing the last two components of the domain part,
5731592Srgrimes * or the entire domain part if the local domain has only one component.
5741592Srgrimes * If either name is unqualified (contains no '.'),
5751592Srgrimes * assume that the host is local, as it will be
5761592Srgrimes * interpreted as such.
5771592Srgrimes */
5781592Srgrimesint
57990334Simplocal_domain(char *h)
5801592Srgrimes{
5811592Srgrimes	char localhost[MAXHOSTNAMELEN];
5821592Srgrimes	char *p1, *p2;
5831592Srgrimes
5841592Srgrimes	localhost[0] = 0;
58545422Sbrian	(void) gethostname(localhost, sizeof(localhost) - 1);
58645422Sbrian	localhost[sizeof(localhost) - 1] = '\0';
58796195Sdes	/* XXX truncation! */
5881592Srgrimes	p1 = topdomain(localhost);
5891592Srgrimes	p2 = topdomain(h);
5901592Srgrimes	if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
5911592Srgrimes		return (1);
5921592Srgrimes	return (0);
5931592Srgrimes}
5941592Srgrimes
5951592Srgrimeschar *
59690334Simptopdomain(char *h)
5971592Srgrimes{
5981592Srgrimes	char *p, *maybe = NULL;
5991592Srgrimes	int dots = 0;
6001592Srgrimes
6011592Srgrimes	for (p = h + strlen(h); p >= h; p--) {
6021592Srgrimes		if (*p == '.') {
6031592Srgrimes			if (++dots == 2)
6041592Srgrimes				return (p);
6051592Srgrimes			maybe = p;
6061592Srgrimes		}
6071592Srgrimes	}
6081592Srgrimes	return (maybe);
6091592Srgrimes}
6101592Srgrimes
6111592Srgrimesvoid
61290334Simpusage(void)
6131592Srgrimes{
6141592Srgrimes
6151592Srgrimes	syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
6161592Srgrimes	exit(2);
6171592Srgrimes}
618