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