155682Smarkm/*
255682Smarkm * Copyright (c) 1983, 1993, 1994
355682Smarkm *	The Regents of the University of California.  All rights reserved.
455682Smarkm *
555682Smarkm * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
13178825Sdfr * 3. Neither the name of the University nor the names of its contributors
1455682Smarkm *    may be used to endorse or promote products derived from this software
1555682Smarkm *    without specific prior written permission.
1655682Smarkm *
1755682Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1855682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1955682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2055682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2155682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2255682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2355682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2455682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2555682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2655682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2755682Smarkm * SUCH DAMAGE.
2855682Smarkm */
2955682Smarkm
3055682Smarkm#include <config.h>
3155682Smarkm
3255682Smarkm#include <stdio.h>
3355682Smarkm#include <ctype.h>
3455682Smarkm#ifdef HAVE_SYS_TYPES_H
3555682Smarkm#include <sys/types.h>
3655682Smarkm#endif
3755682Smarkm#ifdef HAVE_NETINET_IN_H
3855682Smarkm#include <netinet/in.h>
3955682Smarkm#endif
4055682Smarkm#ifdef HAVE_NETINET_IN6_H
4155682Smarkm#include <netinet/in6.h>
4255682Smarkm#endif
4355682Smarkm#ifdef HAVE_NETINET6_IN6_H
4455682Smarkm#include <netinet6/in6.h>
4555682Smarkm#endif
4655682Smarkm#ifdef HAVE_RPCSVC_YPCLNT_H
4755682Smarkm#include <rpcsvc/ypclnt.h>
4855682Smarkm#endif
4955682Smarkm
5055682Smarkm#include "roken.h"
5155682Smarkm
5255682Smarkmint     __check_rhosts_file = 1;
5355682Smarkmchar    *__rcmd_errstr = 0;
5455682Smarkm
5555682Smarkm/*
5655682Smarkm * Returns "true" if match, 0 if no match.
5755682Smarkm */
5855682Smarkmstatic
5955682Smarkmint
6055682Smarkm__icheckhost(unsigned raddr, const char *lhost)
6155682Smarkm{
6255682Smarkm	struct hostent *hp;
6355682Smarkm	u_long laddr;
6455682Smarkm	char **pp;
6555682Smarkm
6655682Smarkm	/* Try for raw ip address first. */
6755682Smarkm	if (isdigit((unsigned char)*lhost)
6855682Smarkm	    && (long)(laddr = inet_addr(lhost)) != -1)
6955682Smarkm		return (raddr == laddr);
7055682Smarkm
7155682Smarkm	/* Better be a hostname. */
7255682Smarkm	if ((hp = gethostbyname(lhost)) == NULL)
7355682Smarkm		return (0);
7455682Smarkm
7555682Smarkm	/* Spin through ip addresses. */
7655682Smarkm	for (pp = hp->h_addr_list; *pp; ++pp)
7755682Smarkm	        if (memcmp(&raddr, *pp, sizeof(u_long)) == 0)
7855682Smarkm			return (1);
7955682Smarkm
8055682Smarkm	/* No match. */
8155682Smarkm	return (0);
8255682Smarkm}
8355682Smarkm
8455682Smarkm/*
8555682Smarkm * Returns 0 if ok, -1 if not ok.
8655682Smarkm */
8755682Smarkmstatic
8855682Smarkmint
8955682Smarkm__ivaliduser(FILE *hostf, unsigned raddr, const char *luser,
9055682Smarkm	     const char *ruser)
9155682Smarkm{
9255682Smarkm	char *user, *p;
9355682Smarkm	int ch;
9455682Smarkm	char buf[MaxHostNameLen + 128];		/* host + login */
9555682Smarkm	char hname[MaxHostNameLen];
9655682Smarkm	struct hostent *hp;
9755682Smarkm	/* Presumed guilty until proven innocent. */
9855682Smarkm	int userok = 0, hostok = 0;
9955682Smarkm#ifdef HAVE_YP_GET_DEFAULT_DOMAIN
10055682Smarkm	char *ypdomain;
10155682Smarkm
10255682Smarkm	if (yp_get_default_domain(&ypdomain))
10355682Smarkm		ypdomain = NULL;
10455682Smarkm#else
10555682Smarkm#define	ypdomain NULL
10655682Smarkm#endif
10755682Smarkm	/* We need to get the damn hostname back for netgroup matching. */
10855682Smarkm	if ((hp = gethostbyaddr((char *)&raddr,
10955682Smarkm				sizeof(u_long),
11055682Smarkm				AF_INET)) == NULL)
11155682Smarkm		return (-1);
11255682Smarkm	strlcpy(hname, hp->h_name, sizeof(hname));
11355682Smarkm
11455682Smarkm	while (fgets(buf, sizeof(buf), hostf)) {
11555682Smarkm		p = buf;
11655682Smarkm		/* Skip lines that are too long. */
11755682Smarkm		if (strchr(p, '\n') == NULL) {
11855682Smarkm			while ((ch = getc(hostf)) != '\n' && ch != EOF);
11955682Smarkm			continue;
12055682Smarkm		}
12155682Smarkm		if (*p == '\n' || *p == '#') {
12255682Smarkm			/* comment... */
12355682Smarkm			continue;
12455682Smarkm		}
12555682Smarkm		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
12655682Smarkm		        if (isupper((unsigned char)*p))
12755682Smarkm			    *p = tolower((unsigned char)*p);
12855682Smarkm			p++;
12955682Smarkm		}
13055682Smarkm		if (*p == ' ' || *p == '\t') {
13155682Smarkm			*p++ = '\0';
13255682Smarkm			while (*p == ' ' || *p == '\t')
13355682Smarkm				p++;
13455682Smarkm			user = p;
13555682Smarkm			while (*p != '\n' && *p != ' ' &&
13655682Smarkm			    *p != '\t' && *p != '\0')
13755682Smarkm				p++;
13855682Smarkm		} else
13955682Smarkm			user = p;
14055682Smarkm		*p = '\0';
14155682Smarkm		/*
14255682Smarkm		 * Do +/- and +@/-@ checking. This looks really nasty,
14355682Smarkm		 * but it matches SunOS's behavior so far as I can tell.
14455682Smarkm		 */
14555682Smarkm		switch(buf[0]) {
14655682Smarkm		case '+':
14755682Smarkm			if (!buf[1]) {     /* '+' matches all hosts */
14855682Smarkm				hostok = 1;
14955682Smarkm				break;
15055682Smarkm			}
15155682Smarkm			if (buf[1] == '@')  /* match a host by netgroup */
15255682Smarkm				hostok = innetgr((char *)&buf[2],
15355682Smarkm					(char *)&hname, NULL, ypdomain);
15455682Smarkm			else		/* match a host by addr */
15555682Smarkm				hostok = __icheckhost(raddr,(char *)&buf[1]);
15655682Smarkm			break;
15755682Smarkm		case '-':     /* reject '-' hosts and all their users */
15855682Smarkm			if (buf[1] == '@') {
15955682Smarkm				if (innetgr((char *)&buf[2],
16055682Smarkm					      (char *)&hname, NULL, ypdomain))
16155682Smarkm					return(-1);
16255682Smarkm			} else {
16355682Smarkm				if (__icheckhost(raddr,(char *)&buf[1]))
16455682Smarkm					return(-1);
16555682Smarkm			}
16655682Smarkm			break;
16755682Smarkm		default:  /* if no '+' or '-', do a simple match */
16855682Smarkm			hostok = __icheckhost(raddr, buf);
16955682Smarkm			break;
17055682Smarkm		}
17155682Smarkm		switch(*user) {
17255682Smarkm		case '+':
17355682Smarkm			if (!*(user+1)) {      /* '+' matches all users */
17455682Smarkm				userok = 1;
17555682Smarkm				break;
17655682Smarkm			}
17755682Smarkm			if (*(user+1) == '@')  /* match a user by netgroup */
17855682Smarkm				userok = innetgr(user+2, NULL, (char *)ruser,
17955682Smarkm						 ypdomain);
18055682Smarkm			else	   /* match a user by direct specification */
18155682Smarkm				userok = !(strcmp(ruser, user+1));
18255682Smarkm			break;
18355682Smarkm		case '-': 		/* if we matched a hostname, */
18455682Smarkm			if (hostok) {   /* check for user field rejections */
18555682Smarkm				if (!*(user+1))
18655682Smarkm					return(-1);
18755682Smarkm				if (*(user+1) == '@') {
18855682Smarkm					if (innetgr(user+2, NULL,
18955682Smarkm						    (char *)ruser, ypdomain))
19055682Smarkm						return(-1);
19155682Smarkm				} else {
19255682Smarkm					if (!strcmp(ruser, user+1))
19355682Smarkm						return(-1);
19455682Smarkm				}
19555682Smarkm			}
19655682Smarkm			break;
19755682Smarkm		default:	/* no rejections: try to match the user */
19855682Smarkm			if (hostok)
19955682Smarkm				userok = !(strcmp(ruser,*user ? user : luser));
20055682Smarkm			break;
20155682Smarkm		}
20255682Smarkm		if (hostok && userok)
20355682Smarkm			return(0);
20455682Smarkm	}
20555682Smarkm	return (-1);
20655682Smarkm}
20755682Smarkm
20855682Smarkm/*
20955682Smarkm * New .rhosts strategy: We are passed an ip address. We spin through
21055682Smarkm * hosts.equiv and .rhosts looking for a match. When the .rhosts only
21155682Smarkm * has ip addresses, we don't have to trust a nameserver.  When it
21255682Smarkm * contains hostnames, we spin through the list of addresses the nameserver
21355682Smarkm * gives us and look for a match.
21455682Smarkm *
21555682Smarkm * Returns 0 if ok, -1 if not ok.
21655682Smarkm */
217233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
21855682Smarkmiruserok(unsigned raddr, int superuser, const char *ruser, const char *luser)
21955682Smarkm{
22055682Smarkm	char *cp;
22155682Smarkm	struct stat sbuf;
22255682Smarkm	struct passwd *pwd;
22355682Smarkm	FILE *hostf;
22455682Smarkm	uid_t uid;
22555682Smarkm	int first;
22655682Smarkm	char pbuf[MaxPathLen];
22755682Smarkm
22855682Smarkm	first = 1;
22955682Smarkm	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
23055682Smarkmagain:
23155682Smarkm	if (hostf) {
23255682Smarkm		if (__ivaliduser(hostf, raddr, luser, ruser) == 0) {
23355682Smarkm			fclose(hostf);
23455682Smarkm			return (0);
23555682Smarkm		}
23655682Smarkm		fclose(hostf);
23755682Smarkm	}
23855682Smarkm	if (first == 1 && (__check_rhosts_file || superuser)) {
23955682Smarkm		first = 0;
24055682Smarkm		if ((pwd = k_getpwnam((char*)luser)) == NULL)
24155682Smarkm			return (-1);
24255682Smarkm		snprintf (pbuf, sizeof(pbuf), "%s/.rhosts", pwd->pw_dir);
24355682Smarkm
24455682Smarkm		/*
24555682Smarkm		 * Change effective uid while opening .rhosts.  If root and
24655682Smarkm		 * reading an NFS mounted file system, can't read files that
24755682Smarkm		 * are protected read/write owner only.
24855682Smarkm		 */
24955682Smarkm		uid = geteuid();
250178825Sdfr		if (seteuid(pwd->pw_uid) < 0)
251178825Sdfr			return (-1);
25255682Smarkm		hostf = fopen(pbuf, "r");
25355682Smarkm		seteuid(uid);
25455682Smarkm
25555682Smarkm		if (hostf == NULL)
25655682Smarkm			return (-1);
25755682Smarkm		/*
25855682Smarkm		 * If not a regular file, or is owned by someone other than
25955682Smarkm		 * user or root or if writeable by anyone but the owner, quit.
26055682Smarkm		 */
26155682Smarkm		cp = NULL;
26255682Smarkm		if (lstat(pbuf, &sbuf) < 0)
26355682Smarkm			cp = ".rhosts lstat failed";
26455682Smarkm		else if (!S_ISREG(sbuf.st_mode))
26555682Smarkm			cp = ".rhosts not regular file";
26655682Smarkm		else if (fstat(fileno(hostf), &sbuf) < 0)
26755682Smarkm			cp = ".rhosts fstat failed";
26855682Smarkm		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
26955682Smarkm			cp = "bad .rhosts owner";
27055682Smarkm		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
27155682Smarkm			cp = ".rhosts writeable by other than owner";
27255682Smarkm		/* If there were any problems, quit. */
27355682Smarkm		if (cp) {
27455682Smarkm			__rcmd_errstr = cp;
27555682Smarkm			fclose(hostf);
27655682Smarkm			return (-1);
27755682Smarkm		}
27855682Smarkm		goto again;
27955682Smarkm	}
28055682Smarkm	return (-1);
28155682Smarkm}
282