1/*	$NetBSD: rshd.c,v 1.51 2017/10/07 19:23:02 ryo Exp $	*/
2
3/*
4 * Copyright (C) 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *    This product includes software developed by WIDE Project and
18 *    its contributors.
19 * 4. Neither the name of the project nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36/*-
37 * Copyright (c) 1988, 1989, 1992, 1993, 1994
38 *	The Regents of the University of California.  All rights reserved.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 *    notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 *    notice, this list of conditions and the following disclaimer in the
47 *    documentation and/or other materials provided with the distribution.
48 * 3. Neither the name of the University nor the names of its contributors
49 *    may be used to endorse or promote products derived from this software
50 *    without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 */
64
65#include <sys/cdefs.h>
66#ifndef lint
67__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\
68 The Regents of the University of California.  All rights reserved.");
69#if 0
70static char sccsid[] = "@(#)rshd.c	8.2 (Berkeley) 4/6/94";
71#else
72__RCSID("$NetBSD: rshd.c,v 1.51 2017/10/07 19:23:02 ryo Exp $");
73#endif
74#endif /* not lint */
75
76/*
77 * remote shell server:
78 *	[port]\0
79 *	remuser\0
80 *	locuser\0
81 *	command\0
82 *	data
83 */
84#include <sys/param.h>
85#include <sys/ioctl.h>
86#include <sys/time.h>
87#include <sys/socket.h>
88
89#include <netinet/in_systm.h>
90#include <netinet/in.h>
91#include <netinet/ip.h>
92#include <netinet/tcp.h>
93#include <arpa/inet.h>
94#include <netdb.h>
95
96#include <errno.h>
97#include <fcntl.h>
98#include <paths.h>
99#include <pwd.h>
100#include <signal.h>
101#include <stdio.h>
102#include <stdlib.h>
103#include <string.h>
104#include <syslog.h>
105#include <unistd.h>
106#include <poll.h>
107#ifdef  LOGIN_CAP
108#include <login_cap.h>
109#endif
110
111#ifdef USE_PAM
112#include <security/pam_appl.h>
113#include <security/openpam.h>
114#include <sys/wait.h>
115
116static struct pam_conv pamc = { openpam_nullconv, NULL };
117static pam_handle_t *pamh;
118static int pam_err;
119
120#define PAM_END do { \
121	if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
122		syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", \
123		    pam_strerror(pamh, pam_err)); \
124	if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
125		syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", \
126		    pam_strerror(pamh, pam_err)); \
127	if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
128		syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", \
129		    pam_strerror(pamh, pam_err)); \
130} while (/*CONSTCOND*/0)
131#else
132#define PAM_END
133#endif
134
135static int	keepalive = 1;
136static int	check_all;
137static int	log_success;		/* If TRUE, log all successful accesses */
138static int	sent_null;
139
140__dead static void	 doit(struct sockaddr *, struct sockaddr *);
141__dead static void	 rshd_errx(int, const char *, ...) __printflike(2, 3);
142static void	 getstr(char *, int, const char *);
143static int	 local_domain(char *);
144static char	*topdomain(char *);
145__dead static void	 usage(void);
146
147#define	OPTIONS	"aLln"
148extern int __check_rhosts_file;
149extern char *__rcmd_errstr;	/* syslog hook from libc/net/rcmd.c. */
150#ifdef USE_PAM
151static const char incorrect[] = "Login incorrect.";
152#endif
153
154int
155main(int argc, char *argv[])
156{
157	struct linger linger;
158	int ch, on = 1;
159	socklen_t fromlen;
160	socklen_t locallen;
161	struct sockaddr_storage from;
162	struct sockaddr_storage local;
163	struct protoent *proto;
164
165	openlog("rshd", LOG_PID, LOG_DAEMON);
166
167	opterr = 0;
168	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
169		switch (ch) {
170		case 'a':
171			check_all = 1;
172			break;
173		case 'l':
174			__check_rhosts_file = 0;
175			break;
176		case 'n':
177			keepalive = 0;
178			break;
179		case 'L':
180			log_success = 1;
181			break;
182		case '?':
183		default:
184			usage();
185			break;
186		}
187
188	argc -= optind;
189	argv += optind;
190
191	fromlen = sizeof(from); /* xxx */
192	locallen = sizeof(local); /* xxx */
193	if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) {
194		syslog(LOG_ERR, "getpeername: %m");
195		return EXIT_FAILURE;
196	}
197	if (getsockname(STDIN_FILENO, (struct sockaddr *)&local,
198	    &locallen) < 0) {
199		syslog(LOG_ERR, "getsockname: %m");
200		return EXIT_FAILURE;
201	}
202#if 0
203	if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
204	    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr) &&
205	    sizeof(struct sockaddr_in) <= sizeof(from)) {
206		struct sockaddr_in sin;
207		struct sockaddr_in6 *sin6;
208		const int off = sizeof(struct sockaddr_in6) -
209		    sizeof(struct sockaddr_in);
210
211		sin6 = (struct sockaddr_in6 *)&from;
212		(void)memset(&sin, 0, sizeof(sin));
213		sin.sin_family = AF_INET;
214		sin.sin_len = sizeof(struct sockaddr_in);
215		(void)memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[off],
216		    sizeof(sin.sin_addr));
217		(void)memcpy(&from, &sin, sizeof(sin));
218		fromlen = sin.sin_len;
219	}
220#else
221	if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
222	    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) {
223		char hbuf[NI_MAXHOST];
224		if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf,
225				sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
226			strlcpy(hbuf, "invalid", sizeof(hbuf));
227		}
228		syslog(LOG_ERR, "malformed \"from\" address (v4 mapped, %s)",
229		    hbuf);
230		return EXIT_FAILURE;
231	}
232#endif
233	if (keepalive &&
234	    setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
235	    sizeof(on)) < 0)
236		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
237	linger.l_onoff = 1;
238	linger.l_linger = 60;			/* XXX */
239	if (setsockopt(STDIN_FILENO, SOL_SOCKET, SO_LINGER, (char *)&linger,
240	    sizeof (linger)) < 0)
241		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
242	proto = getprotobyname("tcp");
243	(void)setsockopt(STDIN_FILENO, proto->p_proto, TCP_NODELAY, &on,
244	    sizeof(on));
245	doit((struct sockaddr *)&from, (struct sockaddr *)&local);
246}
247
248extern char	**environ;
249
250static void
251doit(struct sockaddr *fromp, struct sockaddr *localp)
252{
253	struct passwd *pwd, pwres;
254	in_port_t port;
255	struct pollfd set[2];
256	int cc, pv[2], pid, s = -1;	/* XXX gcc */
257	int one = 1;
258	char *hostname, *errorhost = NULL;	/* XXX gcc */
259	const char *cp;
260	char sig, buf[BUFSIZ];
261	char cmdbuf[NCARGS+1], locuser[16], remuser[16];
262	char remotehost[2 * MAXHOSTNAMELEN + 1];
263	char hostnamebuf[2 * MAXHOSTNAMELEN + 1];
264#ifdef  LOGIN_CAP
265	login_cap_t *lc;
266#endif
267	char naddr[NI_MAXHOST];
268	char saddr[NI_MAXHOST];
269	char raddr[NI_MAXHOST];
270	char pbuf[NI_MAXSERV];
271	int af = fromp->sa_family;
272	u_int16_t *portp;
273	struct addrinfo hints, *res, *res0;
274	int gaierror;
275	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
276	const char *errormsg = NULL, *errorstr = NULL;
277	char pwbuf[1024];
278
279	(void)signal(SIGINT, SIG_DFL);
280	(void)signal(SIGQUIT, SIG_DFL);
281	(void)signal(SIGTERM, SIG_DFL);
282#ifdef DEBUG
283	{
284		int t = open(_PATH_TTY, O_RDWR);
285		if (t >= 0) {
286			ioctl(t, TIOCNOTTY, NULL);
287			(void)close(t);
288		}
289	}
290#endif
291	switch (af) {
292	case AF_INET:
293		portp = &((struct sockaddr_in *)fromp)->sin_port;
294		break;
295#ifdef INET6
296	case AF_INET6:
297		portp = &((struct sockaddr_in6 *)fromp)->sin6_port;
298		break;
299#endif
300	default:
301		syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
302		exit(EXIT_FAILURE);
303	}
304	if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr),
305			pbuf, sizeof(pbuf), niflags) != 0) {
306		syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
307		exit(EXIT_FAILURE);
308	}
309#ifdef IP_OPTIONS
310	if (af == AF_INET) {
311
312	u_char optbuf[BUFSIZ/3];
313	socklen_t optsize = sizeof(optbuf);
314	int ipproto;
315	unsigned int i;
316	struct protoent *ip;
317
318	if ((ip = getprotobyname("ip")) != NULL)
319		ipproto = ip->p_proto;
320	else
321		ipproto = IPPROTO_IP;
322	if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
323	    optsize != 0) {
324	    	for (i = 0; i < optsize;) {
325			u_char c = optbuf[i];
326			if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
327				syslog(LOG_NOTICE,
328				    "Connection refused from %s "
329				    "with IP option %s",
330				    inet_ntoa((
331				    (struct sockaddr_in *)fromp)->sin_addr),
332				    c == IPOPT_LSRR ? "LSRR" : "SSRR");
333				exit(EXIT_FAILURE);
334			}
335			if (c == IPOPT_EOL)
336				break;
337			i += (c == IPOPT_NOP) ? 1 : optbuf[i + 1];
338		}
339	}
340	}
341#endif
342	if (ntohs(*portp) >= IPPORT_RESERVED
343	    || ntohs(*portp) < IPPORT_RESERVED / 2) {
344		syslog(LOG_NOTICE|LOG_AUTH,
345		    "Connection from %s on illegal port %u",
346		    naddr, ntohs(*portp));
347		exit(EXIT_FAILURE);
348	}
349
350	(void) alarm(60);
351	port = 0;
352	for (;;) {
353		char c;
354
355		if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
356			if (cc < 0)
357				syslog(LOG_ERR, "read: %m");
358			(void)shutdown(0, SHUT_RDWR);
359			exit(EXIT_FAILURE);
360		}
361		if (c == 0)
362			break;
363		port = port * 10 + c - '0';
364	}
365
366	(void) alarm(0);
367	if (port != 0) {
368		int lport = IPPORT_RESERVED - 1;
369		s = rresvport_af_addr(&lport, af, localp);
370		if (s < 0) {
371			syslog(LOG_ERR, "can't get stderr port: %m");
372			exit(EXIT_FAILURE);
373		}
374		if (port >= IPPORT_RESERVED) {
375			syslog(LOG_ERR, "2nd port not reserved");
376			exit(EXIT_FAILURE);
377		}
378		*portp = htons(port);
379		if (connect(s, fromp, fromp->sa_len) < 0) {
380			syslog(LOG_ERR, "connect second port %d: %m", port);
381			exit(EXIT_FAILURE);
382		}
383	}
384
385
386#ifdef notdef
387	/* from inetd, socket is already on 0, 1, 2 */
388	(void)dup2(f, STDIN_FILENO);
389	(void)dup2(f, STDOUT_FILENO);
390	(void)dup2(f, STDERR_FILENO);
391#endif
392	if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr),
393			NULL, 0, NI_NAMEREQD) == 0) {
394		/*
395		 * If name returned by getnameinfo is in our domain,
396		 * attempt to verify that we haven't been fooled by someone
397		 * in a remote net; look up the name and check that this
398		 * address corresponds to the name.
399		 */
400		hostname = saddr;
401		res0 = NULL;
402		if (check_all || local_domain(saddr)) {
403			(void)strlcpy(remotehost, saddr, sizeof(remotehost));
404			errorhost = remotehost;
405			(void)memset(&hints, 0, sizeof(hints));
406			hints.ai_family = fromp->sa_family;
407			hints.ai_socktype = SOCK_STREAM;
408			hints.ai_flags = AI_CANONNAME;
409			gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0);
410			if (gaierror) {
411				syslog(LOG_NOTICE,
412				    "Couldn't look up address for %s: %s",
413				    remotehost, gai_strerror(gaierror));
414				errorstr =
415				"Couldn't look up address for your host (%s)\n";
416				hostname = naddr;
417			} else {
418				for (res = res0; res; res = res->ai_next) {
419					if (res->ai_family != fromp->sa_family)
420						continue;
421					if (res->ai_addrlen != fromp->sa_len)
422						continue;
423					if (getnameinfo(res->ai_addr,
424						res->ai_addrlen,
425						raddr, sizeof(raddr), NULL, 0,
426						niflags) == 0
427					 && strcmp(naddr, raddr) == 0) {
428						hostname = res->ai_canonname
429							? res->ai_canonname
430							: saddr;
431						break;
432					}
433				}
434				if (res == NULL) {
435					syslog(LOG_NOTICE,
436					  "Host addr %s not listed for host %s",
437					    naddr, res0->ai_canonname
438						    ? res0->ai_canonname
439						    : saddr);
440					errorstr =
441					    "Host address mismatch for %s\n";
442					hostname = naddr;
443				}
444			}
445		}
446		(void)strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf));
447		hostname = hostnamebuf;
448		if (res0)
449			freeaddrinfo(res0);
450	} else {
451		(void)strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf));
452		errorhost = hostname = hostnamebuf;
453	}
454
455	(void)alarm(60);
456	getstr(remuser, sizeof(remuser), "remuser");
457	getstr(locuser, sizeof(locuser), "locuser");
458	getstr(cmdbuf, sizeof(cmdbuf), "command");
459	(void)alarm(0);
460
461#ifdef USE_PAM
462	pam_err = pam_start("rsh", locuser, &pamc, &pamh);
463	if (pam_err != PAM_SUCCESS) {
464		syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s",
465		    pam_strerror(pamh, pam_err));
466		rshd_errx(EXIT_FAILURE, incorrect);
467	}
468
469	if ((pam_err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS ||
470	    (pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS){
471		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s",
472		    pam_strerror(pamh, pam_err));
473		rshd_errx(EXIT_FAILURE, incorrect);
474	}
475
476	pam_err = pam_authenticate(pamh, 0);
477	if (pam_err == PAM_SUCCESS) {
478		if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) {
479			(void)strlcpy(locuser, cp, sizeof(locuser));
480			/* XXX truncation! */
481 		}
482		pam_err = pam_acct_mgmt(pamh, 0);
483	}
484	if (pam_err != PAM_SUCCESS) {
485		errorstr = incorrect;
486		errormsg = pam_strerror(pamh, pam_err);
487		goto badlogin;
488 	}
489#endif /* USE_PAM */
490	setpwent();
491	if (getpwnam_r(locuser, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
492	    pwd == NULL) {
493		syslog(LOG_INFO|LOG_AUTH,
494		    "%s@%s as %s: unknown login. cmd='%.80s'",
495		    remuser, hostname, locuser, cmdbuf);
496		if (errorstr == NULL)
497			errorstr = "Permission denied.";
498		rshd_errx(EXIT_FAILURE, errorstr, errorhost);
499	}
500#ifdef LOGIN_CAP
501	lc = login_getclass(pwd ? pwd->pw_class : NULL);
502#endif
503
504	if (chdir(pwd->pw_dir) < 0) {
505		if (chdir("/") < 0
506#ifdef LOGIN_CAP
507		    || login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0)
508#endif
509		) {
510			syslog(LOG_INFO|LOG_AUTH,
511			    "%s@%s as %s: no home directory. cmd='%.80s'",
512			    remuser, hostname, locuser, cmdbuf);
513			rshd_errx(EXIT_SUCCESS, "No remote home directory.");
514		}
515	}
516
517#ifndef USE_PAM
518	if (errorstr ||
519	    (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
520		iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser,
521			locuser) < 0)) {
522		errormsg = __rcmd_errstr ? __rcmd_errstr : "unknown error";
523		if (errorstr == NULL)
524			errorstr = "Permission denied.";
525		goto badlogin;
526	}
527
528	if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK))
529		rshd_errx(EXIT_FAILURE, "Logins currently disabled.");
530#endif
531
532#ifdef LOGIN_CAP
533	/*
534	 * PAM modules might add supplementary groups in
535	 * pam_setcred(), so initialize them first.
536	 * But we need to open the session as root.
537	 */
538	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
539		syslog(LOG_ERR, "setusercontext: %m");
540		exit(EXIT_FAILURE);
541	}
542#else
543	initgroups(pwd->pw_name, pwd->pw_gid);
544#endif
545
546#ifdef USE_PAM
547	if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
548		syslog(LOG_ERR, "pam_open_session: %s",
549		    pam_strerror(pamh, pam_err));
550	} else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED))
551	    != PAM_SUCCESS) {
552		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err));
553	}
554#endif
555
556	(void)write(STDERR_FILENO, "\0", 1);
557	sent_null = 1;
558
559	if (port) {
560		if (pipe(pv) < 0)
561			rshd_errx(EXIT_FAILURE, "Can't make pipe. (%s)",
562			    strerror(errno));
563		pid = fork();
564		if (pid == -1)
565			rshd_errx(EXIT_FAILURE, "Can't fork. (%s)",
566			    strerror(errno));
567		if (pid) {
568			(void)close(STDIN_FILENO);
569			(void)close(STDOUT_FILENO);
570			(void)close(STDERR_FILENO);
571			(void)close(pv[1]);
572
573			set[0].fd = s;
574			set[0].events = POLLIN;
575			set[1].fd = pv[0];
576			set[1].events = POLLIN;
577			ioctl(pv[0], FIONBIO, (char *)&one);
578
579			/* should set s nbio! */
580			do {
581				if (poll(set, 2, INFTIM) < 0)
582					break;
583				if (set[0].revents & POLLIN) {
584					int	ret;
585
586					ret = read(s, &sig, 1);
587					if (ret <= 0)
588						set[0].events = 0;
589					else
590						killpg(pid, sig);
591				}
592				if (set[1].revents & POLLIN) {
593					errno = 0;
594					cc = read(pv[0], buf, sizeof(buf));
595					if (cc <= 0) {
596						shutdown(s, SHUT_RDWR);
597						set[1].events = 0;
598					} else {
599						(void)write(s, buf, cc);
600					}
601				}
602
603			} while ((set[0].revents | set[1].revents) & POLLIN);
604			PAM_END;
605			exit(EXIT_SUCCESS);
606		}
607		(void)close(s);
608		(void)close(pv[0]);
609		(void)dup2(pv[1], STDERR_FILENO);
610		close(pv[1]);
611	}
612#ifdef USE_PAM
613	else {
614		pid = fork();
615		if (pid == -1)
616			rshd_errx(EXIT_FAILURE, "Can't fork. (%s)",
617			    strerror(errno));
618		if (pid) {
619			pid_t xpid;
620			int status;
621			if ((xpid = waitpid(pid, &status, 0)) != pid) {
622				pam_err = pam_close_session(pamh, 0);
623				if (pam_err != PAM_SUCCESS) {
624					syslog(LOG_ERR,
625					    "pam_close_session: %s",
626					    pam_strerror(pamh, pam_err));
627				}
628				PAM_END;
629				if (xpid != -1)
630					syslog(LOG_WARNING,
631					    "wrong PID: %d != %d", pid, xpid);
632				else
633					syslog(LOG_WARNING,
634					    "wait pid=%d failed %m", pid);
635				exit(EXIT_FAILURE);
636			}
637			exit(EXIT_SUCCESS);
638		}
639	}
640#endif
641
642#ifdef F_CLOSEM
643	(void)fcntl(STDERR_FILENO + 1, F_CLOSEM, 0);
644#else
645	for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
646		(void)close(fd);
647#endif
648	if (setsid() == -1)
649		syslog(LOG_ERR, "setsid() failed: %m");
650#ifdef USE_PAM
651	if (setlogin(pwd->pw_name) < 0)
652		syslog(LOG_ERR, "setlogin() failed: %m");
653
654	if (*pwd->pw_shell == '\0')
655		pwd->pw_shell = __UNCONST(_PATH_BSHELL);
656
657	(void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
658	(void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
659	(void)pam_setenv(pamh, "USER", pwd->pw_name, 1);
660	(void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
661	environ = pam_getenvlist(pamh);
662	(void)pam_end(pamh, pam_err);
663#else
664#ifdef LOGIN_CAP
665	{
666		char *sh;
667		if ((sh = login_getcapstr(lc, "shell", NULL, NULL))) {
668			if(!(sh = strdup(sh))) {
669				syslog(LOG_ERR, "Cannot alloc mem");
670				exit(EXIT_FAILURE);
671			}
672			pwd->pw_shell = sh;
673		}
674	}
675#endif
676{
677	static char *envinit[] = { NULL };
678	environ = envinit;
679}
680	setenv("PATH", _PATH_DEFPATH, 1);
681	setenv("HOME", pwd->pw_dir, 1);
682	setenv("SHELL", pwd->pw_shell, 1);
683	setenv("USER", pwd->pw_name, 1);
684#endif
685
686	cp = strrchr(pwd->pw_shell, '/');
687	if (cp)
688		cp++;
689	else
690		cp = pwd->pw_shell;
691
692#ifdef LOGIN_CAP
693	if (setusercontext(lc, pwd, pwd->pw_uid,
694		LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) {
695		syslog(LOG_ERR, "setusercontext(): %m");
696		exit(EXIT_FAILURE);
697	}
698	login_close(lc);
699#else
700	(void)setgid((gid_t)pwd->pw_gid);
701	(void)setuid((uid_t)pwd->pw_uid);
702#endif
703	endpwent();
704	if (log_success || pwd->pw_uid == 0) {
705		syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
706		    remuser, hostname, locuser, cmdbuf);
707	}
708	(void)execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
709	rshd_errx(EXIT_FAILURE, "%s: %s", pwd->pw_shell, strerror(errno));
710badlogin:
711	syslog(LOG_INFO|LOG_AUTH,
712	    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
713	    remuser, hostname, locuser, errormsg, cmdbuf);
714	rshd_errx(EXIT_FAILURE, errorstr, errorhost);
715}
716
717/*
718 * Report error to client.  Note: can't be used until second socket has
719 * connected to client, or older clients will hang waiting for that
720 * connection first.
721 */
722
723#include <stdarg.h>
724
725static void
726rshd_errx(int error, const char *fmt, ...)
727{
728	va_list ap;
729	int len, rv;
730	char *bp, buf[BUFSIZ];
731	va_start(ap, fmt);
732	bp = buf;
733	if (sent_null == 0) {
734		*bp++ = 1;
735		len = 1;
736	} else
737		len = 0;
738	rv = vsnprintf(bp, sizeof(buf) - 2, fmt, ap);
739	bp[rv++] = '\n';
740	(void)write(STDERR_FILENO, buf, len + rv);
741	va_end(ap);
742	exit(error);
743}
744
745static void
746getstr(char *buf, int cnt, const char *err)
747{
748	char c;
749
750	do {
751		if (read(STDIN_FILENO, &c, 1) != 1)
752			exit(EXIT_FAILURE);
753		*buf++ = c;
754		if (--cnt == 0)
755			rshd_errx(EXIT_FAILURE, "%s too long", err);
756	} while (c != 0);
757}
758
759/*
760 * Check whether host h is in our local domain,
761 * defined as sharing the last two components of the domain part,
762 * or the entire domain part if the local domain has only one component.
763 * If either name is unqualified (contains no '.'),
764 * assume that the host is local, as it will be
765 * interpreted as such.
766 */
767static int
768local_domain(char *h)
769{
770	char localhost[MAXHOSTNAMELEN + 1];
771	char *p1, *p2;
772
773	localhost[0] = 0;
774	(void)gethostname(localhost, sizeof(localhost));
775	localhost[sizeof(localhost) - 1] = '\0';
776	p1 = topdomain(localhost);
777	p2 = topdomain(h);
778	if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
779		return (1);
780	return (0);
781}
782
783static char *
784topdomain(char *h)
785{
786	char *p, *maybe = NULL;
787	int dots = 0;
788
789	for (p = h + strlen(h); p >= h; p--) {
790		if (*p == '.') {
791			if (++dots == 2)
792				return (p);
793			maybe = p;
794		}
795	}
796	return (maybe);
797}
798
799static void
800usage(void)
801{
802
803	syslog(LOG_ERR, "Usage: %s [-%s]", getprogname(), OPTIONS);
804	exit(EXIT_FAILURE);
805}
806