rcmd.c revision 92889
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 *
3363682Sume * $FreeBSD: head/lib/libc/net/rcmd.c 92889 2002-03-21 18:49:23Z obrien $
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
4071579Sdeischen#include "namespace.h"
411573Srgrimes#include <sys/param.h>
421573Srgrimes#include <sys/socket.h>
431573Srgrimes#include <sys/stat.h>
441573Srgrimes
451573Srgrimes#include <netinet/in.h>
461573Srgrimes#include <arpa/inet.h>
471573Srgrimes
481573Srgrimes#include <signal.h>
491573Srgrimes#include <fcntl.h>
501573Srgrimes#include <netdb.h>
5185342Simp#include <stdlib.h>
521573Srgrimes#include <unistd.h>
531573Srgrimes#include <pwd.h>
541573Srgrimes#include <errno.h>
551573Srgrimes#include <stdio.h>
561573Srgrimes#include <ctype.h>
571573Srgrimes#include <string.h>
589978Swpaul#ifdef YP
599978Swpaul#include <rpc/rpc.h>
609978Swpaul#include <rpcsvc/yp_prot.h>
619978Swpaul#include <rpcsvc/ypclnt.h>
629978Swpaul#endif
6363682Sume#include <arpa/nameser.h>
6471579Sdeischen#include "un-namespace.h"
651573Srgrimes
6655918Sshin/* wrapper for KAME-special getnameinfo() */
6755918Sshin#ifndef NI_WITHSCOPEID
6855918Sshin#define NI_WITHSCOPEID	0
6955918Sshin#endif
7055918Sshin
7117141Sjkhextern int innetgr __P(( const char *, const char *, const char *, const char * ));
7217141Sjkh
732592Scsgr#define max(a, b)	((a > b) ? a : b)
742592Scsgr
7539979Sdfrint	__ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
7669575Sumeint __ivaliduser_af __P((FILE *,const void *, const char *, const char *,
7769575Sume	int, int));
7869575Sumeint __ivaliduser_sa __P((FILE *, const struct sockaddr *, socklen_t,
7969575Sume	const char *,const char *));
8069575Sumestatic int __icheckhost __P((const struct sockaddr *, socklen_t,
8169575Sume	const char *));
821573Srgrimes
8369575Sumechar paddr[NI_MAXHOST];
8455918Sshin
851573Srgrimesint
861573Srgrimesrcmd(ahost, rport, locuser, remuser, cmd, fd2p)
871573Srgrimes	char **ahost;
881573Srgrimes	u_short rport;
891573Srgrimes	const char *locuser, *remuser, *cmd;
901573Srgrimes	int *fd2p;
911573Srgrimes{
9256590Sshin	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
9356590Sshin}
9456590Sshin
9556590Sshinint
9656590Sshinrcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
9756590Sshin	char **ahost;
9856590Sshin	u_short rport;
9956590Sshin	const char *locuser, *remuser, *cmd;
10056590Sshin	int *fd2p;
10156590Sshin	int af;
10256590Sshin{
10355918Sshin	struct addrinfo hints, *res, *ai;
10455918Sshin	struct sockaddr_storage from;
1051573Srgrimes	fd_set reads;
10671579Sdeischen	sigset_t oldmask, newmask;
1071573Srgrimes	pid_t pid;
10855918Sshin	int s, aport, lport, timo, error;
10985342Simp	char c, *p;
11069575Sume	int refused, nres;
11163682Sume	char num[8];
11263682Sume	static char canonnamebuf[MAXDNAME];	/* is it proper here? */
1131573Srgrimes
11485342Simp	/* call rcmdsh() with specified remote shell if appropriate. */
11585342Simp	if (!issetugid() && (p = getenv("RSH"))) {
11685342Simp		struct servent *sp = getservbyname("shell", "tcp");
11785342Simp
11885342Simp		if (sp && sp->s_port == rport)
11985342Simp			return (rcmdsh(ahost, rport, locuser, remuser,
12085342Simp			    cmd, p));
12185342Simp	}
12285342Simp
12385342Simp	/* use rsh(1) if non-root and remote port is shell. */
12485342Simp	if (geteuid()) {
12585342Simp		struct servent *sp = getservbyname("shell", "tcp");
12685342Simp
12785342Simp		if (sp && sp->s_port == rport)
12885342Simp			return (rcmdsh(ahost, rport, locuser, remuser,
12985342Simp			    cmd, NULL));
13085342Simp	}
13185342Simp
1321573Srgrimes	pid = getpid();
13355918Sshin
13455918Sshin	memset(&hints, 0, sizeof(hints));
13555918Sshin	hints.ai_flags = AI_CANONNAME;
13656590Sshin	hints.ai_family = af;
13755918Sshin	hints.ai_socktype = SOCK_STREAM;
13855918Sshin	hints.ai_protocol = 0;
13955918Sshin	(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
14055918Sshin	error = getaddrinfo(*ahost, num, &hints, &res);
14155918Sshin	if (error) {
14255918Sshin		fprintf(stderr, "rcmd: getaddrinfo: %s\n",
14355918Sshin			gai_strerror(error));
14455918Sshin		if (error == EAI_SYSTEM)
14555918Sshin			fprintf(stderr, "rcmd: getaddrinfo: %s\n",
14655918Sshin				strerror(errno));
1471573Srgrimes		return (-1);
1481573Srgrimes	}
14963682Sume
15063682Sume	if (res->ai_canonname
15163682Sume	 && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) {
15263682Sume		strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf));
15363682Sume		*ahost = canonnamebuf;
15463682Sume	}
15569575Sume	nres = 0;
15669575Sume	for (ai = res; ai; ai = ai->ai_next)
15769575Sume		nres++;
15855918Sshin	ai = res;
15969575Sume	refused = 0;
16071579Sdeischen	sigemptyset(&newmask);
16171579Sdeischen	sigaddset(&newmask, SIGURG);
16271579Sdeischen	_sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask);
1631573Srgrimes	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
16455918Sshin		s = rresvport_af(&lport, ai->ai_family);
1651573Srgrimes		if (s < 0) {
16657123Sshin			if (errno != EAGAIN && ai->ai_next) {
16757123Sshin				ai = ai->ai_next;
16857123Sshin				continue;
16957123Sshin			}
1701573Srgrimes			if (errno == EAGAIN)
1711573Srgrimes				(void)fprintf(stderr,
1721573Srgrimes				    "rcmd: socket: All ports in use\n");
1731573Srgrimes			else
1741573Srgrimes				(void)fprintf(stderr, "rcmd: socket: %s\n",
1751573Srgrimes				    strerror(errno));
17657123Sshin			freeaddrinfo(res);
17771579Sdeischen			_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
17871579Sdeischen			    NULL);
1791573Srgrimes			return (-1);
1801573Srgrimes		}
18156698Sjasone		_fcntl(s, F_SETOWN, pid);
18271579Sdeischen		if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
1831573Srgrimes			break;
18456698Sjasone		(void)_close(s);
1851573Srgrimes		if (errno == EADDRINUSE) {
1861573Srgrimes			lport--;
1871573Srgrimes			continue;
1881573Srgrimes		}
18969575Sume		if (errno == ECONNREFUSED)
19069575Sume			refused = 1;
19169575Sume		if (ai->ai_next == NULL && (!refused || timo > 16)) {
19269575Sume			(void)fprintf(stderr, "%s: %s\n",
19369575Sume				      *ahost, strerror(errno));
19469575Sume			freeaddrinfo(res);
19571579Sdeischen			_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
19671579Sdeischen			    NULL);
19769575Sume			return (-1);
19864493Sume		}
19969575Sume		if (nres > 1) {
2001573Srgrimes			int oerrno = errno;
2011573Srgrimes
20255918Sshin			getnameinfo(ai->ai_addr, ai->ai_addrlen,
20355918Sshin				    paddr, sizeof(paddr),
20455918Sshin				    NULL, 0,
20555918Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
2061573Srgrimes			(void)fprintf(stderr, "connect to address %s: ",
20755918Sshin				      paddr);
2081573Srgrimes			errno = oerrno;
2091573Srgrimes			perror(0);
21069575Sume		}
21169575Sume		if ((ai = ai->ai_next) == NULL) {
21269575Sume			/* refused && timo <= 16 */
21369575Sume			struct timespec time_to_sleep, time_remaining;
21469575Sume
21569575Sume			time_to_sleep.tv_sec = timo;
21669575Sume			time_to_sleep.tv_nsec = 0;
21769575Sume			(void)_nanosleep(&time_to_sleep, &time_remaining);
21869575Sume			timo *= 2;
21969575Sume			ai = res;
22069575Sume			refused = 0;
22169575Sume		}
22269575Sume		if (nres > 1) {
22355918Sshin			getnameinfo(ai->ai_addr, ai->ai_addrlen,
22455918Sshin				    paddr, sizeof(paddr),
22555918Sshin				    NULL, 0,
22655918Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
22755918Sshin			fprintf(stderr, "Trying %s...\n", paddr);
2281573Srgrimes		}
2291573Srgrimes	}
2301573Srgrimes	lport--;
2311573Srgrimes	if (fd2p == 0) {
23256698Sjasone		_write(s, "", 1);
2331573Srgrimes		lport = 0;
2341573Srgrimes	} else {
2351573Srgrimes		char num[8];
23655918Sshin		int s2 = rresvport_af(&lport, ai->ai_family), s3;
23755918Sshin		int len = ai->ai_addrlen;
2382592Scsgr		int nfds;
2391573Srgrimes
2401573Srgrimes		if (s2 < 0)
2411573Srgrimes			goto bad;
24271579Sdeischen		_listen(s2, 1);
2431573Srgrimes		(void)snprintf(num, sizeof(num), "%d", lport);
24456698Sjasone		if (_write(s, num, strlen(num)+1) != strlen(num)+1) {
2451573Srgrimes			(void)fprintf(stderr,
2461573Srgrimes			    "rcmd: write (setting up stderr): %s\n",
2471573Srgrimes			    strerror(errno));
24856698Sjasone			(void)_close(s2);
2491573Srgrimes			goto bad;
2501573Srgrimes		}
2512592Scsgr		nfds = max(s, s2)+1;
2522592Scsgr		if(nfds > FD_SETSIZE) {
2532592Scsgr			fprintf(stderr, "rcmd: too many files\n");
25456698Sjasone			(void)_close(s2);
2552592Scsgr			goto bad;
2562592Scsgr		}
25717543Speteragain:
2581573Srgrimes		FD_ZERO(&reads);
2591573Srgrimes		FD_SET(s, &reads);
2601573Srgrimes		FD_SET(s2, &reads);
2611573Srgrimes		errno = 0;
26271579Sdeischen		if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){
2631573Srgrimes			if (errno != 0)
2641573Srgrimes				(void)fprintf(stderr,
2651573Srgrimes				    "rcmd: select (setting up stderr): %s\n",
2661573Srgrimes				    strerror(errno));
2671573Srgrimes			else
2681573Srgrimes				(void)fprintf(stderr,
2691573Srgrimes				"select: protocol failure in circuit setup\n");
27056698Sjasone			(void)_close(s2);
2711573Srgrimes			goto bad;
2721573Srgrimes		}
27371579Sdeischen		s3 = _accept(s2, (struct sockaddr *)&from, &len);
27455918Sshin		switch (from.ss_family) {
27555918Sshin		case AF_INET:
27655918Sshin			aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
27755918Sshin			break;
27855918Sshin#ifdef INET6
27955918Sshin		case AF_INET6:
28055918Sshin			aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
28155918Sshin			break;
28255918Sshin#endif
28355918Sshin		default:
28455918Sshin			aport = 0;	/* error */
28555918Sshin			break;
28655918Sshin		}
28717543Speter		/*
28817543Speter		 * XXX careful for ftp bounce attacks. If discovered, shut them
28917543Speter		 * down and check for the real auxiliary channel to connect.
29017543Speter		 */
29155918Sshin		if (aport == 20) {
29256698Sjasone			_close(s3);
29317543Speter			goto again;
29417543Speter		}
29556698Sjasone		(void)_close(s2);
2961573Srgrimes		if (s3 < 0) {
2971573Srgrimes			(void)fprintf(stderr,
2981573Srgrimes			    "rcmd: accept: %s\n", strerror(errno));
2991573Srgrimes			lport = 0;
3001573Srgrimes			goto bad;
3011573Srgrimes		}
3021573Srgrimes		*fd2p = s3;
30355918Sshin		if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
3041573Srgrimes			(void)fprintf(stderr,
3051573Srgrimes			    "socket: protocol failure in circuit setup.\n");
3061573Srgrimes			goto bad2;
3071573Srgrimes		}
3081573Srgrimes	}
30956698Sjasone	(void)_write(s, locuser, strlen(locuser)+1);
31056698Sjasone	(void)_write(s, remuser, strlen(remuser)+1);
31156698Sjasone	(void)_write(s, cmd, strlen(cmd)+1);
31256698Sjasone	if (_read(s, &c, 1) != 1) {
3131573Srgrimes		(void)fprintf(stderr,
3141573Srgrimes		    "rcmd: %s: %s\n", *ahost, strerror(errno));
3151573Srgrimes		goto bad2;
3161573Srgrimes	}
3171573Srgrimes	if (c != 0) {
31856698Sjasone		while (_read(s, &c, 1) == 1) {
31956698Sjasone			(void)_write(STDERR_FILENO, &c, 1);
3201573Srgrimes			if (c == '\n')
3211573Srgrimes				break;
3221573Srgrimes		}
3231573Srgrimes		goto bad2;
3241573Srgrimes	}
32571579Sdeischen	_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
32655918Sshin	freeaddrinfo(res);
3271573Srgrimes	return (s);
3281573Srgrimesbad2:
3291573Srgrimes	if (lport)
33056698Sjasone		(void)_close(*fd2p);
3311573Srgrimesbad:
33256698Sjasone	(void)_close(s);
33371579Sdeischen	_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
33455918Sshin	freeaddrinfo(res);
3351573Srgrimes	return (-1);
3361573Srgrimes}
3371573Srgrimes
3381573Srgrimesint
33955918Sshinrresvport(port)
34055918Sshin	int *port;
3411573Srgrimes{
34255918Sshin	return rresvport_af(port, AF_INET);
34355918Sshin}
3441573Srgrimes
34555918Sshinint
34655918Sshinrresvport_af(alport, family)
34755918Sshin	int *alport, family;
34855918Sshin{
34955918Sshin	int i, s, len, err;
35055918Sshin	struct sockaddr_storage ss;
35155918Sshin	u_short *sport;
35255918Sshin
35355918Sshin	memset(&ss, 0, sizeof(ss));
35455918Sshin	ss.ss_family = family;
35555918Sshin	switch (family) {
35655918Sshin	case AF_INET:
35755918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
35855918Sshin		sport = &((struct sockaddr_in *)&ss)->sin_port;
35955918Sshin		((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
36055918Sshin		break;
36155918Sshin#ifdef INET6
36255918Sshin	case AF_INET6:
36355918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
36455918Sshin		sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
36555918Sshin		((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
36655918Sshin		break;
36755918Sshin#endif
36855918Sshin	default:
36955918Sshin		errno = EAFNOSUPPORT;
37055918Sshin		return -1;
37155918Sshin	}
37255918Sshin
37371579Sdeischen	s = _socket(ss.ss_family, SOCK_STREAM, 0);
3741573Srgrimes	if (s < 0)
3751573Srgrimes		return (-1);
37617543Speter#if 0 /* compat_exact_traditional_rresvport_semantics */
37717543Speter	sin.sin_port = htons((u_short)*alport);
37871579Sdeischen	if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
37917543Speter		return (s);
38017543Speter	if (errno != EADDRINUSE) {
38156698Sjasone		(void)_close(s);
38217543Speter		return (-1);
38316034Speter	}
38417543Speter#endif
38555918Sshin	*sport = 0;
38656636Sshin	if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) {
38756698Sjasone		(void)_close(s);
38817543Speter		return (-1);
3891573Srgrimes	}
39055918Sshin	*alport = (int)ntohs(*sport);
39117543Speter	return (s);
3921573Srgrimes}
3931573Srgrimes
3941573Srgrimesint	__check_rhosts_file = 1;
3951573Srgrimeschar	*__rcmd_errstr;
3961573Srgrimes
3971573Srgrimesint
3981573Srgrimesruserok(rhost, superuser, ruser, luser)
3991573Srgrimes	const char *rhost, *ruser, *luser;
4001573Srgrimes	int superuser;
4011573Srgrimes{
40256939Sshin	struct addrinfo hints, *res, *r;
40356939Sshin	int error;
40455918Sshin
40556939Sshin	memset(&hints, 0, sizeof(hints));
40656939Sshin	hints.ai_family = PF_UNSPEC;
40756939Sshin	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
40856939Sshin	error = getaddrinfo(rhost, "0", &hints, &res);
40956939Sshin	if (error)
41056939Sshin		return (-1);
4111573Srgrimes
41256939Sshin	for (r = res; r; r = r->ai_next) {
41356939Sshin		if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser,
41456939Sshin		    luser) == 0) {
41556939Sshin			freeaddrinfo(res);
41656939Sshin			return (0);
41755918Sshin		}
4181573Srgrimes	}
41956939Sshin	freeaddrinfo(res);
42056939Sshin	return (-1);
4211573Srgrimes}
4221573Srgrimes
4231573Srgrimes/*
4241573Srgrimes * New .rhosts strategy: We are passed an ip address. We spin through
4251573Srgrimes * hosts.equiv and .rhosts looking for a match. When the .rhosts only
4261573Srgrimes * has ip addresses, we don't have to trust a nameserver.  When it
4271573Srgrimes * contains hostnames, we spin through the list of addresses the nameserver
4281573Srgrimes * gives us and look for a match.
4291573Srgrimes *
4301573Srgrimes * Returns 0 if ok, -1 if not ok.
4311573Srgrimes */
4321573Srgrimesint
4331573Srgrimesiruserok(raddr, superuser, ruser, luser)
43439979Sdfr	unsigned long raddr;
4351573Srgrimes	int superuser;
4361573Srgrimes	const char *ruser, *luser;
4371573Srgrimes{
43869575Sume	struct sockaddr_in sin;
43969575Sume
44069575Sume	memset(&sin, 0, sizeof(sin));
44169575Sume	sin.sin_family = AF_INET;
44269575Sume	sin.sin_len = sizeof(struct sockaddr_in);
44369575Sume	memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
44469575Sume	return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser,
44569575Sume		ruser, luser);
44655918Sshin}
44755918Sshin
44869575Sume/*
44969575Sume * AF independent extension of iruserok.
45069575Sume *
45169575Sume * Returns 0 if ok, -1 if not ok.
45269575Sume */
45369575Sumeint
45469575Sumeiruserok_sa(ra, rlen, superuser, ruser, luser)
45569575Sume	const void *ra;
45669575Sume	int rlen;
45755918Sshin	int superuser;
45855918Sshin	const char *ruser, *luser;
45955918Sshin{
46092889Sobrien	char *cp;
4611573Srgrimes	struct stat sbuf;
4621573Srgrimes	struct passwd *pwd;
4631573Srgrimes	FILE *hostf;
4641573Srgrimes	uid_t uid;
4651573Srgrimes	int first;
4661573Srgrimes	char pbuf[MAXPATHLEN];
46769575Sume	const struct sockaddr *raddr;
46869575Sume	struct sockaddr_storage ss;
4691573Srgrimes
47069575Sume	/* avoid alignment issue */
47169575Sume	if (rlen > sizeof(ss))
47269575Sume		return(-1);
47369575Sume	memcpy(&ss, ra, rlen);
47469575Sume	raddr = (struct sockaddr *)&ss;
47555918Sshin
4761573Srgrimes	first = 1;
4771573Srgrimes	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
4781573Srgrimesagain:
4791573Srgrimes	if (hostf) {
48069575Sume		if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) {
4811573Srgrimes			(void)fclose(hostf);
4821573Srgrimes			return (0);
4831573Srgrimes		}
4841573Srgrimes		(void)fclose(hostf);
4851573Srgrimes	}
4861573Srgrimes	if (first == 1 && (__check_rhosts_file || superuser)) {
4871573Srgrimes		first = 0;
4881573Srgrimes		if ((pwd = getpwnam(luser)) == NULL)
4891573Srgrimes			return (-1);
4901573Srgrimes		(void)strcpy(pbuf, pwd->pw_dir);
4911573Srgrimes		(void)strcat(pbuf, "/.rhosts");
4921573Srgrimes
4931573Srgrimes		/*
4941573Srgrimes		 * Change effective uid while opening .rhosts.  If root and
4951573Srgrimes		 * reading an NFS mounted file system, can't read files that
4961573Srgrimes		 * are protected read/write owner only.
4971573Srgrimes		 */
4981573Srgrimes		uid = geteuid();
4991573Srgrimes		(void)seteuid(pwd->pw_uid);
5001573Srgrimes		hostf = fopen(pbuf, "r");
5011573Srgrimes		(void)seteuid(uid);
5021573Srgrimes
5031573Srgrimes		if (hostf == NULL)
5041573Srgrimes			return (-1);
5051573Srgrimes		/*
5061573Srgrimes		 * If not a regular file, or is owned by someone other than
5071573Srgrimes		 * user or root or if writeable by anyone but the owner, quit.
5081573Srgrimes		 */
5091573Srgrimes		cp = NULL;
5101573Srgrimes		if (lstat(pbuf, &sbuf) < 0)
5111573Srgrimes			cp = ".rhosts lstat failed";
5121573Srgrimes		else if (!S_ISREG(sbuf.st_mode))
5131573Srgrimes			cp = ".rhosts not regular file";
51471579Sdeischen		else if (_fstat(fileno(hostf), &sbuf) < 0)
5151573Srgrimes			cp = ".rhosts fstat failed";
5161573Srgrimes		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
5171573Srgrimes			cp = "bad .rhosts owner";
5181573Srgrimes		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
5191573Srgrimes			cp = ".rhosts writeable by other than owner";
5201573Srgrimes		/* If there were any problems, quit. */
5211573Srgrimes		if (cp) {
5221573Srgrimes			__rcmd_errstr = cp;
5231573Srgrimes			(void)fclose(hostf);
5241573Srgrimes			return (-1);
5251573Srgrimes		}
5261573Srgrimes		goto again;
5271573Srgrimes	}
5281573Srgrimes	return (-1);
5291573Srgrimes}
5301573Srgrimes
5311573Srgrimes/*
53269575Sume * XXX
53369575Sume * Don't make static, used by lpd(8).
53456939Sshin *
53556939Sshin * Returns 0 if ok, -1 if not ok.
53656939Sshin */
53756939Sshinint
53869575Sume__ivaliduser(hostf, raddr, luser, ruser)
53969575Sume	FILE *hostf;
54069575Sume	u_int32_t raddr;
54169575Sume	const char *luser, *ruser;
54256939Sshin{
54369575Sume	struct sockaddr_in sin;
54456939Sshin
54569575Sume	memset(&sin, 0, sizeof(sin));
54669575Sume	sin.sin_family = AF_INET;
54769575Sume	sin.sin_len = sizeof(struct sockaddr_in);
54869575Sume	memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
54969575Sume	return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len,
55069575Sume		luser, ruser);
55169575Sume}
55269575Sume
55369575Sume/*
55469575Sume * Returns 0 if ok, -1 if not ok.
55569575Sume *
55669575Sume * XXX obsolete API.
55769575Sume */
55869575Sumeint
55969575Sume__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
56069575Sume	FILE *hostf;
56169575Sume	const void *raddr;
56269575Sume	const char *luser, *ruser;
56369575Sume	int af, len;
56469575Sume{
56569575Sume	struct sockaddr *sa = NULL;
56669575Sume	struct sockaddr_in *sin = NULL;
56769575Sume#ifdef INET6
56869575Sume	struct sockaddr_in6 *sin6 = NULL;
56969575Sume#endif
57069575Sume	struct sockaddr_storage ss;
57169575Sume
57269575Sume	memset(&ss, 0, sizeof(ss));
57369575Sume	switch (af) {
57456939Sshin	case AF_INET:
57569575Sume		if (len != sizeof(sin->sin_addr))
57669575Sume			return -1;
57769575Sume		sin = (struct sockaddr_in *)&ss;
57869575Sume		sin->sin_family = AF_INET;
57969575Sume		sin->sin_len = sizeof(struct sockaddr_in);
58069575Sume		memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr));
58156939Sshin		break;
58256939Sshin#ifdef INET6
58356939Sshin	case AF_INET6:
58469575Sume		if (len != sizeof(sin6->sin6_addr))
58569575Sume			return -1;
58669575Sume		/* you will lose scope info */
58769575Sume		sin6 = (struct sockaddr_in6 *)&ss;
58869575Sume		sin6->sin6_family = AF_INET6;
58969575Sume		sin6->sin6_len = sizeof(struct sockaddr_in6);
59069575Sume		memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr));
59156939Sshin		break;
59256939Sshin#endif
59369575Sume	default:
59469575Sume		return -1;
59556939Sshin	}
59656939Sshin
59769575Sume	sa = (struct sockaddr *)&ss;
59869575Sume	return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser);
59956939Sshin}
60056939Sshin
6011573Srgrimesint
60269575Sume__ivaliduser_sa(hostf, raddr, salen, luser, ruser)
6031573Srgrimes	FILE *hostf;
60469575Sume	const struct sockaddr *raddr;
60569575Sume	socklen_t salen;
6061573Srgrimes	const char *luser, *ruser;
6071573Srgrimes{
60892889Sobrien	char *user, *p;
6091573Srgrimes	int ch;
6101573Srgrimes	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
61110059Swpaul	char hname[MAXHOSTNAMELEN];
6127183Swpaul	/* Presumed guilty until proven innocent. */
6137183Swpaul	int userok = 0, hostok = 0;
61455918Sshin	int h_error;
6159978Swpaul#ifdef YP
6169978Swpaul	char *ypdomain;
6171573Srgrimes
6189978Swpaul	if (yp_get_default_domain(&ypdomain))
6199978Swpaul		ypdomain = NULL;
6209978Swpaul#else
6219978Swpaul#define	ypdomain NULL
6229978Swpaul#endif
6237183Swpaul	/* We need to get the damn hostname back for netgroup matching. */
62469575Sume	if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0,
62569575Sume			NI_NAMEREQD) != 0)
6267183Swpaul		return (-1);
6277183Swpaul
6281573Srgrimes	while (fgets(buf, sizeof(buf), hostf)) {
6291573Srgrimes		p = buf;
6301573Srgrimes		/* Skip lines that are too long. */
63158154Sbsd		if (strchr(p, '\n') == NULL) {
6321573Srgrimes			while ((ch = getc(hostf)) != '\n' && ch != EOF);
6331573Srgrimes			continue;
6341573Srgrimes		}
6359552Speter		if (*p == '\n' || *p == '#') {
6369552Speter			/* comment... */
6379552Speter			continue;
6389552Speter		}
6391573Srgrimes		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
64052859Sache			*p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p;
6411573Srgrimes			p++;
6421573Srgrimes		}
6431573Srgrimes		if (*p == ' ' || *p == '\t') {
6441573Srgrimes			*p++ = '\0';
6451573Srgrimes			while (*p == ' ' || *p == '\t')
6461573Srgrimes				p++;
6471573Srgrimes			user = p;
6481573Srgrimes			while (*p != '\n' && *p != ' ' &&
6491573Srgrimes			    *p != '\t' && *p != '\0')
6501573Srgrimes				p++;
6511573Srgrimes		} else
6521573Srgrimes			user = p;
6531573Srgrimes		*p = '\0';
6547183Swpaul		/*
6557183Swpaul		 * Do +/- and +@/-@ checking. This looks really nasty,
6567183Swpaul		 * but it matches SunOS's behavior so far as I can tell.
6577183Swpaul		 */
6587183Swpaul		switch(buf[0]) {
6597183Swpaul		case '+':
6607183Swpaul			if (!buf[1]) {     /* '+' matches all hosts */
6617183Swpaul				hostok = 1;
6627183Swpaul				break;
6637183Swpaul			}
6647183Swpaul			if (buf[1] == '@')  /* match a host by netgroup */
66510059Swpaul				hostok = innetgr((char *)&buf[2],
66610059Swpaul					(char *)&hname, NULL, ypdomain);
6677183Swpaul			else		/* match a host by addr */
66869575Sume				hostok = __icheckhost(raddr, salen,
66969575Sume						      (char *)&buf[1]);
6707183Swpaul			break;
6717183Swpaul		case '-':     /* reject '-' hosts and all their users */
6727183Swpaul			if (buf[1] == '@') {
6737183Swpaul				if (innetgr((char *)&buf[2],
67410059Swpaul					      (char *)&hname, NULL, ypdomain))
6757183Swpaul					return(-1);
6767183Swpaul			} else {
67769575Sume				if (__icheckhost(raddr, salen,
67869575Sume						 (char *)&buf[1]))
6797183Swpaul					return(-1);
6807183Swpaul			}
6817183Swpaul			break;
6827183Swpaul		default:  /* if no '+' or '-', do a simple match */
68369575Sume			hostok = __icheckhost(raddr, salen, buf);
6847183Swpaul			break;
6851573Srgrimes		}
6867183Swpaul		switch(*user) {
6877183Swpaul		case '+':
6887183Swpaul			if (!*(user+1)) {      /* '+' matches all users */
6897183Swpaul				userok = 1;
6907183Swpaul				break;
6917183Swpaul			}
6927183Swpaul			if (*(user+1) == '@')  /* match a user by netgroup */
6939978Swpaul				userok = innetgr(user+2, NULL, ruser, ypdomain);
6947183Swpaul			else	   /* match a user by direct specification */
6957183Swpaul				userok = !(strcmp(ruser, user+1));
6967183Swpaul			break;
6977183Swpaul		case '-': 		/* if we matched a hostname, */
6987183Swpaul			if (hostok) {   /* check for user field rejections */
6997183Swpaul				if (!*(user+1))
7007183Swpaul					return(-1);
7017183Swpaul				if (*(user+1) == '@') {
7027183Swpaul					if (innetgr(user+2, NULL,
7039978Swpaul							ruser, ypdomain))
7047183Swpaul						return(-1);
7057183Swpaul				} else {
7067183Swpaul					if (!strcmp(ruser, user+1))
7077183Swpaul						return(-1);
7087183Swpaul				}
7097183Swpaul			}
7107183Swpaul			break;
7117183Swpaul		default:	/* no rejections: try to match the user */
7127183Swpaul			if (hostok)
7137183Swpaul				userok = !(strcmp(ruser,*user ? user : luser));
7147183Swpaul			break;
7157183Swpaul		}
7167183Swpaul		if (hostok && userok)
7177183Swpaul			return(0);
7181573Srgrimes	}
7191573Srgrimes	return (-1);
7201573Srgrimes}
7211573Srgrimes
7221573Srgrimes/*
7231573Srgrimes * Returns "true" if match, 0 if no match.
72469575Sume *
72569575Sume * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion
72669575Sume * if af == AF_INET6.
7271573Srgrimes */
7281573Srgrimesstatic int
72969575Sume__icheckhost(raddr, salen, lhost)
73069575Sume	const struct sockaddr *raddr;
73169575Sume	socklen_t salen;
73269575Sume        const char *lhost;
7331573Srgrimes{
73469575Sume	struct sockaddr_in sin;
73569575Sume	struct sockaddr_in6 *sin6;
73669575Sume	struct addrinfo hints, *res, *r;
73769575Sume	int error;
73869575Sume	char h1[NI_MAXHOST], h2[NI_MAXHOST];
7391573Srgrimes
74069575Sume	if (raddr->sa_family == AF_INET6) {
74169575Sume		sin6 = (struct sockaddr_in6 *)raddr;
74269575Sume		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
74369575Sume			memset(&sin, 0, sizeof(sin));
74469575Sume			sin.sin_family = AF_INET;
74569575Sume			sin.sin_len = sizeof(struct sockaddr_in);
74669575Sume			memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
74769575Sume			       sizeof(sin.sin_addr));
74869575Sume			raddr = (struct sockaddr *)&sin;
74969575Sume			salen = sin.sin_len;
75069575Sume		}
75155918Sshin	}
7521573Srgrimes
75369575Sume	h1[0] = '\0';
75469575Sume	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
75569575Sume			NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
7561573Srgrimes		return (0);
7571573Srgrimes
75869575Sume	/* Resolve laddr into sockaddr */
75969575Sume	memset(&hints, 0, sizeof(hints));
76069575Sume	hints.ai_family = raddr->sa_family;
76169575Sume	hints.ai_socktype = SOCK_DGRAM;	/*XXX dummy*/
76269575Sume	res = NULL;
76369575Sume	error = getaddrinfo(lhost, "0", &hints, &res);
76469575Sume	if (error)
76569575Sume		return (0);
76669575Sume
76769575Sume	for (r = res; r ; r = r->ai_next) {
76869575Sume		h2[0] = '\0';
76969575Sume		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
77069575Sume				NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
77169575Sume			continue;
77269575Sume		if (strcmp(h1, h2) == 0) {
77369575Sume			freeaddrinfo(res);
77469575Sume			return (1);
77555918Sshin		}
77669575Sume	}
7771573Srgrimes
77869575Sume	/* No match. */
77969575Sume	freeaddrinfo(res);
78069575Sume	return (0);
7811573Srgrimes}
782