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