rcmd.c revision 287292
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 * 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: head/lib/libc/net/rcmd.c 287292 2015-08-29 14:25:01Z kib $");
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
761573Srgrimesrcmd(ahost, rport, locuser, remuser, cmd, fd2p)
771573Srgrimes	char **ahost;
781573Srgrimes	u_short rport;
791573Srgrimes	const char *locuser, *remuser, *cmd;
801573Srgrimes	int *fd2p;
811573Srgrimes{
8256590Sshin	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
8356590Sshin}
8456590Sshin
8556590Sshinint
8656590Sshinrcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
8756590Sshin	char **ahost;
8856590Sshin	u_short rport;
8956590Sshin	const char *locuser, *remuser, *cmd;
9056590Sshin	int *fd2p;
9156590Sshin	int af;
9256590Sshin{
9355918Sshin	struct addrinfo hints, *res, *ai;
9455918Sshin	struct sockaddr_storage from;
951573Srgrimes	fd_set reads;
9671579Sdeischen	sigset_t oldmask, newmask;
971573Srgrimes	pid_t pid;
9855918Sshin	int s, aport, lport, timo, error;
9985342Simp	char c, *p;
10069575Sume	int refused, nres;
10163682Sume	char num[8];
10263682Sume	static char canonnamebuf[MAXDNAME];	/* is it proper here? */
1031573Srgrimes
10485342Simp	/* call rcmdsh() with specified remote shell if appropriate. */
10585342Simp	if (!issetugid() && (p = getenv("RSH"))) {
10685342Simp		struct servent *sp = getservbyname("shell", "tcp");
10785342Simp
10885342Simp		if (sp && sp->s_port == rport)
10985342Simp			return (rcmdsh(ahost, rport, locuser, remuser,
11085342Simp			    cmd, p));
11185342Simp	}
11285342Simp
11385342Simp	/* use rsh(1) if non-root and remote port is shell. */
11485342Simp	if (geteuid()) {
11585342Simp		struct servent *sp = getservbyname("shell", "tcp");
11685342Simp
11785342Simp		if (sp && sp->s_port == rport)
11885342Simp			return (rcmdsh(ahost, rport, locuser, remuser,
11985342Simp			    cmd, NULL));
12085342Simp	}
12185342Simp
1221573Srgrimes	pid = getpid();
12355918Sshin
12455918Sshin	memset(&hints, 0, sizeof(hints));
12555918Sshin	hints.ai_flags = AI_CANONNAME;
12656590Sshin	hints.ai_family = af;
12755918Sshin	hints.ai_socktype = SOCK_STREAM;
12855918Sshin	hints.ai_protocol = 0;
12955918Sshin	(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
13055918Sshin	error = getaddrinfo(*ahost, num, &hints, &res);
13155918Sshin	if (error) {
13255918Sshin		fprintf(stderr, "rcmd: getaddrinfo: %s\n",
13355918Sshin			gai_strerror(error));
13455918Sshin		if (error == EAI_SYSTEM)
13555918Sshin			fprintf(stderr, "rcmd: getaddrinfo: %s\n",
13655918Sshin				strerror(errno));
1371573Srgrimes		return (-1);
1381573Srgrimes	}
13963682Sume
14063682Sume	if (res->ai_canonname
14163682Sume	 && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) {
14263682Sume		strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf));
14363682Sume		*ahost = canonnamebuf;
14463682Sume	}
14569575Sume	nres = 0;
14669575Sume	for (ai = res; ai; ai = ai->ai_next)
14769575Sume		nres++;
14855918Sshin	ai = res;
14969575Sume	refused = 0;
15071579Sdeischen	sigemptyset(&newmask);
15171579Sdeischen	sigaddset(&newmask, SIGURG);
152287292Skib	__libc_sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask);
1531573Srgrimes	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
15455918Sshin		s = rresvport_af(&lport, ai->ai_family);
1551573Srgrimes		if (s < 0) {
15657123Sshin			if (errno != EAGAIN && ai->ai_next) {
15757123Sshin				ai = ai->ai_next;
15857123Sshin				continue;
15957123Sshin			}
1601573Srgrimes			if (errno == EAGAIN)
1611573Srgrimes				(void)fprintf(stderr,
1621573Srgrimes				    "rcmd: socket: All ports in use\n");
1631573Srgrimes			else
1641573Srgrimes				(void)fprintf(stderr, "rcmd: socket: %s\n",
1651573Srgrimes				    strerror(errno));
16657123Sshin			freeaddrinfo(res);
167287292Skib			__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
16871579Sdeischen			    NULL);
1691573Srgrimes			return (-1);
1701573Srgrimes		}
17156698Sjasone		_fcntl(s, F_SETOWN, pid);
17271579Sdeischen		if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
1731573Srgrimes			break;
17456698Sjasone		(void)_close(s);
1751573Srgrimes		if (errno == EADDRINUSE) {
1761573Srgrimes			lport--;
1771573Srgrimes			continue;
1781573Srgrimes		}
17969575Sume		if (errno == ECONNREFUSED)
18069575Sume			refused = 1;
18169575Sume		if (ai->ai_next == NULL && (!refused || timo > 16)) {
18269575Sume			(void)fprintf(stderr, "%s: %s\n",
18369575Sume				      *ahost, strerror(errno));
18469575Sume			freeaddrinfo(res);
185287292Skib			__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
18671579Sdeischen			    NULL);
18769575Sume			return (-1);
18864493Sume		}
18969575Sume		if (nres > 1) {
1901573Srgrimes			int oerrno = errno;
1911573Srgrimes
192146187Sume			getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr,
193146187Sume			    sizeof(paddr), NULL, 0, NI_NUMERICHOST);
1941573Srgrimes			(void)fprintf(stderr, "connect to address %s: ",
19555918Sshin				      paddr);
1961573Srgrimes			errno = oerrno;
1971573Srgrimes			perror(0);
19869575Sume		}
19969575Sume		if ((ai = ai->ai_next) == NULL) {
20069575Sume			/* refused && timo <= 16 */
20169575Sume			struct timespec time_to_sleep, time_remaining;
20269575Sume
20369575Sume			time_to_sleep.tv_sec = timo;
20469575Sume			time_to_sleep.tv_nsec = 0;
20569575Sume			(void)_nanosleep(&time_to_sleep, &time_remaining);
20669575Sume			timo *= 2;
20769575Sume			ai = res;
20869575Sume			refused = 0;
20969575Sume		}
21069575Sume		if (nres > 1) {
211146187Sume			getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr,
212146187Sume			    sizeof(paddr), NULL, 0, NI_NUMERICHOST);
21355918Sshin			fprintf(stderr, "Trying %s...\n", paddr);
2141573Srgrimes		}
2151573Srgrimes	}
2161573Srgrimes	lport--;
2171573Srgrimes	if (fd2p == 0) {
21856698Sjasone		_write(s, "", 1);
2191573Srgrimes		lport = 0;
2201573Srgrimes	} else {
22155918Sshin		int s2 = rresvport_af(&lport, ai->ai_family), s3;
222141920Sstefanf		socklen_t len = ai->ai_addrlen;
2232592Scsgr		int nfds;
2241573Srgrimes
2251573Srgrimes		if (s2 < 0)
2261573Srgrimes			goto bad;
22771579Sdeischen		_listen(s2, 1);
2281573Srgrimes		(void)snprintf(num, sizeof(num), "%d", lport);
22956698Sjasone		if (_write(s, num, strlen(num)+1) != strlen(num)+1) {
2301573Srgrimes			(void)fprintf(stderr,
2311573Srgrimes			    "rcmd: write (setting up stderr): %s\n",
2321573Srgrimes			    strerror(errno));
23356698Sjasone			(void)_close(s2);
2341573Srgrimes			goto bad;
2351573Srgrimes		}
2362592Scsgr		nfds = max(s, s2)+1;
2372592Scsgr		if(nfds > FD_SETSIZE) {
2382592Scsgr			fprintf(stderr, "rcmd: too many files\n");
23956698Sjasone			(void)_close(s2);
2402592Scsgr			goto bad;
2412592Scsgr		}
24217543Speteragain:
2431573Srgrimes		FD_ZERO(&reads);
2441573Srgrimes		FD_SET(s, &reads);
2451573Srgrimes		FD_SET(s2, &reads);
2461573Srgrimes		errno = 0;
24771579Sdeischen		if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){
2481573Srgrimes			if (errno != 0)
2491573Srgrimes				(void)fprintf(stderr,
2501573Srgrimes				    "rcmd: select (setting up stderr): %s\n",
2511573Srgrimes				    strerror(errno));
2521573Srgrimes			else
2531573Srgrimes				(void)fprintf(stderr,
2541573Srgrimes				"select: protocol failure in circuit setup\n");
25556698Sjasone			(void)_close(s2);
2561573Srgrimes			goto bad;
2571573Srgrimes		}
25871579Sdeischen		s3 = _accept(s2, (struct sockaddr *)&from, &len);
25955918Sshin		switch (from.ss_family) {
26055918Sshin		case AF_INET:
26155918Sshin			aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
26255918Sshin			break;
26355918Sshin#ifdef INET6
26455918Sshin		case AF_INET6:
26555918Sshin			aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
26655918Sshin			break;
26755918Sshin#endif
26855918Sshin		default:
26955918Sshin			aport = 0;	/* error */
27055918Sshin			break;
27155918Sshin		}
27217543Speter		/*
27317543Speter		 * XXX careful for ftp bounce attacks. If discovered, shut them
27417543Speter		 * down and check for the real auxiliary channel to connect.
27517543Speter		 */
27655918Sshin		if (aport == 20) {
27756698Sjasone			_close(s3);
27817543Speter			goto again;
27917543Speter		}
28056698Sjasone		(void)_close(s2);
2811573Srgrimes		if (s3 < 0) {
2821573Srgrimes			(void)fprintf(stderr,
2831573Srgrimes			    "rcmd: accept: %s\n", strerror(errno));
2841573Srgrimes			lport = 0;
2851573Srgrimes			goto bad;
2861573Srgrimes		}
2871573Srgrimes		*fd2p = s3;
28855918Sshin		if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
2891573Srgrimes			(void)fprintf(stderr,
2901573Srgrimes			    "socket: protocol failure in circuit setup.\n");
2911573Srgrimes			goto bad2;
2921573Srgrimes		}
2931573Srgrimes	}
29456698Sjasone	(void)_write(s, locuser, strlen(locuser)+1);
29556698Sjasone	(void)_write(s, remuser, strlen(remuser)+1);
29656698Sjasone	(void)_write(s, cmd, strlen(cmd)+1);
29756698Sjasone	if (_read(s, &c, 1) != 1) {
2981573Srgrimes		(void)fprintf(stderr,
2991573Srgrimes		    "rcmd: %s: %s\n", *ahost, strerror(errno));
3001573Srgrimes		goto bad2;
3011573Srgrimes	}
3021573Srgrimes	if (c != 0) {
30356698Sjasone		while (_read(s, &c, 1) == 1) {
30456698Sjasone			(void)_write(STDERR_FILENO, &c, 1);
3051573Srgrimes			if (c == '\n')
3061573Srgrimes				break;
3071573Srgrimes		}
3081573Srgrimes		goto bad2;
3091573Srgrimes	}
310287292Skib	__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
31155918Sshin	freeaddrinfo(res);
3121573Srgrimes	return (s);
3131573Srgrimesbad2:
3141573Srgrimes	if (lport)
31556698Sjasone		(void)_close(*fd2p);
3161573Srgrimesbad:
31756698Sjasone	(void)_close(s);
318287292Skib	__libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
31955918Sshin	freeaddrinfo(res);
3201573Srgrimes	return (-1);
3211573Srgrimes}
3221573Srgrimes
3231573Srgrimesint
32455918Sshinrresvport(port)
32555918Sshin	int *port;
3261573Srgrimes{
32755918Sshin	return rresvport_af(port, AF_INET);
32855918Sshin}
3291573Srgrimes
33055918Sshinint
33155918Sshinrresvport_af(alport, family)
33255918Sshin	int *alport, family;
33355918Sshin{
334111082Snectar	int s;
33555918Sshin	struct sockaddr_storage ss;
33655918Sshin	u_short *sport;
33755918Sshin
33855918Sshin	memset(&ss, 0, sizeof(ss));
33955918Sshin	ss.ss_family = family;
34055918Sshin	switch (family) {
34155918Sshin	case AF_INET:
34255918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
34355918Sshin		sport = &((struct sockaddr_in *)&ss)->sin_port;
34455918Sshin		((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
34555918Sshin		break;
34655918Sshin#ifdef INET6
34755918Sshin	case AF_INET6:
34855918Sshin		((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
34955918Sshin		sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
35055918Sshin		((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
35155918Sshin		break;
35255918Sshin#endif
35355918Sshin	default:
35455918Sshin		errno = EAFNOSUPPORT;
35555918Sshin		return -1;
35655918Sshin	}
35755918Sshin
35871579Sdeischen	s = _socket(ss.ss_family, SOCK_STREAM, 0);
3591573Srgrimes	if (s < 0)
3601573Srgrimes		return (-1);
36117543Speter#if 0 /* compat_exact_traditional_rresvport_semantics */
36217543Speter	sin.sin_port = htons((u_short)*alport);
36371579Sdeischen	if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
36417543Speter		return (s);
36517543Speter	if (errno != EADDRINUSE) {
36656698Sjasone		(void)_close(s);
36717543Speter		return (-1);
36816034Speter	}
36917543Speter#endif
37055918Sshin	*sport = 0;
37156636Sshin	if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) {
37256698Sjasone		(void)_close(s);
37317543Speter		return (-1);
3741573Srgrimes	}
37555918Sshin	*alport = (int)ntohs(*sport);
37617543Speter	return (s);
3771573Srgrimes}
3781573Srgrimes
3791573Srgrimesint	__check_rhosts_file = 1;
3801573Srgrimeschar	*__rcmd_errstr;
3811573Srgrimes
3821573Srgrimesint
3831573Srgrimesruserok(rhost, superuser, ruser, luser)
3841573Srgrimes	const char *rhost, *ruser, *luser;
3851573Srgrimes	int superuser;
3861573Srgrimes{
38756939Sshin	struct addrinfo hints, *res, *r;
38856939Sshin	int error;
38955918Sshin
39056939Sshin	memset(&hints, 0, sizeof(hints));
39156939Sshin	hints.ai_family = PF_UNSPEC;
39256939Sshin	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
39356939Sshin	error = getaddrinfo(rhost, "0", &hints, &res);
39456939Sshin	if (error)
39556939Sshin		return (-1);
3961573Srgrimes
39756939Sshin	for (r = res; r; r = r->ai_next) {
39856939Sshin		if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser,
39956939Sshin		    luser) == 0) {
40056939Sshin			freeaddrinfo(res);
40156939Sshin			return (0);
40255918Sshin		}
4031573Srgrimes	}
40456939Sshin	freeaddrinfo(res);
40556939Sshin	return (-1);
4061573Srgrimes}
4071573Srgrimes
4081573Srgrimes/*
4091573Srgrimes * New .rhosts strategy: We are passed an ip address. We spin through
4101573Srgrimes * hosts.equiv and .rhosts looking for a match. When the .rhosts only
4111573Srgrimes * has ip addresses, we don't have to trust a nameserver.  When it
4121573Srgrimes * contains hostnames, we spin through the list of addresses the nameserver
4131573Srgrimes * gives us and look for a match.
4141573Srgrimes *
4151573Srgrimes * Returns 0 if ok, -1 if not ok.
4161573Srgrimes */
4171573Srgrimesint
4181573Srgrimesiruserok(raddr, superuser, ruser, luser)
41939979Sdfr	unsigned long raddr;
4201573Srgrimes	int superuser;
4211573Srgrimes	const char *ruser, *luser;
4221573Srgrimes{
42369575Sume	struct sockaddr_in sin;
42469575Sume
42569575Sume	memset(&sin, 0, sizeof(sin));
42669575Sume	sin.sin_family = AF_INET;
42769575Sume	sin.sin_len = sizeof(struct sockaddr_in);
42869575Sume	memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
42969575Sume	return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser,
43069575Sume		ruser, luser);
43155918Sshin}
43255918Sshin
43369575Sume/*
43469575Sume * AF independent extension of iruserok.
43569575Sume *
43669575Sume * Returns 0 if ok, -1 if not ok.
43769575Sume */
43869575Sumeint
43969575Sumeiruserok_sa(ra, rlen, superuser, ruser, luser)
44069575Sume	const void *ra;
44169575Sume	int rlen;
44255918Sshin	int superuser;
44355918Sshin	const char *ruser, *luser;
44455918Sshin{
44592889Sobrien	char *cp;
4461573Srgrimes	struct stat sbuf;
4471573Srgrimes	struct passwd *pwd;
4481573Srgrimes	FILE *hostf;
4491573Srgrimes	uid_t uid;
4501573Srgrimes	int first;
4511573Srgrimes	char pbuf[MAXPATHLEN];
45269575Sume	const struct sockaddr *raddr;
45369575Sume	struct sockaddr_storage ss;
4541573Srgrimes
45569575Sume	/* avoid alignment issue */
45669575Sume	if (rlen > sizeof(ss))
45769575Sume		return(-1);
45869575Sume	memcpy(&ss, ra, rlen);
45969575Sume	raddr = (struct sockaddr *)&ss;
46055918Sshin
4611573Srgrimes	first = 1;
462254700Sjilles	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
4631573Srgrimesagain:
4641573Srgrimes	if (hostf) {
46569575Sume		if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) {
4661573Srgrimes			(void)fclose(hostf);
4671573Srgrimes			return (0);
4681573Srgrimes		}
4691573Srgrimes		(void)fclose(hostf);
4701573Srgrimes	}
4711573Srgrimes	if (first == 1 && (__check_rhosts_file || superuser)) {
4721573Srgrimes		first = 0;
4731573Srgrimes		if ((pwd = getpwnam(luser)) == NULL)
4741573Srgrimes			return (-1);
4751573Srgrimes		(void)strcpy(pbuf, pwd->pw_dir);
4761573Srgrimes		(void)strcat(pbuf, "/.rhosts");
4771573Srgrimes
4781573Srgrimes		/*
4791573Srgrimes		 * Change effective uid while opening .rhosts.  If root and
4801573Srgrimes		 * reading an NFS mounted file system, can't read files that
4811573Srgrimes		 * are protected read/write owner only.
4821573Srgrimes		 */
4831573Srgrimes		uid = geteuid();
4841573Srgrimes		(void)seteuid(pwd->pw_uid);
485254700Sjilles		hostf = fopen(pbuf, "re");
4861573Srgrimes		(void)seteuid(uid);
4871573Srgrimes
4881573Srgrimes		if (hostf == NULL)
4891573Srgrimes			return (-1);
4901573Srgrimes		/*
4911573Srgrimes		 * If not a regular file, or is owned by someone other than
4921573Srgrimes		 * user or root or if writeable by anyone but the owner, quit.
4931573Srgrimes		 */
4941573Srgrimes		cp = NULL;
4951573Srgrimes		if (lstat(pbuf, &sbuf) < 0)
4961573Srgrimes			cp = ".rhosts lstat failed";
4971573Srgrimes		else if (!S_ISREG(sbuf.st_mode))
4981573Srgrimes			cp = ".rhosts not regular file";
49971579Sdeischen		else if (_fstat(fileno(hostf), &sbuf) < 0)
5001573Srgrimes			cp = ".rhosts fstat failed";
5011573Srgrimes		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
5021573Srgrimes			cp = "bad .rhosts owner";
5031573Srgrimes		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
5041573Srgrimes			cp = ".rhosts writeable by other than owner";
5051573Srgrimes		/* If there were any problems, quit. */
5061573Srgrimes		if (cp) {
5071573Srgrimes			__rcmd_errstr = cp;
5081573Srgrimes			(void)fclose(hostf);
5091573Srgrimes			return (-1);
5101573Srgrimes		}
5111573Srgrimes		goto again;
5121573Srgrimes	}
5131573Srgrimes	return (-1);
5141573Srgrimes}
5151573Srgrimes
5161573Srgrimes/*
51769575Sume * XXX
51869575Sume * Don't make static, used by lpd(8).
51956939Sshin *
52056939Sshin * Returns 0 if ok, -1 if not ok.
52156939Sshin */
52256939Sshinint
52369575Sume__ivaliduser(hostf, raddr, luser, ruser)
52469575Sume	FILE *hostf;
52569575Sume	u_int32_t raddr;
52669575Sume	const char *luser, *ruser;
52756939Sshin{
52869575Sume	struct sockaddr_in sin;
52956939Sshin
53069575Sume	memset(&sin, 0, sizeof(sin));
53169575Sume	sin.sin_family = AF_INET;
53269575Sume	sin.sin_len = sizeof(struct sockaddr_in);
53369575Sume	memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
53469575Sume	return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len,
53569575Sume		luser, ruser);
53669575Sume}
53769575Sume
53869575Sume/*
53969575Sume * Returns 0 if ok, -1 if not ok.
54069575Sume *
54169575Sume * XXX obsolete API.
54269575Sume */
54369575Sumeint
54469575Sume__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
54569575Sume	FILE *hostf;
54669575Sume	const void *raddr;
54769575Sume	const char *luser, *ruser;
54869575Sume	int af, len;
54969575Sume{
55069575Sume	struct sockaddr *sa = NULL;
55169575Sume	struct sockaddr_in *sin = NULL;
55269575Sume#ifdef INET6
55369575Sume	struct sockaddr_in6 *sin6 = NULL;
55469575Sume#endif
55569575Sume	struct sockaddr_storage ss;
55669575Sume
55769575Sume	memset(&ss, 0, sizeof(ss));
55869575Sume	switch (af) {
55956939Sshin	case AF_INET:
56069575Sume		if (len != sizeof(sin->sin_addr))
56169575Sume			return -1;
56269575Sume		sin = (struct sockaddr_in *)&ss;
56369575Sume		sin->sin_family = AF_INET;
56469575Sume		sin->sin_len = sizeof(struct sockaddr_in);
56569575Sume		memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr));
56656939Sshin		break;
56756939Sshin#ifdef INET6
56856939Sshin	case AF_INET6:
56969575Sume		if (len != sizeof(sin6->sin6_addr))
57069575Sume			return -1;
57169575Sume		/* you will lose scope info */
57269575Sume		sin6 = (struct sockaddr_in6 *)&ss;
57369575Sume		sin6->sin6_family = AF_INET6;
57469575Sume		sin6->sin6_len = sizeof(struct sockaddr_in6);
57569575Sume		memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr));
57656939Sshin		break;
57756939Sshin#endif
57869575Sume	default:
57969575Sume		return -1;
58056939Sshin	}
58156939Sshin
58269575Sume	sa = (struct sockaddr *)&ss;
58369575Sume	return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser);
58456939Sshin}
58556939Sshin
5861573Srgrimesint
58769575Sume__ivaliduser_sa(hostf, raddr, salen, luser, ruser)
5881573Srgrimes	FILE *hostf;
58969575Sume	const struct sockaddr *raddr;
59069575Sume	socklen_t salen;
5911573Srgrimes	const char *luser, *ruser;
5921573Srgrimes{
59392889Sobrien	char *user, *p;
5941573Srgrimes	int ch;
5951573Srgrimes	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
59610059Swpaul	char hname[MAXHOSTNAMELEN];
5977183Swpaul	/* Presumed guilty until proven innocent. */
5987183Swpaul	int userok = 0, hostok = 0;
5999978Swpaul#ifdef YP
6009978Swpaul	char *ypdomain;
6011573Srgrimes
6029978Swpaul	if (yp_get_default_domain(&ypdomain))
6039978Swpaul		ypdomain = NULL;
6049978Swpaul#else
6059978Swpaul#define	ypdomain NULL
6069978Swpaul#endif
6077183Swpaul	/* We need to get the damn hostname back for netgroup matching. */
60869575Sume	if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0,
60969575Sume			NI_NAMEREQD) != 0)
610102369Sjdp		hname[0] = '\0';
6117183Swpaul
6121573Srgrimes	while (fgets(buf, sizeof(buf), hostf)) {
6131573Srgrimes		p = buf;
6141573Srgrimes		/* Skip lines that are too long. */
61558154Sbsd		if (strchr(p, '\n') == NULL) {
6161573Srgrimes			while ((ch = getc(hostf)) != '\n' && ch != EOF);
6171573Srgrimes			continue;
6181573Srgrimes		}
6199552Speter		if (*p == '\n' || *p == '#') {
6209552Speter			/* comment... */
6219552Speter			continue;
6229552Speter		}
6231573Srgrimes		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
62452859Sache			*p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p;
6251573Srgrimes			p++;
6261573Srgrimes		}
6271573Srgrimes		if (*p == ' ' || *p == '\t') {
6281573Srgrimes			*p++ = '\0';
6291573Srgrimes			while (*p == ' ' || *p == '\t')
6301573Srgrimes				p++;
6311573Srgrimes			user = p;
6321573Srgrimes			while (*p != '\n' && *p != ' ' &&
6331573Srgrimes			    *p != '\t' && *p != '\0')
6341573Srgrimes				p++;
6351573Srgrimes		} else
6361573Srgrimes			user = p;
6371573Srgrimes		*p = '\0';
6387183Swpaul		/*
6397183Swpaul		 * Do +/- and +@/-@ checking. This looks really nasty,
6407183Swpaul		 * but it matches SunOS's behavior so far as I can tell.
6417183Swpaul		 */
6427183Swpaul		switch(buf[0]) {
6437183Swpaul		case '+':
6447183Swpaul			if (!buf[1]) {     /* '+' matches all hosts */
6457183Swpaul				hostok = 1;
6467183Swpaul				break;
6477183Swpaul			}
6487183Swpaul			if (buf[1] == '@')  /* match a host by netgroup */
649102369Sjdp				hostok = hname[0] != '\0' &&
650102369Sjdp				    innetgr(&buf[2], hname, NULL, ypdomain);
6517183Swpaul			else		/* match a host by addr */
65269575Sume				hostok = __icheckhost(raddr, salen,
65369575Sume						      (char *)&buf[1]);
6547183Swpaul			break;
6557183Swpaul		case '-':     /* reject '-' hosts and all their users */
6567183Swpaul			if (buf[1] == '@') {
657102369Sjdp				if (hname[0] == '\0' ||
658102369Sjdp				    innetgr(&buf[2], hname, NULL, ypdomain))
6597183Swpaul					return(-1);
6607183Swpaul			} else {
66169575Sume				if (__icheckhost(raddr, salen,
66269575Sume						 (char *)&buf[1]))
6637183Swpaul					return(-1);
6647183Swpaul			}
6657183Swpaul			break;
6667183Swpaul		default:  /* if no '+' or '-', do a simple match */
66769575Sume			hostok = __icheckhost(raddr, salen, buf);
6687183Swpaul			break;
6691573Srgrimes		}
6707183Swpaul		switch(*user) {
6717183Swpaul		case '+':
6727183Swpaul			if (!*(user+1)) {      /* '+' matches all users */
6737183Swpaul				userok = 1;
6747183Swpaul				break;
6757183Swpaul			}
6767183Swpaul			if (*(user+1) == '@')  /* match a user by netgroup */
6779978Swpaul				userok = innetgr(user+2, NULL, ruser, ypdomain);
6787183Swpaul			else	   /* match a user by direct specification */
6797183Swpaul				userok = !(strcmp(ruser, user+1));
6807183Swpaul			break;
6817183Swpaul		case '-': 		/* if we matched a hostname, */
6827183Swpaul			if (hostok) {   /* check for user field rejections */
6837183Swpaul				if (!*(user+1))
6847183Swpaul					return(-1);
6857183Swpaul				if (*(user+1) == '@') {
6867183Swpaul					if (innetgr(user+2, NULL,
6879978Swpaul							ruser, ypdomain))
6887183Swpaul						return(-1);
6897183Swpaul				} else {
6907183Swpaul					if (!strcmp(ruser, user+1))
6917183Swpaul						return(-1);
6927183Swpaul				}
6937183Swpaul			}
6947183Swpaul			break;
6957183Swpaul		default:	/* no rejections: try to match the user */
6967183Swpaul			if (hostok)
6977183Swpaul				userok = !(strcmp(ruser,*user ? user : luser));
6987183Swpaul			break;
6997183Swpaul		}
7007183Swpaul		if (hostok && userok)
7017183Swpaul			return(0);
7021573Srgrimes	}
7031573Srgrimes	return (-1);
7041573Srgrimes}
7051573Srgrimes
7061573Srgrimes/*
7071573Srgrimes * Returns "true" if match, 0 if no match.
7081573Srgrimes */
7091573Srgrimesstatic int
71069575Sume__icheckhost(raddr, salen, lhost)
71169575Sume	const struct sockaddr *raddr;
71269575Sume	socklen_t salen;
71369575Sume        const char *lhost;
7141573Srgrimes{
71569575Sume	struct sockaddr_in sin;
71669575Sume	struct sockaddr_in6 *sin6;
71769575Sume	struct addrinfo hints, *res, *r;
71869575Sume	int error;
71969575Sume	char h1[NI_MAXHOST], h2[NI_MAXHOST];
7201573Srgrimes
72169575Sume	if (raddr->sa_family == AF_INET6) {
72269575Sume		sin6 = (struct sockaddr_in6 *)raddr;
72369575Sume		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
72469575Sume			memset(&sin, 0, sizeof(sin));
72569575Sume			sin.sin_family = AF_INET;
72669575Sume			sin.sin_len = sizeof(struct sockaddr_in);
72769575Sume			memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
72869575Sume			       sizeof(sin.sin_addr));
72969575Sume			raddr = (struct sockaddr *)&sin;
73069575Sume			salen = sin.sin_len;
73169575Sume		}
73255918Sshin	}
7331573Srgrimes
73469575Sume	h1[0] = '\0';
73569575Sume	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
736146187Sume			NI_NUMERICHOST) != 0)
7371573Srgrimes		return (0);
7381573Srgrimes
73969575Sume	/* Resolve laddr into sockaddr */
74069575Sume	memset(&hints, 0, sizeof(hints));
74169575Sume	hints.ai_family = raddr->sa_family;
74269575Sume	hints.ai_socktype = SOCK_DGRAM;	/*XXX dummy*/
74369575Sume	res = NULL;
74469575Sume	error = getaddrinfo(lhost, "0", &hints, &res);
74569575Sume	if (error)
74669575Sume		return (0);
74769575Sume
74869575Sume	for (r = res; r ; r = r->ai_next) {
74969575Sume		h2[0] = '\0';
75069575Sume		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
751146187Sume				NULL, 0, NI_NUMERICHOST) != 0)
75269575Sume			continue;
75369575Sume		if (strcmp(h1, h2) == 0) {
75469575Sume			freeaddrinfo(res);
75569575Sume			return (1);
75655918Sshin		}
75769575Sume	}
7581573Srgrimes
75969575Sume	/* No match. */
76069575Sume	freeaddrinfo(res);
76169575Sume	return (0);
7621573Srgrimes}
763