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