xref: /netbsd-src/libexec/rshd/rshd.c (revision 9f03e9b6d3abf9149359dc0ed4f05d47d24b12ea)
1*9f03e9b6Sryo /*	$NetBSD: rshd.c,v 1.51 2017/10/07 19:23:02 ryo Exp $	*/
22a5b88bfSitojun 
32a5b88bfSitojun /*
42a5b88bfSitojun  * Copyright (C) 1998 WIDE Project.
52a5b88bfSitojun  * All rights reserved.
62a5b88bfSitojun  *
72a5b88bfSitojun  * Redistribution and use in source and binary forms, with or without
82a5b88bfSitojun  * modification, are permitted provided that the following conditions
92a5b88bfSitojun  * are met:
102a5b88bfSitojun  * 1. Redistributions of source code must retain the above copyright
112a5b88bfSitojun  *    notice, this list of conditions and the following disclaimer.
122a5b88bfSitojun  * 2. Redistributions in binary form must reproduce the above copyright
132a5b88bfSitojun  *    notice, this list of conditions and the following disclaimer in the
142a5b88bfSitojun  *    documentation and/or other materials provided with the distribution.
152a5b88bfSitojun  * 3. All advertising materials mentioning features or use of this software
162a5b88bfSitojun  *    must display the following acknowledgement:
172a5b88bfSitojun  *    This product includes software developed by WIDE Project and
182a5b88bfSitojun  *    its contributors.
192a5b88bfSitojun  * 4. Neither the name of the project nor the names of its contributors
202a5b88bfSitojun  *    may be used to endorse or promote products derived from this software
212a5b88bfSitojun  *    without specific prior written permission.
222a5b88bfSitojun  *
232a5b88bfSitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
242a5b88bfSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
252a5b88bfSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
262a5b88bfSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
272a5b88bfSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
282a5b88bfSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
292a5b88bfSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
302a5b88bfSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
312a5b88bfSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
322a5b88bfSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
332a5b88bfSitojun  * SUCH DAMAGE.
342a5b88bfSitojun  */
35f37be232Smrg 
3661f28255Scgd /*-
372efe726bScgd  * Copyright (c) 1988, 1989, 1992, 1993, 1994
382efe726bScgd  *	The Regents of the University of California.  All rights reserved.
3961f28255Scgd  *
4061f28255Scgd  * Redistribution and use in source and binary forms, with or without
4161f28255Scgd  * modification, are permitted provided that the following conditions
4261f28255Scgd  * are met:
4361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
4461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
4561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
4661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
4761f28255Scgd  *    documentation and/or other materials provided with the distribution.
488e6ab883Sagc  * 3. Neither the name of the University nor the names of its contributors
4961f28255Scgd  *    may be used to endorse or promote products derived from this software
5061f28255Scgd  *    without specific prior written permission.
5161f28255Scgd  *
5261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6261f28255Scgd  * SUCH DAMAGE.
6361f28255Scgd  */
6461f28255Scgd 
65f37be232Smrg #include <sys/cdefs.h>
6661f28255Scgd #ifndef lint
670c4ddb15Slukem __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\
680c4ddb15Slukem  The Regents of the University of California.  All rights reserved.");
69f37be232Smrg #if 0
70f37be232Smrg static char sccsid[] = "@(#)rshd.c	8.2 (Berkeley) 4/6/94";
71f37be232Smrg #else
72*9f03e9b6Sryo __RCSID("$NetBSD: rshd.c,v 1.51 2017/10/07 19:23:02 ryo Exp $");
73f37be232Smrg #endif
7461f28255Scgd #endif /* not lint */
7561f28255Scgd 
7661f28255Scgd /*
7761f28255Scgd  * remote shell server:
7861f28255Scgd  *	[port]\0
7961f28255Scgd  *	remuser\0
8061f28255Scgd  *	locuser\0
8161f28255Scgd  *	command\0
8261f28255Scgd  *	data
8361f28255Scgd  */
8461f28255Scgd #include <sys/param.h>
8561f28255Scgd #include <sys/ioctl.h>
8661f28255Scgd #include <sys/time.h>
8761f28255Scgd #include <sys/socket.h>
882efe726bScgd 
89a2ee5dcfSchristos #include <netinet/in_systm.h>
9061f28255Scgd #include <netinet/in.h>
91a2ee5dcfSchristos #include <netinet/ip.h>
922c4a7474Sjoff #include <netinet/tcp.h>
9361f28255Scgd #include <arpa/inet.h>
9461f28255Scgd #include <netdb.h>
9561f28255Scgd 
9661f28255Scgd #include <errno.h>
972efe726bScgd #include <fcntl.h>
982efe726bScgd #include <paths.h>
992efe726bScgd #include <pwd.h>
1002efe726bScgd #include <signal.h>
10161f28255Scgd #include <stdio.h>
10261f28255Scgd #include <stdlib.h>
10361f28255Scgd #include <string.h>
1042efe726bScgd #include <syslog.h>
1052efe726bScgd #include <unistd.h>
106a9fc5f30Sitojun #include <poll.h>
107c47ddf60Smjl #ifdef  LOGIN_CAP
108c47ddf60Smjl #include <login_cap.h>
109c47ddf60Smjl #endif
11061f28255Scgd 
1116b2a62b7Schristos #ifdef USE_PAM
1126b2a62b7Schristos #include <security/pam_appl.h>
1136b2a62b7Schristos #include <security/openpam.h>
1146b2a62b7Schristos #include <sys/wait.h>
1156b2a62b7Schristos 
1166b2a62b7Schristos static struct pam_conv pamc = { openpam_nullconv, NULL };
1176b2a62b7Schristos static pam_handle_t *pamh;
1186b2a62b7Schristos static int pam_err;
1196b2a62b7Schristos 
120bb8740ddSchristos #define PAM_END do { \
1216b2a62b7Schristos 	if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
1226b2a62b7Schristos 		syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", \
1236b2a62b7Schristos 		    pam_strerror(pamh, pam_err)); \
1246b2a62b7Schristos 	if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
1256b2a62b7Schristos 		syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", \
1266b2a62b7Schristos 		    pam_strerror(pamh, pam_err)); \
1276b2a62b7Schristos 	if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
1286b2a62b7Schristos 		syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", \
1296b2a62b7Schristos 		    pam_strerror(pamh, pam_err)); \
130bb8740ddSchristos } while (/*CONSTCOND*/0)
1316b2a62b7Schristos #else
1326b2a62b7Schristos #define PAM_END
1336b2a62b7Schristos #endif
1346b2a62b7Schristos 
1350c18f61eSjoerg static int	keepalive = 1;
1360c18f61eSjoerg static int	check_all;
1370c18f61eSjoerg static int	log_success;		/* If TRUE, log all successful accesses */
1380c18f61eSjoerg static int	sent_null;
13961f28255Scgd 
1403ec957d5Sdarrenr __dead static void	 doit(struct sockaddr *, struct sockaddr *);
1410c18f61eSjoerg __dead static void	 rshd_errx(int, const char *, ...) __printflike(2, 3);
1420c18f61eSjoerg static void	 getstr(char *, int, const char *);
1430c18f61eSjoerg static int	 local_domain(char *);
1440c18f61eSjoerg static char	*topdomain(char *);
1450c18f61eSjoerg __dead static void	 usage(void);
1462efe726bScgd 
1474b98ca48Swiz #define	OPTIONS	"aLln"
14820d53d39Schristos extern int __check_rhosts_file;
14920d53d39Schristos extern char *__rcmd_errstr;	/* syslog hook from libc/net/rcmd.c. */
150*9f03e9b6Sryo #ifdef USE_PAM
1516b2a62b7Schristos static const char incorrect[] = "Login incorrect.";
152*9f03e9b6Sryo #endif
15361f28255Scgd 
1542efe726bScgd int
main(int argc,char * argv[])15573857257Smjl main(int argc, char *argv[])
15661f28255Scgd {
15761f28255Scgd 	struct linger linger;
1580c37c63eSmrg 	int ch, on = 1;
1590c37c63eSmrg 	socklen_t fromlen;
1603ec957d5Sdarrenr 	socklen_t locallen;
1612a5b88bfSitojun 	struct sockaddr_storage from;
1623ec957d5Sdarrenr 	struct sockaddr_storage local;
1632c4a7474Sjoff 	struct protoent *proto;
16461f28255Scgd 
165d59d820eSlukem 	openlog("rshd", LOG_PID, LOG_DAEMON);
16661f28255Scgd 
16761f28255Scgd 	opterr = 0;
1687c92c8b7Senami 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
16961f28255Scgd 		switch (ch) {
17061f28255Scgd 		case 'a':
17161f28255Scgd 			check_all = 1;
17261f28255Scgd 			break;
17361f28255Scgd 		case 'l':
174f4abfb1fSpk 			__check_rhosts_file = 0;
17561f28255Scgd 			break;
17661f28255Scgd 		case 'n':
17761f28255Scgd 			keepalive = 0;
17861f28255Scgd 			break;
1792b82a536Scgd 		case 'L':
1802efe726bScgd 			log_success = 1;
1812b82a536Scgd 			break;
18261f28255Scgd 		case '?':
18361f28255Scgd 		default:
18461f28255Scgd 			usage();
1852efe726bScgd 			break;
18661f28255Scgd 		}
18761f28255Scgd 
18861f28255Scgd 	argc -= optind;
18961f28255Scgd 	argv += optind;
19061f28255Scgd 
1912a5b88bfSitojun 	fromlen = sizeof(from); /* xxx */
1923ec957d5Sdarrenr 	locallen = sizeof(local); /* xxx */
1936e498d00Schristos 	if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) {
19461f28255Scgd 		syslog(LOG_ERR, "getpeername: %m");
1956e498d00Schristos 		return EXIT_FAILURE;
19661f28255Scgd 	}
1973ec957d5Sdarrenr 	if (getsockname(STDIN_FILENO, (struct sockaddr *)&local,
1983ec957d5Sdarrenr 	    &locallen) < 0) {
1993ec957d5Sdarrenr 		syslog(LOG_ERR, "getsockname: %m");
2003ec957d5Sdarrenr 		return EXIT_FAILURE;
2013ec957d5Sdarrenr 	}
20242a6c723Sitojun #if 0
20342a6c723Sitojun 	if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
20442a6c723Sitojun 	    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr) &&
20542a6c723Sitojun 	    sizeof(struct sockaddr_in) <= sizeof(from)) {
20642a6c723Sitojun 		struct sockaddr_in sin;
20742a6c723Sitojun 		struct sockaddr_in6 *sin6;
20842a6c723Sitojun 		const int off = sizeof(struct sockaddr_in6) -
20942a6c723Sitojun 		    sizeof(struct sockaddr_in);
21042a6c723Sitojun 
21142a6c723Sitojun 		sin6 = (struct sockaddr_in6 *)&from;
2126e498d00Schristos 		(void)memset(&sin, 0, sizeof(sin));
21342a6c723Sitojun 		sin.sin_family = AF_INET;
21442a6c723Sitojun 		sin.sin_len = sizeof(struct sockaddr_in);
2156e498d00Schristos 		(void)memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[off],
21642a6c723Sitojun 		    sizeof(sin.sin_addr));
2176e498d00Schristos 		(void)memcpy(&from, &sin, sizeof(sin));
21842a6c723Sitojun 		fromlen = sin.sin_len;
21942a6c723Sitojun 	}
22042a6c723Sitojun #else
22142a6c723Sitojun 	if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
22242a6c723Sitojun 	    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) {
22342a6c723Sitojun 		char hbuf[NI_MAXHOST];
22442a6c723Sitojun 		if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf,
22542a6c723Sitojun 				sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
2265a8065dfSitojun 			strlcpy(hbuf, "invalid", sizeof(hbuf));
22742a6c723Sitojun 		}
22873857257Smjl 		syslog(LOG_ERR, "malformed \"from\" address (v4 mapped, %s)",
22942a6c723Sitojun 		    hbuf);
2306e498d00Schristos 		return EXIT_FAILURE;
23142a6c723Sitojun 	}
23242a6c723Sitojun #endif
23361f28255Scgd 	if (keepalive &&
2346e498d00Schristos 	    setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
23561f28255Scgd 	    sizeof(on)) < 0)
23661f28255Scgd 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
23761f28255Scgd 	linger.l_onoff = 1;
23861f28255Scgd 	linger.l_linger = 60;			/* XXX */
2396e498d00Schristos 	if (setsockopt(STDIN_FILENO, SOL_SOCKET, SO_LINGER, (char *)&linger,
24061f28255Scgd 	    sizeof (linger)) < 0)
24161f28255Scgd 		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
2422c4a7474Sjoff 	proto = getprotobyname("tcp");
2436e498d00Schristos 	(void)setsockopt(STDIN_FILENO, proto->p_proto, TCP_NODELAY, &on,
2446e498d00Schristos 	    sizeof(on));
2453ec957d5Sdarrenr 	doit((struct sockaddr *)&from, (struct sockaddr *)&local);
24661f28255Scgd }
24761f28255Scgd 
2480c18f61eSjoerg extern char	**environ;
24961f28255Scgd 
2500c18f61eSjoerg static void
doit(struct sockaddr * fromp,struct sockaddr * localp)2513ec957d5Sdarrenr doit(struct sockaddr *fromp, struct sockaddr *localp)
25261f28255Scgd {
2538ec5371fSchristos 	struct passwd *pwd, pwres;
2542beab49aSmrg 	in_port_t port;
25551581bcbSmycroft 	struct pollfd set[2];
25651581bcbSmycroft 	int cc, pv[2], pid, s = -1;	/* XXX gcc */
25761f28255Scgd 	int one = 1;
2586b2a62b7Schristos 	char *hostname, *errorhost = NULL;	/* XXX gcc */
2595dd823abSmycroft 	const char *cp;
2605dd823abSmycroft 	char sig, buf[BUFSIZ];
2612efe726bScgd 	char cmdbuf[NCARGS+1], locuser[16], remuser[16];
26261f28255Scgd 	char remotehost[2 * MAXHOSTNAMELEN + 1];
263770ca3a8Schristos 	char hostnamebuf[2 * MAXHOSTNAMELEN + 1];
264c47ddf60Smjl #ifdef  LOGIN_CAP
265c47ddf60Smjl 	login_cap_t *lc;
266c47ddf60Smjl #endif
2672a5b88bfSitojun 	char naddr[NI_MAXHOST];
2682a5b88bfSitojun 	char saddr[NI_MAXHOST];
2692a5b88bfSitojun 	char raddr[NI_MAXHOST];
2702a5b88bfSitojun 	char pbuf[NI_MAXSERV];
2712a5b88bfSitojun 	int af = fromp->sa_family;
2722a5b88bfSitojun 	u_int16_t *portp;
2732a5b88bfSitojun 	struct addrinfo hints, *res, *res0;
2742a5b88bfSitojun 	int gaierror;
2752a5b88bfSitojun 	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
2766b2a62b7Schristos 	const char *errormsg = NULL, *errorstr = NULL;
2778ec5371fSchristos 	char pwbuf[1024];
27861f28255Scgd 
27961f28255Scgd 	(void)signal(SIGINT, SIG_DFL);
28061f28255Scgd 	(void)signal(SIGQUIT, SIG_DFL);
28161f28255Scgd 	(void)signal(SIGTERM, SIG_DFL);
28261f28255Scgd #ifdef DEBUG
283a2ee5dcfSchristos 	{
2846e498d00Schristos 		int t = open(_PATH_TTY, O_RDWR);
28561f28255Scgd 		if (t >= 0) {
2866e498d00Schristos 			ioctl(t, TIOCNOTTY, NULL);
28761f28255Scgd 			(void)close(t);
28861f28255Scgd 		}
28961f28255Scgd 	}
29061f28255Scgd #endif
2912a5b88bfSitojun 	switch (af) {
2922a5b88bfSitojun 	case AF_INET:
2932a5b88bfSitojun 		portp = &((struct sockaddr_in *)fromp)->sin_port;
2942a5b88bfSitojun 		break;
2952a5b88bfSitojun #ifdef INET6
2962a5b88bfSitojun 	case AF_INET6:
2972a5b88bfSitojun 		portp = &((struct sockaddr_in6 *)fromp)->sin6_port;
2982a5b88bfSitojun 		break;
2992a5b88bfSitojun #endif
3002a5b88bfSitojun 	default:
30173857257Smjl 		syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
3026e498d00Schristos 		exit(EXIT_FAILURE);
3032a5b88bfSitojun 	}
3042a5b88bfSitojun 	if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr),
3052a5b88bfSitojun 			pbuf, sizeof(pbuf), niflags) != 0) {
30673857257Smjl 		syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
3076e498d00Schristos 		exit(EXIT_FAILURE);
30861f28255Scgd 	}
30961f28255Scgd #ifdef IP_OPTIONS
310a2ee5dcfSchristos 	if (af == AF_INET) {
311a2ee5dcfSchristos 
312a2ee5dcfSchristos 	u_char optbuf[BUFSIZ/3];
3130c37c63eSmrg 	socklen_t optsize = sizeof(optbuf);
314024b6bdcSlukem 	int ipproto;
315024b6bdcSlukem 	unsigned int i;
31661f28255Scgd 	struct protoent *ip;
31761f28255Scgd 
31861f28255Scgd 	if ((ip = getprotobyname("ip")) != NULL)
31961f28255Scgd 		ipproto = ip->p_proto;
32061f28255Scgd 	else
32161f28255Scgd 		ipproto = IPPROTO_IP;
32261f28255Scgd 	if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) &&
32361f28255Scgd 	    optsize != 0) {
324a2ee5dcfSchristos 	    	for (i = 0; i < optsize;) {
325a2ee5dcfSchristos 			u_char c = optbuf[i];
326a2ee5dcfSchristos 			if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
32761f28255Scgd 				syslog(LOG_NOTICE,
328a2ee5dcfSchristos 				    "Connection refused from %s "
329a2ee5dcfSchristos 				    "with IP option %s",
330a2ee5dcfSchristos 				    inet_ntoa((
331a2ee5dcfSchristos 				    (struct sockaddr_in *)fromp)->sin_addr),
332a2ee5dcfSchristos 				    c == IPOPT_LSRR ? "LSRR" : "SSRR");
3336e498d00Schristos 				exit(EXIT_FAILURE);
33461f28255Scgd 			}
335a2ee5dcfSchristos 			if (c == IPOPT_EOL)
336a2ee5dcfSchristos 				break;
337a2ee5dcfSchristos 			i += (c == IPOPT_NOP) ? 1 : optbuf[i + 1];
338a2ee5dcfSchristos 		}
33961f28255Scgd 	}
34061f28255Scgd 	}
34161f28255Scgd #endif
3422a5b88bfSitojun 	if (ntohs(*portp) >= IPPORT_RESERVED
3432a5b88bfSitojun 	    || ntohs(*portp) < IPPORT_RESERVED / 2) {
34461f28255Scgd 		syslog(LOG_NOTICE|LOG_AUTH,
3452efe726bScgd 		    "Connection from %s on illegal port %u",
3462a5b88bfSitojun 		    naddr, ntohs(*portp));
3476e498d00Schristos 		exit(EXIT_FAILURE);
34861f28255Scgd 	}
34961f28255Scgd 
35061f28255Scgd 	(void) alarm(60);
35161f28255Scgd 	port = 0;
35261f28255Scgd 	for (;;) {
35361f28255Scgd 		char c;
35486053c56Senami 
3552efe726bScgd 		if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
35661f28255Scgd 			if (cc < 0)
357d59d820eSlukem 				syslog(LOG_ERR, "read: %m");
3586e498d00Schristos 			(void)shutdown(0, SHUT_RDWR);
3596e498d00Schristos 			exit(EXIT_FAILURE);
36061f28255Scgd 		}
36161f28255Scgd 		if (c == 0)
36261f28255Scgd 			break;
36361f28255Scgd 		port = port * 10 + c - '0';
36461f28255Scgd 	}
36561f28255Scgd 
36661f28255Scgd 	(void) alarm(0);
36761f28255Scgd 	if (port != 0) {
36861f28255Scgd 		int lport = IPPORT_RESERVED - 1;
3693ec957d5Sdarrenr 		s = rresvport_af_addr(&lport, af, localp);
37061f28255Scgd 		if (s < 0) {
37161f28255Scgd 			syslog(LOG_ERR, "can't get stderr port: %m");
3726e498d00Schristos 			exit(EXIT_FAILURE);
37361f28255Scgd 		}
37461f28255Scgd 		if (port >= IPPORT_RESERVED) {
37573857257Smjl 			syslog(LOG_ERR, "2nd port not reserved");
3766e498d00Schristos 			exit(EXIT_FAILURE);
37761f28255Scgd 		}
3782a5b88bfSitojun 		*portp = htons(port);
37998d24e6cSginsbach 		if (connect(s, fromp, fromp->sa_len) < 0) {
380d59d820eSlukem 			syslog(LOG_ERR, "connect second port %d: %m", port);
3816e498d00Schristos 			exit(EXIT_FAILURE);
38261f28255Scgd 		}
38361f28255Scgd 	}
38461f28255Scgd 
38561f28255Scgd 
38661f28255Scgd #ifdef notdef
38761f28255Scgd 	/* from inetd, socket is already on 0, 1, 2 */
3886e498d00Schristos 	(void)dup2(f, STDIN_FILENO);
3896e498d00Schristos 	(void)dup2(f, STDOUT_FILENO);
3906e498d00Schristos 	(void)dup2(f, STDERR_FILENO);
39161f28255Scgd #endif
3922a5b88bfSitojun 	if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr),
3932a5b88bfSitojun 			NULL, 0, NI_NAMEREQD) == 0) {
39461f28255Scgd 		/*
3952a5b88bfSitojun 		 * If name returned by getnameinfo is in our domain,
39661f28255Scgd 		 * attempt to verify that we haven't been fooled by someone
39761f28255Scgd 		 * in a remote net; look up the name and check that this
39861f28255Scgd 		 * address corresponds to the name.
39961f28255Scgd 		 */
4002a5b88bfSitojun 		hostname = saddr;
4017b0d3f1dSitojun 		res0 = NULL;
4022a5b88bfSitojun 		if (check_all || local_domain(saddr)) {
4036e498d00Schristos 			(void)strlcpy(remotehost, saddr, sizeof(remotehost));
40461f28255Scgd 			errorhost = remotehost;
4056e498d00Schristos 			(void)memset(&hints, 0, sizeof(hints));
4062a5b88bfSitojun 			hints.ai_family = fromp->sa_family;
4072a5b88bfSitojun 			hints.ai_socktype = SOCK_STREAM;
4082a5b88bfSitojun 			hints.ai_flags = AI_CANONNAME;
4092a5b88bfSitojun 			gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0);
4102a5b88bfSitojun 			if (gaierror) {
411d59d820eSlukem 				syslog(LOG_NOTICE,
4122a5b88bfSitojun 				    "Couldn't look up address for %s: %s",
4132a5b88bfSitojun 				    remotehost, gai_strerror(gaierror));
41461f28255Scgd 				errorstr =
41561f28255Scgd 				"Couldn't look up address for your host (%s)\n";
4162a5b88bfSitojun 				hostname = naddr;
4172a5b88bfSitojun 			} else {
4182a5b88bfSitojun 				for (res = res0; res; res = res->ai_next) {
4192a5b88bfSitojun 					if (res->ai_family != fromp->sa_family)
4202a5b88bfSitojun 						continue;
4212a5b88bfSitojun 					if (res->ai_addrlen != fromp->sa_len)
4222a5b88bfSitojun 						continue;
4232a5b88bfSitojun 					if (getnameinfo(res->ai_addr,
4242a5b88bfSitojun 						res->ai_addrlen,
4252a5b88bfSitojun 						raddr, sizeof(raddr), NULL, 0,
4262a5b88bfSitojun 						niflags) == 0
4272a5b88bfSitojun 					 && strcmp(naddr, raddr) == 0) {
4282a5b88bfSitojun 						hostname = res->ai_canonname
4292a5b88bfSitojun 							? res->ai_canonname
4302a5b88bfSitojun 							: saddr;
4312a5b88bfSitojun 						break;
4322a5b88bfSitojun 					}
4332a5b88bfSitojun 				}
4342a5b88bfSitojun 				if (res == NULL) {
43561f28255Scgd 					syslog(LOG_NOTICE,
43661f28255Scgd 					  "Host addr %s not listed for host %s",
4372a5b88bfSitojun 					    naddr, res0->ai_canonname
4382a5b88bfSitojun 						    ? res0->ai_canonname
4392a5b88bfSitojun 						    : saddr);
44061f28255Scgd 					errorstr =
44161f28255Scgd 					    "Host address mismatch for %s\n";
4422a5b88bfSitojun 					hostname = naddr;
44361f28255Scgd 				}
44461f28255Scgd 			}
44561f28255Scgd 		}
4466e498d00Schristos 		(void)strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf));
44773857257Smjl 		hostname = hostnamebuf;
4487b0d3f1dSitojun 		if (res0)
4497b0d3f1dSitojun 			freeaddrinfo(res0);
4502a5b88bfSitojun 	} else {
4516e498d00Schristos 		(void)strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf));
45273857257Smjl 		errorhost = hostname = hostnamebuf;
4532a5b88bfSitojun 	}
454770ca3a8Schristos 
4556b2a62b7Schristos 	(void)alarm(60);
45661f28255Scgd 	getstr(remuser, sizeof(remuser), "remuser");
45761f28255Scgd 	getstr(locuser, sizeof(locuser), "locuser");
45861f28255Scgd 	getstr(cmdbuf, sizeof(cmdbuf), "command");
4596b2a62b7Schristos 	(void)alarm(0);
4606b2a62b7Schristos 
4616b2a62b7Schristos #ifdef USE_PAM
4626b2a62b7Schristos 	pam_err = pam_start("rsh", locuser, &pamc, &pamh);
4636b2a62b7Schristos 	if (pam_err != PAM_SUCCESS) {
4646b2a62b7Schristos 		syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s",
4656b2a62b7Schristos 		    pam_strerror(pamh, pam_err));
4666e498d00Schristos 		rshd_errx(EXIT_FAILURE, incorrect);
4676b2a62b7Schristos 	}
4686b2a62b7Schristos 
4696b2a62b7Schristos 	if ((pam_err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS ||
4704448e43fSchristos 	    (pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS){
4716b2a62b7Schristos 		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s",
4726b2a62b7Schristos 		    pam_strerror(pamh, pam_err));
4736e498d00Schristos 		rshd_errx(EXIT_FAILURE, incorrect);
4746b2a62b7Schristos 	}
4756b2a62b7Schristos 
4766b2a62b7Schristos 	pam_err = pam_authenticate(pamh, 0);
4776b2a62b7Schristos 	if (pam_err == PAM_SUCCESS) {
4786b2a62b7Schristos 		if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) {
4796e498d00Schristos 			(void)strlcpy(locuser, cp, sizeof(locuser));
4806b2a62b7Schristos 			/* XXX truncation! */
4816b2a62b7Schristos  		}
4826b2a62b7Schristos 		pam_err = pam_acct_mgmt(pamh, 0);
4836b2a62b7Schristos 	}
4846b2a62b7Schristos 	if (pam_err != PAM_SUCCESS) {
4856b2a62b7Schristos 		errorstr = incorrect;
4866b2a62b7Schristos 		errormsg = pam_strerror(pamh, pam_err);
4876b2a62b7Schristos 		goto badlogin;
4886b2a62b7Schristos  	}
4896b2a62b7Schristos #endif /* USE_PAM */
49061f28255Scgd 	setpwent();
491cce62d09Schristos 	if (getpwnam_r(locuser, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
492cce62d09Schristos 	    pwd == NULL) {
4932efe726bScgd 		syslog(LOG_INFO|LOG_AUTH,
4942efe726bScgd 		    "%s@%s as %s: unknown login. cmd='%.80s'",
4952efe726bScgd 		    remuser, hostname, locuser, cmdbuf);
49661f28255Scgd 		if (errorstr == NULL)
4976b2a62b7Schristos 			errorstr = "Permission denied.";
4986e498d00Schristos 		rshd_errx(EXIT_FAILURE, errorstr, errorhost);
49961f28255Scgd 	}
500c47ddf60Smjl #ifdef LOGIN_CAP
501c47ddf60Smjl 	lc = login_getclass(pwd ? pwd->pw_class : NULL);
502c47ddf60Smjl #endif
503c47ddf60Smjl 
50461f28255Scgd 	if (chdir(pwd->pw_dir) < 0) {
5056b2a62b7Schristos 		if (chdir("/") < 0
506c47ddf60Smjl #ifdef LOGIN_CAP
5076b2a62b7Schristos 		    || login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0)
5086b2a62b7Schristos #endif
5096b2a62b7Schristos 		) {
510c47ddf60Smjl 			syslog(LOG_INFO|LOG_AUTH,
511c47ddf60Smjl 			    "%s@%s as %s: no home directory. cmd='%.80s'",
512c47ddf60Smjl 			    remuser, hostname, locuser, cmdbuf);
5136e498d00Schristos 			rshd_errx(EXIT_SUCCESS, "No remote home directory.");
514c47ddf60Smjl 		}
51561f28255Scgd 	}
51661f28255Scgd 
5176b2a62b7Schristos #ifndef USE_PAM
51861f28255Scgd 	if (errorstr ||
519f37be232Smrg 	    (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
5202a5b88bfSitojun 		iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser,
5212a5b88bfSitojun 			locuser) < 0)) {
5226b2a62b7Schristos 		errormsg = __rcmd_errstr ? __rcmd_errstr : "unknown error";
52361f28255Scgd 		if (errorstr == NULL)
5246b2a62b7Schristos 			errorstr = "Permission denied.";
525216d8f75Schristos 		goto badlogin;
52661f28255Scgd 	}
52761f28255Scgd 
5286b2a62b7Schristos 	if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK))
5296e498d00Schristos 		rshd_errx(EXIT_FAILURE, "Logins currently disabled.");
530bb8740ddSchristos #endif
531bb8740ddSchristos 
532bb8740ddSchristos #ifdef LOGIN_CAP
533bb8740ddSchristos 	/*
534bb8740ddSchristos 	 * PAM modules might add supplementary groups in
535bb8740ddSchristos 	 * pam_setcred(), so initialize them first.
536bb8740ddSchristos 	 * But we need to open the session as root.
537bb8740ddSchristos 	 */
538bb8740ddSchristos 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
539bb8740ddSchristos 		syslog(LOG_ERR, "setusercontext: %m");
5406e498d00Schristos 		exit(EXIT_FAILURE);
541bb8740ddSchristos 	}
542bb8740ddSchristos #else
543bb8740ddSchristos 	initgroups(pwd->pw_name, pwd->pw_gid);
544bb8740ddSchristos #endif
545bb8740ddSchristos 
546bb8740ddSchristos #ifdef USE_PAM
547bb8740ddSchristos 	if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
548bb8740ddSchristos 		syslog(LOG_ERR, "pam_open_session: %s",
549bb8740ddSchristos 		    pam_strerror(pamh, pam_err));
550bb8740ddSchristos 	} else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED))
551bb8740ddSchristos 	    != PAM_SUCCESS) {
552bb8740ddSchristos 		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err));
553bb8740ddSchristos 	}
554bb8740ddSchristos #endif
55561f28255Scgd 
5562efe726bScgd 	(void)write(STDERR_FILENO, "\0", 1);
55761f28255Scgd 	sent_null = 1;
55861f28255Scgd 
55961f28255Scgd 	if (port) {
5606b2a62b7Schristos 		if (pipe(pv) < 0)
5616e498d00Schristos 			rshd_errx(EXIT_FAILURE, "Can't make pipe. (%s)",
5626e498d00Schristos 			    strerror(errno));
56361f28255Scgd 		pid = fork();
5646b2a62b7Schristos 		if (pid == -1)
5656e498d00Schristos 			rshd_errx(EXIT_FAILURE, "Can't fork. (%s)",
5666e498d00Schristos 			    strerror(errno));
56761f28255Scgd 		if (pid) {
5686e498d00Schristos 			(void)close(STDIN_FILENO);
5696e498d00Schristos 			(void)close(STDOUT_FILENO);
5706e498d00Schristos 			(void)close(STDERR_FILENO);
5712efe726bScgd 			(void)close(pv[1]);
57261f28255Scgd 
57351581bcbSmycroft 			set[0].fd = s;
57451581bcbSmycroft 			set[0].events = POLLIN;
57551581bcbSmycroft 			set[1].fd = pv[0];
57651581bcbSmycroft 			set[1].events = POLLIN;
57761f28255Scgd 			ioctl(pv[0], FIONBIO, (char *)&one);
57861f28255Scgd 
57961f28255Scgd 			/* should set s nbio! */
58061f28255Scgd 			do {
58151581bcbSmycroft 				if (poll(set, 2, INFTIM) < 0)
58261f28255Scgd 					break;
58351581bcbSmycroft 				if (set[0].revents & POLLIN) {
58461f28255Scgd 					int	ret;
58586053c56Senami 
58661f28255Scgd 					ret = read(s, &sig, 1);
58761f28255Scgd 					if (ret <= 0)
58851581bcbSmycroft 						set[0].events = 0;
58961f28255Scgd 					else
59061f28255Scgd 						killpg(pid, sig);
59161f28255Scgd 				}
59251581bcbSmycroft 				if (set[1].revents & POLLIN) {
59361f28255Scgd 					errno = 0;
59461f28255Scgd 					cc = read(pv[0], buf, sizeof(buf));
59561f28255Scgd 					if (cc <= 0) {
596d59d820eSlukem 						shutdown(s, SHUT_RDWR);
59751581bcbSmycroft 						set[1].events = 0;
59861f28255Scgd 					} else {
59986053c56Senami 						(void)write(s, buf, cc);
60061f28255Scgd 					}
60161f28255Scgd 				}
60261f28255Scgd 
60351581bcbSmycroft 			} while ((set[0].revents | set[1].revents) & POLLIN);
604bb8740ddSchristos 			PAM_END;
6056e498d00Schristos 			exit(EXIT_SUCCESS);
60661f28255Scgd 		}
6072efe726bScgd 		(void)close(s);
6082efe726bScgd 		(void)close(pv[0]);
6096e498d00Schristos 		(void)dup2(pv[1], STDERR_FILENO);
61061f28255Scgd 		close(pv[1]);
61161f28255Scgd 	}
6126b2a62b7Schristos #ifdef USE_PAM
6136b2a62b7Schristos 	else {
6146b2a62b7Schristos 		pid = fork();
6156b2a62b7Schristos 		if (pid == -1)
6166e498d00Schristos 			rshd_errx(EXIT_FAILURE, "Can't fork. (%s)",
6176e498d00Schristos 			    strerror(errno));
6186b2a62b7Schristos 		if (pid) {
619bb8740ddSchristos 			pid_t xpid;
620bb8740ddSchristos 			int status;
621bb8740ddSchristos 			if ((xpid = waitpid(pid, &status, 0)) != pid) {
622bb8740ddSchristos 				pam_err = pam_close_session(pamh, 0);
623bb8740ddSchristos 				if (pam_err != PAM_SUCCESS) {
624bb8740ddSchristos 					syslog(LOG_ERR,
625bb8740ddSchristos 					    "pam_close_session: %s",
626bb8740ddSchristos 					    pam_strerror(pamh, pam_err));
627bb8740ddSchristos 				}
628bb8740ddSchristos 				PAM_END;
629bb8740ddSchristos 				if (xpid != -1)
630bb8740ddSchristos 					syslog(LOG_WARNING,
631bb8740ddSchristos 					    "wrong PID: %d != %d", pid, xpid);
632bb8740ddSchristos 				else
633bb8740ddSchristos 					syslog(LOG_WARNING,
634bb8740ddSchristos 					    "wait pid=%d failed %m", pid);
6356e498d00Schristos 				exit(EXIT_FAILURE);
636bb8740ddSchristos 			}
6376e498d00Schristos 			exit(EXIT_SUCCESS);
6386b2a62b7Schristos 		}
6396b2a62b7Schristos 	}
6406b2a62b7Schristos #endif
641c47ddf60Smjl 
6426b2a62b7Schristos #ifdef F_CLOSEM
6436e498d00Schristos 	(void)fcntl(STDERR_FILENO + 1, F_CLOSEM, 0);
6446b2a62b7Schristos #else
6456e498d00Schristos 	for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
6466b2a62b7Schristos 		(void)close(fd);
6476b2a62b7Schristos #endif
6486b2a62b7Schristos 	if (setsid() == -1)
6496b2a62b7Schristos 		syslog(LOG_ERR, "setsid() failed: %m");
6506b2a62b7Schristos #ifdef USE_PAM
6516b2a62b7Schristos 	if (setlogin(pwd->pw_name) < 0)
6526b2a62b7Schristos 		syslog(LOG_ERR, "setlogin() failed: %m");
653c47ddf60Smjl 
654bb8740ddSchristos 	if (*pwd->pw_shell == '\0')
6556e498d00Schristos 		pwd->pw_shell = __UNCONST(_PATH_BSHELL);
656bb8740ddSchristos 
6576b2a62b7Schristos 	(void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
6586b2a62b7Schristos 	(void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
6596b2a62b7Schristos 	(void)pam_setenv(pamh, "USER", pwd->pw_name, 1);
6606b2a62b7Schristos 	(void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
6616b2a62b7Schristos 	environ = pam_getenvlist(pamh);
6626b2a62b7Schristos 	(void)pam_end(pamh, pam_err);
663bb8740ddSchristos #else
6646b2a62b7Schristos #ifdef LOGIN_CAP
665bb8740ddSchristos 	{
666bb8740ddSchristos 		char *sh;
667c47ddf60Smjl 		if ((sh = login_getcapstr(lc, "shell", NULL, NULL))) {
668c47ddf60Smjl 			if(!(sh = strdup(sh))) {
669d59d820eSlukem 				syslog(LOG_ERR, "Cannot alloc mem");
6706e498d00Schristos 				exit(EXIT_FAILURE);
671c47ddf60Smjl 			}
672c47ddf60Smjl 			pwd->pw_shell = sh;
673c47ddf60Smjl 		}
674bb8740ddSchristos 	}
675c47ddf60Smjl #endif
6760c18f61eSjoerg {
6770c18f61eSjoerg 	static char *envinit[] = { NULL };
67861f28255Scgd 	environ = envinit;
6790c18f61eSjoerg }
6800c18f61eSjoerg 	setenv("PATH", _PATH_DEFPATH, 1);
6810c18f61eSjoerg 	setenv("HOME", pwd->pw_dir, 1);
6820c18f61eSjoerg 	setenv("SHELL", pwd->pw_shell, 1);
6830c18f61eSjoerg 	setenv("USER", pwd->pw_name, 1);
6846b2a62b7Schristos #endif
6856b2a62b7Schristos 
686c47ddf60Smjl 	cp = strrchr(pwd->pw_shell, '/');
687c47ddf60Smjl 	if (cp)
688c47ddf60Smjl 		cp++;
689c47ddf60Smjl 	else
690c47ddf60Smjl 		cp = pwd->pw_shell;
6916b2a62b7Schristos 
6926b2a62b7Schristos #ifdef LOGIN_CAP
6936b2a62b7Schristos 	if (setusercontext(lc, pwd, pwd->pw_uid,
6946b2a62b7Schristos 		LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) {
6956b2a62b7Schristos 		syslog(LOG_ERR, "setusercontext(): %m");
6966e498d00Schristos 		exit(EXIT_FAILURE);
69761f28255Scgd 	}
6986b2a62b7Schristos 	login_close(lc);
6996b2a62b7Schristos #else
7006b2a62b7Schristos 	(void)setgid((gid_t)pwd->pw_gid);
7016b2a62b7Schristos 	(void)setuid((uid_t)pwd->pw_uid);
7026b2a62b7Schristos #endif
7036b2a62b7Schristos 	endpwent();
7046b2a62b7Schristos 	if (log_success || pwd->pw_uid == 0) {
7056b2a62b7Schristos 		syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
7066b2a62b7Schristos 		    remuser, hostname, locuser, cmdbuf);
7076b2a62b7Schristos 	}
7086e498d00Schristos 	(void)execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
7096e498d00Schristos 	rshd_errx(EXIT_FAILURE, "%s: %s", pwd->pw_shell, strerror(errno));
7106b2a62b7Schristos badlogin:
7116b2a62b7Schristos 	syslog(LOG_INFO|LOG_AUTH,
7126b2a62b7Schristos 	    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
7136b2a62b7Schristos 	    remuser, hostname, locuser, errormsg, cmdbuf);
7146e498d00Schristos 	rshd_errx(EXIT_FAILURE, errorstr, errorhost);
7156b2a62b7Schristos }
71661f28255Scgd 
71761f28255Scgd /*
7182efe726bScgd  * Report error to client.  Note: can't be used until second socket has
7192efe726bScgd  * connected to client, or older clients will hang waiting for that
7202efe726bScgd  * connection first.
72161f28255Scgd  */
72261f28255Scgd 
72373857257Smjl #include <stdarg.h>
72473857257Smjl 
7250c18f61eSjoerg static void
rshd_errx(int error,const char * fmt,...)7266b2a62b7Schristos rshd_errx(int error, const char *fmt, ...)
7272efe726bScgd {
7282efe726bScgd 	va_list ap;
7296b2a62b7Schristos 	int len, rv;
7302efe726bScgd 	char *bp, buf[BUFSIZ];
7312efe726bScgd 	va_start(ap, fmt);
7322efe726bScgd 	bp = buf;
7332efe726bScgd 	if (sent_null == 0) {
73461f28255Scgd 		*bp++ = 1;
7352efe726bScgd 		len = 1;
7362efe726bScgd 	} else
7372efe726bScgd 		len = 0;
7386b2a62b7Schristos 	rv = vsnprintf(bp, sizeof(buf) - 2, fmt, ap);
7396b2a62b7Schristos 	bp[rv++] = '\n';
7406b2a62b7Schristos 	(void)write(STDERR_FILENO, buf, len + rv);
7414c999163Swiz 	va_end(ap);
7426b2a62b7Schristos 	exit(error);
74361f28255Scgd }
74461f28255Scgd 
7450c18f61eSjoerg static void
getstr(char * buf,int cnt,const char * err)7466e498d00Schristos getstr(char *buf, int cnt, const char *err)
74761f28255Scgd {
74861f28255Scgd 	char c;
74961f28255Scgd 
75061f28255Scgd 	do {
7512efe726bScgd 		if (read(STDIN_FILENO, &c, 1) != 1)
7526e498d00Schristos 			exit(EXIT_FAILURE);
75361f28255Scgd 		*buf++ = c;
7546b2a62b7Schristos 		if (--cnt == 0)
7556e498d00Schristos 			rshd_errx(EXIT_FAILURE, "%s too long", err);
75661f28255Scgd 	} while (c != 0);
75761f28255Scgd }
75861f28255Scgd 
75961f28255Scgd /*
76061f28255Scgd  * Check whether host h is in our local domain,
76161f28255Scgd  * defined as sharing the last two components of the domain part,
76261f28255Scgd  * or the entire domain part if the local domain has only one component.
76361f28255Scgd  * If either name is unqualified (contains no '.'),
76461f28255Scgd  * assume that the host is local, as it will be
76561f28255Scgd  * interpreted as such.
76661f28255Scgd  */
7670c18f61eSjoerg static int
local_domain(char * h)76873857257Smjl local_domain(char *h)
76961f28255Scgd {
7702beab49aSmrg 	char localhost[MAXHOSTNAMELEN + 1];
7712efe726bScgd 	char *p1, *p2;
77261f28255Scgd 
77361f28255Scgd 	localhost[0] = 0;
77461f28255Scgd 	(void)gethostname(localhost, sizeof(localhost));
7752beab49aSmrg 	localhost[sizeof(localhost) - 1] = '\0';
77661f28255Scgd 	p1 = topdomain(localhost);
77761f28255Scgd 	p2 = topdomain(h);
77861f28255Scgd 	if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
77961f28255Scgd 		return (1);
78061f28255Scgd 	return (0);
78161f28255Scgd }
78261f28255Scgd 
7830c18f61eSjoerg static char *
topdomain(char * h)78473857257Smjl topdomain(char *h)
78561f28255Scgd {
7862efe726bScgd 	char *p, *maybe = NULL;
78761f28255Scgd 	int dots = 0;
78861f28255Scgd 
78961f28255Scgd 	for (p = h + strlen(h); p >= h; p--) {
79061f28255Scgd 		if (*p == '.') {
79161f28255Scgd 			if (++dots == 2)
79261f28255Scgd 				return (p);
79361f28255Scgd 			maybe = p;
79461f28255Scgd 		}
79561f28255Scgd 	}
79661f28255Scgd 	return (maybe);
79761f28255Scgd }
79861f28255Scgd 
7990c18f61eSjoerg static void
usage(void)80073857257Smjl usage(void)
80161f28255Scgd {
8022efe726bScgd 
8036e498d00Schristos 	syslog(LOG_ERR, "Usage: %s [-%s]", getprogname(), OPTIONS);
8046e498d00Schristos 	exit(EXIT_FAILURE);
80561f28255Scgd }
806