xref: /netbsd/lib/libc/net/rcmd.c (revision bf9ec67e)
1 /*	$NetBSD: rcmd.c,v 1.48 2002/05/22 14:42:37 tron Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Matthew R. Green.
5  * Copyright (c) 1983, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 #if 0
40 static char sccsid[] = "@(#)rcmd.c	8.3 (Berkeley) 3/26/94";
41 #else
42 __RCSID("$NetBSD: rcmd.c,v 1.48 2002/05/22 14:42:37 tron Exp $");
43 #endif
44 #endif /* LIBC_SCCS and not lint */
45 
46 #ifdef _LIBC
47 #include "namespace.h"
48 #endif
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <sys/stat.h>
52 #include <sys/poll.h>
53 #include <sys/wait.h>
54 
55 #include <netinet/in.h>
56 #include <rpc/rpc.h>
57 #include <arpa/inet.h>
58 #include <netgroup.h>
59 
60 #include <assert.h>
61 #include <ctype.h>
62 #include <err.h>
63 #include <errno.h>
64 #include <fcntl.h>
65 #include <grp.h>
66 #include <netdb.h>
67 #include <paths.h>
68 #include <pwd.h>
69 #include <signal.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <syslog.h>
74 #include <unistd.h>
75 
76 #include "pathnames.h"
77 
78 int	orcmd __P((char **, u_int, const char *, const char *, const char *,
79 	    int *));
80 int	orcmd_af __P((char **, u_int, const char *, const char *, const char *,
81 	    int *, int));
82 int	__ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
83 int	__ivaliduser_sa __P((FILE *, struct sockaddr *, socklen_t, const char *,
84 	    const char *));
85 static	int rshrcmd __P((char **, u_int32_t, const char *, const char *,
86 	    const char *, int *, const char *));
87 static	int resrcmd __P((struct addrinfo *, char **, u_int32_t, const char *,
88 	    const char *, const char *, int *));
89 static	int __icheckhost __P((struct sockaddr *, socklen_t, const char *));
90 static	char *__gethostloop __P((struct sockaddr *, socklen_t));
91 
92 int
93 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
94 	char **ahost;
95 	u_short rport;
96 	const char *locuser, *remuser, *cmd;
97 	int *fd2p;
98 {
99 
100 	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
101 }
102 
103 int
104 rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
105 	char **ahost;
106 	u_short rport;
107 	const char *locuser, *remuser, *cmd;
108 	int *fd2p;
109 	int af;
110 {
111 	static char hbuf[MAXHOSTNAMELEN];
112 	char pbuf[NI_MAXSERV];
113 	struct addrinfo hints, *res;
114 	int error;
115 	struct servent *sp;
116 
117 	_DIAGASSERT(ahost != NULL);
118 	_DIAGASSERT(locuser != NULL);
119 	_DIAGASSERT(remuser != NULL);
120 	_DIAGASSERT(cmd != NULL);
121 	/* fd2p may be NULL */
122 
123 	snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
124 	memset(&hints, 0, sizeof(hints));
125 	hints.ai_family = af;
126 	hints.ai_socktype = SOCK_STREAM;
127 	hints.ai_flags = AI_CANONNAME;
128 	error = getaddrinfo(*ahost, pbuf, &hints, &res);
129 	if (error) {
130 		warnx("%s: %s", *ahost, gai_strerror(error));	/*XXX*/
131 		return (-1);
132 	}
133 	if (res->ai_canonname) {
134 		/*
135 		 * Canonicalise hostname.
136 		 * XXX: Should we really do this?
137 		 */
138 		strncpy(hbuf, res->ai_canonname, sizeof(hbuf) - 1);
139 		hbuf[sizeof(hbuf) - 1] = '\0';
140 		*ahost = hbuf;
141 	}
142 
143 	/*
144 	 * Check if rport is the same as the shell port, and that the fd2p.  If
145 	 * it is not, the program isn't expecting 'rsh' and so we can't use the
146 	 * RCMD_CMD environment.
147 	 */
148 	sp = getservbyname("shell", "tcp");
149 	if (sp != NULL && sp->s_port == rport)
150 		error = rshrcmd(ahost, (u_int32_t)rport,
151 		    locuser, remuser, cmd, fd2p, getenv("RCMD_CMD"));
152 	else
153 		error = resrcmd(res, ahost, (u_int32_t)rport,
154 		    locuser, remuser, cmd, fd2p);
155 	freeaddrinfo(res);
156 	return (error);
157 }
158 
159 /* this is simply a wrapper around hprcmd() that handles ahost first */
160 int
161 orcmd(ahost, rport, locuser, remuser, cmd, fd2p)
162 	char **ahost;
163 	u_int rport;
164 	const char *locuser, *remuser, *cmd;
165 	int *fd2p;
166 {
167 	return orcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
168 }
169 
170 int
171 orcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
172 	char **ahost;
173 	u_int rport;
174 	const char *locuser, *remuser, *cmd;
175 	int *fd2p;
176 	int af;
177 {
178 	static char hbuf[MAXHOSTNAMELEN];
179 	char pbuf[NI_MAXSERV];
180 	struct addrinfo hints, *res;
181 	int error;
182 
183 	_DIAGASSERT(ahost != NULL);
184 	_DIAGASSERT(locuser != NULL);
185 	_DIAGASSERT(remuser != NULL);
186 	_DIAGASSERT(cmd != NULL);
187 	/* fd2p may be NULL */
188 
189 	snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
190 	memset(&hints, 0, sizeof(hints));
191 	hints.ai_family = af;
192 	hints.ai_socktype = SOCK_STREAM;
193 	hints.ai_flags = AI_CANONNAME;
194 	error = getaddrinfo(*ahost, pbuf, &hints, &res);
195 	if (error) {
196 		warnx("%s: %s", *ahost, gai_strerror(error));	/*XXX*/
197 		return (-1);
198 	}
199 	if (res->ai_canonname) {
200 		strncpy(hbuf, res->ai_canonname, sizeof(hbuf) - 1);
201 		hbuf[sizeof(hbuf) - 1] = '\0';
202 		*ahost = hbuf;
203 	}
204 
205 	error = resrcmd(res, ahost, rport, locuser, remuser, cmd, fd2p);
206 	freeaddrinfo(res);
207 	return (error);
208 }
209 
210 /*ARGSUSED*/
211 static int
212 resrcmd(res, ahost, rport, locuser, remuser, cmd, fd2p)
213 	struct addrinfo *res;
214 	char **ahost;
215 	u_int32_t rport;
216 	const char *locuser, *remuser, *cmd;
217 	int *fd2p;
218 {
219 	struct addrinfo *r;
220 	struct sockaddr_storage from;
221 	struct pollfd reads[2];
222 	sigset_t nmask, omask;
223 	pid_t pid;
224 	int s, lport, timo;
225 	int pollr;
226 	char c;
227 	int refused;
228 
229 	_DIAGASSERT(res != NULL);
230 	_DIAGASSERT(ahost != NULL);
231 	_DIAGASSERT(locuser != NULL);
232 	_DIAGASSERT(remuser != NULL);
233 	_DIAGASSERT(cmd != NULL);
234 	/* fd2p may be NULL */
235 
236 	r = res;
237 	refused = 0;
238 	pid = getpid();
239 	sigemptyset(&nmask);
240 	sigaddset(&nmask, SIGURG);
241 	if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)
242 		return -1;
243 	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
244 		s = rresvport_af(&lport, r->ai_family);
245 		if (s < 0) {
246 			if (errno == EAGAIN)
247 				warnx("rcmd: socket: All ports in use");
248 			else
249 				warn("rcmd: socket");
250 			if (r->ai_next) {
251 				r = r->ai_next;
252 				continue;
253 			} else {
254 				(void)sigprocmask(SIG_SETMASK, &omask, NULL);
255 				return (-1);
256 			}
257 		}
258 		fcntl(s, F_SETOWN, pid);
259 		if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
260 			break;
261 		(void)close(s);
262 		if (errno == EADDRINUSE) {
263 			lport--;
264 			continue;
265 		} else if (errno == ECONNREFUSED)
266 			refused++;
267 		if (r->ai_next) {
268 			int oerrno = errno;
269 			char hbuf[NI_MAXHOST];
270 #ifdef NI_WITHSCOPEID
271 			const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
272 #else
273 			const int niflags = NI_NUMERICHOST;
274 #endif
275 
276 			hbuf[0] = '\0';
277 			if (getnameinfo(r->ai_addr, r->ai_addrlen,
278 			    hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
279 				strcpy(hbuf, "(invalid)");
280 			warnx("rcmd: connect to address %s", hbuf);
281 			errno = oerrno;
282 			perror(0);
283 			r = r->ai_next;
284 			hbuf[0] = '\0';
285 			if (getnameinfo(r->ai_addr, r->ai_addrlen,
286 			    hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
287 				strcpy(hbuf, "(invalid)");
288 			(void)fprintf(stderr, "Trying %s...\n", hbuf);
289 			continue;
290 		}
291 		if (refused && timo <= 16) {
292 			(void)sleep((unsigned int)timo);
293 			timo *= 2;
294 			r = res;
295 			refused = 0;
296 			continue;
297 		}
298 		(void)fprintf(stderr, "%s: %s\n", res->ai_canonname,
299 		    strerror(errno));
300 		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
301 		return (-1);
302 	}
303 	lport--;
304 	if (fd2p == 0) {
305 		write(s, "", 1);
306 		lport = 0;
307 	} else {
308 		char num[8];
309 		int s2 = rresvport_af(&lport, r->ai_family), s3;
310 		socklen_t len = sizeof(from);
311 
312 		if (s2 < 0)
313 			goto bad;
314 		listen(s2, 1);
315 		(void)snprintf(num, sizeof(num), "%d", lport);
316 		if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
317 			warn("rcmd: write (setting up stderr)");
318 			(void)close(s2);
319 			goto bad;
320 		}
321 		reads[0].fd = s;
322 		reads[0].events = POLLIN;
323 		reads[1].fd = s2;
324 		reads[1].events = POLLIN;
325 		errno = 0;
326 		pollr = poll(reads, 2, INFTIM);
327 		if (pollr < 1 || (reads[1].revents & POLLIN) == 0) {
328 			if (errno != 0)
329 				warn("poll: setting up stderr");
330 			else
331 				warnx("poll: protocol failure in circuit setup");
332 			(void)close(s2);
333 			goto bad;
334 		}
335 		s3 = accept(s2, (struct sockaddr *)(void *)&from, &len);
336 		(void)close(s2);
337 		if (s3 < 0) {
338 			warn("rcmd: accept");
339 			lport = 0;
340 			goto bad;
341 		}
342 		*fd2p = s3;
343 		switch (((struct sockaddr *)(void *)&from)->sa_family) {
344 		case AF_INET:
345 #ifdef INET6
346 		case AF_INET6:
347 #endif
348 			if (getnameinfo((struct sockaddr *)(void *)&from, len,
349 			    NULL, 0, num, sizeof(num), NI_NUMERICSERV) != 0 ||
350 			    (atoi(num) >= IPPORT_RESERVED ||
351 			     atoi(num) < IPPORT_RESERVED / 2)) {
352 				warnx("rcmd: protocol failure in circuit setup.");
353 				goto bad2;
354 			}
355 			break;
356 		default:
357 			break;
358 		}
359 	}
360 
361 	(void)write(s, locuser, strlen(locuser)+1);
362 	(void)write(s, remuser, strlen(remuser)+1);
363 	(void)write(s, cmd, strlen(cmd)+1);
364 	if (read(s, &c, 1) != 1) {
365 		warn("%s", *ahost);
366 		goto bad2;
367 	}
368 	if (c != 0) {
369 		while (read(s, &c, 1) == 1) {
370 			(void)write(STDERR_FILENO, &c, 1);
371 			if (c == '\n')
372 				break;
373 		}
374 		goto bad2;
375 	}
376 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
377 	return (s);
378 bad2:
379 	if (lport)
380 		(void)close(*fd2p);
381 bad:
382 	(void)close(s);
383 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
384 	return (-1);
385 }
386 
387 /*
388  * based on code written by Chris Siebenmann <cks@utcc.utoronto.ca>
389  */
390 /* ARGSUSED */
391 static int
392 rshrcmd(ahost, rport, locuser, remuser, cmd, fd2p, rshcmd)
393 	char  	**ahost;
394 	u_int32_t	rport;
395 	const	char *locuser, *remuser, *cmd;
396 	int	*fd2p;
397 	const	char *rshcmd;
398 {
399 	pid_t pid;
400 	int sp[2], ep[2];
401 	char *p;
402 	struct passwd *pw;
403 
404 	_DIAGASSERT(ahost != NULL);
405 	_DIAGASSERT(locuser != NULL);
406 	_DIAGASSERT(remuser != NULL);
407 	_DIAGASSERT(cmd != NULL);
408 	/* fd2p may be NULL */
409 
410 	/* What rsh/shell to use. */
411 	if (rshcmd == NULL)
412 		rshcmd = _PATH_BIN_RCMD;
413 
414 	/* locuser must exist on this host. */
415 	if ((pw = getpwnam(locuser)) == NULL) {
416 		warnx("rshrcmd: unknown user: %s", locuser);
417 		return(-1);
418 	}
419 
420 	/* get a socketpair we'll use for stdin and stdout. */
421 	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sp) < 0) {
422 		warn("rshrcmd: socketpair");
423 		return (-1);
424 	}
425 	/* we will use this for the fd2 pointer */
426 	if (fd2p) {
427 		if (socketpair(AF_LOCAL, SOCK_STREAM, 0, ep) < 0) {
428 			warn("rshrcmd: socketpair");
429 			return (-1);
430 		}
431 		*fd2p = ep[0];
432 	}
433 
434 	pid = fork();
435 	if (pid < 0) {
436 		warn("rshrcmd: fork");
437 		return (-1);
438 	}
439 	if (pid == 0) {
440 		/*
441 		 * child
442 		 * - we use sp[1] to be stdin/stdout, and close sp[0]
443 		 * - with fd2p, we use ep[1] for stderr, and close ep[0]
444 		 */
445 		(void)close(sp[0]);
446 		if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) {
447 			warn("rshrcmd: dup2");
448 			_exit(1);
449 		}
450 		if (fd2p) {
451 			if (dup2(ep[1], 2) < 0) {
452 				warn("rshrcmd: dup2");
453 				_exit(1);
454 			}
455 			(void)close(ep[0]);
456 			(void)close(ep[1]);
457 		} else if (dup2(0, 2) < 0) {
458 			warn("rshrcmd: dup2");
459 			_exit(1);
460 		}
461 		/* fork again to lose parent. */
462 		pid = fork();
463 		if (pid < 0) {
464 			warn("rshrcmd: second fork");
465 			_exit(1);
466 		}
467 		if (pid > 0)
468 			_exit(0);
469 
470 		/* Orphan.  Become local user for rshprog. */
471 		if (setuid(pw->pw_uid)) {
472 			warn("rshrcmd: setuid(%lu)", (u_long)pw->pw_uid);
473 			_exit(1);
474 		}
475 
476 		/*
477 		 * If we are rcmd'ing to "localhost" as the same user as we are,
478 		 * then avoid running remote shell for efficiency.
479 		 */
480 		if (strcmp(*ahost, "localhost") == 0 &&
481 		    strcmp(locuser, remuser) == 0) {
482 			if (pw->pw_shell[0] == '\0')
483 				rshcmd = _PATH_BSHELL;
484 			else
485 				rshcmd = pw->pw_shell;
486 			p = strrchr(rshcmd, '/');
487 			execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL);
488 		} else {
489 			p = strrchr(rshcmd, '/');
490 			execlp(rshcmd, p ? p + 1 : rshcmd, *ahost, "-l",
491 			    remuser, cmd, NULL);
492 		}
493 		warn("rshrcmd: exec %s", rshcmd);
494 		_exit(1);
495 	}
496 	/* Parent */
497 	(void)close(sp[1]);
498 	if (fd2p)
499 		(void)close(ep[1]);
500 
501 	(void)waitpid(pid, NULL, 0);
502 	return (sp[0]);
503 }
504 
505 int
506 rresvport(alport)
507 	int *alport;
508 {
509 
510 	_DIAGASSERT(alport != NULL);
511 
512 	return rresvport_af(alport, AF_INET);
513 }
514 
515 int
516 rresvport_af(alport, family)
517 	int *alport;
518 	int family;
519 {
520 	struct sockaddr_storage ss;
521 	struct sockaddr *sa;
522 	int salen;
523 	int s;
524 	u_int16_t *portp;
525 
526 	_DIAGASSERT(alport != NULL);
527 
528 	memset(&ss, 0, sizeof(ss));
529 	sa = (struct sockaddr *)(void *)&ss;
530 	switch (family) {
531 	case AF_INET:
532 #ifdef BSD4_4
533 		sa->sa_len =
534 #endif
535 		salen = sizeof(struct sockaddr_in);
536 		portp = &((struct sockaddr_in *)(void *)sa)->sin_port;
537 		break;
538 #ifdef INET6
539 	case AF_INET6:
540 #ifdef BSD4_4
541 		sa->sa_len =
542 #endif
543 		salen = sizeof(struct sockaddr_in6);
544 		portp = &((struct sockaddr_in6 *)(void *)sa)->sin6_port;
545 		break;
546 #endif
547 	default:
548 		portp = NULL;
549 		return EAFNOSUPPORT;
550 	}
551 	sa->sa_family = family;
552 	s = socket(family, SOCK_STREAM, 0);
553 	if (s < 0)
554 		return (-1);
555 #ifdef BSD4_4
556 	switch (family) {
557 	case AF_INET:
558 	case AF_INET6:
559 		*portp = 0;
560 		if (bindresvport(s, (struct sockaddr_in *)(void *)sa) < 0) {
561 			int sverr = errno;
562 
563 			(void)close(s);
564 			errno = sverr;
565 			return (-1);
566 		}
567 		*alport = (int)ntohs(*portp);
568 		return (s);
569 	default:
570 		/* is it necessary to try keep code for other AFs? */
571 		break;
572 	}
573 #endif
574 	for (;;) {
575 		*portp = htons((u_short)*alport);
576 		if (bind(s, sa, (socklen_t)salen) >= 0)
577 			return (s);
578 		if (errno != EADDRINUSE) {
579 			(void)close(s);
580 			return (-1);
581 		}
582 		(*alport)--;
583 		if (*alport == IPPORT_RESERVED/2) {
584 			(void)close(s);
585 			errno = EAGAIN;		/* close */
586 			return (-1);
587 		}
588 	}
589 }
590 
591 int	__check_rhosts_file = 1;
592 char	*__rcmd_errstr;
593 
594 int
595 ruserok(rhost, superuser, ruser, luser)
596 	const char *rhost, *ruser, *luser;
597 	int superuser;
598 {
599 	struct addrinfo hints, *res, *r;
600 	int error;
601 
602 	_DIAGASSERT(rhost != NULL);
603 	_DIAGASSERT(ruser != NULL);
604 	_DIAGASSERT(luser != NULL);
605 
606 	memset(&hints, 0, sizeof(hints));
607 	hints.ai_family = PF_UNSPEC;
608 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
609 	error = getaddrinfo(rhost, "0", &hints, &res);
610 	if (error)
611 		return (-1);
612 
613 	for (r = res; r; r = r->ai_next) {
614 		if (iruserok_sa(r->ai_addr, (int)r->ai_addrlen, superuser,
615 		    ruser, luser) == 0) {
616 			freeaddrinfo(res);
617 			return (0);
618 		}
619 	}
620 	freeaddrinfo(res);
621 	return (-1);
622 }
623 
624 /*
625  * New .rhosts strategy: We are passed an ip address. We spin through
626  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
627  * has ip addresses, we don't have to trust a nameserver.  When it
628  * contains hostnames, we spin through the list of addresses the nameserver
629  * gives us and look for a match.
630  *
631  * Returns 0 if ok, -1 if not ok.
632  */
633 int
634 iruserok(raddr, superuser, ruser, luser)
635 	u_int32_t raddr;
636 	int superuser;
637 	const char *ruser, *luser;
638 {
639 	struct sockaddr_in irsin;
640 
641 	memset(&irsin, 0, sizeof(irsin));
642 	irsin.sin_family = AF_INET;
643 #ifdef BSD4_4
644 	irsin.sin_len = sizeof(struct sockaddr_in);
645 #endif
646 	memcpy(&irsin.sin_addr, &raddr, sizeof(irsin.sin_addr));
647 	return iruserok_sa(&irsin, sizeof(struct sockaddr_in), superuser, ruser,
648 		    luser);
649 }
650 
651 /*
652  * 2nd and 3rd arguments are typed like this, to avoid dependency between
653  * unistd.h and sys/socket.h.  There's no better way.
654  */
655 int
656 iruserok_sa(raddr, rlen, superuser, ruser, luser)
657 	const void *raddr;
658 	int rlen;
659 	int superuser;
660 	const char *ruser, *luser;
661 {
662 	struct sockaddr *sa;
663 	register char *cp;
664 	struct stat sbuf;
665 	struct passwd *pwd;
666 	FILE *hostf;
667 	uid_t uid;
668 	gid_t gid;
669 	int first;
670 	char pbuf[MAXPATHLEN];
671 
672 	_DIAGASSERT(raddr != NULL);
673 	_DIAGASSERT(ruser != NULL);
674 	_DIAGASSERT(luser != NULL);
675 
676 	/*LINTED const castaway*/
677 	sa = (struct sockaddr *)(void *)raddr;
678 
679 	first = 1;
680 	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
681 again:
682 	if (hostf) {
683 		if (__ivaliduser_sa(hostf, sa, (socklen_t)rlen, luser,
684 		    ruser) == 0) {
685 			(void)fclose(hostf);
686 			return (0);
687 		}
688 		(void)fclose(hostf);
689 	}
690 	if (first == 1 && (__check_rhosts_file || superuser)) {
691 		first = 0;
692 		if ((pwd = getpwnam(luser)) == NULL)
693 			return (-1);
694 		(void)strlcpy(pbuf, pwd->pw_dir, sizeof(pbuf));
695 		(void)strlcat(pbuf, "/.rhosts", sizeof(pbuf));
696 
697 		/*
698 		 * Change effective uid while opening .rhosts.  If root and
699 		 * reading an NFS mounted file system, can't read files that
700 		 * are protected read/write owner only.
701 		 */
702 		uid = geteuid();
703 		gid = getegid();
704 		(void)setegid(pwd->pw_gid);
705 		initgroups(pwd->pw_name, pwd->pw_gid);
706 		(void)seteuid(pwd->pw_uid);
707 		hostf = fopen(pbuf, "r");
708 		(void)seteuid(uid);
709 		(void)setegid(gid);
710 
711 		if (hostf == NULL)
712 			return (-1);
713 		/*
714 		 * If not a regular file, or is owned by someone other than
715 		 * user or root or if writeable by anyone but the owner, quit.
716 		 */
717 		cp = NULL;
718 		if (lstat(pbuf, &sbuf) < 0)
719 			cp = ".rhosts lstat failed";
720 		else if (!S_ISREG(sbuf.st_mode))
721 			cp = ".rhosts not regular file";
722 		else if (fstat(fileno(hostf), &sbuf) < 0)
723 			cp = ".rhosts fstat failed";
724 		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
725 			cp = "bad .rhosts owner";
726 		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
727 			cp = ".rhosts writeable by other than owner";
728 		/* If there were any problems, quit. */
729 		if (cp) {
730 			__rcmd_errstr = cp;
731 			(void)fclose(hostf);
732 			return (-1);
733 		}
734 		goto again;
735 	}
736 	return (-1);
737 }
738 
739 /*
740  * XXX
741  * Don't make static, used by lpd(8).  We will be able to change the function
742  * into static function, when we bump libc major #.
743  *
744  * Returns 0 if ok, -1 if not ok.
745  */
746 #ifdef notdef	/*_LIBC*/
747 static
748 #endif
749 int
750 __ivaliduser(hostf, raddr, luser, ruser)
751 	FILE *hostf;
752 	u_int32_t raddr;
753 	const char *luser, *ruser;
754 {
755 	struct sockaddr_in ivusin;
756 
757 	memset(&ivusin, 0, sizeof(ivusin));
758 	ivusin.sin_family = AF_INET;
759 #ifdef BSD4_4
760 	ivusin.sin_len = sizeof(struct sockaddr_in);
761 #endif
762 	memcpy(&ivusin.sin_addr, &raddr, sizeof(ivusin.sin_addr));
763 	return __ivaliduser_sa(hostf, (struct sockaddr *)(void *)&ivusin,
764 	    sizeof(struct sockaddr_in), luser, ruser);
765 }
766 
767 #ifdef notdef	/*_LIBC*/
768 static
769 #endif
770 int
771 __ivaliduser_sa(hostf, raddr, salen, luser, ruser)
772 	FILE *hostf;
773 	struct sockaddr *raddr;
774 	socklen_t salen;
775 	const char *luser, *ruser;
776 {
777 	register char *user, *p;
778 	int ch;
779 	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
780 	const char *auser, *ahost;
781 	int hostok, userok;
782 	char *rhost = NULL;
783 	int firsttime = 1;
784 	char domain[MAXHOSTNAMELEN];
785 
786 	getdomainname(domain, sizeof(domain));
787 
788 	_DIAGASSERT(hostf != NULL);
789 	_DIAGASSERT(luser != NULL);
790 	_DIAGASSERT(ruser != NULL);
791 
792 	while (fgets(buf, sizeof(buf), hostf)) {
793 		p = buf;
794 		/* Skip lines that are too long. */
795 		if (strchr(p, '\n') == NULL) {
796 			while ((ch = getc(hostf)) != '\n' && ch != EOF)
797 				;
798 			continue;
799 		}
800 		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
801 			*p = isupper((unsigned char)*p) ?
802 			    tolower((unsigned char)*p) : *p;
803 			p++;
804 		}
805 		if (*p == ' ' || *p == '\t') {
806 			*p++ = '\0';
807 			while (*p == ' ' || *p == '\t')
808 				p++;
809 			user = p;
810 			while (*p != '\n' && *p != ' ' &&
811 			    *p != '\t' && *p != '\0')
812 				p++;
813 		} else
814 			user = p;
815 		*p = '\0';
816 
817 		if (p == buf)
818 			continue;
819 
820 		auser = *user ? user : luser;
821 		ahost = buf;
822 
823 		if (ahost[0] == '+')
824 			switch (ahost[1]) {
825 			case '\0':
826 				hostok = 1;
827 				break;
828 
829 			case '@':
830 				if (firsttime) {
831 					rhost = __gethostloop(raddr, salen);
832 					firsttime = 0;
833 				}
834 				if (rhost)
835 					hostok = innetgr(&ahost[2], rhost,
836 					    NULL, domain);
837 				else
838 					hostok = 0;
839 				break;
840 
841 			default:
842 				hostok = __icheckhost(raddr, salen, &ahost[1]);
843 				break;
844 			}
845 		else if (ahost[0] == '-')
846 			switch (ahost[1]) {
847 			case '\0':
848 				hostok = -1;
849 				break;
850 
851 			case '@':
852 				if (firsttime) {
853 					rhost = __gethostloop(raddr, salen);
854 					firsttime = 0;
855 				}
856 				if (rhost)
857 					hostok = -innetgr(&ahost[2], rhost,
858 					    NULL, domain);
859 				else
860 					hostok = 0;
861 				break;
862 
863 			default:
864 				hostok = -__icheckhost(raddr, salen, &ahost[1]);
865 				break;
866 			}
867 		else
868 			hostok = __icheckhost(raddr, salen, ahost);
869 
870 
871 		if (auser[0] == '+')
872 			switch (auser[1]) {
873 			case '\0':
874 				userok = 1;
875 				break;
876 
877 			case '@':
878 				userok = innetgr(&auser[2], NULL, ruser,
879 				    domain);
880 				break;
881 
882 			default:
883 				userok = strcmp(ruser, &auser[1]) == 0;
884 				break;
885 			}
886 		else if (auser[0] == '-')
887 			switch (auser[1]) {
888 			case '\0':
889 				userok = -1;
890 				break;
891 
892 			case '@':
893 				userok = -innetgr(&auser[2], NULL, ruser,
894 				    domain);
895 				break;
896 
897 			default:
898 				userok =
899 				    -(strcmp(ruser, &auser[1]) == 0 ? 1 : 0);
900 				break;
901 			}
902 		else
903 			userok = strcmp(ruser, auser) == 0;
904 
905 		/* Check if one component did not match */
906 		if (hostok == 0 || userok == 0)
907 			continue;
908 
909 		/* Check if we got a forbidden pair */
910 		if (userok == -1 || hostok == -1)
911 			return -1;
912 
913 		/* Check if we got a valid pair */
914 		if (hostok == 1 && userok == 1)
915 			return 0;
916 	}
917 	return -1;
918 }
919 
920 /*
921  * Returns "true" if match, 0 if no match.
922  *
923  * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion
924  * if af == AF_INET6.
925  */
926 static int
927 __icheckhost(raddr, salen, lhost)
928 	struct sockaddr *raddr;
929 	socklen_t salen;
930 	const char *lhost;
931 {
932 	struct addrinfo hints, *res, *r;
933 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
934 	int error;
935 #ifdef NI_WITHSCOPEID
936 	const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
937 #else
938 	const int niflags = NI_NUMERICHOST;
939 #endif
940 
941 	_DIAGASSERT(raddr != NULL);
942 	_DIAGASSERT(lhost != NULL);
943 
944 	h1[0] = '\0';
945 	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
946 	    niflags) != 0)
947 		return (0);
948 
949 	/* Resolve laddr into sockaddr */
950 	memset(&hints, 0, sizeof(hints));
951 	hints.ai_family = raddr->sa_family;
952 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
953 	res = NULL;
954 	error = getaddrinfo(lhost, "0", &hints, &res);
955 	if (error)
956 		return (0);
957 
958 	/*
959 	 * Try string comparisons between raddr and laddr.
960 	 */
961 	for (r = res; r; r = r->ai_next) {
962 		h2[0] = '\0';
963 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
964 		    NULL, 0, niflags) != 0)
965 			continue;
966 		if (strcmp(h1, h2) == 0) {
967 			freeaddrinfo(res);
968 			return (1);
969 		}
970 	}
971 
972 	/* No match. */
973 	freeaddrinfo(res);
974 	return (0);
975 }
976 
977 /*
978  * Return the hostname associated with the supplied address.
979  * Do a reverse lookup as well for security. If a loop cannot
980  * be found, pack the numeric IP address into the string.
981  *
982  * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion
983  * if af == AF_INET6.
984  */
985 static char *
986 __gethostloop(raddr, salen)
987 	struct sockaddr *raddr;
988 	socklen_t salen;
989 {
990 	static char remotehost[NI_MAXHOST];
991 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
992 	struct addrinfo hints, *res, *r;
993 	int error;
994 #ifdef NI_WITHSCOPEID
995 	const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
996 #else
997 	const int niflags = NI_NUMERICHOST;
998 #endif
999 
1000 	_DIAGASSERT(raddr != NULL);
1001 
1002 	h1[0] = remotehost[0] = '\0';
1003 	if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost),
1004 	    NULL, 0, NI_NAMEREQD) != 0)
1005 		return (NULL);
1006 	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
1007 	    niflags) != 0)
1008 		return (NULL);
1009 
1010 	/*
1011 	 * Look up the name and check that the supplied
1012 	 * address is in the list
1013 	 */
1014 	memset(&hints, 0, sizeof(hints));
1015 	hints.ai_family = raddr->sa_family;
1016 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
1017 	hints.ai_flags = AI_CANONNAME;
1018 	res = NULL;
1019 	error = getaddrinfo(remotehost, "0", &hints, &res);
1020 	if (error)
1021 		return (NULL);
1022 
1023 	for (r = res; r; r = r->ai_next) {
1024 		h2[0] = '\0';
1025 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
1026 		    NULL, 0, niflags) != 0)
1027 			continue;
1028 		if (strcmp(h1, h2) == 0) {
1029 			freeaddrinfo(res);
1030 			return (remotehost);
1031 		}
1032 	}
1033 
1034 	/*
1035 	 * either the DNS adminstrator has made a configuration
1036 	 * mistake, or someone has attempted to spoof us
1037 	 */
1038 	syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s",
1039 	    h1, res->ai_canonname ? res->ai_canonname : remotehost);
1040 	freeaddrinfo(res);
1041 	return (NULL);
1042 }
1043