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