xref: /netbsd-src/lib/libc/net/rcmd.c (revision d896261208b4610b0c4e86ea95caad3cdc86a4ac)
1 /*	$NetBSD: rcmd.c,v 1.34 1999/09/20 04:39:16 lukem 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.34 1999/09/20 04:39:16 lukem Exp $");
43 #endif
44 #endif /* LIBC_SCCS and not lint */
45 
46 #include "namespace.h"
47 #include <sys/param.h>
48 #include <sys/socket.h>
49 #include <sys/stat.h>
50 #include <sys/poll.h>
51 #include <sys/wait.h>
52 
53 #include <netinet/in.h>
54 #include <rpc/rpc.h>
55 #include <arpa/inet.h>
56 #include <netgroup.h>
57 
58 #include <assert.h>
59 #include <ctype.h>
60 #include <err.h>
61 #include <errno.h>
62 #include <fcntl.h>
63 #include <grp.h>
64 #include <netdb.h>
65 #include <paths.h>
66 #include <pwd.h>
67 #include <signal.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <syslog.h>
72 #include <unistd.h>
73 
74 #include "pathnames.h"
75 
76 int	orcmd __P((char **, u_int, const char *, const char *, const char *,
77 	    int *));
78 int	__ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
79 static	int rshrcmd __P((char **, u_int32_t, const char *, const char *,
80 	    const char *, int *, const char *));
81 static	int hprcmd __P((struct hostent *, char **, u_int32_t, const char *,
82 	    const char *, const char *, int *));
83 static	int __icheckhost __P((u_int32_t, const char *));
84 static	char *__gethostloop __P((u_int32_t));
85 
86 int
87 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
88 	char **ahost;
89 	u_short rport;
90 	const char *locuser, *remuser, *cmd;
91 	int *fd2p;
92 {
93 	struct hostent *hp;
94 	struct servent *sp;
95 
96 	_DIAGASSERT(ahost != NULL);
97 	_DIAGASSERT(locuser != NULL);
98 	_DIAGASSERT(remuser != NULL);
99 	_DIAGASSERT(cmd != NULL);
100 	/* fd2p may be NULL */
101 
102 	/*
103 	 * Canonicalise hostname.
104 	 * XXX: Should we really do this?
105 	 */
106 	hp = gethostbyname(*ahost);
107 	if (hp == NULL) {
108 		herror(*ahost);
109 		return (-1);
110 	}
111 	*ahost = hp->h_name;
112 
113 	/*
114 	 * Check if rport is the same as the shell port, and that the fd2p.  If
115 	 * it is not, the program isn't expecting 'rsh' and so we can't use the
116 	 * RCMD_CMD environment.
117 	 */
118 	sp = getservbyname("shell", "tcp");
119 	if (sp != NULL && sp->s_port == rport)
120 		return (rshrcmd(ahost, (u_int32_t)rport,
121 		    locuser, remuser, cmd, fd2p, getenv("RCMD_CMD")));
122 	else
123 		return (hprcmd(hp, ahost, (u_int32_t)rport,
124 		    locuser, remuser, cmd, fd2p));
125 }
126 
127 /* this is simply a wrapper around hprcmd() that handles ahost first */
128 int
129 orcmd(ahost, rport, locuser, remuser, cmd, fd2p)
130 	char **ahost;
131 	u_int rport;
132 	const char *locuser, *remuser, *cmd;
133 	int *fd2p;
134 {
135 	struct hostent *hp;
136 
137 	_DIAGASSERT(ahost != NULL);
138 	_DIAGASSERT(locuser != NULL);
139 	_DIAGASSERT(remuser != NULL);
140 	_DIAGASSERT(cmd != NULL);
141 	/* fd2p may be NULL */
142 
143 	hp = gethostbyname(*ahost);
144 	if (hp == NULL) {
145 		herror(*ahost);
146 		return (-1);
147 	}
148 	*ahost = hp->h_name;
149 
150 	return (hprcmd(hp, ahost, rport, locuser, remuser, cmd, fd2p));
151 }
152 
153 static int
154 hprcmd(hp, ahost, rport, locuser, remuser, cmd, fd2p)
155 	struct hostent *hp;
156 	char **ahost;
157 	u_int32_t rport;
158 	const char *locuser, *remuser, *cmd;
159 	int *fd2p;
160 {
161 	struct sockaddr_in sin, from;
162 	struct pollfd reads[2];
163 	sigset_t nmask, omask;
164 	pid_t pid;
165 	int s, lport, timo;
166 	int pollr;
167 	char c;
168 
169 	_DIAGASSERT(hp != NULL);
170 	_DIAGASSERT(ahost != NULL);
171 	_DIAGASSERT(locuser != NULL);
172 	_DIAGASSERT(remuser != NULL);
173 	_DIAGASSERT(cmd != NULL);
174 	/* fd2p may be NULL */
175 
176 	pid = getpid();
177 	sigemptyset(&nmask);
178 	sigaddset(&nmask, SIGURG);
179 	if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)
180 		return -1;
181 	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
182 		s = rresvport(&lport);
183 		if (s < 0) {
184 			if (errno == EAGAIN)
185 				warnx("rcmd: socket: All ports in use");
186 			else
187 				warn("rcmd: socket");
188 			(void)sigprocmask(SIG_SETMASK, &omask, NULL);
189 			return (-1);
190 		}
191 		fcntl(s, F_SETOWN, pid);
192 #ifdef BSD4_4
193 		sin.sin_len = sizeof(struct sockaddr_in);
194 #endif
195 		sin.sin_family = hp->h_addrtype;
196 		sin.sin_port = rport;
197 		memmove(&sin.sin_addr,
198 		    hp->h_addr_list[0], (size_t)hp->h_length);
199 		if (connect(s, (struct sockaddr *)(void *)&sin, sizeof(sin)) >= 0)
200 			break;
201 		(void)close(s);
202 		if (errno == EADDRINUSE) {
203 			lport--;
204 			continue;
205 		}
206 		if (errno == ECONNREFUSED && timo <= 16) {
207 			(void)sleep((unsigned int)timo);
208 			timo *= 2;
209 			continue;
210 		}
211 		if (hp->h_addr_list[1] != NULL) {
212 			int oerrno = errno;
213 
214 			warnx("rcmd: connect to address %s",
215 			    inet_ntoa(sin.sin_addr));
216 			errno = oerrno;
217 			perror(0);
218 			hp->h_addr_list++;
219 			memmove(&sin.sin_addr, hp->h_addr_list[0],
220 			    (size_t)hp->h_length);
221 			(void)fprintf(stderr, "Trying %s...\n",
222 			    inet_ntoa(sin.sin_addr));
223 			continue;
224 		}
225 		(void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno));
226 		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
227 		return (-1);
228 	}
229 	lport--;
230 	if (fd2p == 0) {
231 		write(s, "", 1);
232 		lport = 0;
233 	} else {
234 		char num[8];
235 		int s2 = rresvport(&lport), s3;
236 		socklen_t len = sizeof(from);
237 
238 		if (s2 < 0)
239 			goto bad;
240 		listen(s2, 1);
241 		(void)snprintf(num, sizeof(num), "%d", lport);
242 		if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
243 			warn("rcmd: write (setting up stderr)");
244 			(void)close(s2);
245 			goto bad;
246 		}
247 		reads[0].fd = s;
248 		reads[0].events = POLLIN;
249 		reads[1].fd = s2;
250 		reads[1].events = POLLIN;
251 		errno = 0;
252 		pollr = poll(reads, 2, INFTIM);
253 		if (pollr < 1 || (reads[1].revents & POLLIN) == 0) {
254 			if (errno != 0)
255 				warn("poll: setting up stderr");
256 			else
257 				warnx("poll: protocol failure in circuit setup");
258 			(void)close(s2);
259 			goto bad;
260 		}
261 		s3 = accept(s2, (struct sockaddr *)(void *)&from, &len);
262 		(void)close(s2);
263 		if (s3 < 0) {
264 			warn("rcmd: accept");
265 			lport = 0;
266 			goto bad;
267 		}
268 		*fd2p = s3;
269 		from.sin_port = ntohs(from.sin_port);
270 		if (from.sin_family != AF_INET ||
271 		    from.sin_port >= IPPORT_RESERVED ||
272 		    from.sin_port < IPPORT_RESERVED / 2) {
273 			warnx("rcmd: protocol failure in circuit setup.");
274 			goto bad2;
275 		}
276 	}
277 
278 	(void)write(s, locuser, strlen(locuser)+1);
279 	(void)write(s, remuser, strlen(remuser)+1);
280 	(void)write(s, cmd, strlen(cmd)+1);
281 	if (read(s, &c, 1) != 1) {
282 		warn("%s", *ahost);
283 		goto bad2;
284 	}
285 	if (c != 0) {
286 		while (read(s, &c, 1) == 1) {
287 			(void)write(STDERR_FILENO, &c, 1);
288 			if (c == '\n')
289 				break;
290 		}
291 		goto bad2;
292 	}
293 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
294 	return (s);
295 bad2:
296 	if (lport)
297 		(void)close(*fd2p);
298 bad:
299 	(void)close(s);
300 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
301 	return (-1);
302 }
303 
304 /*
305  * based on code written by Chris Siebenmann <cks@utcc.utoronto.ca>
306  */
307 /* ARGSUSED */
308 static int
309 rshrcmd(ahost, rport, locuser, remuser, cmd, fd2p, rshcmd)
310 	char  	**ahost;
311 	u_int32_t	rport;
312 	const	char *locuser, *remuser, *cmd;
313 	int	*fd2p;
314 	const	char *rshcmd;
315 {
316 	pid_t pid;
317 	int sp[2], ep[2];
318 	char *p;
319 	struct passwd *pw;
320 
321 	_DIAGASSERT(ahost != NULL);
322 	_DIAGASSERT(locuser != NULL);
323 	_DIAGASSERT(remuser != NULL);
324 	_DIAGASSERT(cmd != NULL);
325 	/* fd2p may be NULL */
326 
327 	/* What rsh/shell to use. */
328 	if (rshcmd == NULL)
329 		rshcmd = _PATH_BIN_RCMD;
330 
331 	/* locuser must exist on this host. */
332 	if ((pw = getpwnam(locuser)) == NULL) {
333 		warnx("rshrcmd: unknown user: %s", locuser);
334 		return(-1);
335 	}
336 
337 	/* get a socketpair we'll use for stdin and stdout. */
338 	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sp) < 0) {
339 		warn("rshrcmd: socketpair");
340 		return (-1);
341 	}
342 	/* we will use this for the fd2 pointer */
343 	if (fd2p) {
344 		if (socketpair(AF_LOCAL, SOCK_STREAM, 0, ep) < 0) {
345 			warn("rshrcmd: socketpair");
346 			return (-1);
347 		}
348 		*fd2p = ep[0];
349 	}
350 
351 	pid = fork();
352 	if (pid < 0) {
353 		warn("rshrcmd: fork");
354 		return (-1);
355 	}
356 	if (pid == 0) {
357 		/*
358 		 * child
359 		 * - we use sp[1] to be stdin/stdout, and close sp[0]
360 		 * - with fd2p, we use ep[1] for stderr, and close ep[0]
361 		 */
362 		(void)close(sp[0]);
363 		if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) {
364 			warn("rshrcmd: dup2");
365 			_exit(1);
366 		}
367 		if (fd2p) {
368 			if (dup2(ep[1], 2) < 0) {
369 				warn("rshrcmd: dup2");
370 				_exit(1);
371 			}
372 			(void)close(ep[0]);
373 			(void)close(ep[1]);
374 		} else if (dup2(0, 2) < 0) {
375 			warn("rshrcmd: dup2");
376 			_exit(1);
377 		}
378 		/* fork again to lose parent. */
379 		pid = fork();
380 		if (pid < 0) {
381 			warn("rshrcmd: second fork");
382 			_exit(1);
383 		}
384 		if (pid > 0)
385 			_exit(0);
386 
387 		/* Orphan.  Become local user for rshprog. */
388 		if (setuid(pw->pw_uid)) {
389 			warn("rshrcmd: setuid(%lu)", (u_long)pw->pw_uid);
390 			_exit(1);
391 		}
392 
393 		/*
394 		 * If we are rcmd'ing to "localhost" as the same user as we are,
395 		 * then avoid running remote shell for efficiency.
396 		 */
397 		if (strcmp(*ahost, "localhost") == 0 &&
398 		    strcmp(locuser, remuser) == 0) {
399 			if (pw->pw_shell[0] == '\0')
400 				rshcmd = _PATH_BSHELL;
401 			else
402 				rshcmd = pw->pw_shell;
403 			p = strrchr(rshcmd, '/');
404 			execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL);
405 		} else {
406 			p = strrchr(rshcmd, '/');
407 			execlp(rshcmd, p ? p + 1 : rshcmd, *ahost, "-l",
408 			    remuser, cmd, NULL);
409 		}
410 		warn("rshrcmd: exec %s", rshcmd);
411 		_exit(1);
412 	}
413 	/* Parent */
414 	(void)close(sp[1]);
415 	if (fd2p)
416 		(void)close(ep[1]);
417 
418 	(void)waitpid(pid, NULL, 0);
419 	return (sp[0]);
420 }
421 
422 int
423 rresvport(alport)
424 	int *alport;
425 {
426 
427 	_DIAGASSERT(alport != NULL);
428 
429 	return rresvport_af(alport, AF_INET);
430 }
431 
432 int
433 rresvport_af(alport, family)
434 	int *alport;
435 	int family;
436 {
437 	struct sockaddr_storage ss;
438 	struct sockaddr *sa;
439 	int salen;
440 	int s;
441 	u_int16_t *portp;
442 
443 	_DIAGASSERT(alport != NULL);
444 
445 	memset(&ss, 0, sizeof(ss));
446 	sa = (struct sockaddr *)&ss;
447 	switch (family) {
448 	case AF_INET:
449 #ifdef BSD4_4
450 		sa->sa_len =
451 #endif
452 		salen = sizeof(struct sockaddr_in);
453 		portp = &((struct sockaddr_in *)sa)->sin_port;
454 		break;
455 	case AF_INET6:
456 #ifdef BSD4_4
457 		sa->sa_len =
458 #endif
459 		salen = sizeof(struct sockaddr_in6);
460 		portp = &((struct sockaddr_in6 *)sa)->sin6_port;
461 		break;
462 	default:
463 		portp = NULL;
464 		return EAFNOSUPPORT;
465 	}
466 	sa->sa_family = family;
467 	s = socket(family, SOCK_STREAM, 0);
468 	if (s < 0)
469 		return (-1);
470 #ifdef BSD4_4
471 	if (family == AF_INET) {
472 		*portp = 0;
473 		if (bindresvport(s, (struct sockaddr_in *)sa) < 0) {
474 			int sverr = errno;
475 
476 			(void)close(s);
477 			errno = sverr;
478 			return (-1);
479 		}
480 		*alport = (int)ntohs(*portp);
481 		return (s);
482 	}
483 #endif
484 	for (;;) {
485 		*portp = htons((u_short)*alport);
486 		if (bind(s, sa, salen) >= 0)
487 			return (s);
488 		if (errno != EADDRINUSE) {
489 			(void)close(s);
490 			return (-1);
491 		}
492 		(*alport)--;
493 		if (*alport == IPPORT_RESERVED/2) {
494 			(void)close(s);
495 			errno = EAGAIN;		/* close */
496 			return (-1);
497 		}
498 	}
499 }
500 
501 int	__check_rhosts_file = 1;
502 char	*__rcmd_errstr;
503 
504 int
505 ruserok(rhost, superuser, ruser, luser)
506 	const char *rhost, *ruser, *luser;
507 	int superuser;
508 {
509 	struct hostent *hp;
510 	char **ap;
511 	int i;
512 #define MAXADDRS	35
513 	u_int32_t addrs[MAXADDRS + 1];
514 
515 	_DIAGASSERT(rhost != NULL);
516 	_DIAGASSERT(ruser != NULL);
517 	_DIAGASSERT(luser != NULL);
518 
519 	if ((hp = gethostbyname(rhost)) == NULL)
520 		return (-1);
521 	for (i = 0, ap = hp->h_addr_list; *ap && i < MAXADDRS; ++ap, ++i)
522 		memmove(&addrs[i], *ap, sizeof(addrs[i]));
523 	addrs[i] = 0;
524 
525 	for (i = 0; i < MAXADDRS && addrs[i]; i++)
526 		if (iruserok(addrs[i], superuser, ruser, luser) == 0)
527 			return (0);
528 	return (-1);
529 }
530 
531 /*
532  * New .rhosts strategy: We are passed an ip address. We spin through
533  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
534  * has ip addresses, we don't have to trust a nameserver.  When it
535  * contains hostnames, we spin through the list of addresses the nameserver
536  * gives us and look for a match.
537  *
538  * Returns 0 if ok, -1 if not ok.
539  */
540 int
541 iruserok(raddr, superuser, ruser, luser)
542 	u_int32_t raddr;
543 	int superuser;
544 	const char *ruser, *luser;
545 {
546 	register char *cp;
547 	struct stat sbuf;
548 	struct passwd *pwd;
549 	FILE *hostf;
550 	uid_t uid;
551 	gid_t gid;
552 	int first;
553 	char pbuf[MAXPATHLEN];
554 
555 	_DIAGASSERT(ruser != NULL);
556 	_DIAGASSERT(luser != NULL);
557 
558 	first = 1;
559 	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
560 again:
561 	if (hostf) {
562 		if (__ivaliduser(hostf, raddr, luser, ruser) == 0) {
563 			(void)fclose(hostf);
564 			return (0);
565 		}
566 		(void)fclose(hostf);
567 	}
568 	if (first == 1 && (__check_rhosts_file || superuser)) {
569 		first = 0;
570 		if ((pwd = getpwnam(luser)) == NULL)
571 			return (-1);
572 		(void)strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
573 		(void)strncat(pbuf, "/.rhosts", sizeof(pbuf) - strlen(pbuf) - 1);
574 
575 		/*
576 		 * Change effective uid while opening .rhosts.  If root and
577 		 * reading an NFS mounted file system, can't read files that
578 		 * are protected read/write owner only.
579 		 */
580 		uid = geteuid();
581 		gid = getegid();
582 		(void)setegid(pwd->pw_gid);
583 		initgroups(pwd->pw_name, pwd->pw_gid);
584 		(void)seteuid(pwd->pw_uid);
585 		hostf = fopen(pbuf, "r");
586 		(void)seteuid(uid);
587 		(void)setegid(gid);
588 
589 		if (hostf == NULL)
590 			return (-1);
591 		/*
592 		 * If not a regular file, or is owned by someone other than
593 		 * user or root or if writeable by anyone but the owner, quit.
594 		 */
595 		cp = NULL;
596 		if (lstat(pbuf, &sbuf) < 0)
597 			cp = ".rhosts lstat failed";
598 		else if (!S_ISREG(sbuf.st_mode))
599 			cp = ".rhosts not regular file";
600 		else if (fstat(fileno(hostf), &sbuf) < 0)
601 			cp = ".rhosts fstat failed";
602 		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
603 			cp = "bad .rhosts owner";
604 		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
605 			cp = ".rhosts writeable by other than owner";
606 		/* If there were any problems, quit. */
607 		if (cp) {
608 			__rcmd_errstr = cp;
609 			(void)fclose(hostf);
610 			return (-1);
611 		}
612 		goto again;
613 	}
614 	return (-1);
615 }
616 
617 /*
618  * XXX
619  * Don't make static, used by lpd(8).
620  *
621  * Returns 0 if ok, -1 if not ok.
622  */
623 int
624 __ivaliduser(hostf, raddr, luser, ruser)
625 	FILE *hostf;
626 	u_int32_t raddr;
627 	const char *luser, *ruser;
628 {
629 	register char *user, *p;
630 	int ch;
631 	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
632 	const char *auser, *ahost;
633 	int hostok, userok;
634 	char *rhost = NULL;
635 	int firsttime = 1;
636 	char domain[MAXHOSTNAMELEN];
637 
638 	getdomainname(domain, sizeof(domain));
639 
640 	_DIAGASSERT(hostf != NULL);
641 	_DIAGASSERT(luser != NULL);
642 	_DIAGASSERT(ruser != NULL);
643 
644 	while (fgets(buf, sizeof(buf), hostf)) {
645 		p = buf;
646 		/* Skip lines that are too long. */
647 		if (strchr(p, '\n') == NULL) {
648 			while ((ch = getc(hostf)) != '\n' && ch != EOF)
649 				;
650 			continue;
651 		}
652 		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
653 			*p = isupper((unsigned char)*p) ? tolower(*p) : *p;
654 			p++;
655 		}
656 		if (*p == ' ' || *p == '\t') {
657 			*p++ = '\0';
658 			while (*p == ' ' || *p == '\t')
659 				p++;
660 			user = p;
661 			while (*p != '\n' && *p != ' ' &&
662 			    *p != '\t' && *p != '\0')
663 				p++;
664 		} else
665 			user = p;
666 		*p = '\0';
667 
668 		if (p == buf)
669 			continue;
670 
671 		auser = *user ? user : luser;
672 		ahost = buf;
673 
674 		if (ahost[0] == '+')
675 			switch (ahost[1]) {
676 			case '\0':
677 				hostok = 1;
678 				break;
679 
680 			case '@':
681 				if (firsttime) {
682 					rhost = __gethostloop(raddr);
683 					firsttime = 0;
684 				}
685 				if (rhost)
686 					hostok = innetgr(&ahost[2], rhost,
687 					    NULL, domain);
688 				else
689 					hostok = 0;
690 				break;
691 
692 			default:
693 				hostok = __icheckhost(raddr, &ahost[1]);
694 				break;
695 			}
696 		else if (ahost[0] == '-')
697 			switch (ahost[1]) {
698 			case '\0':
699 				hostok = -1;
700 				break;
701 
702 			case '@':
703 				if (firsttime) {
704 					rhost = __gethostloop(raddr);
705 					firsttime = 0;
706 				}
707 				if (rhost)
708 					hostok = -innetgr(&ahost[2], rhost,
709 					    NULL, domain);
710 				else
711 					hostok = 0;
712 				break;
713 
714 			default:
715 				hostok = -__icheckhost(raddr, &ahost[1]);
716 				break;
717 			}
718 		else
719 			hostok = __icheckhost(raddr, ahost);
720 
721 
722 		if (auser[0] == '+')
723 			switch (auser[1]) {
724 			case '\0':
725 				userok = 1;
726 				break;
727 
728 			case '@':
729 				userok = innetgr(&auser[2], NULL, ruser,
730 				    domain);
731 				break;
732 
733 			default:
734 				userok = strcmp(ruser, &auser[1]) == 0;
735 				break;
736 			}
737 		else if (auser[0] == '-')
738 			switch (auser[1]) {
739 			case '\0':
740 				userok = -1;
741 				break;
742 
743 			case '@':
744 				userok = -innetgr(&auser[2], NULL, ruser,
745 				    domain);
746 				break;
747 
748 			default:
749 				userok =
750 				    -(strcmp(ruser, &auser[1]) == 0 ? 1 : 0);
751 				break;
752 			}
753 		else
754 			userok = strcmp(ruser, auser) == 0;
755 
756 		/* Check if one component did not match */
757 		if (hostok == 0 || userok == 0)
758 			continue;
759 
760 		/* Check if we got a forbidden pair */
761 		if (userok == -1 || hostok == -1)
762 			return -1;
763 
764 		/* Check if we got a valid pair */
765 		if (hostok == 1 && userok == 1)
766 			return 0;
767 	}
768 	return -1;
769 }
770 
771 /*
772  * Returns "true" if match, 0 if no match.
773  */
774 static int
775 __icheckhost(raddr, lhost)
776 	u_int32_t raddr;
777 	const char *lhost;
778 {
779 	struct hostent *hp;
780 	struct in_addr laddr;
781 	char **pp;
782 
783 	_DIAGASSERT(lhost != NULL);
784 
785 	/* Try for raw ip address first. */
786 	if (isdigit((unsigned char)*lhost) && inet_aton(lhost, &laddr) != 0)
787 		return (raddr == laddr.s_addr);
788 
789 	/* Better be a hostname. */
790 	if ((hp = gethostbyname(lhost)) == NULL)
791 		return (0);
792 
793 	/* Spin through ip addresses. */
794 	for (pp = hp->h_addr_list; *pp; ++pp)
795 		if (!memcmp(&raddr, *pp, sizeof(u_int32_t)))
796 			return (1);
797 
798 	/* No match. */
799 	return (0);
800 }
801 
802 /*
803  * Return the hostname associated with the supplied address.
804  * Do a reverse lookup as well for security. If a loop cannot
805  * be found, pack the result of inet_ntoa() into the string.
806  */
807 static char *
808 __gethostloop(raddr)
809 	u_int32_t raddr;
810 {
811 	static char remotehost[MAXHOSTNAMELEN];
812 	struct hostent *hp;
813 	struct in_addr in;
814 
815 	hp = gethostbyaddr((char *)(void *)&raddr, sizeof(raddr), AF_INET);
816 	if (hp == NULL)
817 		return (NULL);
818 
819 	/*
820 	 * Look up the name and check that the supplied
821 	 * address is in the list
822 	 */
823 	strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
824 	remotehost[sizeof(remotehost) - 1] = '\0';
825 	hp = gethostbyname(remotehost);
826 	if (hp == NULL)
827 		return (NULL);
828 
829 	for (; hp->h_addr_list[0] != NULL; hp->h_addr_list++)
830 		if (!memcmp(hp->h_addr_list[0], &raddr, sizeof(raddr)))
831 			return (remotehost);
832 
833 	/*
834 	 * either the DNS adminstrator has made a configuration
835 	 * mistake, or someone has attempted to spoof us
836 	 */
837 	in.s_addr = raddr;
838 	syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s",
839 	    inet_ntoa(in), hp->h_name);
840 	return (NULL);
841 }
842