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