rcmd.c revision 57123
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.
321573Srgrimes *
331573Srgrimes * $FreeBSD: head/lib/libc/net/rcmd.c 57123 2000-02-10 19:46:47Z 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>
561573Srgrimes#ifdef YP
571573Srgrimes#include <rpc/rpc.h>
581573Srgrimes#include <rpcsvc/yp_prot.h>
591573Srgrimes#include <rpcsvc/ypclnt.h>
601573Srgrimes#endif
611573Srgrimes
621573Srgrimes/* wrapper for KAME-special getnameinfo() */
631573Srgrimes#ifndef NI_WITHSCOPEID
641573Srgrimes#define NI_WITHSCOPEID	0
651573Srgrimes#endif
661573Srgrimes
671573Srgrimesextern int innetgr __P(( const char *, const char *, const char *, const char * ));
681573Srgrimes
691573Srgrimes#define max(a, b)	((a > b) ? a : b)
701573Srgrimes
711573Srgrimesstatic int __iruserok_af __P((void *, int, const char *, const char *, int));
721573Srgrimesint	__ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
731573Srgrimesstatic int __icheckhost __P((void *, char *, int, int));
741573Srgrimes
751573Srgrimeschar paddr[INET6_ADDRSTRLEN];
761573Srgrimes
771573Srgrimesint
781573Srgrimesrcmd(ahost, rport, locuser, remuser, cmd, fd2p)
791573Srgrimes	char **ahost;
801573Srgrimes	u_short rport;
811573Srgrimes	const char *locuser, *remuser, *cmd;
821573Srgrimes	int *fd2p;
831573Srgrimes{
841573Srgrimes	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
851573Srgrimes}
861573Srgrimes
871573Srgrimesint
881573Srgrimesrcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
891573Srgrimes	char **ahost;
901573Srgrimes	u_short rport;
911573Srgrimes	const char *locuser, *remuser, *cmd;
921573Srgrimes	int *fd2p;
931573Srgrimes	int af;
941573Srgrimes{
951573Srgrimes	struct addrinfo hints, *res, *ai;
961573Srgrimes	struct sockaddr_storage from;
971573Srgrimes	fd_set reads;
981573Srgrimes	long oldmask;
991573Srgrimes	pid_t pid;
1001573Srgrimes	int s, aport, lport, timo, error;
1011573Srgrimes	char c;
1021573Srgrimes	int refused;
1031573Srgrimes	char num[8];
1041573Srgrimes
1051573Srgrimes	pid = getpid();
1061573Srgrimes
1071573Srgrimes	memset(&hints, 0, sizeof(hints));
1081573Srgrimes	hints.ai_flags = AI_CANONNAME;
1091573Srgrimes	hints.ai_family = af;
1101573Srgrimes	hints.ai_socktype = SOCK_STREAM;
1111573Srgrimes	hints.ai_protocol = 0;
1121573Srgrimes	(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
1131573Srgrimes	error = getaddrinfo(*ahost, num, &hints, &res);
1141573Srgrimes	if (error) {
1151573Srgrimes		fprintf(stderr, "rcmd: getaddrinfo: %s\n",
1161573Srgrimes			gai_strerror(error));
1171573Srgrimes		if (error == EAI_SYSTEM)
1181573Srgrimes			fprintf(stderr, "rcmd: getaddrinfo: %s\n",
1191573Srgrimes				strerror(errno));
1201573Srgrimes		return (-1);
1211573Srgrimes	}
1221573Srgrimes	if (res->ai_canonname)
1231573Srgrimes		*ahost = res->ai_canonname;
1241573Srgrimes	ai = res;
1251573Srgrimes	refused = 0;
1261573Srgrimes	oldmask = sigblock(sigmask(SIGURG));
1271573Srgrimes	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
1281573Srgrimes		s = rresvport_af(&lport, ai->ai_family);
1291573Srgrimes		if (s < 0) {
1301573Srgrimes			if (errno != EAGAIN && ai->ai_next) {
1311573Srgrimes				ai = ai->ai_next;
1321573Srgrimes				continue;
1331573Srgrimes			}
1341573Srgrimes			if (errno == EAGAIN)
1351573Srgrimes				(void)fprintf(stderr,
1361573Srgrimes				    "rcmd: socket: All ports in use\n");
1371573Srgrimes			else
1381573Srgrimes				(void)fprintf(stderr, "rcmd: socket: %s\n",
1391573Srgrimes				    strerror(errno));
1401573Srgrimes			freeaddrinfo(res);
1411573Srgrimes			sigsetmask(oldmask);
1421573Srgrimes			return (-1);
1431573Srgrimes		}
1441573Srgrimes		_fcntl(s, F_SETOWN, pid);
1451573Srgrimes		if (connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
1461573Srgrimes			break;
1471573Srgrimes		(void)_close(s);
1481573Srgrimes		if (errno == EADDRINUSE) {
1491573Srgrimes			lport--;
1501573Srgrimes			continue;
1511573Srgrimes		}
1521573Srgrimes		if (errno == ECONNREFUSED)
1531573Srgrimes			refused = 1;
1541573Srgrimes		if (ai->ai_next != NULL) {
1551573Srgrimes			int oerrno = errno;
1561573Srgrimes
1571573Srgrimes			getnameinfo(ai->ai_addr, ai->ai_addrlen,
1581573Srgrimes				    paddr, sizeof(paddr),
1591573Srgrimes				    NULL, 0,
1601573Srgrimes				    NI_NUMERICHOST|NI_WITHSCOPEID);
1611573Srgrimes			(void)fprintf(stderr, "connect to address %s: ",
1621573Srgrimes				      paddr);
1631573Srgrimes			errno = oerrno;
1641573Srgrimes			perror(0);
1651573Srgrimes			ai = ai->ai_next;
1661573Srgrimes			getnameinfo(ai->ai_addr, ai->ai_addrlen,
1671573Srgrimes				    paddr, sizeof(paddr),
1681573Srgrimes				    NULL, 0,
1691573Srgrimes				    NI_NUMERICHOST|NI_WITHSCOPEID);
1701573Srgrimes			fprintf(stderr, "Trying %s...\n", paddr);
1711573Srgrimes			continue;
1721573Srgrimes		}
1731573Srgrimes		if (refused && timo <= 16) {
1741573Srgrimes			struct timespec time_to_sleep, time_remaining;
1751573Srgrimes
1761573Srgrimes			time_to_sleep.tv_sec = timo;
1771573Srgrimes			time_to_sleep.tv_nsec = 0;
1781573Srgrimes			(void)_nanosleep(&time_to_sleep, &time_remaining);
1791573Srgrimes
1801573Srgrimes			timo *= 2;
1811573Srgrimes			ai = res;
1821573Srgrimes			refused = 0;
1831573Srgrimes			continue;
1841573Srgrimes		}
1853041Swollman		freeaddrinfo(res);
1861573Srgrimes		(void)fprintf(stderr, "%s: %s\n", *ahost, strerror(errno));
1871573Srgrimes		sigsetmask(oldmask);
1881573Srgrimes		return (-1);
1891573Srgrimes	}
1901573Srgrimes	lport--;
1911573Srgrimes	if (fd2p == 0) {
1921573Srgrimes		_write(s, "", 1);
1931573Srgrimes		lport = 0;
1941573Srgrimes	} else {
1951573Srgrimes		char num[8];
1961573Srgrimes		int s2 = rresvport_af(&lport, ai->ai_family), s3;
1971573Srgrimes		int len = ai->ai_addrlen;
1981573Srgrimes		int nfds;
1991573Srgrimes
2001573Srgrimes		if (s2 < 0)
2011573Srgrimes			goto bad;
2021573Srgrimes		listen(s2, 1);
2031573Srgrimes		(void)snprintf(num, sizeof(num), "%d", lport);
2041573Srgrimes		if (_write(s, num, strlen(num)+1) != strlen(num)+1) {
2051573Srgrimes			(void)fprintf(stderr,
2061573Srgrimes			    "rcmd: write (setting up stderr): %s\n",
2071573Srgrimes			    strerror(errno));
2081573Srgrimes			(void)_close(s2);
2091573Srgrimes			goto bad;
2101573Srgrimes		}
2111573Srgrimes		nfds = max(s, s2)+1;
2121573Srgrimes		if(nfds > FD_SETSIZE) {
2131573Srgrimes			fprintf(stderr, "rcmd: too many files\n");
2141573Srgrimes			(void)_close(s2);
2151573Srgrimes			goto bad;
2161573Srgrimes		}
2171573Srgrimesagain:
2181573Srgrimes		FD_ZERO(&reads);
2191573Srgrimes		FD_SET(s, &reads);
2201573Srgrimes		FD_SET(s2, &reads);
2211573Srgrimes		errno = 0;
2221573Srgrimes		if (select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){
2231573Srgrimes			if (errno != 0)
2241573Srgrimes				(void)fprintf(stderr,
2251573Srgrimes				    "rcmd: select (setting up stderr): %s\n",
2261573Srgrimes				    strerror(errno));
2271573Srgrimes			else
2281573Srgrimes				(void)fprintf(stderr,
2291856Sdg				"select: protocol failure in circuit setup\n");
2301573Srgrimes			(void)_close(s2);
2311573Srgrimes			goto bad;
2321573Srgrimes		}
2331573Srgrimes		s3 = accept(s2, (struct sockaddr *)&from, &len);
2341573Srgrimes		switch (from.ss_family) {
2351573Srgrimes		case AF_INET:
2361573Srgrimes			aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
2371573Srgrimes			break;
2381573Srgrimes#ifdef INET6
2391573Srgrimes		case AF_INET6:
2401573Srgrimes			aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
2411573Srgrimes			break;
2421573Srgrimes#endif
2431573Srgrimes		default:
2441573Srgrimes			aport = 0;	/* error */
2451573Srgrimes			break;
2461573Srgrimes		}
2471573Srgrimes		/*
2481573Srgrimes		 * XXX careful for ftp bounce attacks. If discovered, shut them
2491573Srgrimes		 * down and check for the real auxiliary channel to connect.
2501573Srgrimes		 */
2511573Srgrimes		if (aport == 20) {
2521573Srgrimes			_close(s3);
2531573Srgrimes			goto again;
2541573Srgrimes		}
2551573Srgrimes		(void)_close(s2);
2561573Srgrimes		if (s3 < 0) {
2571573Srgrimes			(void)fprintf(stderr,
2581573Srgrimes			    "rcmd: accept: %s\n", strerror(errno));
2591573Srgrimes			lport = 0;
2601573Srgrimes			goto bad;
2611573Srgrimes		}
2621573Srgrimes		*fd2p = s3;
2631573Srgrimes		if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
2641573Srgrimes			(void)fprintf(stderr,
2651573Srgrimes			    "socket: protocol failure in circuit setup.\n");
2661573Srgrimes			goto bad2;
2671573Srgrimes		}
2681573Srgrimes	}
2691573Srgrimes	(void)_write(s, locuser, strlen(locuser)+1);
2701573Srgrimes	(void)_write(s, remuser, strlen(remuser)+1);
2711573Srgrimes	(void)_write(s, cmd, strlen(cmd)+1);
2721573Srgrimes	if (_read(s, &c, 1) != 1) {
2731573Srgrimes		(void)fprintf(stderr,
2741573Srgrimes		    "rcmd: %s: %s\n", *ahost, strerror(errno));
2751573Srgrimes		goto bad2;
2761573Srgrimes	}
2771573Srgrimes	if (c != 0) {
2781573Srgrimes		while (_read(s, &c, 1) == 1) {
2791573Srgrimes			(void)_write(STDERR_FILENO, &c, 1);
2801573Srgrimes			if (c == '\n')
2811573Srgrimes				break;
2821573Srgrimes		}
2831573Srgrimes		goto bad2;
2841573Srgrimes	}
2851573Srgrimes	sigsetmask(oldmask);
2861573Srgrimes	freeaddrinfo(res);
2871573Srgrimes	return (s);
2881573Srgrimesbad2:
2891573Srgrimes	if (lport)
2901573Srgrimes		(void)_close(*fd2p);
2911573Srgrimesbad:
2921573Srgrimes	(void)_close(s);
2931573Srgrimes	sigsetmask(oldmask);
2941573Srgrimes	freeaddrinfo(res);
2951573Srgrimes	return (-1);
2961573Srgrimes}
2971573Srgrimes
2981573Srgrimesint
2991573Srgrimesrresvport(port)
3001573Srgrimes	int *port;
3011573Srgrimes{
3021573Srgrimes	return rresvport_af(port, AF_INET);
3031573Srgrimes}
3041573Srgrimes
3051573Srgrimesint
3061573Srgrimesrresvport_af(alport, family)
3071573Srgrimes	int *alport, family;
3081573Srgrimes{
3091573Srgrimes	int i, s, len, err;
3101573Srgrimes	struct sockaddr_storage ss;
3111573Srgrimes	u_short *sport;
3121573Srgrimes
3131573Srgrimes	memset(&ss, 0, sizeof(ss));
3141573Srgrimes	ss.ss_family = family;
3151573Srgrimes	switch (family) {
3161573Srgrimes	case AF_INET:
3171573Srgrimes		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
3181573Srgrimes		sport = &((struct sockaddr_in *)&ss)->sin_port;
3191573Srgrimes		((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
3201573Srgrimes		break;
3211573Srgrimes#ifdef INET6
3221573Srgrimes	case AF_INET6:
3231573Srgrimes		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
3241573Srgrimes		sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
3251573Srgrimes		((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
3261573Srgrimes		break;
3271573Srgrimes#endif
3281573Srgrimes	default:
3291573Srgrimes		errno = EAFNOSUPPORT;
3301573Srgrimes		return -1;
3311573Srgrimes	}
3321573Srgrimes
3331573Srgrimes	s = socket(ss.ss_family, SOCK_STREAM, 0);
3341573Srgrimes	if (s < 0)
3351573Srgrimes		return (-1);
3361573Srgrimes#if 0 /* compat_exact_traditional_rresvport_semantics */
3371573Srgrimes	sin.sin_port = htons((u_short)*alport);
3381573Srgrimes	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
3391573Srgrimes		return (s);
3401573Srgrimes	if (errno != EADDRINUSE) {
3411573Srgrimes		(void)_close(s);
3421573Srgrimes		return (-1);
3431573Srgrimes	}
3441573Srgrimes#endif
3451573Srgrimes	*sport = 0;
3461573Srgrimes	if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) {
3471573Srgrimes		(void)_close(s);
3481573Srgrimes		return (-1);
3491573Srgrimes	}
3501573Srgrimes	*alport = (int)ntohs(*sport);
3511573Srgrimes	return (s);
3521573Srgrimes}
3531573Srgrimes
3541573Srgrimesint	__check_rhosts_file = 1;
3551573Srgrimeschar	*__rcmd_errstr;
3561573Srgrimes
3571573Srgrimesint
3581573Srgrimesruserok(rhost, superuser, ruser, luser)
3591573Srgrimes	const char *rhost, *ruser, *luser;
3601573Srgrimes	int superuser;
3611573Srgrimes{
3621573Srgrimes	struct addrinfo hints, *res, *r;
3631573Srgrimes	int error;
3641573Srgrimes
3651573Srgrimes	memset(&hints, 0, sizeof(hints));
3661573Srgrimes	hints.ai_family = PF_UNSPEC;
3671573Srgrimes	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
3681573Srgrimes	error = getaddrinfo(rhost, "0", &hints, &res);
3691573Srgrimes	if (error)
3701573Srgrimes		return (-1);
3711573Srgrimes
3721573Srgrimes	for (r = res; r; r = r->ai_next) {
3731573Srgrimes		if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser,
3741573Srgrimes		    luser) == 0) {
3751573Srgrimes			freeaddrinfo(res);
3761573Srgrimes			return (0);
3771573Srgrimes		}
3781573Srgrimes	}
3791573Srgrimes	freeaddrinfo(res);
3801573Srgrimes	return (-1);
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)
3941573Srgrimes	unsigned long raddr;
3951573Srgrimes	int superuser;
3961573Srgrimes	const char *ruser, *luser;
3971573Srgrimes{
3981573Srgrimes	return __iruserok_af(&raddr, superuser, ruser, luser, AF_INET);
3991573Srgrimes}
4001573Srgrimes
4011573Srgrimes/* Other AF support extension of iruserok. */
4021573Srgrimesstatic int
4031573Srgrimes__iruserok_af(raddr, superuser, ruser, luser, af)
4041573Srgrimes	void *raddr;
4051573Srgrimes	int superuser;
4061573Srgrimes	const char *ruser, *luser;
4071573Srgrimes	int af;
4081573Srgrimes{
4091573Srgrimes	register char *cp;
4101573Srgrimes	struct stat sbuf;
4111573Srgrimes	struct passwd *pwd;
4121573Srgrimes	FILE *hostf;
4131573Srgrimes	uid_t uid;
4141573Srgrimes	int first;
4151573Srgrimes	char pbuf[MAXPATHLEN];
4161573Srgrimes	int len = 0;
4171573Srgrimes
4181573Srgrimes	switch (af) {
4191573Srgrimes	case AF_INET:
4201573Srgrimes		len = sizeof(struct in_addr);
4211573Srgrimes		break;
4221573Srgrimes#ifdef INET6
4231573Srgrimes	case AF_INET6:
4241573Srgrimes		len = sizeof(struct in6_addr);
4251573Srgrimes		break;
4261573Srgrimes#endif
4271573Srgrimes	}
4281573Srgrimes
4291573Srgrimes	first = 1;
4301573Srgrimes	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
4311573Srgrimesagain:
4321573Srgrimes	if (hostf) {
4331573Srgrimes		if (__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
4341573Srgrimes		    == 0) {
4351573Srgrimes			(void)fclose(hostf);
4361573Srgrimes			return (0);
4371573Srgrimes		}
4381573Srgrimes		(void)fclose(hostf);
4391573Srgrimes	}
4401573Srgrimes	if (first == 1 && (__check_rhosts_file || superuser)) {
4411573Srgrimes		first = 0;
4421573Srgrimes		if ((pwd = getpwnam(luser)) == NULL)
4431573Srgrimes			return (-1);
4441573Srgrimes		(void)strcpy(pbuf, pwd->pw_dir);
4451573Srgrimes		(void)strcat(pbuf, "/.rhosts");
4461573Srgrimes
4471573Srgrimes		/*
4481573Srgrimes		 * Change effective uid while opening .rhosts.  If root and
4491573Srgrimes		 * reading an NFS mounted file system, can't read files that
4501573Srgrimes		 * are protected read/write owner only.
4511573Srgrimes		 */
4521573Srgrimes		uid = geteuid();
4531573Srgrimes		(void)seteuid(pwd->pw_uid);
4541573Srgrimes		hostf = fopen(pbuf, "r");
4551573Srgrimes		(void)seteuid(uid);
4561573Srgrimes
4571573Srgrimes		if (hostf == NULL)
4581573Srgrimes			return (-1);
4591573Srgrimes		/*
4601573Srgrimes		 * If not a regular file, or is owned by someone other than
4611573Srgrimes		 * user or root or if writeable by anyone but the owner, quit.
4621573Srgrimes		 */
4631573Srgrimes		cp = NULL;
4641573Srgrimes		if (lstat(pbuf, &sbuf) < 0)
4651573Srgrimes			cp = ".rhosts lstat failed";
4661573Srgrimes		else if (!S_ISREG(sbuf.st_mode))
4671573Srgrimes			cp = ".rhosts not regular file";
4681573Srgrimes		else if (fstat(fileno(hostf), &sbuf) < 0)
4691573Srgrimes			cp = ".rhosts fstat failed";
4701573Srgrimes		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
4711573Srgrimes			cp = "bad .rhosts owner";
4721573Srgrimes		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
4731573Srgrimes			cp = ".rhosts writeable by other than owner";
4741573Srgrimes		/* If there were any problems, quit. */
4751573Srgrimes		if (cp) {
4761573Srgrimes			__rcmd_errstr = cp;
4771573Srgrimes			(void)fclose(hostf);
4781573Srgrimes			return (-1);
4791573Srgrimes		}
4801573Srgrimes		goto again;
4811573Srgrimes	}
4821573Srgrimes	return (-1);
4831573Srgrimes}
4841573Srgrimes
4851573Srgrimes/*
4861573Srgrimes * AF independent extension of iruserok. We are passed an sockaddr, and
4871573Srgrimes * then call iruserok_af() as the type of sockaddr.
4881573Srgrimes *
4891573Srgrimes * Returns 0 if ok, -1 if not ok.
4901573Srgrimes */
4911573Srgrimesint
4921573Srgrimesiruserok_sa(addr, addrlen, superuser, ruser, luser)
4931573Srgrimes	const void *addr;
4941573Srgrimes	int addrlen;
4951573Srgrimes	int superuser;
4961573Srgrimes	const char *ruser, *luser;
4971573Srgrimes{
4981573Srgrimes	struct sockaddr *sa;
4991573Srgrimes	void *raddr = NULL;
5001573Srgrimes
5011573Srgrimes	sa = (struct sockaddr *)addr;
5021573Srgrimes	switch (sa->sa_family) {
5031573Srgrimes	case AF_INET:
5041573Srgrimes		raddr = &((struct sockaddr_in *)sa)->sin_addr;
5051573Srgrimes		break;
5061573Srgrimes#ifdef INET6
5071573Srgrimes	case AF_INET6:
5081573Srgrimes		raddr = &((struct sockaddr_in6 *)sa)->sin6_addr;
5091573Srgrimes		break;
5101573Srgrimes#endif
5111573Srgrimes	}
5121573Srgrimes
5131573Srgrimes	__iruserok_af(raddr, superuser, ruser, luser, sa->sa_family);
5141573Srgrimes}
5151573Srgrimes
5161573Srgrimes/*
5171573Srgrimes * XXX
5181573Srgrimes * Don't make static, used by lpd(8).
5191573Srgrimes *
5201573Srgrimes * Returns 0 if ok, -1 if not ok.
5211573Srgrimes */
5221573Srgrimesint
5231573Srgrimes__ivaliduser(hostf, raddr, luser, ruser)
5241573Srgrimes	FILE *hostf;
5251573Srgrimes	u_int32_t raddr;
5261573Srgrimes	const char *luser, *ruser;
5271573Srgrimes{
5281573Srgrimes	return __ivaliduser_af(hostf, &raddr, luser, ruser, AF_INET,
5291573Srgrimes			       sizeof(raddr));
5301573Srgrimes}
5311573Srgrimes
5321573Srgrimesint
5331573Srgrimes__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
5341573Srgrimes	FILE *hostf;
5351573Srgrimes	void *raddr;
5361573Srgrimes	const char *luser, *ruser;
5371573Srgrimes	int af, len;
5381573Srgrimes{
5391573Srgrimes	register char *user, *p;
5401573Srgrimes	int ch;
541	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
542	char hname[MAXHOSTNAMELEN];
543	struct hostent *hp;
544	/* Presumed guilty until proven innocent. */
545	int userok = 0, hostok = 0;
546	int h_error;
547#ifdef YP
548	char *ypdomain;
549
550	if (yp_get_default_domain(&ypdomain))
551		ypdomain = NULL;
552#else
553#define	ypdomain NULL
554#endif
555	/* We need to get the damn hostname back for netgroup matching. */
556	if ((hp = getipnodebyaddr((char *)raddr, len, af, &h_error)) == NULL)
557		return (-1);
558	strncpy(hname, hp->h_name, sizeof(hname));
559	hname[sizeof(hname) - 1] = '\0';
560	freehostent(hp);
561
562	while (fgets(buf, sizeof(buf), hostf)) {
563		p = buf;
564		/* Skip lines that are too long. */
565		if (strchr(p, '\n') == NULL) {
566			while ((ch = getc(hostf)) != '\n' && ch != EOF);
567			continue;
568		}
569		if (*p == '\n' || *p == '#') {
570			/* comment... */
571			continue;
572		}
573		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
574			*p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p;
575			p++;
576		}
577		if (*p == ' ' || *p == '\t') {
578			*p++ = '\0';
579			while (*p == ' ' || *p == '\t')
580				p++;
581			user = p;
582			while (*p != '\n' && *p != ' ' &&
583			    *p != '\t' && *p != '\0')
584				p++;
585		} else
586			user = p;
587		*p = '\0';
588		/*
589		 * Do +/- and +@/-@ checking. This looks really nasty,
590		 * but it matches SunOS's behavior so far as I can tell.
591		 */
592		switch(buf[0]) {
593		case '+':
594			if (!buf[1]) {     /* '+' matches all hosts */
595				hostok = 1;
596				break;
597			}
598			if (buf[1] == '@')  /* match a host by netgroup */
599				hostok = innetgr((char *)&buf[2],
600					(char *)&hname, NULL, ypdomain);
601			else		/* match a host by addr */
602				hostok = __icheckhost(raddr,(char *)&buf[1],
603						      af, len);
604			break;
605		case '-':     /* reject '-' hosts and all their users */
606			if (buf[1] == '@') {
607				if (innetgr((char *)&buf[2],
608					      (char *)&hname, NULL, ypdomain))
609					return(-1);
610			} else {
611				if (__icheckhost(raddr,(char *)&buf[1],af,len))
612					return(-1);
613			}
614			break;
615		default:  /* if no '+' or '-', do a simple match */
616			hostok = __icheckhost(raddr, buf, af, len);
617			break;
618		}
619		switch(*user) {
620		case '+':
621			if (!*(user+1)) {      /* '+' matches all users */
622				userok = 1;
623				break;
624			}
625			if (*(user+1) == '@')  /* match a user by netgroup */
626				userok = innetgr(user+2, NULL, ruser, ypdomain);
627			else	   /* match a user by direct specification */
628				userok = !(strcmp(ruser, user+1));
629			break;
630		case '-': 		/* if we matched a hostname, */
631			if (hostok) {   /* check for user field rejections */
632				if (!*(user+1))
633					return(-1);
634				if (*(user+1) == '@') {
635					if (innetgr(user+2, NULL,
636							ruser, ypdomain))
637						return(-1);
638				} else {
639					if (!strcmp(ruser, user+1))
640						return(-1);
641				}
642			}
643			break;
644		default:	/* no rejections: try to match the user */
645			if (hostok)
646				userok = !(strcmp(ruser,*user ? user : luser));
647			break;
648		}
649		if (hostok && userok)
650			return(0);
651	}
652	return (-1);
653}
654
655/*
656 * Returns "true" if match, 0 if no match.
657 */
658static int
659__icheckhost(raddr, lhost, af, len)
660	void *raddr;
661	register char *lhost;
662	int af, len;
663{
664	register struct hostent *hp;
665	char laddr[BUFSIZ]; /* xxx */
666	register char **pp;
667	int h_error;
668	int match;
669
670	/* Try for raw ip address first. */
671	if (inet_pton(af, lhost, laddr) == 1) {
672		if (memcmp(raddr, laddr, len) == 0)
673			return (1);
674		else
675			return (0);
676	}
677
678	/* Better be a hostname. */
679	if ((hp = getipnodebyname(lhost, af, AI_ALL|AI_DEFAULT, &h_error))
680	    == NULL)
681		return (0);
682
683	/* Spin through ip addresses. */
684	match = 0;
685	for (pp = hp->h_addr_list; *pp; ++pp)
686		if (!bcmp(raddr, *pp, len)) {
687			match = 1;
688			break;
689		}
690
691	freehostent(hp);
692	return (match);
693}
694