rcmd.c revision 56590
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 56590 2000-01-25 14:52:10Z 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{
8356590Sshin	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
8456590Sshin}
8556590Sshin
8656590Sshinint
8756590Sshinrcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
8856590Sshin	char **ahost;
8956590Sshin	u_short rport;
9056590Sshin	const char *locuser, *remuser, *cmd;
9156590Sshin	int *fd2p;
9256590Sshin	int af;
9356590Sshin{
9455918Sshin	struct addrinfo hints, *res, *ai;
9555918Sshin	struct sockaddr_storage from;
961573Srgrimes	fd_set reads;
971573Srgrimes	long oldmask;
981573Srgrimes	pid_t pid;
9955918Sshin	int s, aport, lport, timo, error;
1001573Srgrimes	char c;
10155918Sshin	int refused;
10255918Sshin	char num[8];
1031573Srgrimes
1041573Srgrimes	pid = getpid();
10555918Sshin
10655918Sshin	memset(&hints, 0, sizeof(hints));
10755918Sshin	hints.ai_flags = AI_CANONNAME;
10856590Sshin	hints.ai_family = af;
10955918Sshin	hints.ai_socktype = SOCK_STREAM;
11055918Sshin	hints.ai_protocol = 0;
11155918Sshin	(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
11255918Sshin	error = getaddrinfo(*ahost, num, &hints, &res);
11355918Sshin	if (error) {
11455918Sshin		fprintf(stderr, "rcmd: getaddrinfo: %s\n",
11555918Sshin			gai_strerror(error));
11655918Sshin		if (error == EAI_SYSTEM)
11755918Sshin			fprintf(stderr, "rcmd: getaddrinfo: %s\n",
11855918Sshin				strerror(errno));
1191573Srgrimes		return (-1);
1201573Srgrimes	}
12155918Sshin	if (res->ai_canonname)
12255918Sshin		*ahost = res->ai_canonname;
12355918Sshin	ai = res;
12455918Sshin	refused = 0;
1251573Srgrimes	oldmask = sigblock(sigmask(SIGURG));
1261573Srgrimes	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
12755918Sshin		s = rresvport_af(&lport, ai->ai_family);
1281573Srgrimes		if (s < 0) {
1291573Srgrimes			if (errno == EAGAIN)
1301573Srgrimes				(void)fprintf(stderr,
1311573Srgrimes				    "rcmd: socket: All ports in use\n");
1321573Srgrimes			else
1331573Srgrimes				(void)fprintf(stderr, "rcmd: socket: %s\n",
1341573Srgrimes				    strerror(errno));
1351573Srgrimes			sigsetmask(oldmask);
13655918Sshin			freeaddrinfo(res);
1371573Srgrimes			return (-1);
1381573Srgrimes		}
13955837Sjasone		_libc_fcntl(s, F_SETOWN, pid);
14055918Sshin		if (connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
1411573Srgrimes			break;
14255837Sjasone		(void)_libc_close(s);
1431573Srgrimes		if (errno == EADDRINUSE) {
1441573Srgrimes			lport--;
1451573Srgrimes			continue;
1461573Srgrimes		}
14755918Sshin		if (errno == ECONNREFUSED)
14855918Sshin			refused = 1;
14955918Sshin		if (ai->ai_next != NULL) {
1501573Srgrimes			int oerrno = errno;
1511573Srgrimes
15255918Sshin			getnameinfo(ai->ai_addr, ai->ai_addrlen,
15355918Sshin				    paddr, sizeof(paddr),
15455918Sshin				    NULL, 0,
15555918Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
1561573Srgrimes			(void)fprintf(stderr, "connect to address %s: ",
15755918Sshin				      paddr);
1581573Srgrimes			errno = oerrno;
1591573Srgrimes			perror(0);
16055918Sshin			ai = ai->ai_next;
16155918Sshin			getnameinfo(ai->ai_addr, ai->ai_addrlen,
16255918Sshin				    paddr, sizeof(paddr),
16355918Sshin				    NULL, 0,
16455918Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
16555918Sshin			fprintf(stderr, "Trying %s...\n", paddr);
1661573Srgrimes			continue;
1671573Srgrimes		}
16855918Sshin		if (refused && timo <= 16) {
16955918Sshin			(void)_libc_sleep(timo);
17055918Sshin			timo *= 2;
17155918Sshin			ai = res;
17255918Sshin			refused = 0;
17355918Sshin			continue;
17455918Sshin		}
17555918Sshin		freeaddrinfo(res);
17655918Sshin		(void)fprintf(stderr, "%s: %s\n", *ahost, strerror(errno));
1771573Srgrimes		sigsetmask(oldmask);
1781573Srgrimes		return (-1);
1791573Srgrimes	}
1801573Srgrimes	lport--;
1811573Srgrimes	if (fd2p == 0) {
18255837Sjasone		_libc_write(s, "", 1);
1831573Srgrimes		lport = 0;
1841573Srgrimes	} else {
1851573Srgrimes		char num[8];
18655918Sshin		int s2 = rresvport_af(&lport, ai->ai_family), s3;
18755918Sshin		int len = ai->ai_addrlen;
1882592Scsgr		int nfds;
1891573Srgrimes
1901573Srgrimes		if (s2 < 0)
1911573Srgrimes			goto bad;
1921573Srgrimes		listen(s2, 1);
1931573Srgrimes		(void)snprintf(num, sizeof(num), "%d", lport);
19455837Sjasone		if (_libc_write(s, num, strlen(num)+1) != strlen(num)+1) {
1951573Srgrimes			(void)fprintf(stderr,
1961573Srgrimes			    "rcmd: write (setting up stderr): %s\n",
1971573Srgrimes			    strerror(errno));
19855837Sjasone			(void)_libc_close(s2);
1991573Srgrimes			goto bad;
2001573Srgrimes		}
2012592Scsgr		nfds = max(s, s2)+1;
2022592Scsgr		if(nfds > FD_SETSIZE) {
2032592Scsgr			fprintf(stderr, "rcmd: too many files\n");
20455837Sjasone			(void)_libc_close(s2);
2052592Scsgr			goto bad;
2062592Scsgr		}
20717543Speteragain:
2081573Srgrimes		FD_ZERO(&reads);
2091573Srgrimes		FD_SET(s, &reads);
2101573Srgrimes		FD_SET(s2, &reads);
2111573Srgrimes		errno = 0;
2122592Scsgr		if (select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){
2131573Srgrimes			if (errno != 0)
2141573Srgrimes				(void)fprintf(stderr,
2151573Srgrimes				    "rcmd: select (setting up stderr): %s\n",
2161573Srgrimes				    strerror(errno));
2171573Srgrimes			else
2181573Srgrimes				(void)fprintf(stderr,
2191573Srgrimes				"select: protocol failure in circuit setup\n");
22055837Sjasone			(void)_libc_close(s2);
2211573Srgrimes			goto bad;
2221573Srgrimes		}
2231573Srgrimes		s3 = accept(s2, (struct sockaddr *)&from, &len);
22455918Sshin		switch (from.ss_family) {
22555918Sshin		case AF_INET:
22655918Sshin			aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
22755918Sshin			break;
22855918Sshin#ifdef INET6
22955918Sshin		case AF_INET6:
23055918Sshin			aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
23155918Sshin			break;
23255918Sshin#endif
23355918Sshin		default:
23455918Sshin			aport = 0;	/* error */
23555918Sshin			break;
23655918Sshin		}
23717543Speter		/*
23817543Speter		 * XXX careful for ftp bounce attacks. If discovered, shut them
23917543Speter		 * down and check for the real auxiliary channel to connect.
24017543Speter		 */
24155918Sshin		if (aport == 20) {
24255837Sjasone			_libc_close(s3);
24317543Speter			goto again;
24417543Speter		}
24555837Sjasone		(void)_libc_close(s2);
2461573Srgrimes		if (s3 < 0) {
2471573Srgrimes			(void)fprintf(stderr,
2481573Srgrimes			    "rcmd: accept: %s\n", strerror(errno));
2491573Srgrimes			lport = 0;
2501573Srgrimes			goto bad;
2511573Srgrimes		}
2521573Srgrimes		*fd2p = s3;
25355918Sshin		if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
2541573Srgrimes			(void)fprintf(stderr,
2551573Srgrimes			    "socket: protocol failure in circuit setup.\n");
2561573Srgrimes			goto bad2;
2571573Srgrimes		}
2581573Srgrimes	}
25955837Sjasone	(void)_libc_write(s, locuser, strlen(locuser)+1);
26055837Sjasone	(void)_libc_write(s, remuser, strlen(remuser)+1);
26155837Sjasone	(void)_libc_write(s, cmd, strlen(cmd)+1);
26255837Sjasone	if (_libc_read(s, &c, 1) != 1) {
2631573Srgrimes		(void)fprintf(stderr,
2641573Srgrimes		    "rcmd: %s: %s\n", *ahost, strerror(errno));
2651573Srgrimes		goto bad2;
2661573Srgrimes	}
2671573Srgrimes	if (c != 0) {
26855837Sjasone		while (_libc_read(s, &c, 1) == 1) {
26955837Sjasone			(void)_libc_write(STDERR_FILENO, &c, 1);
2701573Srgrimes			if (c == '\n')
2711573Srgrimes				break;
2721573Srgrimes		}
2731573Srgrimes		goto bad2;
2741573Srgrimes	}
2751573Srgrimes	sigsetmask(oldmask);
27655918Sshin	freeaddrinfo(res);
2771573Srgrimes	return (s);
2781573Srgrimesbad2:
2791573Srgrimes	if (lport)
28055837Sjasone		(void)_libc_close(*fd2p);
2811573Srgrimesbad:
28255837Sjasone	(void)_libc_close(s);
2831573Srgrimes	sigsetmask(oldmask);
28455918Sshin	freeaddrinfo(res);
2851573Srgrimes	return (-1);
2861573Srgrimes}
2871573Srgrimes
2881573Srgrimesint
28955918Sshinrresvport(port)
29055918Sshin	int *port;
2911573Srgrimes{
29255918Sshin	return rresvport_af(port, AF_INET);
29355918Sshin}
2941573Srgrimes
29555918Sshinint
29655918Sshinrresvport_af(alport, family)
29755918Sshin	int *alport, family;
29855918Sshin{
29955918Sshin	int i, s, len, err;
30055918Sshin	struct sockaddr_storage ss;
30155918Sshin	u_short *sport;
30255918Sshin
30355918Sshin	memset(&ss, 0, sizeof(ss));
30455918Sshin	ss.ss_family = family;
30555918Sshin	switch (family) {
30655918Sshin	case AF_INET:
30755918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
30855918Sshin		sport = &((struct sockaddr_in *)&ss)->sin_port;
30955918Sshin		((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
31055918Sshin		break;
31155918Sshin#ifdef INET6
31255918Sshin	case AF_INET6:
31355918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
31455918Sshin		sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
31555918Sshin		((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
31655918Sshin		break;
31755918Sshin#endif
31855918Sshin	default:
31955918Sshin		errno = EAFNOSUPPORT;
32055918Sshin		return -1;
32155918Sshin	}
32255918Sshin
32355918Sshin	s = socket(ss.ss_family, SOCK_STREAM, 0);
3241573Srgrimes	if (s < 0)
3251573Srgrimes		return (-1);
32617543Speter#if 0 /* compat_exact_traditional_rresvport_semantics */
32717543Speter	sin.sin_port = htons((u_short)*alport);
32817543Speter	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
32917543Speter		return (s);
33017543Speter	if (errno != EADDRINUSE) {
33155837Sjasone		(void)_libc_close(s);
33217543Speter		return (-1);
33316034Speter	}
33417543Speter#endif
33555918Sshin	*sport = 0;
33655918Sshin	if (bindresvport2(s, (struct sockaddr *)&ss,
33755918Sshin			  ((struct sockaddr *)&ss)->sa_len) == -1) {
33855837Sjasone		(void)_libc_close(s);
33917543Speter		return (-1);
3401573Srgrimes	}
34155918Sshin	*alport = (int)ntohs(*sport);
34217543Speter	return (s);
3431573Srgrimes}
3441573Srgrimes
3451573Srgrimesint	__check_rhosts_file = 1;
3461573Srgrimeschar	*__rcmd_errstr;
3471573Srgrimes
3481573Srgrimesint
3491573Srgrimesruserok(rhost, superuser, ruser, luser)
3501573Srgrimes	const char *rhost, *ruser, *luser;
3511573Srgrimes	int superuser;
3521573Srgrimes{
35355918Sshin	return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
35455918Sshin}
35555918Sshin
35655918Sshinint
35755918Sshinruserok_af(rhost, superuser, ruser, luser, af)
35855918Sshin	const char *rhost, *ruser, *luser;
35955918Sshin	int superuser, af;
36055918Sshin{
3611573Srgrimes	struct hostent *hp;
36255918Sshin	union {
36355918Sshin		struct in_addr addr_in;
36455918Sshin		struct in6_addr addr_in6;
36555918Sshin	} addr;
3661573Srgrimes	char **ap;
36755918Sshin	int ret, h_error;
3681573Srgrimes
36955918Sshin	if ((hp = getipnodebyname(rhost, af, AI_DEFAULT, &h_error)) == NULL)
3701573Srgrimes		return (-1);
37155918Sshin	ret = -1;
3721573Srgrimes	for (ap = hp->h_addr_list; *ap; ++ap) {
37355918Sshin		bcopy(*ap, &addr, hp->h_length);
37455918Sshin		if (iruserok_af(&addr, superuser, ruser, luser, af) == 0) {
37555918Sshin			ret = 0;
37655918Sshin			break;
37755918Sshin		}
3781573Srgrimes	}
37955918Sshin	freehostent(hp);
38055918Sshin	return (ret);
3811573Srgrimes}
3821573Srgrimes
3831573Srgrimes/*
3841573Srgrimes * New .rhosts strategy: We are passed an ip address. We spin through
3851573Srgrimes * hosts.equiv and .rhosts looking for a match. When the .rhosts only
3861573Srgrimes * has ip addresses, we don't have to trust a nameserver.  When it
3871573Srgrimes * contains hostnames, we spin through the list of addresses the nameserver
3881573Srgrimes * gives us and look for a match.
3891573Srgrimes *
3901573Srgrimes * Returns 0 if ok, -1 if not ok.
3911573Srgrimes */
3921573Srgrimesint
3931573Srgrimesiruserok(raddr, superuser, ruser, luser)
39439979Sdfr	unsigned long raddr;
3951573Srgrimes	int superuser;
3961573Srgrimes	const char *ruser, *luser;
3971573Srgrimes{
39855918Sshin	return iruserok_af(&raddr, superuser, ruser, luser, AF_INET);
39955918Sshin}
40055918Sshin
40155918Sshinint
40255918Sshiniruserok_af(raddr, superuser, ruser, luser, af)
40355918Sshin	void *raddr;
40455918Sshin	int superuser;
40555918Sshin	const char *ruser, *luser;
40655918Sshin	int af;
40755918Sshin{
4081573Srgrimes	register char *cp;
4091573Srgrimes	struct stat sbuf;
4101573Srgrimes	struct passwd *pwd;
4111573Srgrimes	FILE *hostf;
4121573Srgrimes	uid_t uid;
4131573Srgrimes	int first;
4141573Srgrimes	char pbuf[MAXPATHLEN];
41555918Sshin	int len = 0;
4161573Srgrimes
41755918Sshin	switch (af) {
41855918Sshin	case AF_INET:
41955918Sshin		len = sizeof(struct in_addr);
42055918Sshin		break;
42155918Sshin#ifdef INET6
42255918Sshin	case AF_INET6:
42355918Sshin		len = sizeof(struct in6_addr);
42455918Sshin		break;
42555918Sshin#endif
42655918Sshin	}
42755918Sshin
4281573Srgrimes	first = 1;
4291573Srgrimes	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
4301573Srgrimesagain:
4311573Srgrimes	if (hostf) {
43255918Sshin		if (__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
43355918Sshin		    == 0) {
4341573Srgrimes			(void)fclose(hostf);
4351573Srgrimes			return (0);
4361573Srgrimes		}
4371573Srgrimes		(void)fclose(hostf);
4381573Srgrimes	}
4391573Srgrimes	if (first == 1 && (__check_rhosts_file || superuser)) {
4401573Srgrimes		first = 0;
4411573Srgrimes		if ((pwd = getpwnam(luser)) == NULL)
4421573Srgrimes			return (-1);
4431573Srgrimes		(void)strcpy(pbuf, pwd->pw_dir);
4441573Srgrimes		(void)strcat(pbuf, "/.rhosts");
4451573Srgrimes
4461573Srgrimes		/*
4471573Srgrimes		 * Change effective uid while opening .rhosts.  If root and
4481573Srgrimes		 * reading an NFS mounted file system, can't read files that
4491573Srgrimes		 * are protected read/write owner only.
4501573Srgrimes		 */
4511573Srgrimes		uid = geteuid();
4521573Srgrimes		(void)seteuid(pwd->pw_uid);
4531573Srgrimes		hostf = fopen(pbuf, "r");
4541573Srgrimes		(void)seteuid(uid);
4551573Srgrimes
4561573Srgrimes		if (hostf == NULL)
4571573Srgrimes			return (-1);
4581573Srgrimes		/*
4591573Srgrimes		 * If not a regular file, or is owned by someone other than
4601573Srgrimes		 * user or root or if writeable by anyone but the owner, quit.
4611573Srgrimes		 */
4621573Srgrimes		cp = NULL;
4631573Srgrimes		if (lstat(pbuf, &sbuf) < 0)
4641573Srgrimes			cp = ".rhosts lstat failed";
4651573Srgrimes		else if (!S_ISREG(sbuf.st_mode))
4661573Srgrimes			cp = ".rhosts not regular file";
4671573Srgrimes		else if (fstat(fileno(hostf), &sbuf) < 0)
4681573Srgrimes			cp = ".rhosts fstat failed";
4691573Srgrimes		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
4701573Srgrimes			cp = "bad .rhosts owner";
4711573Srgrimes		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
4721573Srgrimes			cp = ".rhosts writeable by other than owner";
4731573Srgrimes		/* If there were any problems, quit. */
4741573Srgrimes		if (cp) {
4751573Srgrimes			__rcmd_errstr = cp;
4761573Srgrimes			(void)fclose(hostf);
4771573Srgrimes			return (-1);
4781573Srgrimes		}
4791573Srgrimes		goto again;
4801573Srgrimes	}
4811573Srgrimes	return (-1);
4821573Srgrimes}
4831573Srgrimes
4841573Srgrimes/*
4851573Srgrimes * XXX
4861573Srgrimes * Don't make static, used by lpd(8).
4871573Srgrimes *
4881573Srgrimes * Returns 0 if ok, -1 if not ok.
4891573Srgrimes */
4901573Srgrimesint
4911573Srgrimes__ivaliduser(hostf, raddr, luser, ruser)
4921573Srgrimes	FILE *hostf;
49339979Sdfr	u_int32_t raddr;
4941573Srgrimes	const char *luser, *ruser;
4951573Srgrimes{
49655918Sshin	return __ivaliduser_af(hostf, &raddr, luser, ruser, AF_INET,
49755918Sshin			       sizeof(raddr));
49855918Sshin}
49955918Sshin
50055918Sshinint
50155918Sshin__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
50255918Sshin	FILE *hostf;
50355918Sshin	void *raddr;
50455918Sshin	const char *luser, *ruser;
50555918Sshin	int af, len;
50655918Sshin{
5071573Srgrimes	register char *user, *p;
5081573Srgrimes	int ch;
5091573Srgrimes	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
51010059Swpaul	char hname[MAXHOSTNAMELEN];
5117183Swpaul	struct hostent *hp;
5127183Swpaul	/* Presumed guilty until proven innocent. */
5137183Swpaul	int userok = 0, hostok = 0;
51455918Sshin	int h_error;
5159978Swpaul#ifdef YP
5169978Swpaul	char *ypdomain;
5171573Srgrimes
5189978Swpaul	if (yp_get_default_domain(&ypdomain))
5199978Swpaul		ypdomain = NULL;
5209978Swpaul#else
5219978Swpaul#define	ypdomain NULL
5229978Swpaul#endif
5237183Swpaul	/* We need to get the damn hostname back for netgroup matching. */
52455918Sshin	if ((hp = getipnodebyaddr((char *)raddr, len, af, &h_error)) == NULL)
5257183Swpaul		return (-1);
52623128Simp	strncpy(hname, hp->h_name, sizeof(hname));
52723128Simp	hname[sizeof(hname) - 1] = '\0';
52855918Sshin	freehostent(hp);
5297183Swpaul
5301573Srgrimes	while (fgets(buf, sizeof(buf), hostf)) {
5311573Srgrimes		p = buf;
5321573Srgrimes		/* Skip lines that are too long. */
5331573Srgrimes		if (strchr(p, '\n') == NULL) {
5341573Srgrimes			while ((ch = getc(hostf)) != '\n' && ch != EOF);
5351573Srgrimes			continue;
5361573Srgrimes		}
5379552Speter		if (*p == '\n' || *p == '#') {
5389552Speter			/* comment... */
5399552Speter			continue;
5409552Speter		}
5411573Srgrimes		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
54252859Sache			*p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p;
5431573Srgrimes			p++;
5441573Srgrimes		}
5451573Srgrimes		if (*p == ' ' || *p == '\t') {
5461573Srgrimes			*p++ = '\0';
5471573Srgrimes			while (*p == ' ' || *p == '\t')
5481573Srgrimes				p++;
5491573Srgrimes			user = p;
5501573Srgrimes			while (*p != '\n' && *p != ' ' &&
5511573Srgrimes			    *p != '\t' && *p != '\0')
5521573Srgrimes				p++;
5531573Srgrimes		} else
5541573Srgrimes			user = p;
5551573Srgrimes		*p = '\0';
5567183Swpaul		/*
5577183Swpaul		 * Do +/- and +@/-@ checking. This looks really nasty,
5587183Swpaul		 * but it matches SunOS's behavior so far as I can tell.
5597183Swpaul		 */
5607183Swpaul		switch(buf[0]) {
5617183Swpaul		case '+':
5627183Swpaul			if (!buf[1]) {     /* '+' matches all hosts */
5637183Swpaul				hostok = 1;
5647183Swpaul				break;
5657183Swpaul			}
5667183Swpaul			if (buf[1] == '@')  /* match a host by netgroup */
56710059Swpaul				hostok = innetgr((char *)&buf[2],
56810059Swpaul					(char *)&hname, NULL, ypdomain);
5697183Swpaul			else		/* match a host by addr */
57055918Sshin				hostok = __icheckhost(raddr,(char *)&buf[1],
57155918Sshin						      af, len);
5727183Swpaul			break;
5737183Swpaul		case '-':     /* reject '-' hosts and all their users */
5747183Swpaul			if (buf[1] == '@') {
5757183Swpaul				if (innetgr((char *)&buf[2],
57610059Swpaul					      (char *)&hname, NULL, ypdomain))
5777183Swpaul					return(-1);
5787183Swpaul			} else {
57955918Sshin				if (__icheckhost(raddr,(char *)&buf[1],af,len))
5807183Swpaul					return(-1);
5817183Swpaul			}
5827183Swpaul			break;
5837183Swpaul		default:  /* if no '+' or '-', do a simple match */
58455918Sshin			hostok = __icheckhost(raddr, buf, af, len);
5857183Swpaul			break;
5861573Srgrimes		}
5877183Swpaul		switch(*user) {
5887183Swpaul		case '+':
5897183Swpaul			if (!*(user+1)) {      /* '+' matches all users */
5907183Swpaul				userok = 1;
5917183Swpaul				break;
5927183Swpaul			}
5937183Swpaul			if (*(user+1) == '@')  /* match a user by netgroup */
5949978Swpaul				userok = innetgr(user+2, NULL, ruser, ypdomain);
5957183Swpaul			else	   /* match a user by direct specification */
5967183Swpaul				userok = !(strcmp(ruser, user+1));
5977183Swpaul			break;
5987183Swpaul		case '-': 		/* if we matched a hostname, */
5997183Swpaul			if (hostok) {   /* check for user field rejections */
6007183Swpaul				if (!*(user+1))
6017183Swpaul					return(-1);
6027183Swpaul				if (*(user+1) == '@') {
6037183Swpaul					if (innetgr(user+2, NULL,
6049978Swpaul							ruser, ypdomain))
6057183Swpaul						return(-1);
6067183Swpaul				} else {
6077183Swpaul					if (!strcmp(ruser, user+1))
6087183Swpaul						return(-1);
6097183Swpaul				}
6107183Swpaul			}
6117183Swpaul			break;
6127183Swpaul		default:	/* no rejections: try to match the user */
6137183Swpaul			if (hostok)
6147183Swpaul				userok = !(strcmp(ruser,*user ? user : luser));
6157183Swpaul			break;
6167183Swpaul		}
6177183Swpaul		if (hostok && userok)
6187183Swpaul			return(0);
6191573Srgrimes	}
6201573Srgrimes	return (-1);
6211573Srgrimes}
6221573Srgrimes
6231573Srgrimes/*
6241573Srgrimes * Returns "true" if match, 0 if no match.
6251573Srgrimes */
6261573Srgrimesstatic int
62755918Sshin__icheckhost(raddr, lhost, af, len)
62855918Sshin	void *raddr;
6291573Srgrimes	register char *lhost;
63055918Sshin	int af, len;
6311573Srgrimes{
6321573Srgrimes	register struct hostent *hp;
63355918Sshin	char laddr[BUFSIZ]; /* xxx */
6341573Srgrimes	register char **pp;
63555918Sshin	int h_error;
63655918Sshin	int match;
6371573Srgrimes
6381573Srgrimes	/* Try for raw ip address first. */
63955918Sshin	if (inet_pton(af, lhost, laddr) == 1) {
64055918Sshin		if (memcmp(raddr, laddr, len) == 0)
64155918Sshin			return (1);
64255918Sshin		else
64355918Sshin			return (0);
64455918Sshin	}
6451573Srgrimes
6461573Srgrimes	/* Better be a hostname. */
64755918Sshin	if ((hp = getipnodebyname(lhost, af, AI_DEFAULT, &h_error)) == NULL)
6481573Srgrimes		return (0);
6491573Srgrimes
6501573Srgrimes	/* Spin through ip addresses. */
65155918Sshin	match = 0;
6521573Srgrimes	for (pp = hp->h_addr_list; *pp; ++pp)
65355918Sshin		if (!bcmp(raddr, *pp, len)) {
65455918Sshin			match = 1;
65555918Sshin			break;
65655918Sshin		}
6571573Srgrimes
65855918Sshin	freehostent(hp);
65955918Sshin	return (match);
6601573Srgrimes}
661