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