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