xref: /illumos-gate/usr/src/lib/libsocket/inet/rcmd.c (revision 7257d1b4)
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 2008 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 int  usingypmap();
79 
80 static int _validuser(FILE *hostf, char *rhost, const char *luser,
81 			const char *ruser, int baselen);
82 static int _checkhost(char *rhost, char *lhost, int len);
83 
84 
85 #ifdef NIS
86 static char *domain;
87 #endif
88 
89 int 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 rcmd_af(char **ahost, unsigned short rport, const char *locuser,
100     const char *remuser, const char *cmd, int *fd2p, int af)
101 {
102 	int s, timo = 1;
103 	ssize_t retval;
104 	pid_t pid;
105 	struct sockaddr_storage caddr, faddr;
106 	struct sockaddr_in *sin;
107 	struct sockaddr_in6 *sin6;
108 	struct addrinfo hints;
109 	struct addrinfo *res, *resp;
110 	size_t addrlen;
111 	int rc;
112 #define	MAX_SHORTSTRLEN 6
113 	char aport[MAX_SHORTSTRLEN];
114 	char c;
115 	int lport = 0;
116 #ifdef SYSV
117 	sigset_t oldmask;
118 	sigset_t newmask;
119 	struct sigaction oldaction;
120 	struct sigaction newaction;
121 #else
122 	int oldmask;
123 #endif /* SYSV */
124 	fd_set fdset;
125 	int selret;
126 	char *addr;
127 	static char hostname[MAXHOSTNAMELEN];
128 	socklen_t len;
129 	char abuf[INET6_ADDRSTRLEN];
130 
131 	if (!(af == AF_INET || af == AF_INET6 || af == AF_UNSPEC)) {
132 		errno = EAFNOSUPPORT;
133 		return (-1);
134 	}
135 
136 	pid = getpid();
137 	memset(&hints, 0, sizeof (hints));
138 	hints.ai_socktype = SOCK_STREAM;
139 	hints.ai_flags = AI_CANONNAME;
140 	if (af == AF_INET6) {
141 		hints.ai_flags |= AI_V4MAPPED;
142 		hints.ai_family = AF_UNSPEC;
143 	} else {
144 		hints.ai_family = af;
145 	}
146 	(void) snprintf(aport, MAX_SHORTSTRLEN, "%u", ntohs(rport));
147 	rc = getaddrinfo(*ahost, aport, &hints, &res);
148 	if (rc != 0) {
149 		(void) fprintf(stderr,
150 		    dgettext(TEXT_DOMAIN, "%s: unknown host%s\n"),
151 		    *ahost, rc == EAI_AGAIN ? " (try again later)" : "");
152 		return (-1);
153 	}
154 	resp = res;
155 	(void) strlcpy(hostname, res->ai_canonname, MAXHOSTNAMELEN);
156 	*ahost = hostname;
157 #ifdef SYSV
158 	/* ignore SIGPIPE */
159 	bzero((char *)&newaction, sizeof (newaction));
160 	newaction.sa_handler = SIG_IGN;
161 	(void) sigaction(SIGPIPE, &newaction, &oldaction);
162 
163 	/* block SIGURG */
164 	bzero((char *)&newmask, sizeof (newmask));
165 	(void) sigaddset(&newmask, SIGURG);
166 	(void) sigprocmask(SIG_BLOCK, &newmask, &oldmask);
167 #else
168 	oldmask = _sigblock(sigmask(SIGURG));
169 #endif /* SYSV */
170 	for (;;) {
171 		s = rresvport_af(&lport, res->ai_family);
172 		if (s < 0) {
173 			int af = res->ai_family;
174 
175 			/*
176 			 * See if we have any addresses of a different type
177 			 * to try.
178 			 */
179 			while (res != NULL && res->ai_family == af)
180 				res = res->ai_next;
181 
182 			if (res != NULL)
183 				continue;
184 
185 			if (errno == EAGAIN)
186 				(void) fprintf(stderr,
187 				    dgettext(TEXT_DOMAIN,
188 				    "socket: All ports in use\n"));
189 			else
190 				perror("rcmd: socket");
191 #ifdef SYSV
192 			/* restore original SIGPIPE handler */
193 			(void) sigaction(SIGPIPE, &oldaction,
194 			    (struct sigaction *)0);
195 
196 			/* restore original signal mask */
197 			(void) sigprocmask(SIG_SETMASK, &oldmask,
198 			    (sigset_t *)0);
199 #else
200 			sigsetmask(oldmask);
201 #endif /* SYSV */
202 			freeaddrinfo(resp);
203 			return (-1);
204 		}
205 		bzero((char *)&caddr, sizeof (caddr));
206 		bcopy(res->ai_addr, &caddr, res->ai_addrlen);
207 		addrlen = res->ai_addrlen;
208 		if (af == AF_INET6 && res->ai_addr->sa_family == AF_INET) {
209 			struct in6_addr ia6;
210 			struct sockaddr_in6 *in6addr;
211 			IN6_INADDR_TO_V4MAPPED(&((struct sockaddr_in *)
212 			    res->ai_addr)->sin_addr, &ia6);
213 			in6addr = (struct sockaddr_in6 *)&caddr;
214 			in6addr->sin6_addr = ia6;
215 			in6addr->sin6_family = AF_INET6;
216 			addrlen = sizeof (struct sockaddr_in6);
217 		}
218 		(void) fcntl(s, F_SETOWN, pid);
219 		if (connect(s, (struct sockaddr *)&caddr, addrlen) >= 0)
220 			break;
221 		(void) close(s);
222 		if (errno == EADDRINUSE) {
223 			lport = 0;
224 			continue;
225 		}
226 		if (errno == ECONNREFUSED && timo <= 16) {
227 			(void) sleep(timo);
228 			timo *= 2;
229 			continue;
230 		}
231 		if (res->ai_next != NULL) {
232 			int oerrno = errno;
233 			if (res->ai_addr->sa_family == AF_INET6)
234 				addr = (char *)&((struct sockaddr_in6 *)
235 				    res->ai_addr)->sin6_addr;
236 			else
237 				addr = (char *)&((struct sockaddr_in *)
238 				    res->ai_addr)->sin_addr;
239 			(void) fprintf(stderr,
240 			    dgettext(TEXT_DOMAIN, "connect to address %s: "),
241 			    inet_ntop(res->ai_addr->sa_family, addr,
242 			    abuf, sizeof (abuf)));
243 			errno = oerrno;
244 			perror(0);
245 			res = res->ai_next;
246 			if (res->ai_addr->sa_family == AF_INET6)
247 				addr = (char *)&((struct sockaddr_in6 *)
248 				    res->ai_addr)->sin6_addr;
249 			else
250 				addr = (char *)&((struct sockaddr_in *)
251 				    res->ai_addr)->sin_addr;
252 			(void) fprintf(stderr,
253 			    dgettext(TEXT_DOMAIN, "Trying %s...\n"),
254 			    inet_ntop(res->ai_addr->sa_family, addr,
255 			    abuf, sizeof (abuf)));
256 			continue;
257 		}
258 		perror(*ahost);
259 		freeaddrinfo(resp);
260 #ifdef SYSV
261 		/* restore original SIGPIPE handler */
262 		(void) sigaction(SIGPIPE, &oldaction,
263 		    (struct sigaction *)0);
264 
265 		/* restore original signal mask */
266 		(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
267 #else
268 		sigsetmask(oldmask);
269 #endif /* SYSV */
270 		return (-1);
271 	}
272 	lport = 0;
273 	if (fd2p == 0) {
274 		(void) write(s, "", 1);
275 	} else {
276 		int s2 = rresvport_af(&lport, res->ai_family), s3;
277 
278 		len = (socklen_t)sizeof (faddr);
279 
280 		if (s2 < 0)
281 			goto bad;
282 		(void) listen(s2, 1);
283 		(void) snprintf(aport, MAX_SHORTSTRLEN, "%d", lport);
284 		if (write(s, aport, strlen(aport)+1) != strlen(aport)+1) {
285 			perror(dgettext(TEXT_DOMAIN,
286 			    "write: setting up stderr"));
287 			(void) close(s2);
288 			goto bad;
289 		}
290 		FD_ZERO(&fdset);
291 		FD_SET(s, &fdset);
292 		FD_SET(s2, &fdset);
293 		while ((selret = select(FD_SETSIZE, &fdset, (fd_set *)0,
294 		    (fd_set *)0, (struct timeval *)0)) > 0) {
295 			if (FD_ISSET(s, &fdset)) {
296 				/*
297 				 *	Something's wrong:  we should get no
298 				 *	data on this connection at this point,
299 				 *	so we assume that the connection has
300 				 *	gone away.
301 				 */
302 				(void) close(s2);
303 				goto bad;
304 			}
305 			if (FD_ISSET(s2, &fdset)) {
306 				/*
307 				 *	We assume this is an incoming connect
308 				 *	request and proceed normally.
309 				 */
310 				s3 = accept(s2, (struct sockaddr *)&faddr,
311 				    &len);
312 				FD_CLR(s2, &fdset);
313 				(void) close(s2);
314 				if (s3 < 0) {
315 					perror("accept");
316 					lport = 0;
317 					goto bad;
318 				}
319 				else
320 					break;
321 			}
322 		}
323 		if (selret == -1) {
324 			/*
325 			 *	This should not happen, and we treat it as
326 			 *	a fatal error.
327 			 */
328 			(void) close(s2);
329 			goto bad;
330 		}
331 
332 		*fd2p = s3;
333 		switch (faddr.ss_family) {
334 		case AF_INET:
335 			sin = (struct sockaddr_in *)&faddr;
336 			if (ntohs(sin->sin_port) >= IPPORT_RESERVED) {
337 				(void) fprintf(stderr,
338 				    dgettext(TEXT_DOMAIN,
339 				    "socket: protocol failure in circuit "
340 				    "setup.\n"));
341 				goto bad2;
342 			}
343 			break;
344 		case AF_INET6:
345 			sin6 = (struct sockaddr_in6 *)&faddr;
346 			if (ntohs(sin6->sin6_port) >= IPPORT_RESERVED) {
347 				(void) fprintf(stderr,
348 				    dgettext(TEXT_DOMAIN,
349 				    "socket: protocol failure in circuit "
350 				    "setup.\n"));
351 				goto bad2;
352 			}
353 			break;
354 		default:
355 			(void) fprintf(stderr,
356 			    dgettext(TEXT_DOMAIN,
357 			    "socket: protocol failure in circuit setup.\n"));
358 			goto bad2;
359 		}
360 	}
361 	(void) write(s, locuser, strlen(locuser)+1);
362 	(void) write(s, remuser, strlen(remuser)+1);
363 	(void) write(s, cmd, strlen(cmd)+1);
364 	retval = read(s, &c, 1);
365 	if (retval != 1) {
366 		if (retval == 0) {
367 			(void) fprintf(stderr,
368 			    dgettext(TEXT_DOMAIN,
369 			    "Protocol error, %s closed connection\n"),
370 			    *ahost);
371 		} else if (retval < 0) {
372 			perror(*ahost);
373 		} else {
374 			(void) fprintf(stderr,
375 			    dgettext(TEXT_DOMAIN,
376 			    "Protocol error, %s sent %d bytes\n"),
377 			    *ahost, retval);
378 		}
379 		goto bad2;
380 	}
381 	if (c != 0) {
382 		while (read(s, &c, 1) == 1) {
383 			(void) write(2, &c, 1);
384 			if (c == '\n')
385 				break;
386 		}
387 		goto bad2;
388 	}
389 #ifdef SYSV
390 	/* restore original SIGPIPE handler */
391 	(void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0);
392 
393 	/* restore original signal mask */
394 	(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
395 #else
396 	sigsetmask(oldmask);
397 #endif /* SYSV */
398 	freeaddrinfo(resp);
399 	return (s);
400 bad2:
401 	if (lport)
402 		(void) close(*fd2p);
403 bad:
404 	(void) close(s);
405 #ifdef SYSV
406 	/* restore original SIGPIPE handler */
407 	(void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0);
408 
409 	/* restore original signal mask */
410 	(void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
411 #else
412 	sigsetmask(oldmask);
413 #endif /* SYSV */
414 	freeaddrinfo(resp);
415 	return (-1);
416 }
417 
418 static int
419 _rresvport_addr(int *alport, struct sockaddr_storage *addr)
420 {
421 	struct sockaddr_in *sin;
422 	struct sockaddr_in6 *sin6;
423 	int s;
424 	socklen_t len;
425 	int on = 1;
426 	int off = 0;
427 
428 	if (addr->ss_family == AF_INET) {
429 		sin = (struct sockaddr_in *)addr;
430 		len = sizeof (struct sockaddr_in);
431 	} else if (addr->ss_family == AF_INET6) {
432 		sin6 = (struct sockaddr_in6 *)addr;
433 		len = sizeof (struct sockaddr_in6);
434 	} else {
435 		errno = EAFNOSUPPORT;
436 		return (-1);
437 	}
438 	s = socket(addr->ss_family, SOCK_STREAM, 0);
439 	if (s < 0)
440 		return (-1);
441 
442 	/*
443 	 * Set SO_EXCLBIND to get a "unique" port, which is not bound
444 	 * to any other sockets.
445 	 */
446 	if (setsockopt(s, SOL_SOCKET, SO_EXCLBIND, &on, sizeof (on)) < 0) {
447 		(void) close(s);
448 		return (-1);
449 	}
450 
451 	/* Try to bind() to the given port first. */
452 	if (*alport != 0) {
453 		if (addr->ss_family == AF_INET) {
454 			sin->sin_port = htons((ushort_t)*alport);
455 		} else {
456 			sin6->sin6_port = htons((ushort_t)*alport);
457 		}
458 		if (bind(s, (struct sockaddr *)addr, len) >= 0) {
459 			/* To be safe, need to turn off SO_EXCLBIND. */
460 			(void) setsockopt(s, SOL_SOCKET, SO_EXCLBIND, &off,
461 			    sizeof (off));
462 			return (s);
463 		}
464 		if (errno != EADDRINUSE) {
465 			(void) close(s);
466 			return (-1);
467 		}
468 	}
469 
470 	/*
471 	 * If no port is given or the above bind() does not succeed, set
472 	 * TCP_ANONPRIVBIND option to ask the kernel to pick a port in the
473 	 * priviledged range for us.
474 	 */
475 	if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
476 	    sizeof (on)) < 0) {
477 		(void) close(s);
478 		return (-1);
479 	}
480 	if (addr->ss_family == AF_INET) {
481 		sin->sin_port = 0;
482 	} else {
483 		sin6->sin6_port = 0;
484 	}
485 	if (bind(s, (struct sockaddr *)addr, len) >= 0) {
486 		/*
487 		 * We need to tell the caller what the port is.
488 		 */
489 		if (getsockname(s, (struct sockaddr *)addr, &len) < 0) {
490 			(void) close(s);
491 			return (-1);
492 		}
493 		switch (addr->ss_family) {
494 		case AF_INET6:
495 			sin6 = (struct sockaddr_in6 *)addr;
496 			*alport = ntohs(sin6->sin6_port);
497 			break;
498 		case AF_INET:
499 			sin = (struct sockaddr_in *)addr;
500 			*alport = ntohs(sin->sin_port);
501 			break;
502 		}
503 
504 		/*
505 		 * To be safe, always turn off these options when we are done.
506 		 */
507 		(void) setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &off,
508 		    sizeof (off));
509 		(void) setsockopt(s, SOL_SOCKET, SO_EXCLBIND, &off,
510 		    sizeof (off));
511 		return (s);
512 	}
513 	(void) close(s);
514 	return (-1);
515 }
516 
517 int
518 rresvport_addr(int *alport, struct sockaddr_storage *addr)
519 {
520 	int res, err;
521 
522 	(void) __priv_bracket(PRIV_ON);
523 
524 	res = _rresvport_addr(alport, addr);
525 
526 	err = errno;
527 	(void) __priv_bracket(PRIV_OFF);
528 	errno = err;
529 
530 	return (res);
531 }
532 
533 int
534 rresvport_af(int *alport, int af)
535 {
536 	struct sockaddr_storage laddr;
537 
538 	bzero(&laddr, sizeof (laddr));
539 	if (af == AF_INET || af == AF_INET6) {
540 		laddr.ss_family = (sa_family_t)af;
541 	} else {
542 		errno = EAFNOSUPPORT;
543 		return (-1);
544 	}
545 	return (rresvport_addr(alport, &laddr));
546 }
547 
548 int
549 rresvport(int *alport)
550 {
551 	return (rresvport_af(alport, AF_INET));
552 }
553 
554 int
555 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
556 {
557 	FILE *hostf;
558 	char fhost[MAXHOSTNAMELEN];
559 	const char *sp;
560 	char *p;
561 	int baselen = -1;
562 
563 	struct stat64 sbuf;
564 	struct passwd *pwd;
565 	char pbuf[MAXPATHLEN];
566 	uid_t uid = (uid_t)-1;
567 	gid_t gid = (gid_t)-1;
568 	gid_t grouplist[NGROUPS_MAX];
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(NGROUPS_MAX, 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] = NULL;
803 		if ((domainp = index(ldomain, '.')) == (char *)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