1331722Seadler/*
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 * 4. Neither the name of the University nor the names of its contributors
141573Srgrimes *    may be used to endorse or promote products derived from this software
151573Srgrimes *    without specific prior written permission.
161573Srgrimes *
171573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271573Srgrimes * SUCH DAMAGE.
281573Srgrimes */
291573Srgrimes
301573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
311573Srgrimesstatic char sccsid[] = "@(#)rcmd.c	8.3 (Berkeley) 3/26/94";
321573Srgrimes#endif /* LIBC_SCCS and not lint */
3392986Sobrien#include <sys/cdefs.h>
3492986Sobrien__FBSDID("$FreeBSD$");
351573Srgrimes
3671579Sdeischen#include "namespace.h"
371573Srgrimes#include <sys/param.h>
381573Srgrimes#include <sys/socket.h>
391573Srgrimes#include <sys/stat.h>
401573Srgrimes
411573Srgrimes#include <netinet/in.h>
421573Srgrimes#include <arpa/inet.h>
431573Srgrimes
441573Srgrimes#include <signal.h>
451573Srgrimes#include <fcntl.h>
461573Srgrimes#include <netdb.h>
4785342Simp#include <stdlib.h>
481573Srgrimes#include <unistd.h>
491573Srgrimes#include <pwd.h>
501573Srgrimes#include <errno.h>
511573Srgrimes#include <stdio.h>
521573Srgrimes#include <ctype.h>
531573Srgrimes#include <string.h>
54137675Sbz#include <rpc/rpc.h>
559978Swpaul#ifdef YP
569978Swpaul#include <rpcsvc/yp_prot.h>
579978Swpaul#include <rpcsvc/ypclnt.h>
589978Swpaul#endif
5963682Sume#include <arpa/nameser.h>
6071579Sdeischen#include "un-namespace.h"
61287292Skib#include "libc_private.h"
621573Srgrimes
6392905Sobrienextern int innetgr( const char *, const char *, const char *, const char * );
6417141Sjkh
652592Scsgr#define max(a, b)	((a > b) ? a : b)
662592Scsgr
6792941Sobrienint __ivaliduser(FILE *, u_int32_t, const char *, const char *);
6892941Sobrienint __ivaliduser_af(FILE *,const void *, const char *, const char *, int, int);
6992941Sobrienint __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t, const char *,
7092941Sobrien    const char *);
7192941Sobrienstatic int __icheckhost(const struct sockaddr *, socklen_t, const char *);
721573Srgrimes
7369575Sumechar paddr[NI_MAXHOST];
7455918Sshin
751573Srgrimesint
76288046Srodrigcrcmd(char **ahost, int rport, const char *locuser, const char *remuser,
77288046Srodrigc    const char *cmd, int *fd2p)
781573Srgrimes{
7956590Sshin	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
8056590Sshin}
8156590Sshin
8256590Sshinint
83288046Srodrigcrcmd_af(char **ahost, int rport, const char *locuser, const char *remuser,
84288046Srodrigc    const char *cmd, int *fd2p, int af)
8556590Sshin{
8655918Sshin	struct addrinfo hints, *res, *ai;
8755918Sshin	struct sockaddr_storage from;
881573Srgrimes	fd_set reads;
8971579Sdeischen	sigset_t oldmask, newmask;
901573Srgrimes	pid_t pid;
9155918Sshin	int s, aport, lport, timo, error;
9285342Simp	char c, *p;
9369575Sume	int refused, nres;
9463682Sume	char num[8];
9563682Sume	static char canonnamebuf[MAXDNAME];	/* is it proper here? */
961573Srgrimes
9785342Simp	/* call rcmdsh() with specified remote shell if appropriate. */
9885342Simp	if (!issetugid() && (p = getenv("RSH"))) {
9985342Simp		struct servent *sp = getservbyname("shell", "tcp");
10085342Simp
10185342Simp		if (sp && sp->s_port == rport)
10285342Simp			return (rcmdsh(ahost, rport, locuser, remuser,
10385342Simp			    cmd, p));
10485342Simp	}
10585342Simp
10685342Simp	/* use rsh(1) if non-root and remote port is shell. */
10785342Simp	if (geteuid()) {
10885342Simp		struct servent *sp = getservbyname("shell", "tcp");
10985342Simp
11085342Simp		if (sp && sp->s_port == rport)
11185342Simp			return (rcmdsh(ahost, rport, locuser, remuser,
11285342Simp			    cmd, NULL));
11385342Simp	}
11485342Simp
1151573Srgrimes	pid = getpid();
11655918Sshin
11755918Sshin	memset(&hints, 0, sizeof(hints));
11855918Sshin	hints.ai_flags = AI_CANONNAME;
11956590Sshin	hints.ai_family = af;
12055918Sshin	hints.ai_socktype = SOCK_STREAM;
12155918Sshin	hints.ai_protocol = 0;
12255918Sshin	(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
12355918Sshin	error = getaddrinfo(*ahost, num, &hints, &res);
12455918Sshin	if (error) {
12555918Sshin		fprintf(stderr, "rcmd: getaddrinfo: %s\n",
12655918Sshin			gai_strerror(error));
12755918Sshin		if (error == EAI_SYSTEM)
12855918Sshin			fprintf(stderr, "rcmd: getaddrinfo: %s\n",
12955918Sshin				strerror(errno));
1301573Srgrimes		return (-1);
1311573Srgrimes	}
13263682Sume
13363682Sume	if (res->ai_canonname
13463682Sume	 && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) {
13563682Sume		strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf));
13663682Sume		*ahost = canonnamebuf;
13763682Sume	}
13869575Sume	nres = 0;
13969575Sume	for (ai = res; ai; ai = ai->ai_next)
14069575Sume		nres++;
14155918Sshin	ai = res;
14269575Sume	refused = 0;
14371579Sdeischen	sigemptyset(&newmask);
14471579Sdeischen	sigaddset(&newmask, SIGURG);
145287292Skib	__libc_sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask);
1461573Srgrimes	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
14755918Sshin		s = rresvport_af(&lport, ai->ai_family);
1481573Srgrimes		if (s < 0) {
14957123Sshin			if (errno != EAGAIN && ai->ai_next) {
15057123Sshin				ai = ai->ai_next;
15157123Sshin				continue;
15257123Sshin			}
1531573Srgrimes			if (errno == EAGAIN)
1541573Srgrimes				(void)fprintf(stderr,
1551573Srgrimes				    "rcmd: socket: All ports in use\n");
1561573Srgrimes			else
1571573Srgrimes				(void)fprintf(stderr, "rcmd: socket: %s\n",
1581573Srgrimes				    strerror(errno));
15957123Sshin			freeaddrinfo(res);
160287292Skib			__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
16171579Sdeischen			    NULL);
1621573Srgrimes			return (-1);
1631573Srgrimes		}
16456698Sjasone		_fcntl(s, F_SETOWN, pid);
16571579Sdeischen		if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
1661573Srgrimes			break;
16756698Sjasone		(void)_close(s);
1681573Srgrimes		if (errno == EADDRINUSE) {
1691573Srgrimes			lport--;
1701573Srgrimes			continue;
1711573Srgrimes		}
17269575Sume		if (errno == ECONNREFUSED)
17369575Sume			refused = 1;
17469575Sume		if (ai->ai_next == NULL && (!refused || timo > 16)) {
17569575Sume			(void)fprintf(stderr, "%s: %s\n",
17669575Sume				      *ahost, strerror(errno));
17769575Sume			freeaddrinfo(res);
178287292Skib			__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
17971579Sdeischen			    NULL);
18069575Sume			return (-1);
18164493Sume		}
18269575Sume		if (nres > 1) {
1831573Srgrimes			int oerrno = errno;
1841573Srgrimes
185146187Sume			getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr,
186146187Sume			    sizeof(paddr), NULL, 0, NI_NUMERICHOST);
1871573Srgrimes			(void)fprintf(stderr, "connect to address %s: ",
18855918Sshin				      paddr);
1891573Srgrimes			errno = oerrno;
1901573Srgrimes			perror(0);
19169575Sume		}
19269575Sume		if ((ai = ai->ai_next) == NULL) {
19369575Sume			/* refused && timo <= 16 */
19469575Sume			struct timespec time_to_sleep, time_remaining;
19569575Sume
19669575Sume			time_to_sleep.tv_sec = timo;
19769575Sume			time_to_sleep.tv_nsec = 0;
19869575Sume			(void)_nanosleep(&time_to_sleep, &time_remaining);
19969575Sume			timo *= 2;
20069575Sume			ai = res;
20169575Sume			refused = 0;
20269575Sume		}
20369575Sume		if (nres > 1) {
204146187Sume			getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr,
205146187Sume			    sizeof(paddr), NULL, 0, NI_NUMERICHOST);
20655918Sshin			fprintf(stderr, "Trying %s...\n", paddr);
2071573Srgrimes		}
2081573Srgrimes	}
2091573Srgrimes	lport--;
210297790Spfg	if (fd2p == NULL) {
21156698Sjasone		_write(s, "", 1);
2121573Srgrimes		lport = 0;
2131573Srgrimes	} else {
21455918Sshin		int s2 = rresvport_af(&lport, ai->ai_family), s3;
215141920Sstefanf		socklen_t len = ai->ai_addrlen;
2162592Scsgr		int nfds;
2171573Srgrimes
2181573Srgrimes		if (s2 < 0)
2191573Srgrimes			goto bad;
22071579Sdeischen		_listen(s2, 1);
2211573Srgrimes		(void)snprintf(num, sizeof(num), "%d", lport);
22256698Sjasone		if (_write(s, num, strlen(num)+1) != strlen(num)+1) {
2231573Srgrimes			(void)fprintf(stderr,
2241573Srgrimes			    "rcmd: write (setting up stderr): %s\n",
2251573Srgrimes			    strerror(errno));
22656698Sjasone			(void)_close(s2);
2271573Srgrimes			goto bad;
2281573Srgrimes		}
2292592Scsgr		nfds = max(s, s2)+1;
2302592Scsgr		if(nfds > FD_SETSIZE) {
2312592Scsgr			fprintf(stderr, "rcmd: too many files\n");
23256698Sjasone			(void)_close(s2);
2332592Scsgr			goto bad;
2342592Scsgr		}
23517543Speteragain:
2361573Srgrimes		FD_ZERO(&reads);
2371573Srgrimes		FD_SET(s, &reads);
2381573Srgrimes		FD_SET(s2, &reads);
2391573Srgrimes		errno = 0;
24071579Sdeischen		if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){
2411573Srgrimes			if (errno != 0)
2421573Srgrimes				(void)fprintf(stderr,
2431573Srgrimes				    "rcmd: select (setting up stderr): %s\n",
2441573Srgrimes				    strerror(errno));
2451573Srgrimes			else
2461573Srgrimes				(void)fprintf(stderr,
2471573Srgrimes				"select: protocol failure in circuit setup\n");
24856698Sjasone			(void)_close(s2);
2491573Srgrimes			goto bad;
2501573Srgrimes		}
25171579Sdeischen		s3 = _accept(s2, (struct sockaddr *)&from, &len);
25255918Sshin		switch (from.ss_family) {
25355918Sshin		case AF_INET:
25455918Sshin			aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
25555918Sshin			break;
25655918Sshin#ifdef INET6
25755918Sshin		case AF_INET6:
25855918Sshin			aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
25955918Sshin			break;
26055918Sshin#endif
26155918Sshin		default:
26255918Sshin			aport = 0;	/* error */
26355918Sshin			break;
26455918Sshin		}
26517543Speter		/*
26617543Speter		 * XXX careful for ftp bounce attacks. If discovered, shut them
26717543Speter		 * down and check for the real auxiliary channel to connect.
26817543Speter		 */
26955918Sshin		if (aport == 20) {
27056698Sjasone			_close(s3);
27117543Speter			goto again;
27217543Speter		}
27356698Sjasone		(void)_close(s2);
2741573Srgrimes		if (s3 < 0) {
2751573Srgrimes			(void)fprintf(stderr,
2761573Srgrimes			    "rcmd: accept: %s\n", strerror(errno));
2771573Srgrimes			lport = 0;
2781573Srgrimes			goto bad;
2791573Srgrimes		}
2801573Srgrimes		*fd2p = s3;
28155918Sshin		if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
2821573Srgrimes			(void)fprintf(stderr,
2831573Srgrimes			    "socket: protocol failure in circuit setup.\n");
2841573Srgrimes			goto bad2;
2851573Srgrimes		}
2861573Srgrimes	}
28756698Sjasone	(void)_write(s, locuser, strlen(locuser)+1);
28856698Sjasone	(void)_write(s, remuser, strlen(remuser)+1);
28956698Sjasone	(void)_write(s, cmd, strlen(cmd)+1);
29056698Sjasone	if (_read(s, &c, 1) != 1) {
2911573Srgrimes		(void)fprintf(stderr,
2921573Srgrimes		    "rcmd: %s: %s\n", *ahost, strerror(errno));
2931573Srgrimes		goto bad2;
2941573Srgrimes	}
2951573Srgrimes	if (c != 0) {
29656698Sjasone		while (_read(s, &c, 1) == 1) {
29756698Sjasone			(void)_write(STDERR_FILENO, &c, 1);
2981573Srgrimes			if (c == '\n')
2991573Srgrimes				break;
3001573Srgrimes		}
3011573Srgrimes		goto bad2;
3021573Srgrimes	}
303287292Skib	__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
30455918Sshin	freeaddrinfo(res);
3051573Srgrimes	return (s);
3061573Srgrimesbad2:
3071573Srgrimes	if (lport)
30856698Sjasone		(void)_close(*fd2p);
3091573Srgrimesbad:
31056698Sjasone	(void)_close(s);
311287292Skib	__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
31255918Sshin	freeaddrinfo(res);
3131573Srgrimes	return (-1);
3141573Srgrimes}
3151573Srgrimes
3161573Srgrimesint
317288046Srodrigcrresvport(int *port)
3181573Srgrimes{
31955918Sshin	return rresvport_af(port, AF_INET);
32055918Sshin}
3211573Srgrimes
32255918Sshinint
323288046Srodrigcrresvport_af(int *alport, int family)
32455918Sshin{
325111082Snectar	int s;
32655918Sshin	struct sockaddr_storage ss;
32755918Sshin	u_short *sport;
32855918Sshin
32955918Sshin	memset(&ss, 0, sizeof(ss));
33055918Sshin	ss.ss_family = family;
33155918Sshin	switch (family) {
33255918Sshin	case AF_INET:
33355918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
33455918Sshin		sport = &((struct sockaddr_in *)&ss)->sin_port;
33555918Sshin		((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
33655918Sshin		break;
33755918Sshin#ifdef INET6
33855918Sshin	case AF_INET6:
33955918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
34055918Sshin		sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
34155918Sshin		((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
34255918Sshin		break;
34355918Sshin#endif
34455918Sshin	default:
34555918Sshin		errno = EAFNOSUPPORT;
34655918Sshin		return -1;
34755918Sshin	}
34855918Sshin
34971579Sdeischen	s = _socket(ss.ss_family, SOCK_STREAM, 0);
3501573Srgrimes	if (s < 0)
3511573Srgrimes		return (-1);
35217543Speter#if 0 /* compat_exact_traditional_rresvport_semantics */
35317543Speter	sin.sin_port = htons((u_short)*alport);
35471579Sdeischen	if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
35517543Speter		return (s);
35617543Speter	if (errno != EADDRINUSE) {
35756698Sjasone		(void)_close(s);
35817543Speter		return (-1);
35916034Speter	}
36017543Speter#endif
36155918Sshin	*sport = 0;
36256636Sshin	if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) {
36356698Sjasone		(void)_close(s);
36417543Speter		return (-1);
3651573Srgrimes	}
36655918Sshin	*alport = (int)ntohs(*sport);
36717543Speter	return (s);
3681573Srgrimes}
3691573Srgrimes
3701573Srgrimesint	__check_rhosts_file = 1;
3711573Srgrimeschar	*__rcmd_errstr;
3721573Srgrimes
3731573Srgrimesint
374288046Srodrigcruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
3751573Srgrimes{
37656939Sshin	struct addrinfo hints, *res, *r;
37756939Sshin	int error;
37855918Sshin
37956939Sshin	memset(&hints, 0, sizeof(hints));
38056939Sshin	hints.ai_family = PF_UNSPEC;
38156939Sshin	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
38256939Sshin	error = getaddrinfo(rhost, "0", &hints, &res);
38356939Sshin	if (error)
38456939Sshin		return (-1);
3851573Srgrimes
38656939Sshin	for (r = res; r; r = r->ai_next) {
38756939Sshin		if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser,
38856939Sshin		    luser) == 0) {
38956939Sshin			freeaddrinfo(res);
39056939Sshin			return (0);
39155918Sshin		}
3921573Srgrimes	}
39356939Sshin	freeaddrinfo(res);
39456939Sshin	return (-1);
3951573Srgrimes}
3961573Srgrimes
3971573Srgrimes/*
3981573Srgrimes * New .rhosts strategy: We are passed an ip address. We spin through
3991573Srgrimes * hosts.equiv and .rhosts looking for a match. When the .rhosts only
4001573Srgrimes * has ip addresses, we don't have to trust a nameserver.  When it
4011573Srgrimes * contains hostnames, we spin through the list of addresses the nameserver
4021573Srgrimes * gives us and look for a match.
4031573Srgrimes *
4041573Srgrimes * Returns 0 if ok, -1 if not ok.
4051573Srgrimes */
4061573Srgrimesint
407288046Srodrigciruserok(unsigned long raddr, int superuser, const char *ruser, const char *luser)
4081573Srgrimes{
40969575Sume	struct sockaddr_in sin;
41069575Sume
41169575Sume	memset(&sin, 0, sizeof(sin));
41269575Sume	sin.sin_family = AF_INET;
41369575Sume	sin.sin_len = sizeof(struct sockaddr_in);
41469575Sume	memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
41569575Sume	return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser,
41669575Sume		ruser, luser);
41755918Sshin}
41855918Sshin
41969575Sume/*
42069575Sume * AF independent extension of iruserok.
42169575Sume *
42269575Sume * Returns 0 if ok, -1 if not ok.
42369575Sume */
42469575Sumeint
425288046Srodrigciruserok_sa(const void *ra, int rlen, int superuser, const char *ruser,
426288046Srodrigc    const char *luser)
42755918Sshin{
42892889Sobrien	char *cp;
4291573Srgrimes	struct stat sbuf;
4301573Srgrimes	struct passwd *pwd;
4311573Srgrimes	FILE *hostf;
4321573Srgrimes	uid_t uid;
4331573Srgrimes	int first;
4341573Srgrimes	char pbuf[MAXPATHLEN];
43569575Sume	const struct sockaddr *raddr;
43669575Sume	struct sockaddr_storage ss;
4371573Srgrimes
43869575Sume	/* avoid alignment issue */
43969575Sume	if (rlen > sizeof(ss))
44069575Sume		return(-1);
44169575Sume	memcpy(&ss, ra, rlen);
44269575Sume	raddr = (struct sockaddr *)&ss;
44355918Sshin
4441573Srgrimes	first = 1;
445254700Sjilles	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
4461573Srgrimesagain:
4471573Srgrimes	if (hostf) {
44869575Sume		if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) {
4491573Srgrimes			(void)fclose(hostf);
4501573Srgrimes			return (0);
4511573Srgrimes		}
4521573Srgrimes		(void)fclose(hostf);
4531573Srgrimes	}
4541573Srgrimes	if (first == 1 && (__check_rhosts_file || superuser)) {
4551573Srgrimes		first = 0;
4561573Srgrimes		if ((pwd = getpwnam(luser)) == NULL)
4571573Srgrimes			return (-1);
4581573Srgrimes		(void)strcpy(pbuf, pwd->pw_dir);
4591573Srgrimes		(void)strcat(pbuf, "/.rhosts");
4601573Srgrimes
4611573Srgrimes		/*
4621573Srgrimes		 * Change effective uid while opening .rhosts.  If root and
4631573Srgrimes		 * reading an NFS mounted file system, can't read files that
4641573Srgrimes		 * are protected read/write owner only.
4651573Srgrimes		 */
4661573Srgrimes		uid = geteuid();
4671573Srgrimes		(void)seteuid(pwd->pw_uid);
468254700Sjilles		hostf = fopen(pbuf, "re");
4691573Srgrimes		(void)seteuid(uid);
4701573Srgrimes
4711573Srgrimes		if (hostf == NULL)
4721573Srgrimes			return (-1);
4731573Srgrimes		/*
4741573Srgrimes		 * If not a regular file, or is owned by someone other than
4751573Srgrimes		 * user or root or if writeable by anyone but the owner, quit.
4761573Srgrimes		 */
4771573Srgrimes		cp = NULL;
4781573Srgrimes		if (lstat(pbuf, &sbuf) < 0)
4791573Srgrimes			cp = ".rhosts lstat failed";
4801573Srgrimes		else if (!S_ISREG(sbuf.st_mode))
4811573Srgrimes			cp = ".rhosts not regular file";
48271579Sdeischen		else if (_fstat(fileno(hostf), &sbuf) < 0)
4831573Srgrimes			cp = ".rhosts fstat failed";
4841573Srgrimes		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
4851573Srgrimes			cp = "bad .rhosts owner";
4861573Srgrimes		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
4871573Srgrimes			cp = ".rhosts writeable by other than owner";
4881573Srgrimes		/* If there were any problems, quit. */
4891573Srgrimes		if (cp) {
4901573Srgrimes			__rcmd_errstr = cp;
4911573Srgrimes			(void)fclose(hostf);
4921573Srgrimes			return (-1);
4931573Srgrimes		}
4941573Srgrimes		goto again;
4951573Srgrimes	}
4961573Srgrimes	return (-1);
4971573Srgrimes}
4981573Srgrimes
4991573Srgrimes/*
50069575Sume * XXX
50169575Sume * Don't make static, used by lpd(8).
50256939Sshin *
50356939Sshin * Returns 0 if ok, -1 if not ok.
50456939Sshin */
50556939Sshinint
506288046Srodrigc__ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser)
50756939Sshin{
50869575Sume	struct sockaddr_in sin;
50956939Sshin
51069575Sume	memset(&sin, 0, sizeof(sin));
51169575Sume	sin.sin_family = AF_INET;
51269575Sume	sin.sin_len = sizeof(struct sockaddr_in);
51369575Sume	memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
51469575Sume	return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len,
51569575Sume		luser, ruser);
51669575Sume}
51769575Sume
51869575Sume/*
51969575Sume * Returns 0 if ok, -1 if not ok.
52069575Sume *
52169575Sume * XXX obsolete API.
52269575Sume */
52369575Sumeint
524288046Srodrigc__ivaliduser_af(FILE *hostf, const void *raddr, const char *luser,
525288046Srodrigc    const char *ruser, int af, int len)
52669575Sume{
52769575Sume	struct sockaddr *sa = NULL;
52869575Sume	struct sockaddr_in *sin = NULL;
52969575Sume#ifdef INET6
53069575Sume	struct sockaddr_in6 *sin6 = NULL;
53169575Sume#endif
53269575Sume	struct sockaddr_storage ss;
53369575Sume
53469575Sume	memset(&ss, 0, sizeof(ss));
53569575Sume	switch (af) {
53656939Sshin	case AF_INET:
53769575Sume		if (len != sizeof(sin->sin_addr))
53869575Sume			return -1;
53969575Sume		sin = (struct sockaddr_in *)&ss;
54069575Sume		sin->sin_family = AF_INET;
54169575Sume		sin->sin_len = sizeof(struct sockaddr_in);
54269575Sume		memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr));
54356939Sshin		break;
54456939Sshin#ifdef INET6
54556939Sshin	case AF_INET6:
54669575Sume		if (len != sizeof(sin6->sin6_addr))
54769575Sume			return -1;
54869575Sume		/* you will lose scope info */
54969575Sume		sin6 = (struct sockaddr_in6 *)&ss;
55069575Sume		sin6->sin6_family = AF_INET6;
55169575Sume		sin6->sin6_len = sizeof(struct sockaddr_in6);
55269575Sume		memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr));
55356939Sshin		break;
55456939Sshin#endif
55569575Sume	default:
55669575Sume		return -1;
55756939Sshin	}
55856939Sshin
55969575Sume	sa = (struct sockaddr *)&ss;
56069575Sume	return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser);
56156939Sshin}
56256939Sshin
5631573Srgrimesint
564288046Srodrigc__ivaliduser_sa(FILE *hostf, const struct sockaddr *raddr, socklen_t salen,
565288046Srodrigc    const char *luser, const char *ruser)
5661573Srgrimes{
56792889Sobrien	char *user, *p;
5681573Srgrimes	int ch;
5691573Srgrimes	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
57010059Swpaul	char hname[MAXHOSTNAMELEN];
5717183Swpaul	/* Presumed guilty until proven innocent. */
5727183Swpaul	int userok = 0, hostok = 0;
5739978Swpaul#ifdef YP
5749978Swpaul	char *ypdomain;
5751573Srgrimes
5769978Swpaul	if (yp_get_default_domain(&ypdomain))
5779978Swpaul		ypdomain = NULL;
5789978Swpaul#else
5799978Swpaul#define	ypdomain NULL
5809978Swpaul#endif
5817183Swpaul	/* We need to get the damn hostname back for netgroup matching. */
58269575Sume	if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0,
58369575Sume			NI_NAMEREQD) != 0)
584102369Sjdp		hname[0] = '\0';
5857183Swpaul
5861573Srgrimes	while (fgets(buf, sizeof(buf), hostf)) {
5871573Srgrimes		p = buf;
5881573Srgrimes		/* Skip lines that are too long. */
58958154Sbsd		if (strchr(p, '\n') == NULL) {
5901573Srgrimes			while ((ch = getc(hostf)) != '\n' && ch != EOF);
5911573Srgrimes			continue;
5921573Srgrimes		}
5939552Speter		if (*p == '\n' || *p == '#') {
5949552Speter			/* comment... */
5959552Speter			continue;
5969552Speter		}
5971573Srgrimes		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
59852859Sache			*p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p;
5991573Srgrimes			p++;
6001573Srgrimes		}
6011573Srgrimes		if (*p == ' ' || *p == '\t') {
6021573Srgrimes			*p++ = '\0';
6031573Srgrimes			while (*p == ' ' || *p == '\t')
6041573Srgrimes				p++;
6051573Srgrimes			user = p;
6061573Srgrimes			while (*p != '\n' && *p != ' ' &&
6071573Srgrimes			    *p != '\t' && *p != '\0')
6081573Srgrimes				p++;
6091573Srgrimes		} else
6101573Srgrimes			user = p;
6111573Srgrimes		*p = '\0';
6127183Swpaul		/*
6137183Swpaul		 * Do +/- and +@/-@ checking. This looks really nasty,
6147183Swpaul		 * but it matches SunOS's behavior so far as I can tell.
6157183Swpaul		 */
6167183Swpaul		switch(buf[0]) {
6177183Swpaul		case '+':
6187183Swpaul			if (!buf[1]) {     /* '+' matches all hosts */
6197183Swpaul				hostok = 1;
6207183Swpaul				break;
6217183Swpaul			}
6227183Swpaul			if (buf[1] == '@')  /* match a host by netgroup */
623102369Sjdp				hostok = hname[0] != '\0' &&
624102369Sjdp				    innetgr(&buf[2], hname, NULL, ypdomain);
6257183Swpaul			else		/* match a host by addr */
62669575Sume				hostok = __icheckhost(raddr, salen,
62769575Sume						      (char *)&buf[1]);
6287183Swpaul			break;
6297183Swpaul		case '-':     /* reject '-' hosts and all their users */
6307183Swpaul			if (buf[1] == '@') {
631102369Sjdp				if (hname[0] == '\0' ||
632102369Sjdp				    innetgr(&buf[2], hname, NULL, ypdomain))
6337183Swpaul					return(-1);
6347183Swpaul			} else {
63569575Sume				if (__icheckhost(raddr, salen,
63669575Sume						 (char *)&buf[1]))
6377183Swpaul					return(-1);
6387183Swpaul			}
6397183Swpaul			break;
6407183Swpaul		default:  /* if no '+' or '-', do a simple match */
64169575Sume			hostok = __icheckhost(raddr, salen, buf);
6427183Swpaul			break;
6431573Srgrimes		}
6447183Swpaul		switch(*user) {
6457183Swpaul		case '+':
6467183Swpaul			if (!*(user+1)) {      /* '+' matches all users */
6477183Swpaul				userok = 1;
6487183Swpaul				break;
6497183Swpaul			}
6507183Swpaul			if (*(user+1) == '@')  /* match a user by netgroup */
6519978Swpaul				userok = innetgr(user+2, NULL, ruser, ypdomain);
6527183Swpaul			else	   /* match a user by direct specification */
6537183Swpaul				userok = !(strcmp(ruser, user+1));
6547183Swpaul			break;
6557183Swpaul		case '-': 		/* if we matched a hostname, */
6567183Swpaul			if (hostok) {   /* check for user field rejections */
6577183Swpaul				if (!*(user+1))
6587183Swpaul					return(-1);
6597183Swpaul				if (*(user+1) == '@') {
6607183Swpaul					if (innetgr(user+2, NULL,
6619978Swpaul							ruser, ypdomain))
6627183Swpaul						return(-1);
6637183Swpaul				} else {
6647183Swpaul					if (!strcmp(ruser, user+1))
6657183Swpaul						return(-1);
6667183Swpaul				}
6677183Swpaul			}
6687183Swpaul			break;
6697183Swpaul		default:	/* no rejections: try to match the user */
6707183Swpaul			if (hostok)
6717183Swpaul				userok = !(strcmp(ruser,*user ? user : luser));
6727183Swpaul			break;
6737183Swpaul		}
6747183Swpaul		if (hostok && userok)
6757183Swpaul			return(0);
6761573Srgrimes	}
6771573Srgrimes	return (-1);
6781573Srgrimes}
6791573Srgrimes
6801573Srgrimes/*
6811573Srgrimes * Returns "true" if match, 0 if no match.
6821573Srgrimes */
6831573Srgrimesstatic int
684288046Srodrigc__icheckhost(const struct sockaddr *raddr, socklen_t salen, const char *lhost)
6851573Srgrimes{
68669575Sume	struct sockaddr_in sin;
68769575Sume	struct sockaddr_in6 *sin6;
68869575Sume	struct addrinfo hints, *res, *r;
68969575Sume	int error;
69069575Sume	char h1[NI_MAXHOST], h2[NI_MAXHOST];
6911573Srgrimes
69269575Sume	if (raddr->sa_family == AF_INET6) {
69369575Sume		sin6 = (struct sockaddr_in6 *)raddr;
69469575Sume		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
69569575Sume			memset(&sin, 0, sizeof(sin));
69669575Sume			sin.sin_family = AF_INET;
69769575Sume			sin.sin_len = sizeof(struct sockaddr_in);
69869575Sume			memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
69969575Sume			       sizeof(sin.sin_addr));
70069575Sume			raddr = (struct sockaddr *)&sin;
70169575Sume			salen = sin.sin_len;
70269575Sume		}
70355918Sshin	}
7041573Srgrimes
70569575Sume	h1[0] = '\0';
70669575Sume	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
707146187Sume			NI_NUMERICHOST) != 0)
7081573Srgrimes		return (0);
7091573Srgrimes
71069575Sume	/* Resolve laddr into sockaddr */
71169575Sume	memset(&hints, 0, sizeof(hints));
71269575Sume	hints.ai_family = raddr->sa_family;
71369575Sume	hints.ai_socktype = SOCK_DGRAM;	/*XXX dummy*/
71469575Sume	res = NULL;
71569575Sume	error = getaddrinfo(lhost, "0", &hints, &res);
71669575Sume	if (error)
71769575Sume		return (0);
71869575Sume
71969575Sume	for (r = res; r ; r = r->ai_next) {
72069575Sume		h2[0] = '\0';
72169575Sume		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
722146187Sume				NULL, 0, NI_NUMERICHOST) != 0)
72369575Sume			continue;
72469575Sume		if (strcmp(h1, h2) == 0) {
72569575Sume			freeaddrinfo(res);
72669575Sume			return (1);
72755918Sshin		}
72869575Sume	}
7291573Srgrimes
73069575Sume	/* No match. */
73169575Sume	freeaddrinfo(res);
73269575Sume	return (0);
7331573Srgrimes}
734