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