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