xref: /illumos-gate/usr/src/lib/libsocket/inet/rcmd.c (revision bde334a8dbd66dfa70ce4d7fc9dcad6e1ae45fe4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #include <limits.h>
41 #include <stdio.h>
42 #include <ctype.h>
43 #include <pwd.h>
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/file.h>
47 #include <signal.h>
48 #include <libintl.h>
49 #include <sys/socket.h>
50 #include <sys/stat.h>
51 
52 #include <netinet/in.h>
53 #include <netinet/tcp.h>
54 #include <inet/common.h>
55 
56 #include <netdb.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #include <grp.h>
63 #include <alloca.h>
64 #include <arpa/inet.h>
65 
66 #include <priv_utils.h>
67 
68 #ifdef SYSV
69 #define	bcopy(s1, s2, len)	(void) memcpy(s2, s1, len)
70 #define	bzero(s, len)		(void) memset(s, 0, len)
71 #define	index(s, c)		strchr(s, c)
72 char	*strchr();
73 #else
74 char	*index();
75 #endif /* SYSV */
76 
77 extern int  usingypmap();
78 
79 static int _validuser(FILE *hostf, char *rhost, const char *luser,
80 			const char *ruser, int baselen);
81 static int _checkhost(char *rhost, char *lhost, int len);
82 
83 
84 #ifdef NIS
85 static char *domain;
86 #endif
87 
88 int rcmd(char **ahost, unsigned short rport, const char *locuser,
89     const char *remuser, const char *cmd, int *fd2p)
90 {
91 	int rcmd_ret;
92 
93 	rcmd_ret = rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p,
94 	    AF_INET);
95 	return (rcmd_ret);
96 }
97 
98 int rcmd_af(char **ahost, unsigned short rport, const char *locuser,
99     const char *remuser, const char *cmd, int *fd2p, int af)
100 {
101 	int s, timo = 1;
102 	ssize_t retval;
103 	pid_t pid;
104 	struct sockaddr_storage caddr, faddr;
105 	struct sockaddr_in *sin;
106 	struct sockaddr_in6 *sin6;
107 	struct addrinfo hints;
108 	struct addrinfo *res, *resp;
109 	size_t addrlen;
110 	int rc;
111 #define	MAX_SHORTSTRLEN 6
112 	char aport[MAX_SHORTSTRLEN];
113 	char c;
114 	int lport = 0;
115 #ifdef SYSV
116 	sigset_t oldmask;
117 	sigset_t newmask;
118 	struct sigaction oldaction;
119 	struct sigaction newaction;
120 #else
121 	int oldmask;
122 #endif /* SYSV */
123 	fd_set fdset;
124 	int selret;
125 	char *addr;
126 	static char hostname[MAXHOSTNAMELEN];
127 	socklen_t len;
128 	char abuf[INET6_ADDRSTRLEN];
129 
130 	if (!(af == AF_INET || af == AF_INET6 || af == AF_UNSPEC)) {
131 		errno = EAFNOSUPPORT;
132 		return (-1);
133 	}
134 
135 	pid = getpid();
136 	memset(&hints, 0, sizeof (hints));
137 	hints.ai_socktype = SOCK_STREAM;
138 	hints.ai_flags = AI_CANONNAME;
139 	if (af == AF_INET6) {
140 		hints.ai_flags |= AI_V4MAPPED;
141 		hints.ai_family = AF_UNSPEC;
142 	} else {
143 		hints.ai_family = af;
144 	}
145 	(void) snprintf(aport, MAX_SHORTSTRLEN, "%u", ntohs(rport));
146 	rc = getaddrinfo(*ahost, aport, &hints, &res);
147 	if (rc != 0) {
148 		(void) fprintf(stderr,
149 		    dgettext(TEXT_DOMAIN, "%s: unknown host%s\n"),
150 		    *ahost, rc == EAI_AGAIN ? " (try again later)" : "");
151 		return (-1);
152 	}
153 	resp = res;
154 	(void) strlcpy(hostname, res->ai_canonname, MAXHOSTNAMELEN);
155 	*ahost = hostname;
156 #ifdef SYSV
157 	/* ignore SIGPIPE */
158 	bzero((char *)&newaction, sizeof (newaction));
159 	newaction.sa_handler = SIG_IGN;
160 	(void) sigaction(SIGPIPE, &newaction, &oldaction);
161 
162 	/* block SIGURG */
163 	bzero((char *)&newmask, sizeof (newmask));
164 	(void) sigaddset(&newmask, SIGURG);
165 	(void) sigprocmask(SIG_BLOCK, &newmask, &oldmask);
166 #else
167 	oldmask = _sigblock(sigmask(SIGURG));
168 #endif /* SYSV */
169 	for (;;) {
170 		s = rresvport_af(&lport, res->ai_family);
171 		if (s < 0) {
172 			int af = res->ai_family;
173 
174 			/*
175 			 * See if we have any addresses of a different type
176 			 * to try.
177 			 */
178 			while (res != NULL && res->ai_family == af)
179 				res = res->ai_next;
180 
181 			if (res != NULL)
182 				continue;
183 
184 			if (errno == EAGAIN)
185 				(void) fprintf(stderr,
186 				    dgettext(TEXT_DOMAIN,
187 				    "socket: All ports in use\n"));
188 			else
189 				perror("rcmd: socket");
190 #ifdef SYSV
191 			/* restore original SIGPIPE handler */
192 			(void) sigaction(SIGPIPE, &oldaction,
193 			    (struct sigaction *)0);
194 
195 			/* restore original signal mask */
196 			(void) sigprocmask(SIG_SETMASK, &oldmask,
197 			    (sigset_t *)0);
198 #else
199 			sigsetmask(oldmask);
200 #endif /* SYSV */
201 			freeaddrinfo(resp);
202 			return (-1);
203 		}
204 		bzero((char *)&caddr, sizeof (caddr));
205 		bcopy(res->ai_addr, &caddr, res->ai_addrlen);
206 		addrlen = res->ai_addrlen;
207 		if (af == AF_INET6 && res->ai_addr->sa_family == AF_INET) {
208 			struct in6_addr ia6;
209 			struct sockaddr_in6 *in6addr;
210 			IN6_INADDR_TO_V4MAPPED(&((struct sockaddr_in *)
211 			    res->ai_addr)->sin_addr, &ia6);
212 			in6addr = (struct sockaddr_in6 *)&caddr;
213 			in6addr->sin6_addr = ia6;
214 			in6addr->sin6_family = AF_INET6;
215 			addrlen = sizeof (struct sockaddr_in6);
216 		}
217 		(void) fcntl(s, F_SETOWN, pid);
218 		if (connect(s, (struct sockaddr *)&caddr, addrlen) >= 0)
219 			break;
220 		(void) close(s);
221 		if (errno == EADDRINUSE) {
222 			lport = 0;
223 			continue;
224 		}
225 		if (errno == ECONNREFUSED && timo <= 16) {
226 			(void) sleep(timo);
227 			timo *= 2;
228 			continue;
229 		}
230 		if (res->ai_next != NULL) {
231 			int oerrno = errno;
232 			if (res->ai_addr->sa_family == AF_INET6)
233 				addr = (char *)&((struct sockaddr_in6 *)
234 				    res->ai_addr)->sin6_addr;
235 			else
236 				addr = (char *)&((struct sockaddr_in *)
237 				    res->ai_addr)->sin_addr;
238 			(void) fprintf(stderr,
239 			    dgettext(TEXT_DOMAIN, "connect to address %s: "),
240 			    inet_ntop(res->ai_addr->sa_family, addr,
241 			    abuf, sizeof (abuf)));
242 			errno = oerrno;
243 			perror(0);
244 			res = res->ai_next;
245 			if (res->ai_addr->sa_family == AF_INET6)
246 				addr = (char *)&((struct sockaddr_in6 *)
247 				    res->ai_addr)->sin6_addr;
248 			else
249 				addr = (char *)&((struct sockaddr_in *)
250 				    res->ai_addr)->sin_addr;
251 			(void) fprintf(stderr,
252 			    dgettext(TEXT_DOMAIN, "Trying %s...\n"),
253 			    inet_ntop(res->ai_addr->sa_family, addr,
254 			    abuf, sizeof (abuf)));
255 			continue;
256 		}
257 		perror(*ahost);
258 		freeaddrinfo(resp);
259 #ifdef SYSV
260 		/* restore original SIGPIPE handler */
261 		(void) sigaction(SIGPIPE, &oldaction,
262 		    (struct sigaction *)0);
263 
264 		/* restore original signal mask */
265 		(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
266 #else
267 		sigsetmask(oldmask);
268 #endif /* SYSV */
269 		return (-1);
270 	}
271 	lport = 0;
272 	if (fd2p == 0) {
273 		(void) write(s, "", 1);
274 	} else {
275 		int s2 = rresvport_af(&lport, res->ai_family), s3;
276 
277 		len = (socklen_t)sizeof (faddr);
278 
279 		if (s2 < 0)
280 			goto bad;
281 		(void) listen(s2, 1);
282 		(void) snprintf(aport, MAX_SHORTSTRLEN, "%d", lport);
283 		if (write(s, aport, strlen(aport)+1) != strlen(aport)+1) {
284 			perror(dgettext(TEXT_DOMAIN,
285 			    "write: setting up stderr"));
286 			(void) close(s2);
287 			goto bad;
288 		}
289 		FD_ZERO(&fdset);
290 		FD_SET(s, &fdset);
291 		FD_SET(s2, &fdset);
292 		while ((selret = select(FD_SETSIZE, &fdset, (fd_set *)0,
293 		    (fd_set *)0, (struct timeval *)0)) > 0) {
294 			if (FD_ISSET(s, &fdset)) {
295 				/*
296 				 *	Something's wrong:  we should get no
297 				 *	data on this connection at this point,
298 				 *	so we assume that the connection has
299 				 *	gone away.
300 				 */
301 				(void) close(s2);
302 				goto bad;
303 			}
304 			if (FD_ISSET(s2, &fdset)) {
305 				/*
306 				 *	We assume this is an incoming connect
307 				 *	request and proceed normally.
308 				 */
309 				s3 = accept(s2, (struct sockaddr *)&faddr,
310 				    &len);
311 				FD_CLR(s2, &fdset);
312 				(void) close(s2);
313 				if (s3 < 0) {
314 					perror("accept");
315 					lport = 0;
316 					goto bad;
317 				}
318 				else
319 					break;
320 			}
321 		}
322 		if (selret == -1) {
323 			/*
324 			 *	This should not happen, and we treat it as
325 			 *	a fatal error.
326 			 */
327 			(void) close(s2);
328 			goto bad;
329 		}
330 
331 		*fd2p = s3;
332 		switch (faddr.ss_family) {
333 		case AF_INET:
334 			sin = (struct sockaddr_in *)&faddr;
335 			if (ntohs(sin->sin_port) >= IPPORT_RESERVED) {
336 				(void) fprintf(stderr,
337 				    dgettext(TEXT_DOMAIN,
338 				    "socket: protocol failure in circuit "
339 				    "setup.\n"));
340 				goto bad2;
341 			}
342 			break;
343 		case AF_INET6:
344 			sin6 = (struct sockaddr_in6 *)&faddr;
345 			if (ntohs(sin6->sin6_port) >= IPPORT_RESERVED) {
346 				(void) fprintf(stderr,
347 				    dgettext(TEXT_DOMAIN,
348 				    "socket: protocol failure in circuit "
349 				    "setup.\n"));
350 				goto bad2;
351 			}
352 			break;
353 		default:
354 			(void) fprintf(stderr,
355 			    dgettext(TEXT_DOMAIN,
356 			    "socket: protocol failure in circuit setup.\n"));
357 			goto bad2;
358 		}
359 	}
360 	(void) write(s, locuser, strlen(locuser)+1);
361 	(void) write(s, remuser, strlen(remuser)+1);
362 	(void) write(s, cmd, strlen(cmd)+1);
363 	retval = read(s, &c, 1);
364 	if (retval != 1) {
365 		if (retval == 0) {
366 			(void) fprintf(stderr,
367 			    dgettext(TEXT_DOMAIN,
368 			    "Protocol error, %s closed connection\n"),
369 			    *ahost);
370 		} else if (retval < 0) {
371 			perror(*ahost);
372 		} else {
373 			(void) fprintf(stderr,
374 			    dgettext(TEXT_DOMAIN,
375 			    "Protocol error, %s sent %d bytes\n"),
376 			    *ahost, retval);
377 		}
378 		goto bad2;
379 	}
380 	if (c != 0) {
381 		while (read(s, &c, 1) == 1) {
382 			(void) write(2, &c, 1);
383 			if (c == '\n')
384 				break;
385 		}
386 		goto bad2;
387 	}
388 #ifdef SYSV
389 	/* restore original SIGPIPE handler */
390 	(void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0);
391 
392 	/* restore original signal mask */
393 	(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
394 #else
395 	sigsetmask(oldmask);
396 #endif /* SYSV */
397 	freeaddrinfo(resp);
398 	return (s);
399 bad2:
400 	if (lport)
401 		(void) close(*fd2p);
402 bad:
403 	(void) close(s);
404 #ifdef SYSV
405 	/* restore original SIGPIPE handler */
406 	(void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0);
407 
408 	/* restore original signal mask */
409 	(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
410 #else
411 	sigsetmask(oldmask);
412 #endif /* SYSV */
413 	freeaddrinfo(resp);
414 	return (-1);
415 }
416 
417 static int
418 _rresvport_addr(int *alport, struct sockaddr_storage *addr)
419 {
420 	struct sockaddr_in *sin;
421 	struct sockaddr_in6 *sin6;
422 	int s;
423 	socklen_t len;
424 	int on = 1;
425 	int off = 0;
426 
427 	if (addr->ss_family == AF_INET) {
428 		sin = (struct sockaddr_in *)addr;
429 		len = sizeof (struct sockaddr_in);
430 	} else if (addr->ss_family == AF_INET6) {
431 		sin6 = (struct sockaddr_in6 *)addr;
432 		len = sizeof (struct sockaddr_in6);
433 	} else {
434 		errno = EAFNOSUPPORT;
435 		return (-1);
436 	}
437 	s = socket(addr->ss_family, SOCK_STREAM, 0);
438 	if (s < 0)
439 		return (-1);
440 
441 	/*
442 	 * Set SO_EXCLBIND to get a "unique" port, which is not bound
443 	 * to any other sockets.
444 	 */
445 	if (setsockopt(s, SOL_SOCKET, SO_EXCLBIND, &on, sizeof (on)) < 0) {
446 		(void) close(s);
447 		return (-1);
448 	}
449 
450 	/* Try to bind() to the given port first. */
451 	if (*alport != 0) {
452 		if (addr->ss_family == AF_INET) {
453 			sin->sin_port = htons((ushort_t)*alport);
454 		} else {
455 			sin6->sin6_port = htons((ushort_t)*alport);
456 		}
457 		if (bind(s, (struct sockaddr *)addr, len) >= 0) {
458 			/* To be safe, need to turn off SO_EXCLBIND. */
459 			(void) setsockopt(s, SOL_SOCKET, SO_EXCLBIND, &off,
460 			    sizeof (off));
461 			return (s);
462 		}
463 		if (errno != EADDRINUSE) {
464 			(void) close(s);
465 			return (-1);
466 		}
467 	}
468 
469 	/*
470 	 * If no port is given or the above bind() does not succeed, set
471 	 * TCP_ANONPRIVBIND option to ask the kernel to pick a port in the
472 	 * priviledged range for us.
473 	 */
474 	if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
475 	    sizeof (on)) < 0) {
476 		(void) close(s);
477 		return (-1);
478 	}
479 	if (addr->ss_family == AF_INET) {
480 		sin->sin_port = 0;
481 	} else {
482 		sin6->sin6_port = 0;
483 	}
484 	if (bind(s, (struct sockaddr *)addr, len) >= 0) {
485 		/*
486 		 * We need to tell the caller what the port is.
487 		 */
488 		if (getsockname(s, (struct sockaddr *)addr, &len) < 0) {
489 			(void) close(s);
490 			return (-1);
491 		}
492 		switch (addr->ss_family) {
493 		case AF_INET6:
494 			sin6 = (struct sockaddr_in6 *)addr;
495 			*alport = ntohs(sin6->sin6_port);
496 			break;
497 		case AF_INET:
498 			sin = (struct sockaddr_in *)addr;
499 			*alport = ntohs(sin->sin_port);
500 			break;
501 		}
502 
503 		/*
504 		 * To be safe, always turn off these options when we are done.
505 		 */
506 		(void) setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &off,
507 		    sizeof (off));
508 		(void) setsockopt(s, SOL_SOCKET, SO_EXCLBIND, &off,
509 		    sizeof (off));
510 		return (s);
511 	}
512 	(void) close(s);
513 	return (-1);
514 }
515 
516 int
517 rresvport_addr(int *alport, struct sockaddr_storage *addr)
518 {
519 	int res, err;
520 
521 	(void) __priv_bracket(PRIV_ON);
522 
523 	res = _rresvport_addr(alport, addr);
524 
525 	err = errno;
526 	(void) __priv_bracket(PRIV_OFF);
527 	errno = err;
528 
529 	return (res);
530 }
531 
532 int
533 rresvport_af(int *alport, int af)
534 {
535 	struct sockaddr_storage laddr;
536 
537 	bzero(&laddr, sizeof (laddr));
538 	if (af == AF_INET || af == AF_INET6) {
539 		laddr.ss_family = (sa_family_t)af;
540 	} else {
541 		errno = EAFNOSUPPORT;
542 		return (-1);
543 	}
544 	return (rresvport_addr(alport, &laddr));
545 }
546 
547 int
548 rresvport(int *alport)
549 {
550 	return (rresvport_af(alport, AF_INET));
551 }
552 
553 int
554 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
555 {
556 	FILE *hostf;
557 	char fhost[MAXHOSTNAMELEN];
558 	const char *sp;
559 	char *p;
560 	int baselen = -1;
561 
562 	struct stat64 sbuf;
563 	struct passwd *pwd;
564 	char pbuf[MAXPATHLEN];
565 	uid_t uid = (uid_t)-1;
566 	gid_t gid = (gid_t)-1;
567 	int maxgrp = getgroups(0, NULL);
568 	gid_t *grouplist = alloca(maxgrp * sizeof (gid_t));
569 	int ngroups;
570 
571 	sp = rhost;
572 	p = fhost;
573 	while (*sp) {
574 		if (*sp == '.') {
575 			if (baselen == -1)
576 				baselen = (int)(sp - rhost);
577 			*p++ = *sp++;
578 		} else {
579 			*p++ = isupper(*sp) ? tolower(*sp++) : *sp++;
580 		}
581 	}
582 	*p = '\0';
583 
584 	/* check /etc/hosts.equiv */
585 	if (!superuser) {
586 		if ((hostf = fopen("/etc/hosts.equiv", "rF")) != NULL) {
587 			if (!_validuser(hostf, fhost, luser, ruser, baselen)) {
588 				(void) fclose(hostf);
589 				return (0);
590 			}
591 			(void) fclose(hostf);
592 		}
593 	}
594 
595 	/* check ~/.rhosts */
596 
597 	if ((pwd = getpwnam(luser)) == NULL)
598 		return (-1);
599 	(void) strcpy(pbuf, pwd->pw_dir);
600 	(void) strcat(pbuf, "/.rhosts");
601 
602 	/*
603 	 * Read .rhosts as the local user to avoid NFS mapping the root uid
604 	 * to something that can't read .rhosts.
605 	 */
606 	gid = getegid();
607 	uid = geteuid();
608 	if ((ngroups = getgroups(maxgrp, grouplist)) == -1)
609 		return (-1);
610 
611 	(void) setegid(pwd->pw_gid);
612 	initgroups(pwd->pw_name, pwd->pw_gid);
613 	(void) seteuid(pwd->pw_uid);
614 	if ((hostf = fopen(pbuf, "rF")) == NULL) {
615 		if (gid != (gid_t)-1)
616 			(void) setegid(gid);
617 		if (uid != (uid_t)-1)
618 			(void) seteuid(uid);
619 		setgroups(ngroups, grouplist);
620 		return (-1);
621 	}
622 	(void) fstat64(fileno(hostf), &sbuf);
623 	if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) {
624 		(void) fclose(hostf);
625 		if (gid != (gid_t)-1)
626 			(void) setegid(gid);
627 		if (uid != (uid_t)-1)
628 			(void) seteuid(uid);
629 		setgroups(ngroups, grouplist);
630 		return (-1);
631 	}
632 
633 	if (!_validuser(hostf, fhost, luser, ruser, baselen)) {
634 		(void) fclose(hostf);
635 		if (gid != (gid_t)-1)
636 			(void) setegid(gid);
637 		if (uid != (uid_t)-1)
638 			(void) seteuid(uid);
639 		setgroups(ngroups, grouplist);
640 		return (0);
641 	}
642 
643 	(void) fclose(hostf);
644 	if (gid != (gid_t)-1)
645 		(void) setegid(gid);
646 	if (uid != (uid_t)-1)
647 		(void) seteuid(uid);
648 	setgroups(ngroups, grouplist);
649 	return (-1);
650 }
651 
652 static int
653 _validuser(FILE *hostf, char *rhost, const char *luser,
654     const char *ruser, int baselen)
655 {
656 	char *user;
657 	char ahost[BUFSIZ];
658 	char *uchost = (char *)NULL;
659 	int hostmatch, usermatch;
660 	char *p;
661 
662 #ifdef NIS
663 	if (domain == NULL) {
664 		(void) usingypmap(&domain, NULL);
665 	}
666 #endif /* NIS */
667 
668 	while (fgets(ahost, (int)sizeof (ahost), hostf)) {
669 		uchost = (char *)NULL;
670 		hostmatch = usermatch = 0;
671 		p = ahost;
672 		/*
673 		 * We can get a line bigger than our buffer.  If so we skip
674 		 * the offending line.
675 		 */
676 		if (strchr(p, '\n') == NULL) {
677 			while (fgets(ahost, (int)sizeof (ahost), hostf) &&
678 			    strchr(ahost, '\n') == NULL)
679 				;
680 			continue;
681 		}
682 		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
683 			/*
684 			 *	Both host and user ``names'' can be netgroups,
685 			 *	and must have their case preserved.  Case is
686 			 *	preserved for user names because we break out
687 			 *	of this loop when finding a field separator.
688 			 *	To do so for host names, we must make a copy of
689 			 *	the host name field.
690 			 */
691 			if (isupper(*p)) {
692 				if (uchost == (char *)NULL)
693 					uchost = strdup(ahost);
694 				*p = tolower(*p);
695 			}
696 			p++;
697 		}
698 		if (*p != '\0' && uchost != (char *)NULL)
699 			uchost[p - ahost] = '\0';
700 		if (*p == ' ' || *p == '\t') {
701 			*p++ = '\0';
702 			while (*p == ' ' || *p == '\t')
703 				p++;
704 			user = p;
705 			while (*p != '\n' && *p != ' ' && *p != '\t' &&
706 			    *p != '\0')
707 				p++;
708 		} else
709 			user = p;
710 		*p = '\0';
711 		if (ahost[0] == '+' && ahost[1] == 0)
712 			hostmatch = 1;
713 #ifdef NIS
714 		else if (ahost[0] == '+' && ahost[1] == '@')
715 			if (uchost != (char *)NULL)
716 				hostmatch = innetgr(uchost + 2, rhost,
717 				    NULL, domain);
718 			else
719 				hostmatch = innetgr(ahost + 2, rhost,
720 				    NULL, domain);
721 		else if (ahost[0] == '-' && ahost[1] == '@') {
722 			if (uchost != (char *)NULL) {
723 				if (innetgr(uchost + 2, rhost, NULL, domain))
724 					break;
725 			} else {
726 				if (innetgr(ahost + 2, rhost, NULL, domain))
727 					break;
728 			}
729 		}
730 #endif /* NIS */
731 		else if (ahost[0] == '-') {
732 			if (_checkhost(rhost, ahost+1, baselen))
733 				break;
734 		}
735 		else
736 			hostmatch = _checkhost(rhost, ahost, baselen);
737 		if (user[0]) {
738 			if (user[0] == '+' && user[1] == 0)
739 				usermatch = 1;
740 #ifdef NIS
741 			else if (user[0] == '+' && user[1] == '@')
742 				usermatch = innetgr(user+2, NULL,
743 				    ruser, domain);
744 			else if (user[0] == '-' && user[1] == '@') {
745 				if (hostmatch &&
746 				    innetgr(user+2, NULL, ruser, domain))
747 					break;
748 			}
749 #endif /* NIS */
750 			else if (user[0] == '-') {
751 				if (hostmatch && (strcmp(user+1, ruser) == 0))
752 					break;
753 			}
754 			else
755 				usermatch = (strcmp(user, ruser) == 0);
756 		}
757 		else
758 			usermatch = (strcmp(ruser, luser) == 0);
759 		if (uchost != (char *)NULL)
760 			free(uchost);
761 		if (hostmatch && usermatch)
762 			return (0);
763 	}
764 
765 	if (uchost != (char *)NULL)
766 		free(uchost);
767 	return (-1);
768 }
769 
770 static int
771 _checkhost(char *rhost, char *lhost, int len)
772 {
773 	static char *ldomain;
774 	static char *domainp;
775 	static int nodomain;
776 	char *cp;
777 
778 	if (ldomain == NULL) {
779 		ldomain = (char *)malloc(MAXHOSTNAMELEN+1);
780 		if (ldomain == 0)
781 			return (0);
782 	}
783 
784 	if (len == -1)
785 		return (strcmp(rhost, lhost) == 0);
786 	if (strncmp(rhost, lhost, len))
787 		return (0);
788 	if (strcmp(rhost, lhost) == 0)
789 		return (1);
790 	if (*(lhost + len) != '\0')
791 		return (0);
792 	if (nodomain)
793 		return (0);
794 	if (!domainp) {
795 		/*
796 		 * "domainp" points after the first dot in the host name
797 		 */
798 		if (gethostname(ldomain, MAXHOSTNAMELEN) == -1) {
799 			nodomain = 1;
800 			return (0);
801 		}
802 		ldomain[MAXHOSTNAMELEN] = '\0';
803 		if ((domainp = index(ldomain, '.')) == NULL) {
804 			nodomain = 1;
805 			return (0);
806 		}
807 		domainp++;
808 		cp = domainp;
809 		while (*cp) {
810 			*cp = isupper(*cp) ? tolower(*cp) : *cp;
811 			cp++;
812 		}
813 	}
814 	return (strcmp(domainp, rhost + len + 1) == 0);
815 }
816