xref: /openbsd-src/lib/libc/net/ruserok.c (revision 2c53affbcc0119d6480b86c18f2790523b6a0aad)
123599751Sguenther /*
223599751Sguenther  * Copyright (c) 1995, 1996, 1998 Theo de Raadt.  All rights reserved.
323599751Sguenther  * Copyright (c) 1983, 1993, 1994
423599751Sguenther  *	The Regents of the University of California.  All rights reserved.
523599751Sguenther  *
623599751Sguenther  * Redistribution and use in source and binary forms, with or without
723599751Sguenther  * modification, are permitted provided that the following conditions
823599751Sguenther  * are met:
923599751Sguenther  * 1. Redistributions of source code must retain the above copyright
1023599751Sguenther  *    notice, this list of conditions and the following disclaimer.
1123599751Sguenther  * 2. Redistributions in binary form must reproduce the above copyright
1223599751Sguenther  *    notice, this list of conditions and the following disclaimer in the
1323599751Sguenther  *    documentation and/or other materials provided with the distribution.
1423599751Sguenther  * 3. Neither the name of the University nor the names of its contributors
1523599751Sguenther  *    may be used to endorse or promote products derived from this software
1623599751Sguenther  *    without specific prior written permission.
1723599751Sguenther  *
1823599751Sguenther  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1923599751Sguenther  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2023599751Sguenther  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2123599751Sguenther  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2223599751Sguenther  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2323599751Sguenther  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2423599751Sguenther  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2523599751Sguenther  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2623599751Sguenther  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2723599751Sguenther  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2823599751Sguenther  * SUCH DAMAGE.
2923599751Sguenther  */
3023599751Sguenther 
3123599751Sguenther #include <sys/socket.h>
3223599751Sguenther #include <sys/stat.h>
3323599751Sguenther 
3423599751Sguenther #include <netinet/in.h>
3523599751Sguenther #include <arpa/inet.h>
3623599751Sguenther 
3723599751Sguenther #include <ctype.h>
3869245ebdSmillert #include <errno.h>
3969245ebdSmillert #include <fcntl.h>
4069245ebdSmillert #include <limits.h>
4169245ebdSmillert #include <netdb.h>
4269245ebdSmillert #include <netgroup.h>
4369245ebdSmillert #include <pwd.h>
4469245ebdSmillert #include <signal.h>
4569245ebdSmillert #include <stdio.h>
4669245ebdSmillert #include <stdlib.h>
4723599751Sguenther #include <string.h>
4823599751Sguenther #include <syslog.h>
4969245ebdSmillert #include <unistd.h>
5023599751Sguenther 
51df1557ebSguenther static int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t,
5223599751Sguenther 	    const char *, const char *);
5323599751Sguenther static int __icheckhost(struct sockaddr *, socklen_t, const char *);
5423599751Sguenther static char *__gethostloop(struct sockaddr *, socklen_t);
55df1557ebSguenther static int iruserok_sa(const void *, int, int, const char *, const char *);
5623599751Sguenther 
5723599751Sguenther int
ruserok(const char * rhost,int superuser,const char * ruser,const char * luser)5823599751Sguenther ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
5923599751Sguenther {
6023599751Sguenther 	struct addrinfo hints, *res, *r;
6123599751Sguenther 	int error;
6223599751Sguenther 
6323599751Sguenther 	memset(&hints, 0, sizeof(hints));
6423599751Sguenther 	hints.ai_family = PF_UNSPEC;
6523599751Sguenther 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
6623599751Sguenther 	error = getaddrinfo(rhost, "0", &hints, &res);
6723599751Sguenther 	if (error)
6823599751Sguenther 		return (-1);
6923599751Sguenther 
7023599751Sguenther 	for (r = res; r; r = r->ai_next) {
7123599751Sguenther 		if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser,
7223599751Sguenther 		    luser) == 0) {
7323599751Sguenther 			freeaddrinfo(res);
7423599751Sguenther 			return (0);
7523599751Sguenther 		}
7623599751Sguenther 	}
7723599751Sguenther 	freeaddrinfo(res);
7823599751Sguenther 	return (-1);
7923599751Sguenther }
8023599751Sguenther 
8123599751Sguenther int
iruserok_sa(const void * raddr,int rlen,int superuser,const char * ruser,const char * luser)8223599751Sguenther iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser,
8323599751Sguenther     const char *luser)
8423599751Sguenther {
8523599751Sguenther 	struct sockaddr *sa;
8623599751Sguenther 	char *cp;
8723599751Sguenther 	struct stat sbuf;
88cd245bcaSmillert 	struct passwd pwstore, *pwd;
8923599751Sguenther 	FILE *hostf;
9023599751Sguenther 	uid_t uid;
9123599751Sguenther 	int first;
92cd245bcaSmillert 	char pbuf[PATH_MAX], pwbuf[_PW_BUF_LEN];
9323599751Sguenther 
9423599751Sguenther 	sa = (struct sockaddr *)raddr;
9523599751Sguenther 	first = 1;
96241db059Sguenther 	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
9723599751Sguenther again:
9823599751Sguenther 	if (hostf) {
9923599751Sguenther 		if (__ivaliduser_sa(hostf, sa, rlen, luser, ruser) == 0) {
10023599751Sguenther 			(void)fclose(hostf);
10123599751Sguenther 			return (0);
10223599751Sguenther 		}
10323599751Sguenther 		(void)fclose(hostf);
10423599751Sguenther 	}
105e3b47d8eSguenther 	if (first == 1) {
10623599751Sguenther 		int len;
10723599751Sguenther 
10823599751Sguenther 		first = 0;
109cd245bcaSmillert 		pwd = NULL;
110cd245bcaSmillert 		getpwnam_r(luser, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
111cd245bcaSmillert 		if (pwd == NULL)
11223599751Sguenther 			return (-1);
11323599751Sguenther 		len = snprintf(pbuf, sizeof pbuf, "%s/.rhosts", pwd->pw_dir);
11423599751Sguenther 		if (len < 0 || len >= sizeof pbuf)
11523599751Sguenther 			return (-1);
11623599751Sguenther 
11723599751Sguenther 		/*
11823599751Sguenther 		 * Change effective uid while opening .rhosts.  If root and
11923599751Sguenther 		 * reading an NFS mounted file system, can't read files that
12023599751Sguenther 		 * are protected read/write owner only.
12123599751Sguenther 		 */
12223599751Sguenther 		uid = geteuid();
12323599751Sguenther 		(void)seteuid(pwd->pw_uid);
124241db059Sguenther 		hostf = fopen(pbuf, "re");
12523599751Sguenther 		(void)seteuid(uid);
12623599751Sguenther 
12723599751Sguenther 		if (hostf == NULL)
12823599751Sguenther 			return (-1);
12923599751Sguenther 		/*
13023599751Sguenther 		 * If not a regular file, or is owned by someone other than
13123599751Sguenther 		 * user or root or if writeable by anyone but the owner, quit.
13223599751Sguenther 		 */
13323599751Sguenther 		cp = NULL;
134df69c215Sderaadt 		if (lstat(pbuf, &sbuf) == -1)
13523599751Sguenther 			cp = ".rhosts lstat failed";
13623599751Sguenther 		else if (!S_ISREG(sbuf.st_mode))
13723599751Sguenther 			cp = ".rhosts not regular file";
138df69c215Sderaadt 		else if (fstat(fileno(hostf), &sbuf) == -1)
13923599751Sguenther 			cp = ".rhosts fstat failed";
14023599751Sguenther 		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
14123599751Sguenther 			cp = "bad .rhosts owner";
14223599751Sguenther 		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
14323599751Sguenther 			cp = ".rhosts writable by other than owner";
14423599751Sguenther 		/* If there were any problems, quit. */
14523599751Sguenther 		if (cp) {
14623599751Sguenther 			(void)fclose(hostf);
14723599751Sguenther 			return (-1);
14823599751Sguenther 		}
14923599751Sguenther 		goto again;
15023599751Sguenther 	}
15123599751Sguenther 	return (-1);
15223599751Sguenther }
15323599751Sguenther 
15423599751Sguenther int
__ivaliduser_sa(FILE * hostf,struct sockaddr * raddr,socklen_t salen,const char * luser,const char * ruser)15523599751Sguenther __ivaliduser_sa(FILE *hostf, struct sockaddr *raddr, socklen_t salen,
15623599751Sguenther     const char *luser, const char *ruser)
15723599751Sguenther {
15823599751Sguenther 	char *user, *p;
15923599751Sguenther 	char *buf;
16023599751Sguenther 	const char *auser, *ahost;
16123599751Sguenther 	int hostok, userok;
16223599751Sguenther 	char *rhost = (char *)-1;
163aea60beeSderaadt 	char domain[HOST_NAME_MAX+1];
16423599751Sguenther 	size_t buflen;
16523599751Sguenther 
16623599751Sguenther 	getdomainname(domain, sizeof(domain));
16723599751Sguenther 
16823599751Sguenther 	while ((buf = fgetln(hostf, &buflen))) {
16923599751Sguenther 		p = buf;
17023599751Sguenther 		if (*p == '#')
17123599751Sguenther 			continue;
17223599751Sguenther 		while (p < buf + buflen && *p != '\n' && *p != ' ' && *p != '\t') {
173dfe5467eSderaadt 			if (!isprint((unsigned char)*p))
17423599751Sguenther 				goto bail;
175dfe5467eSderaadt 			*p = isupper((unsigned char)*p) ?
176dfe5467eSderaadt 			    tolower((unsigned char)*p) : *p;
17723599751Sguenther 			p++;
17823599751Sguenther 		}
17923599751Sguenther 		if (p >= buf + buflen)
18023599751Sguenther 			continue;
18123599751Sguenther 		if (*p == ' ' || *p == '\t') {
18223599751Sguenther 			*p++ = '\0';
18323599751Sguenther 			while (p < buf + buflen && (*p == ' ' || *p == '\t'))
18423599751Sguenther 				p++;
18523599751Sguenther 			if (p >= buf + buflen)
18623599751Sguenther 				continue;
18723599751Sguenther 			user = p;
18823599751Sguenther 			while (p < buf + buflen && *p != '\n' && *p != ' ' &&
18923599751Sguenther 			    *p != '\t') {
190dfe5467eSderaadt 				if (!isprint((unsigned char)*p))
19123599751Sguenther 					goto bail;
19223599751Sguenther 				p++;
19323599751Sguenther 			}
19423599751Sguenther 		} else
19523599751Sguenther 			user = p;
19623599751Sguenther 		*p = '\0';
19723599751Sguenther 
19823599751Sguenther 		if (p == buf)
19923599751Sguenther 			continue;
20023599751Sguenther 
20123599751Sguenther 		auser = *user ? user : luser;
20223599751Sguenther 		ahost = buf;
20323599751Sguenther 
2049b6be490Smillert 		if (strlen(ahost) > HOST_NAME_MAX)
20523599751Sguenther 			continue;
20623599751Sguenther 
20723599751Sguenther 		/*
20823599751Sguenther 		 * innetgr() must lookup a hostname (we do not attempt
20923599751Sguenther 		 * to change the semantics so that netgroups may have
21023599751Sguenther 		 * #.#.#.# addresses in the list.)
21123599751Sguenther 		 */
21223599751Sguenther 		if (ahost[0] == '+')
21323599751Sguenther 			switch (ahost[1]) {
21423599751Sguenther 			case '\0':
21523599751Sguenther 				hostok = 1;
21623599751Sguenther 				break;
21723599751Sguenther 			case '@':
21823599751Sguenther 				if (rhost == (char *)-1)
21923599751Sguenther 					rhost = __gethostloop(raddr, salen);
22023599751Sguenther 				hostok = 0;
22123599751Sguenther 				if (rhost)
22223599751Sguenther 					hostok = innetgr(&ahost[2], rhost,
22323599751Sguenther 					    NULL, domain);
22423599751Sguenther 				break;
22523599751Sguenther 			default:
22623599751Sguenther 				hostok = __icheckhost(raddr, salen, &ahost[1]);
22723599751Sguenther 				break;
22823599751Sguenther 			}
22923599751Sguenther 		else if (ahost[0] == '-')
23023599751Sguenther 			switch (ahost[1]) {
23123599751Sguenther 			case '\0':
23223599751Sguenther 				hostok = -1;
23323599751Sguenther 				break;
23423599751Sguenther 			case '@':
23523599751Sguenther 				if (rhost == (char *)-1)
23623599751Sguenther 					rhost = __gethostloop(raddr, salen);
23723599751Sguenther 				hostok = 0;
23823599751Sguenther 				if (rhost)
23923599751Sguenther 					hostok = -innetgr(&ahost[2], rhost,
24023599751Sguenther 					    NULL, domain);
24123599751Sguenther 				break;
24223599751Sguenther 			default:
24323599751Sguenther 				hostok = -__icheckhost(raddr, salen, &ahost[1]);
24423599751Sguenther 				break;
24523599751Sguenther 			}
24623599751Sguenther 		else
24723599751Sguenther 			hostok = __icheckhost(raddr, salen, ahost);
24823599751Sguenther 
24923599751Sguenther 
25023599751Sguenther 		if (auser[0] == '+')
25123599751Sguenther 			switch (auser[1]) {
25223599751Sguenther 			case '\0':
25323599751Sguenther 				userok = 1;
25423599751Sguenther 				break;
25523599751Sguenther 			case '@':
25623599751Sguenther 				userok = innetgr(&auser[2], NULL, ruser,
25723599751Sguenther 				    domain);
25823599751Sguenther 				break;
25923599751Sguenther 			default:
26023599751Sguenther 				userok = strcmp(ruser, &auser[1]) ? 0 : 1;
26123599751Sguenther 				break;
26223599751Sguenther 			}
26323599751Sguenther 		else if (auser[0] == '-')
26423599751Sguenther 			switch (auser[1]) {
26523599751Sguenther 			case '\0':
26623599751Sguenther 				userok = -1;
26723599751Sguenther 				break;
26823599751Sguenther 			case '@':
26923599751Sguenther 				userok = -innetgr(&auser[2], NULL, ruser,
27023599751Sguenther 				    domain);
27123599751Sguenther 				break;
27223599751Sguenther 			default:
27323599751Sguenther 				userok = strcmp(ruser, &auser[1]) ? 0 : -1;
27423599751Sguenther 				break;
27523599751Sguenther 			}
27623599751Sguenther 		else
27723599751Sguenther 			userok = strcmp(ruser, auser) ? 0 : 1;
27823599751Sguenther 
27923599751Sguenther 		/* Check if one component did not match */
28023599751Sguenther 		if (hostok == 0 || userok == 0)
28123599751Sguenther 			continue;
28223599751Sguenther 
28323599751Sguenther 		/* Check if we got a forbidden pair */
28423599751Sguenther 		if (userok <= -1 || hostok <= -1)
28523599751Sguenther 			return (-1);
28623599751Sguenther 
28723599751Sguenther 		/* Check if we got a valid pair */
28823599751Sguenther 		if (hostok >= 1 && userok >= 1)
28923599751Sguenther 			return (0);
29023599751Sguenther 	}
29123599751Sguenther bail:
29223599751Sguenther 	return (-1);
29323599751Sguenther }
29423599751Sguenther 
29523599751Sguenther /*
29623599751Sguenther  * Returns "true" if match, 0 if no match.  If we do not find any
29723599751Sguenther  * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work.
29823599751Sguenther  */
29923599751Sguenther static int
__icheckhost(struct sockaddr * raddr,socklen_t salen,const char * lhost)30023599751Sguenther __icheckhost(struct sockaddr *raddr, socklen_t salen, const char *lhost)
30123599751Sguenther {
30223599751Sguenther 	struct addrinfo hints, *res, *r;
30323599751Sguenther 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
30423599751Sguenther 	int error;
30523599751Sguenther 	const int niflags = NI_NUMERICHOST;
30623599751Sguenther 
30723599751Sguenther 	h1[0] = '\0';
30823599751Sguenther 	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
30923599751Sguenther 	    niflags) != 0)
31023599751Sguenther 		return (0);
31123599751Sguenther 
31223599751Sguenther 	/* Resolve laddr into sockaddr */
31323599751Sguenther 	memset(&hints, 0, sizeof(hints));
31423599751Sguenther 	hints.ai_family = raddr->sa_family;
31523599751Sguenther 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
31623599751Sguenther 	res = NULL;
31723599751Sguenther 	error = getaddrinfo(lhost, "0", &hints, &res);
31823599751Sguenther 	if (error)
31923599751Sguenther 		return (0);
32023599751Sguenther 
32123599751Sguenther 	/*
32223599751Sguenther 	 * Try string comparisons between raddr and laddr.
32323599751Sguenther 	 */
32423599751Sguenther 	for (r = res; r; r = r->ai_next) {
32523599751Sguenther 		h2[0] = '\0';
32623599751Sguenther 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
32723599751Sguenther 		    NULL, 0, niflags) != 0)
32823599751Sguenther 			continue;
32923599751Sguenther 		if (strcmp(h1, h2) == 0) {
33023599751Sguenther 			freeaddrinfo(res);
33123599751Sguenther 			return (1);
33223599751Sguenther 		}
33323599751Sguenther 	}
33423599751Sguenther 
33523599751Sguenther 	/* No match. */
33623599751Sguenther 	freeaddrinfo(res);
33723599751Sguenther 	return (0);
33823599751Sguenther }
33923599751Sguenther 
34023599751Sguenther /*
34123599751Sguenther  * Return the hostname associated with the supplied address.
34223599751Sguenther  * Do a reverse lookup as well for security. If a loop cannot
34323599751Sguenther  * be found, pack the result of inet_ntoa() into the string.
34423599751Sguenther  */
34523599751Sguenther static char *
__gethostloop(struct sockaddr * raddr,socklen_t salen)34623599751Sguenther __gethostloop(struct sockaddr *raddr, socklen_t salen)
34723599751Sguenther {
34823599751Sguenther 	static char remotehost[NI_MAXHOST];
34923599751Sguenther 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
35023599751Sguenther 	struct addrinfo hints, *res, *r;
35123599751Sguenther 	int error;
35223599751Sguenther 	const int niflags = NI_NUMERICHOST;
35323599751Sguenther 
35423599751Sguenther 	h1[0] = remotehost[0] = '\0';
35523599751Sguenther 	if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost),
35623599751Sguenther 	    NULL, 0, NI_NAMEREQD) != 0)
35723599751Sguenther 		return (NULL);
35823599751Sguenther 	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
35923599751Sguenther 	    niflags) != 0)
36023599751Sguenther 		return (NULL);
36123599751Sguenther 
36223599751Sguenther 	/*
36323599751Sguenther 	 * Look up the name and check that the supplied
36423599751Sguenther 	 * address is in the list
36523599751Sguenther 	 */
36623599751Sguenther 	memset(&hints, 0, sizeof(hints));
36723599751Sguenther 	hints.ai_family = raddr->sa_family;
36823599751Sguenther 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
36923599751Sguenther 	hints.ai_flags = AI_CANONNAME;
37023599751Sguenther 	res = NULL;
37123599751Sguenther 	error = getaddrinfo(remotehost, "0", &hints, &res);
37223599751Sguenther 	if (error)
37323599751Sguenther 		return (NULL);
37423599751Sguenther 
37523599751Sguenther 	for (r = res; r; r = r->ai_next) {
37623599751Sguenther 		h2[0] = '\0';
37723599751Sguenther 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
37823599751Sguenther 		    NULL, 0, niflags) != 0)
37923599751Sguenther 			continue;
38023599751Sguenther 		if (strcmp(h1, h2) == 0) {
38123599751Sguenther 			freeaddrinfo(res);
38223599751Sguenther 			return (remotehost);
38323599751Sguenther 		}
38423599751Sguenther 	}
38523599751Sguenther 
38623599751Sguenther 	/*
387*2c53affbSjmc 	 * either the DNS administrator has made a configuration
38823599751Sguenther 	 * mistake, or someone has attempted to spoof us
38923599751Sguenther 	 */
39023599751Sguenther 	freeaddrinfo(res);
39123599751Sguenther 	return (NULL);
39223599751Sguenther }
393