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.
201592Srgrimes * 3. All advertising materials mentioning features or use of this software
211592Srgrimes *    must display the following acknowledgement:
221592Srgrimes *	This product includes software developed by the University of
231592Srgrimes *	California, Berkeley and its contributors.
241592Srgrimes * 4. Neither the name of the University nor the names of its contributors
251592Srgrimes *    may be used to endorse or promote products derived from this software
261592Srgrimes *    without specific prior written permission.
271592Srgrimes *
281592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
291592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
301592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
311592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
321592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
331592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
341592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
351592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
361592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
371592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
381592Srgrimes * SUCH DAMAGE.
391592Srgrimes */
401592Srgrimes
411592Srgrimes#ifndef lint
4229917Smarkmstatic const char copyright[] =
431592Srgrimes"@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\
441592Srgrimes	The Regents of the University of California.  All rights reserved.\n";
451592Srgrimes#endif /* not lint */
461592Srgrimes
471592Srgrimes#ifndef lint
4831490Scharnier#if 0
4929917Smarkmstatic const char sccsid[] = "@(#)rshd.c	8.2 (Berkeley) 4/6/94";
5031490Scharnier#endif
511592Srgrimes#endif /* not lint */
521592Srgrimes
5398885Smarkm#include <sys/cdefs.h>
5498885Smarkm__FBSDID("$FreeBSD$");
5598885Smarkm
561592Srgrimes/*
571592Srgrimes * remote shell server:
581592Srgrimes *	[port]\0
5996195Sdes *	ruser\0
6096195Sdes *	luser\0
611592Srgrimes *	command\0
621592Srgrimes *	data
631592Srgrimes */
641592Srgrimes#include <sys/param.h>
651592Srgrimes#include <sys/ioctl.h>
661592Srgrimes#include <sys/time.h>
671592Srgrimes#include <sys/socket.h>
681592Srgrimes
6922454Simp#include <netinet/in_systm.h>
701592Srgrimes#include <netinet/in.h>
7122454Simp#include <netinet/ip.h>
7241445Sdg#include <netinet/tcp.h>
731592Srgrimes#include <arpa/inet.h>
741592Srgrimes#include <netdb.h>
751592Srgrimes
7676094Smarkm#include <err.h>
771592Srgrimes#include <errno.h>
781592Srgrimes#include <fcntl.h>
7945393Sbrian#include <libutil.h>
801592Srgrimes#include <paths.h>
811592Srgrimes#include <pwd.h>
821592Srgrimes#include <signal.h>
8376134Smarkm#include <stdarg.h>
841592Srgrimes#include <stdio.h>
851592Srgrimes#include <stdlib.h>
861592Srgrimes#include <string.h>
871592Srgrimes#include <syslog.h>
881592Srgrimes#include <unistd.h>
8925099Sdavidn#include <login_cap.h>
901592Srgrimes
9174874Smarkm#include <security/pam_appl.h>
9296195Sdes#include <security/openpam.h>
9374874Smarkm#include <sys/wait.h>
9474874Smarkm
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
1341592Srgrimes	openlog("rshd", LOG_PID | LOG_ODELAY, 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;
19496195Sdes	int cc, fd, 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);
25951433Smarkm		exit(1);
26051433Smarkm	}
2611592Srgrimes
2621592Srgrimes	(void) alarm(60);
2631592Srgrimes	port = 0;
26441860Speter	s = 0;		/* not set or used if port == 0 */
2651592Srgrimes	for (;;) {
2661592Srgrimes		char c;
2671592Srgrimes		if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
2681592Srgrimes			if (cc < 0)
2691592Srgrimes				syslog(LOG_NOTICE, "read: %m");
270146074Sjmallett			shutdown(0, SHUT_RDWR);
2711592Srgrimes			exit(1);
2721592Srgrimes		}
27341860Speter		if (c == 0)
2741592Srgrimes			break;
2751592Srgrimes		port = port * 10 + c - '0';
2761592Srgrimes	}
2771592Srgrimes
2781592Srgrimes	(void) alarm(0);
2791592Srgrimes	if (port != 0) {
2801592Srgrimes		int lport = IPPORT_RESERVED - 1;
28156590Sshin		s = rresvport_af(&lport, af);
2821592Srgrimes		if (s < 0) {
2831592Srgrimes			syslog(LOG_ERR, "can't get stderr port: %m");
2841592Srgrimes			exit(1);
2851592Srgrimes		}
28651433Smarkm		if (port >= IPPORT_RESERVED ||
28751433Smarkm		    port < IPPORT_RESERVED/2) {
28851433Smarkm			syslog(LOG_NOTICE|LOG_AUTH,
28951433Smarkm			    "2nd socket from %s on unreserved port %u",
29056590Sshin			    numericname,
29151433Smarkm			    port);
29251433Smarkm			exit(1);
29351433Smarkm		}
29496195Sdes		*((in_port_t *)&fromp->sa_data) = htons(port);
29596195Sdes		if (connect(s, fromp, fromp->sa_len) < 0) {
2961592Srgrimes			syslog(LOG_INFO, "connect second port %d: %m", port);
2971592Srgrimes			exit(1);
2981592Srgrimes		}
2991592Srgrimes	}
3001592Srgrimes
3011592Srgrimes	errorstr = NULL;
30296195Sdes	realhostname_sa(rhost, sizeof(rhost) - 1, fromp, fromp->sa_len);
30396195Sdes	rhost[sizeof(rhost) - 1] = '\0';
30496195Sdes	/* XXX truncation! */
3051592Srgrimes
30696195Sdes	(void) alarm(60);
30796195Sdes	getstr(ruser, sizeof(ruser), "ruser");
30896195Sdes	getstr(luser, sizeof(luser), "luser");
309143907Sdas	getstr(cmdbuf, maxcmdlen, "command");
31096195Sdes	(void) alarm(0);
31174874Smarkm
31296195Sdes	pam_err = pam_start("rsh", luser, &pamc, &pamh);
31396195Sdes	if (pam_err != PAM_SUCCESS) {
31496195Sdes		syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s",
31596195Sdes		    pam_strerror(pamh, pam_err));
31676134Smarkm		rshd_errx(1, "Login incorrect.");
31774874Smarkm	}
31874874Smarkm
31996195Sdes	if ((pam_err = pam_set_item(pamh, PAM_RUSER, ruser)) != PAM_SUCCESS ||
320227208Sbrueffer	    (pam_err = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) {
32196195Sdes		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s",
32296195Sdes		    pam_strerror(pamh, pam_err));
32376134Smarkm		rshd_errx(1, "Login incorrect.");
32474874Smarkm	}
32574874Smarkm
32696195Sdes	pam_err = pam_authenticate(pamh, 0);
32796195Sdes	if (pam_err == PAM_SUCCESS) {
32896195Sdes		if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) {
32996195Sdes			strncpy(luser, cp, sizeof(luser));
33096195Sdes			luser[sizeof(luser) - 1] = '\0';
33196195Sdes			/* XXX truncation! */
33296195Sdes		}
33396195Sdes		pam_err = pam_acct_mgmt(pamh, 0);
33474874Smarkm	}
33596195Sdes	if (pam_err != PAM_SUCCESS) {
33696195Sdes		syslog(LOG_INFO|LOG_AUTH,
33796195Sdes		    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
33896195Sdes		    ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf);
33976134Smarkm		rshd_errx(1, "Login incorrect.");
34074874Smarkm	}
34174874Smarkm
3421592Srgrimes	setpwent();
34396195Sdes	pwd = getpwnam(luser);
3441592Srgrimes	if (pwd == NULL) {
3451592Srgrimes		syslog(LOG_INFO|LOG_AUTH,
3461592Srgrimes		    "%s@%s as %s: unknown login. cmd='%.80s'",
34796195Sdes		    ruser, rhost, luser, cmdbuf);
3481592Srgrimes		if (errorstr == NULL)
34976094Smarkm			errorstr = "Login incorrect.";
35096195Sdes		rshd_errx(1, errorstr, rhost);
3511592Srgrimes	}
35274874Smarkm
35325674Sdavidn	lc = login_getpwclass(pwd);
35474874Smarkm	if (pwd->pw_uid)
35574874Smarkm		auth_checknologin(lc);
35674874Smarkm
3571592Srgrimes	if (chdir(pwd->pw_dir) < 0) {
35825099Sdavidn		if (chdir("/") < 0 ||
35925099Sdavidn		    login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) {
36025099Sdavidn			syslog(LOG_INFO|LOG_AUTH,
36125099Sdavidn			"%s@%s as %s: no home directory. cmd='%.80s'",
36296195Sdes			ruser, rhost, luser, cmdbuf);
36376134Smarkm			rshd_errx(0, "No remote home directory.");
36425099Sdavidn		}
36598885Smarkm		pwd->pw_dir = slash;
3661592Srgrimes	}
3671592Srgrimes
36896195Sdes	if (lc != NULL && fromp->sa_family == AF_INET) {	/*XXX*/
36925099Sdavidn		char	remote_ip[MAXHOSTNAMELEN];
37025099Sdavidn
37156590Sshin		strncpy(remote_ip, numericname,
37225099Sdavidn			sizeof(remote_ip) - 1);
37325099Sdavidn		remote_ip[sizeof(remote_ip) - 1] = 0;
37496195Sdes		/* XXX truncation! */
37596195Sdes		if (!auth_hostok(lc, rhost, remote_ip)) {
37625099Sdavidn			syslog(LOG_INFO|LOG_AUTH,
37725099Sdavidn			    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
37896195Sdes			    ruser, rhost, luser, __rcmd_errstr,
37925099Sdavidn			    cmdbuf);
38076134Smarkm			rshd_errx(1, "Login incorrect.");
38125099Sdavidn		}
38276094Smarkm		if (!auth_timeok(lc, time(NULL)))
38376134Smarkm			rshd_errx(1, "Logins not available right now");
38425099Sdavidn	}
3851592Srgrimes
38674874Smarkm	/*
38774874Smarkm	 * PAM modules might add supplementary groups in
38874874Smarkm	 * pam_setcred(), so initialize them first.
38974874Smarkm	 * But we need to open the session as root.
39074874Smarkm	 */
39174874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
39296195Sdes		syslog(LOG_ERR, "setusercontext: %m");
39374874Smarkm		exit(1);
39474874Smarkm	}
39574874Smarkm
39696195Sdes	if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
39796195Sdes		syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err));
39896195Sdes	} else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
39996195Sdes		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err));
40074874Smarkm	}
40174874Smarkm
4021592Srgrimes	(void) write(STDERR_FILENO, "\0", 1);
4031592Srgrimes	sent_null = 1;
4041592Srgrimes
4051592Srgrimes	if (port) {
40676094Smarkm		if (pipe(pv) < 0)
40776134Smarkm			rshd_errx(1, "Can't make pipe.");
4081592Srgrimes		pid = fork();
40976094Smarkm		if (pid == -1)
41076134Smarkm			rshd_errx(1, "Can't fork; try again.");
4111592Srgrimes		if (pid) {
41298885Smarkm			(void) close(0);
41398885Smarkm			(void) close(1);
4141592Srgrimes			(void) close(2);
4151592Srgrimes			(void) close(pv[1]);
4161592Srgrimes
4171592Srgrimes			FD_ZERO(&readfrom);
4181592Srgrimes			FD_SET(s, &readfrom);
4191592Srgrimes			FD_SET(pv[0], &readfrom);
4201592Srgrimes			if (pv[0] > s)
4211592Srgrimes				nfd = pv[0];
4221592Srgrimes			else
4231592Srgrimes				nfd = s;
4241592Srgrimes				ioctl(pv[0], FIONBIO, (char *)&one);
4251592Srgrimes
4261592Srgrimes			/* should set s nbio! */
4271592Srgrimes			nfd++;
4281592Srgrimes			do {
4291592Srgrimes				ready = readfrom;
43098885Smarkm				if (select(nfd, &ready, (fd_set *)0,
43198885Smarkm				  (fd_set *)0, (struct timeval *)0) < 0)
43298885Smarkm					break;
4331592Srgrimes				if (FD_ISSET(s, &ready)) {
4341592Srgrimes					int	ret;
4351592Srgrimes						ret = read(s, &sig, 1);
43698885Smarkm				if (ret <= 0)
43798885Smarkm					FD_CLR(s, &readfrom);
43898885Smarkm				else
43998885Smarkm					killpg(pid, sig);
4401592Srgrimes				}
4411592Srgrimes				if (FD_ISSET(pv[0], &ready)) {
4421592Srgrimes					errno = 0;
4431592Srgrimes					cc = read(pv[0], buf, sizeof(buf));
4441592Srgrimes					if (cc <= 0) {
445146074Sjmallett						shutdown(s, SHUT_RDWR);
4461592Srgrimes						FD_CLR(pv[0], &readfrom);
4471592Srgrimes					} else {
44898885Smarkm						(void)write(s, buf, cc);
4491592Srgrimes					}
4501592Srgrimes				}
4511592Srgrimes
4521592Srgrimes			} while (FD_ISSET(s, &readfrom) ||
4531592Srgrimes			    FD_ISSET(pv[0], &readfrom));
45474874Smarkm			PAM_END;
4551592Srgrimes			exit(0);
4561592Srgrimes		}
4571592Srgrimes		(void) close(s);
4581592Srgrimes		(void) close(pv[0]);
4591592Srgrimes		dup2(pv[1], 2);
4601592Srgrimes		close(pv[1]);
4611592Srgrimes	}
46274874Smarkm	else {
46374874Smarkm		pid = fork();
46476094Smarkm		if (pid == -1)
46576134Smarkm			rshd_errx(1, "Can't fork; try again.");
46674874Smarkm		if (pid) {
46774874Smarkm			/* Parent. */
46896195Sdes			while (wait(NULL) > 0 || errno == EINTR)
46996195Sdes				/* nothing */ ;
47074874Smarkm			PAM_END;
47174874Smarkm			exit(0);
47274874Smarkm		}
47374874Smarkm	}
47474874Smarkm
47596195Sdes	for (fd = getdtablesize(); fd > 2; fd--)
47696195Sdes		(void) close(fd);
47796195Sdes	if (setsid() == -1)
47896195Sdes		syslog(LOG_ERR, "setsid() failed: %m");
47996195Sdes	if (setlogin(pwd->pw_name) < 0)
48096195Sdes		syslog(LOG_ERR, "setlogin() failed: %m");
48196195Sdes
4821592Srgrimes	if (*pwd->pw_shell == '\0')
48398885Smarkm		pwd->pw_shell = bshell;
48496195Sdes	(void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
48596195Sdes	(void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
48696195Sdes	(void) pam_setenv(pamh, "USER", pwd->pw_name, 1);
48796195Sdes	(void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
48896195Sdes	environ = pam_getenvlist(pamh);
48996195Sdes	(void) pam_end(pamh, pam_err);
4901592Srgrimes	cp = strrchr(pwd->pw_shell, '/');
4911592Srgrimes	if (cp)
4921592Srgrimes		cp++;
4931592Srgrimes	else
4941592Srgrimes		cp = pwd->pw_shell;
49574874Smarkm
49696195Sdes	if (setusercontext(lc, pwd, pwd->pw_uid,
49796195Sdes		LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) {
49896195Sdes		syslog(LOG_ERR, "setusercontext(): %m");
49925099Sdavidn		exit(1);
50025099Sdavidn	}
50125099Sdavidn	login_close(lc);
5021592Srgrimes	endpwent();
5031592Srgrimes	if (log_success || pwd->pw_uid == 0) {
5041592Srgrimes		    syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
50596195Sdes			ruser, rhost, luser, cmdbuf);
5061592Srgrimes	}
507127864Smux	execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL);
50896195Sdes	err(1, "%s", pwd->pw_shell);
5091592Srgrimes	exit(1);
5101592Srgrimes}
5111592Srgrimes
51276134Smarkm/*
51376134Smarkm * Report error to client.  Note: can't be used until second socket has
51476134Smarkm * connected to client, or older clients will hang waiting for that
51576134Smarkm * connection first.
51676134Smarkm */
51776134Smarkm
51876134Smarkmstatic void
51976134Smarkmrshd_errx(int errcode, const char *fmt, ...)
52076134Smarkm{
52176134Smarkm	va_list ap;
52276134Smarkm
52376134Smarkm	va_start(ap, fmt);
52476134Smarkm
52576134Smarkm	if (sent_null == 0)
52676134Smarkm		write(STDERR_FILENO, "\1", 1);
52776134Smarkm
52876134Smarkm	verrx(errcode, fmt, ap);
52976134Smarkm	/* NOTREACHED */
53076134Smarkm}
53176134Smarkm
5321592Srgrimesvoid
53390335Simpgetstr(char *buf, int cnt, const char *error)
5341592Srgrimes{
5351592Srgrimes	char c;
5361592Srgrimes
5371592Srgrimes	do {
5381592Srgrimes		if (read(STDIN_FILENO, &c, 1) != 1)
5391592Srgrimes			exit(1);
5401592Srgrimes		*buf++ = c;
54176125Smarkm		if (--cnt == 0)
54290335Simp			rshd_errx(1, "%s too long", error);
5431592Srgrimes	} while (c != 0);
5441592Srgrimes}
5451592Srgrimes
5461592Srgrimes/*
5471592Srgrimes * Check whether host h is in our local domain,
5481592Srgrimes * defined as sharing the last two components of the domain part,
5491592Srgrimes * or the entire domain part if the local domain has only one component.
5501592Srgrimes * If either name is unqualified (contains no '.'),
5511592Srgrimes * assume that the host is local, as it will be
5521592Srgrimes * interpreted as such.
5531592Srgrimes */
5541592Srgrimesint
55590334Simplocal_domain(char *h)
5561592Srgrimes{
5571592Srgrimes	char localhost[MAXHOSTNAMELEN];
5581592Srgrimes	char *p1, *p2;
5591592Srgrimes
5601592Srgrimes	localhost[0] = 0;
56145422Sbrian	(void) gethostname(localhost, sizeof(localhost) - 1);
56245422Sbrian	localhost[sizeof(localhost) - 1] = '\0';
56396195Sdes	/* XXX truncation! */
5641592Srgrimes	p1 = topdomain(localhost);
5651592Srgrimes	p2 = topdomain(h);
5661592Srgrimes	if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
5671592Srgrimes		return (1);
5681592Srgrimes	return (0);
5691592Srgrimes}
5701592Srgrimes
5711592Srgrimeschar *
57290334Simptopdomain(char *h)
5731592Srgrimes{
5741592Srgrimes	char *p, *maybe = NULL;
5751592Srgrimes	int dots = 0;
5761592Srgrimes
5771592Srgrimes	for (p = h + strlen(h); p >= h; p--) {
5781592Srgrimes		if (*p == '.') {
5791592Srgrimes			if (++dots == 2)
5801592Srgrimes				return (p);
5811592Srgrimes			maybe = p;
5821592Srgrimes		}
5831592Srgrimes	}
5841592Srgrimes	return (maybe);
5851592Srgrimes}
5861592Srgrimes
5871592Srgrimesvoid
58890334Simpusage(void)
5891592Srgrimes{
5901592Srgrimes
5911592Srgrimes	syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
5921592Srgrimes	exit(2);
5931592Srgrimes}
594