rcmd.c revision 55918
11573Srgrimes/*
21573Srgrimes * Copyright (c) 1983, 1993, 1994
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * Redistribution and use in source and binary forms, with or without
61573Srgrimes * modification, are permitted provided that the following conditions
71573Srgrimes * are met:
81573Srgrimes * 1. Redistributions of source code must retain the above copyright
91573Srgrimes *    notice, this list of conditions and the following disclaimer.
101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111573Srgrimes *    notice, this list of conditions and the following disclaimer in the
121573Srgrimes *    documentation and/or other materials provided with the distribution.
131573Srgrimes * 3. All advertising materials mentioning features or use of this software
141573Srgrimes *    must display the following acknowledgement:
151573Srgrimes *	This product includes software developed by the University of
161573Srgrimes *	California, Berkeley and its contributors.
171573Srgrimes * 4. Neither the name of the University nor the names of its contributors
181573Srgrimes *    may be used to endorse or promote products derived from this software
191573Srgrimes *    without specific prior written permission.
201573Srgrimes *
211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311573Srgrimes * SUCH DAMAGE.
3252859Sache *
3352859Sache * $FreeBSD: head/lib/libc/net/rcmd.c 55918 2000-01-13 15:09:48Z shin $
341573Srgrimes */
351573Srgrimes
361573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
371573Srgrimesstatic char sccsid[] = "@(#)rcmd.c	8.3 (Berkeley) 3/26/94";
381573Srgrimes#endif /* LIBC_SCCS and not lint */
391573Srgrimes
401573Srgrimes#include <sys/param.h>
411573Srgrimes#include <sys/socket.h>
421573Srgrimes#include <sys/stat.h>
431573Srgrimes
441573Srgrimes#include <netinet/in.h>
451573Srgrimes#include <arpa/inet.h>
461573Srgrimes
471573Srgrimes#include <signal.h>
481573Srgrimes#include <fcntl.h>
491573Srgrimes#include <netdb.h>
501573Srgrimes#include <unistd.h>
511573Srgrimes#include <pwd.h>
521573Srgrimes#include <errno.h>
531573Srgrimes#include <stdio.h>
541573Srgrimes#include <ctype.h>
551573Srgrimes#include <string.h>
569978Swpaul#ifdef YP
579978Swpaul#include <rpc/rpc.h>
589978Swpaul#include <rpcsvc/yp_prot.h>
599978Swpaul#include <rpcsvc/ypclnt.h>
609978Swpaul#endif
611573Srgrimes
6255918Sshin/* wrapper for KAME-special getnameinfo() */
6355918Sshin#ifndef NI_WITHSCOPEID
6455918Sshin#define NI_WITHSCOPEID	0
6555918Sshin#endif
6655918Sshin
6717141Sjkhextern int innetgr __P(( const char *, const char *, const char *, const char * ));
6817141Sjkh
692592Scsgr#define max(a, b)	((a > b) ? a : b)
702592Scsgr
7139979Sdfrint	__ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
7255918Sshinstatic int __icheckhost __P((void *, char *, int, int));
731573Srgrimes
7455918Sshinchar paddr[INET6_ADDRSTRLEN];
7555918Sshin
761573Srgrimesint
771573Srgrimesrcmd(ahost, rport, locuser, remuser, cmd, fd2p)
781573Srgrimes	char **ahost;
791573Srgrimes	u_short rport;
801573Srgrimes	const char *locuser, *remuser, *cmd;
811573Srgrimes	int *fd2p;
821573Srgrimes{
8355918Sshin	struct addrinfo hints, *res, *ai;
8455918Sshin	struct sockaddr_storage from;
851573Srgrimes	fd_set reads;
861573Srgrimes	long oldmask;
871573Srgrimes	pid_t pid;
8855918Sshin	int s, aport, lport, timo, error;
891573Srgrimes	char c;
9055918Sshin	int refused;
9155918Sshin	char num[8];
921573Srgrimes
931573Srgrimes	pid = getpid();
9455918Sshin
9555918Sshin	memset(&hints, 0, sizeof(hints));
9655918Sshin	hints.ai_flags = AI_CANONNAME;
9755918Sshin	hints.ai_family = AF_UNSPEC;
9855918Sshin	hints.ai_socktype = SOCK_STREAM;
9955918Sshin	hints.ai_protocol = 0;
10055918Sshin	(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
10155918Sshin	error = getaddrinfo(*ahost, num, &hints, &res);
10255918Sshin	if (error) {
10355918Sshin		fprintf(stderr, "rcmd: getaddrinfo: %s\n",
10455918Sshin			gai_strerror(error));
10555918Sshin		if (error == EAI_SYSTEM)
10655918Sshin			fprintf(stderr, "rcmd: getaddrinfo: %s\n",
10755918Sshin				strerror(errno));
1081573Srgrimes		return (-1);
1091573Srgrimes	}
11055918Sshin	if (res->ai_canonname)
11155918Sshin		*ahost = res->ai_canonname;
11255918Sshin	ai = res;
11355918Sshin	refused = 0;
1141573Srgrimes	oldmask = sigblock(sigmask(SIGURG));
1151573Srgrimes	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
11655918Sshin		s = rresvport_af(&lport, ai->ai_family);
1171573Srgrimes		if (s < 0) {
1181573Srgrimes			if (errno == EAGAIN)
1191573Srgrimes				(void)fprintf(stderr,
1201573Srgrimes				    "rcmd: socket: All ports in use\n");
1211573Srgrimes			else
1221573Srgrimes				(void)fprintf(stderr, "rcmd: socket: %s\n",
1231573Srgrimes				    strerror(errno));
1241573Srgrimes			sigsetmask(oldmask);
12555918Sshin			freeaddrinfo(res);
1261573Srgrimes			return (-1);
1271573Srgrimes		}
12855837Sjasone		_libc_fcntl(s, F_SETOWN, pid);
12955918Sshin		if (connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
1301573Srgrimes			break;
13155837Sjasone		(void)_libc_close(s);
1321573Srgrimes		if (errno == EADDRINUSE) {
1331573Srgrimes			lport--;
1341573Srgrimes			continue;
1351573Srgrimes		}
13655918Sshin		if (errno == ECONNREFUSED)
13755918Sshin			refused = 1;
13855918Sshin		if (ai->ai_next != NULL) {
1391573Srgrimes			int oerrno = errno;
1401573Srgrimes
14155918Sshin			getnameinfo(ai->ai_addr, ai->ai_addrlen,
14255918Sshin				    paddr, sizeof(paddr),
14355918Sshin				    NULL, 0,
14455918Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
1451573Srgrimes			(void)fprintf(stderr, "connect to address %s: ",
14655918Sshin				      paddr);
1471573Srgrimes			errno = oerrno;
1481573Srgrimes			perror(0);
14955918Sshin			ai = ai->ai_next;
15055918Sshin			getnameinfo(ai->ai_addr, ai->ai_addrlen,
15155918Sshin				    paddr, sizeof(paddr),
15255918Sshin				    NULL, 0,
15355918Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
15455918Sshin			fprintf(stderr, "Trying %s...\n", paddr);
1551573Srgrimes			continue;
1561573Srgrimes		}
15755918Sshin		if (refused && timo <= 16) {
15855918Sshin			(void)_libc_sleep(timo);
15955918Sshin			timo *= 2;
16055918Sshin			ai = res;
16155918Sshin			refused = 0;
16255918Sshin			continue;
16355918Sshin		}
16455918Sshin		freeaddrinfo(res);
16555918Sshin		(void)fprintf(stderr, "%s: %s\n", *ahost, strerror(errno));
1661573Srgrimes		sigsetmask(oldmask);
1671573Srgrimes		return (-1);
1681573Srgrimes	}
1691573Srgrimes	lport--;
1701573Srgrimes	if (fd2p == 0) {
17155837Sjasone		_libc_write(s, "", 1);
1721573Srgrimes		lport = 0;
1731573Srgrimes	} else {
1741573Srgrimes		char num[8];
17555918Sshin		int s2 = rresvport_af(&lport, ai->ai_family), s3;
17655918Sshin		int len = ai->ai_addrlen;
1772592Scsgr		int nfds;
1781573Srgrimes
1791573Srgrimes		if (s2 < 0)
1801573Srgrimes			goto bad;
1811573Srgrimes		listen(s2, 1);
1821573Srgrimes		(void)snprintf(num, sizeof(num), "%d", lport);
18355837Sjasone		if (_libc_write(s, num, strlen(num)+1) != strlen(num)+1) {
1841573Srgrimes			(void)fprintf(stderr,
1851573Srgrimes			    "rcmd: write (setting up stderr): %s\n",
1861573Srgrimes			    strerror(errno));
18755837Sjasone			(void)_libc_close(s2);
1881573Srgrimes			goto bad;
1891573Srgrimes		}
1902592Scsgr		nfds = max(s, s2)+1;
1912592Scsgr		if(nfds > FD_SETSIZE) {
1922592Scsgr			fprintf(stderr, "rcmd: too many files\n");
19355837Sjasone			(void)_libc_close(s2);
1942592Scsgr			goto bad;
1952592Scsgr		}
19617543Speteragain:
1971573Srgrimes		FD_ZERO(&reads);
1981573Srgrimes		FD_SET(s, &reads);
1991573Srgrimes		FD_SET(s2, &reads);
2001573Srgrimes		errno = 0;
2012592Scsgr		if (select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){
2021573Srgrimes			if (errno != 0)
2031573Srgrimes				(void)fprintf(stderr,
2041573Srgrimes				    "rcmd: select (setting up stderr): %s\n",
2051573Srgrimes				    strerror(errno));
2061573Srgrimes			else
2071573Srgrimes				(void)fprintf(stderr,
2081573Srgrimes				"select: protocol failure in circuit setup\n");
20955837Sjasone			(void)_libc_close(s2);
2101573Srgrimes			goto bad;
2111573Srgrimes		}
2121573Srgrimes		s3 = accept(s2, (struct sockaddr *)&from, &len);
21355918Sshin		switch (from.ss_family) {
21455918Sshin		case AF_INET:
21555918Sshin			aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
21655918Sshin			break;
21755918Sshin#ifdef INET6
21855918Sshin		case AF_INET6:
21955918Sshin			aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
22055918Sshin			break;
22155918Sshin#endif
22255918Sshin		default:
22355918Sshin			aport = 0;	/* error */
22455918Sshin			break;
22555918Sshin		}
22617543Speter		/*
22717543Speter		 * XXX careful for ftp bounce attacks. If discovered, shut them
22817543Speter		 * down and check for the real auxiliary channel to connect.
22917543Speter		 */
23055918Sshin		if (aport == 20) {
23155837Sjasone			_libc_close(s3);
23217543Speter			goto again;
23317543Speter		}
23455837Sjasone		(void)_libc_close(s2);
2351573Srgrimes		if (s3 < 0) {
2361573Srgrimes			(void)fprintf(stderr,
2371573Srgrimes			    "rcmd: accept: %s\n", strerror(errno));
2381573Srgrimes			lport = 0;
2391573Srgrimes			goto bad;
2401573Srgrimes		}
2411573Srgrimes		*fd2p = s3;
24255918Sshin		if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
2431573Srgrimes			(void)fprintf(stderr,
2441573Srgrimes			    "socket: protocol failure in circuit setup.\n");
2451573Srgrimes			goto bad2;
2461573Srgrimes		}
2471573Srgrimes	}
24855837Sjasone	(void)_libc_write(s, locuser, strlen(locuser)+1);
24955837Sjasone	(void)_libc_write(s, remuser, strlen(remuser)+1);
25055837Sjasone	(void)_libc_write(s, cmd, strlen(cmd)+1);
25155837Sjasone	if (_libc_read(s, &c, 1) != 1) {
2521573Srgrimes		(void)fprintf(stderr,
2531573Srgrimes		    "rcmd: %s: %s\n", *ahost, strerror(errno));
2541573Srgrimes		goto bad2;
2551573Srgrimes	}
2561573Srgrimes	if (c != 0) {
25755837Sjasone		while (_libc_read(s, &c, 1) == 1) {
25855837Sjasone			(void)_libc_write(STDERR_FILENO, &c, 1);
2591573Srgrimes			if (c == '\n')
2601573Srgrimes				break;
2611573Srgrimes		}
2621573Srgrimes		goto bad2;
2631573Srgrimes	}
2641573Srgrimes	sigsetmask(oldmask);
26555918Sshin	freeaddrinfo(res);
2661573Srgrimes	return (s);
2671573Srgrimesbad2:
2681573Srgrimes	if (lport)
26955837Sjasone		(void)_libc_close(*fd2p);
2701573Srgrimesbad:
27155837Sjasone	(void)_libc_close(s);
2721573Srgrimes	sigsetmask(oldmask);
27355918Sshin	freeaddrinfo(res);
2741573Srgrimes	return (-1);
2751573Srgrimes}
2761573Srgrimes
2771573Srgrimesint
27855918Sshinrresvport(port)
27955918Sshin	int *port;
2801573Srgrimes{
28155918Sshin	return rresvport_af(port, AF_INET);
28255918Sshin}
2831573Srgrimes
28455918Sshinint
28555918Sshinrresvport_af(alport, family)
28655918Sshin	int *alport, family;
28755918Sshin{
28855918Sshin	int i, s, len, err;
28955918Sshin	struct sockaddr_storage ss;
29055918Sshin	u_short *sport;
29155918Sshin
29255918Sshin	memset(&ss, 0, sizeof(ss));
29355918Sshin	ss.ss_family = family;
29455918Sshin	switch (family) {
29555918Sshin	case AF_INET:
29655918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
29755918Sshin		sport = &((struct sockaddr_in *)&ss)->sin_port;
29855918Sshin		((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
29955918Sshin		break;
30055918Sshin#ifdef INET6
30155918Sshin	case AF_INET6:
30255918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
30355918Sshin		sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
30455918Sshin		((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
30555918Sshin		break;
30655918Sshin#endif
30755918Sshin	default:
30855918Sshin		errno = EAFNOSUPPORT;
30955918Sshin		return -1;
31055918Sshin	}
31155918Sshin
31255918Sshin	s = socket(ss.ss_family, SOCK_STREAM, 0);
3131573Srgrimes	if (s < 0)
3141573Srgrimes		return (-1);
31517543Speter#if 0 /* compat_exact_traditional_rresvport_semantics */
31617543Speter	sin.sin_port = htons((u_short)*alport);
31717543Speter	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
31817543Speter		return (s);
31917543Speter	if (errno != EADDRINUSE) {
32055837Sjasone		(void)_libc_close(s);
32117543Speter		return (-1);
32216034Speter	}
32317543Speter#endif
32455918Sshin	*sport = 0;
32555918Sshin	if (bindresvport2(s, (struct sockaddr *)&ss,
32655918Sshin			  ((struct sockaddr *)&ss)->sa_len) == -1) {
32755837Sjasone		(void)_libc_close(s);
32817543Speter		return (-1);
3291573Srgrimes	}
33055918Sshin	*alport = (int)ntohs(*sport);
33117543Speter	return (s);
3321573Srgrimes}
3331573Srgrimes
3341573Srgrimesint	__check_rhosts_file = 1;
3351573Srgrimeschar	*__rcmd_errstr;
3361573Srgrimes
3371573Srgrimesint
3381573Srgrimesruserok(rhost, superuser, ruser, luser)
3391573Srgrimes	const char *rhost, *ruser, *luser;
3401573Srgrimes	int superuser;
3411573Srgrimes{
34255918Sshin	return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
34355918Sshin}
34455918Sshin
34555918Sshinint
34655918Sshinruserok_af(rhost, superuser, ruser, luser, af)
34755918Sshin	const char *rhost, *ruser, *luser;
34855918Sshin	int superuser, af;
34955918Sshin{
3501573Srgrimes	struct hostent *hp;
35155918Sshin	union {
35255918Sshin		struct in_addr addr_in;
35355918Sshin		struct in6_addr addr_in6;
35455918Sshin	} addr;
3551573Srgrimes	char **ap;
35655918Sshin	int ret, h_error;
3571573Srgrimes
35855918Sshin	if ((hp = getipnodebyname(rhost, af, AI_DEFAULT, &h_error)) == NULL)
3591573Srgrimes		return (-1);
36055918Sshin	ret = -1;
3611573Srgrimes	for (ap = hp->h_addr_list; *ap; ++ap) {
36255918Sshin		bcopy(*ap, &addr, hp->h_length);
36355918Sshin		if (iruserok_af(&addr, superuser, ruser, luser, af) == 0) {
36455918Sshin			ret = 0;
36555918Sshin			break;
36655918Sshin		}
3671573Srgrimes	}
36855918Sshin	freehostent(hp);
36955918Sshin	return (ret);
3701573Srgrimes}
3711573Srgrimes
3721573Srgrimes/*
3731573Srgrimes * New .rhosts strategy: We are passed an ip address. We spin through
3741573Srgrimes * hosts.equiv and .rhosts looking for a match. When the .rhosts only
3751573Srgrimes * has ip addresses, we don't have to trust a nameserver.  When it
3761573Srgrimes * contains hostnames, we spin through the list of addresses the nameserver
3771573Srgrimes * gives us and look for a match.
3781573Srgrimes *
3791573Srgrimes * Returns 0 if ok, -1 if not ok.
3801573Srgrimes */
3811573Srgrimesint
3821573Srgrimesiruserok(raddr, superuser, ruser, luser)
38339979Sdfr	unsigned long raddr;
3841573Srgrimes	int superuser;
3851573Srgrimes	const char *ruser, *luser;
3861573Srgrimes{
38755918Sshin	return iruserok_af(&raddr, superuser, ruser, luser, AF_INET);
38855918Sshin}
38955918Sshin
39055918Sshinint
39155918Sshiniruserok_af(raddr, superuser, ruser, luser, af)
39255918Sshin	void *raddr;
39355918Sshin	int superuser;
39455918Sshin	const char *ruser, *luser;
39555918Sshin	int af;
39655918Sshin{
3971573Srgrimes	register char *cp;
3981573Srgrimes	struct stat sbuf;
3991573Srgrimes	struct passwd *pwd;
4001573Srgrimes	FILE *hostf;
4011573Srgrimes	uid_t uid;
4021573Srgrimes	int first;
4031573Srgrimes	char pbuf[MAXPATHLEN];
40455918Sshin	int len = 0;
4051573Srgrimes
40655918Sshin	switch (af) {
40755918Sshin	case AF_INET:
40855918Sshin		len = sizeof(struct in_addr);
40955918Sshin		break;
41055918Sshin#ifdef INET6
41155918Sshin	case AF_INET6:
41255918Sshin		len = sizeof(struct in6_addr);
41355918Sshin		break;
41455918Sshin#endif
41555918Sshin	}
41655918Sshin
4171573Srgrimes	first = 1;
4181573Srgrimes	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
4191573Srgrimesagain:
4201573Srgrimes	if (hostf) {
42155918Sshin		if (__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
42255918Sshin		    == 0) {
4231573Srgrimes			(void)fclose(hostf);
4241573Srgrimes			return (0);
4251573Srgrimes		}
4261573Srgrimes		(void)fclose(hostf);
4271573Srgrimes	}
4281573Srgrimes	if (first == 1 && (__check_rhosts_file || superuser)) {
4291573Srgrimes		first = 0;
4301573Srgrimes		if ((pwd = getpwnam(luser)) == NULL)
4311573Srgrimes			return (-1);
4321573Srgrimes		(void)strcpy(pbuf, pwd->pw_dir);
4331573Srgrimes		(void)strcat(pbuf, "/.rhosts");
4341573Srgrimes
4351573Srgrimes		/*
4361573Srgrimes		 * Change effective uid while opening .rhosts.  If root and
4371573Srgrimes		 * reading an NFS mounted file system, can't read files that
4381573Srgrimes		 * are protected read/write owner only.
4391573Srgrimes		 */
4401573Srgrimes		uid = geteuid();
4411573Srgrimes		(void)seteuid(pwd->pw_uid);
4421573Srgrimes		hostf = fopen(pbuf, "r");
4431573Srgrimes		(void)seteuid(uid);
4441573Srgrimes
4451573Srgrimes		if (hostf == NULL)
4461573Srgrimes			return (-1);
4471573Srgrimes		/*
4481573Srgrimes		 * If not a regular file, or is owned by someone other than
4491573Srgrimes		 * user or root or if writeable by anyone but the owner, quit.
4501573Srgrimes		 */
4511573Srgrimes		cp = NULL;
4521573Srgrimes		if (lstat(pbuf, &sbuf) < 0)
4531573Srgrimes			cp = ".rhosts lstat failed";
4541573Srgrimes		else if (!S_ISREG(sbuf.st_mode))
4551573Srgrimes			cp = ".rhosts not regular file";
4561573Srgrimes		else if (fstat(fileno(hostf), &sbuf) < 0)
4571573Srgrimes			cp = ".rhosts fstat failed";
4581573Srgrimes		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
4591573Srgrimes			cp = "bad .rhosts owner";
4601573Srgrimes		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
4611573Srgrimes			cp = ".rhosts writeable by other than owner";
4621573Srgrimes		/* If there were any problems, quit. */
4631573Srgrimes		if (cp) {
4641573Srgrimes			__rcmd_errstr = cp;
4651573Srgrimes			(void)fclose(hostf);
4661573Srgrimes			return (-1);
4671573Srgrimes		}
4681573Srgrimes		goto again;
4691573Srgrimes	}
4701573Srgrimes	return (-1);
4711573Srgrimes}
4721573Srgrimes
4731573Srgrimes/*
4741573Srgrimes * XXX
4751573Srgrimes * Don't make static, used by lpd(8).
4761573Srgrimes *
4771573Srgrimes * Returns 0 if ok, -1 if not ok.
4781573Srgrimes */
4791573Srgrimesint
4801573Srgrimes__ivaliduser(hostf, raddr, luser, ruser)
4811573Srgrimes	FILE *hostf;
48239979Sdfr	u_int32_t raddr;
4831573Srgrimes	const char *luser, *ruser;
4841573Srgrimes{
48555918Sshin	return __ivaliduser_af(hostf, &raddr, luser, ruser, AF_INET,
48655918Sshin			       sizeof(raddr));
48755918Sshin}
48855918Sshin
48955918Sshinint
49055918Sshin__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
49155918Sshin	FILE *hostf;
49255918Sshin	void *raddr;
49355918Sshin	const char *luser, *ruser;
49455918Sshin	int af, len;
49555918Sshin{
4961573Srgrimes	register char *user, *p;
4971573Srgrimes	int ch;
4981573Srgrimes	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
49910059Swpaul	char hname[MAXHOSTNAMELEN];
5007183Swpaul	struct hostent *hp;
5017183Swpaul	/* Presumed guilty until proven innocent. */
5027183Swpaul	int userok = 0, hostok = 0;
50355918Sshin	int h_error;
5049978Swpaul#ifdef YP
5059978Swpaul	char *ypdomain;
5061573Srgrimes
5079978Swpaul	if (yp_get_default_domain(&ypdomain))
5089978Swpaul		ypdomain = NULL;
5099978Swpaul#else
5109978Swpaul#define	ypdomain NULL
5119978Swpaul#endif
5127183Swpaul	/* We need to get the damn hostname back for netgroup matching. */
51355918Sshin	if ((hp = getipnodebyaddr((char *)raddr, len, af, &h_error)) == NULL)
5147183Swpaul		return (-1);
51523128Simp	strncpy(hname, hp->h_name, sizeof(hname));
51623128Simp	hname[sizeof(hname) - 1] = '\0';
51755918Sshin	freehostent(hp);
5187183Swpaul
5191573Srgrimes	while (fgets(buf, sizeof(buf), hostf)) {
5201573Srgrimes		p = buf;
5211573Srgrimes		/* Skip lines that are too long. */
5221573Srgrimes		if (strchr(p, '\n') == NULL) {
5231573Srgrimes			while ((ch = getc(hostf)) != '\n' && ch != EOF);
5241573Srgrimes			continue;
5251573Srgrimes		}
5269552Speter		if (*p == '\n' || *p == '#') {
5279552Speter			/* comment... */
5289552Speter			continue;
5299552Speter		}
5301573Srgrimes		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
53152859Sache			*p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p;
5321573Srgrimes			p++;
5331573Srgrimes		}
5341573Srgrimes		if (*p == ' ' || *p == '\t') {
5351573Srgrimes			*p++ = '\0';
5361573Srgrimes			while (*p == ' ' || *p == '\t')
5371573Srgrimes				p++;
5381573Srgrimes			user = p;
5391573Srgrimes			while (*p != '\n' && *p != ' ' &&
5401573Srgrimes			    *p != '\t' && *p != '\0')
5411573Srgrimes				p++;
5421573Srgrimes		} else
5431573Srgrimes			user = p;
5441573Srgrimes		*p = '\0';
5457183Swpaul		/*
5467183Swpaul		 * Do +/- and +@/-@ checking. This looks really nasty,
5477183Swpaul		 * but it matches SunOS's behavior so far as I can tell.
5487183Swpaul		 */
5497183Swpaul		switch(buf[0]) {
5507183Swpaul		case '+':
5517183Swpaul			if (!buf[1]) {     /* '+' matches all hosts */
5527183Swpaul				hostok = 1;
5537183Swpaul				break;
5547183Swpaul			}
5557183Swpaul			if (buf[1] == '@')  /* match a host by netgroup */
55610059Swpaul				hostok = innetgr((char *)&buf[2],
55710059Swpaul					(char *)&hname, NULL, ypdomain);
5587183Swpaul			else		/* match a host by addr */
55955918Sshin				hostok = __icheckhost(raddr,(char *)&buf[1],
56055918Sshin						      af, len);
5617183Swpaul			break;
5627183Swpaul		case '-':     /* reject '-' hosts and all their users */
5637183Swpaul			if (buf[1] == '@') {
5647183Swpaul				if (innetgr((char *)&buf[2],
56510059Swpaul					      (char *)&hname, NULL, ypdomain))
5667183Swpaul					return(-1);
5677183Swpaul			} else {
56855918Sshin				if (__icheckhost(raddr,(char *)&buf[1],af,len))
5697183Swpaul					return(-1);
5707183Swpaul			}
5717183Swpaul			break;
5727183Swpaul		default:  /* if no '+' or '-', do a simple match */
57355918Sshin			hostok = __icheckhost(raddr, buf, af, len);
5747183Swpaul			break;
5751573Srgrimes		}
5767183Swpaul		switch(*user) {
5777183Swpaul		case '+':
5787183Swpaul			if (!*(user+1)) {      /* '+' matches all users */
5797183Swpaul				userok = 1;
5807183Swpaul				break;
5817183Swpaul			}
5827183Swpaul			if (*(user+1) == '@')  /* match a user by netgroup */
5839978Swpaul				userok = innetgr(user+2, NULL, ruser, ypdomain);
5847183Swpaul			else	   /* match a user by direct specification */
5857183Swpaul				userok = !(strcmp(ruser, user+1));
5867183Swpaul			break;
5877183Swpaul		case '-': 		/* if we matched a hostname, */
5887183Swpaul			if (hostok) {   /* check for user field rejections */
5897183Swpaul				if (!*(user+1))
5907183Swpaul					return(-1);
5917183Swpaul				if (*(user+1) == '@') {
5927183Swpaul					if (innetgr(user+2, NULL,
5939978Swpaul							ruser, ypdomain))
5947183Swpaul						return(-1);
5957183Swpaul				} else {
5967183Swpaul					if (!strcmp(ruser, user+1))
5977183Swpaul						return(-1);
5987183Swpaul				}
5997183Swpaul			}
6007183Swpaul			break;
6017183Swpaul		default:	/* no rejections: try to match the user */
6027183Swpaul			if (hostok)
6037183Swpaul				userok = !(strcmp(ruser,*user ? user : luser));
6047183Swpaul			break;
6057183Swpaul		}
6067183Swpaul		if (hostok && userok)
6077183Swpaul			return(0);
6081573Srgrimes	}
6091573Srgrimes	return (-1);
6101573Srgrimes}
6111573Srgrimes
6121573Srgrimes/*
6131573Srgrimes * Returns "true" if match, 0 if no match.
6141573Srgrimes */
6151573Srgrimesstatic int
61655918Sshin__icheckhost(raddr, lhost, af, len)
61755918Sshin	void *raddr;
6181573Srgrimes	register char *lhost;
61955918Sshin	int af, len;
6201573Srgrimes{
6211573Srgrimes	register struct hostent *hp;
62255918Sshin	char laddr[BUFSIZ]; /* xxx */
6231573Srgrimes	register char **pp;
62455918Sshin	int h_error;
62555918Sshin	int match;
6261573Srgrimes
6271573Srgrimes	/* Try for raw ip address first. */
62855918Sshin	if (inet_pton(af, lhost, laddr) == 1) {
62955918Sshin		if (memcmp(raddr, laddr, len) == 0)
63055918Sshin			return (1);
63155918Sshin		else
63255918Sshin			return (0);
63355918Sshin	}
6341573Srgrimes
6351573Srgrimes	/* Better be a hostname. */
63655918Sshin	if ((hp = getipnodebyname(lhost, af, AI_DEFAULT, &h_error)) == NULL)
6371573Srgrimes		return (0);
6381573Srgrimes
6391573Srgrimes	/* Spin through ip addresses. */
64055918Sshin	match = 0;
6411573Srgrimes	for (pp = hp->h_addr_list; *pp; ++pp)
64255918Sshin		if (!bcmp(raddr, *pp, len)) {
64355918Sshin			match = 1;
64455918Sshin			break;
64555918Sshin		}
6461573Srgrimes
64755918Sshin	freehostent(hp);
64855918Sshin	return (match);
6491573Srgrimes}
650