144743Smarkm /*
244743Smarkm  * This module determines the type of socket (datagram, stream), the client
344743Smarkm  * socket address and port, the server socket address and port. In addition,
444743Smarkm  * it provides methods to map a transport address to a printable host name
544743Smarkm  * or address. Socket address information results are in static memory.
644743Smarkm  *
744743Smarkm  * The result from the hostname lookup method is STRING_PARANOID when a host
844743Smarkm  * pretends to have someone elses name, or when a host name is available but
944743Smarkm  * could not be verified.
1044743Smarkm  *
1144743Smarkm  * When lookup or conversion fails the result is set to STRING_UNKNOWN.
1244743Smarkm  *
1344743Smarkm  * Diagnostics are reported through syslog(3).
1444743Smarkm  *
1544743Smarkm  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
1656977Sshin  *
1756977Sshin  * $FreeBSD: releng/10.3/contrib/tcp_wrappers/socket.c 146187 2005-05-13 16:31:11Z ume $
1844743Smarkm  */
1944743Smarkm
2044743Smarkm#ifndef lint
2144743Smarkmstatic char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
2244743Smarkm#endif
2344743Smarkm
2444743Smarkm/* System libraries. */
2544743Smarkm
2644743Smarkm#include <sys/types.h>
2744743Smarkm#include <sys/param.h>
2844743Smarkm#include <sys/socket.h>
2944743Smarkm#include <netinet/in.h>
3044743Smarkm#include <netdb.h>
3144743Smarkm#include <stdio.h>
3244743Smarkm#include <syslog.h>
3344743Smarkm#include <string.h>
3444743Smarkm
35146187Sume#ifndef INET6
3663158Sumeextern char *inet_ntoa();
3756977Sshin#endif
3856977Sshin
3944743Smarkm/* Local stuff. */
4044743Smarkm
4144743Smarkm#include "tcpd.h"
4244743Smarkm
4344743Smarkm/* Forward declarations. */
4444743Smarkm
4544743Smarkmstatic void sock_sink();
4644743Smarkm
4744743Smarkm#ifdef APPEND_DOT
4844743Smarkm
4944743Smarkm /*
5044743Smarkm  * Speed up DNS lookups by terminating the host name with a dot. Should be
5144743Smarkm  * done with care. The speedup can give problems with lookups from sources
5244743Smarkm  * that lack DNS-style trailing dot magic, such as local files or NIS maps.
5344743Smarkm  */
5444743Smarkm
5544743Smarkmstatic struct hostent *gethostbyname_dot(name)
5644743Smarkmchar   *name;
5744743Smarkm{
5844743Smarkm    char    dot_name[MAXHOSTNAMELEN + 1];
5944743Smarkm
6044743Smarkm    /*
6144743Smarkm     * Don't append dots to unqualified names. Such names are likely to come
6244743Smarkm     * from local hosts files or from NIS.
6344743Smarkm     */
6444743Smarkm
6544743Smarkm    if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) {
6644743Smarkm	return (gethostbyname(name));
6744743Smarkm    } else {
6844743Smarkm	sprintf(dot_name, "%s.", name);
6944743Smarkm	return (gethostbyname(dot_name));
7044743Smarkm    }
7144743Smarkm}
7244743Smarkm
7344743Smarkm#define gethostbyname gethostbyname_dot
7444743Smarkm#endif
7544743Smarkm
7644743Smarkm/* sock_host - look up endpoint addresses and install conversion methods */
7744743Smarkm
7844743Smarkmvoid    sock_host(request)
7944743Smarkmstruct request_info *request;
8044743Smarkm{
8156977Sshin#ifdef INET6
8256977Sshin    static struct sockaddr_storage client;
8356977Sshin    static struct sockaddr_storage server;
8456977Sshin#else
8544743Smarkm    static struct sockaddr_in client;
8644743Smarkm    static struct sockaddr_in server;
8756977Sshin#endif
8844743Smarkm    int     len;
8944743Smarkm    char    buf[BUFSIZ];
9044743Smarkm    int     fd = request->fd;
9144743Smarkm
9244743Smarkm    sock_methods(request);
9344743Smarkm
9444743Smarkm    /*
9544743Smarkm     * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
9644743Smarkm     * suggested how to get the client host info in case of UDP connections:
9744743Smarkm     * peek at the first message without actually looking at its contents. We
9844743Smarkm     * really should verify that client.sin_family gets the value AF_INET,
9944743Smarkm     * but this program has already caused too much grief on systems with
10044743Smarkm     * broken library code.
10144743Smarkm     */
10244743Smarkm
10344743Smarkm    len = sizeof(client);
10444743Smarkm    if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) {
10544743Smarkm	request->sink = sock_sink;
10644743Smarkm	len = sizeof(client);
10744743Smarkm	if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK,
10844743Smarkm		     (struct sockaddr *) & client, &len) < 0) {
10944743Smarkm	    tcpd_warn("can't get client address: %m");
11044743Smarkm	    return;				/* give up */
11144743Smarkm	}
11244743Smarkm#ifdef really_paranoid
113123895Sceri	memset(buf, 0, sizeof(buf));
11444743Smarkm#endif
11544743Smarkm    }
11656977Sshin#ifdef INET6
11756977Sshin    request->client->sin = (struct sockaddr *)&client;
11856977Sshin#else
11944743Smarkm    request->client->sin = &client;
12056977Sshin#endif
12144743Smarkm
12244743Smarkm    /*
12344743Smarkm     * Determine the server binding. This is used for client username
12444743Smarkm     * lookups, and for access control rules that trigger on the server
12544743Smarkm     * address or name.
12644743Smarkm     */
12744743Smarkm
12844743Smarkm    len = sizeof(server);
12944743Smarkm    if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) {
13044743Smarkm	tcpd_warn("getsockname: %m");
13144743Smarkm	return;
13244743Smarkm    }
13356977Sshin#ifdef INET6
13456977Sshin    request->server->sin = (struct sockaddr *)&server;
13556977Sshin#else
13644743Smarkm    request->server->sin = &server;
13756977Sshin#endif
13844743Smarkm}
13944743Smarkm
14044743Smarkm/* sock_hostaddr - map endpoint address to printable form */
14144743Smarkm
14244743Smarkmvoid    sock_hostaddr(host)
14344743Smarkmstruct host_info *host;
14444743Smarkm{
14556977Sshin#ifdef INET6
14656977Sshin    struct sockaddr *sin = host->sin;
14763158Sume    int salen;
14856977Sshin
14956977Sshin    if (!sin)
15056977Sshin	return;
15163158Sume#ifdef SIN6_LEN
15263158Sume    salen = sin->sa_len;
15356977Sshin#else
15463158Sume    salen = (sin->sa_family == AF_INET) ? sizeof(struct sockaddr_in)
15563158Sume					: sizeof(struct sockaddr_in6);
15663158Sume#endif
15763158Sume    getnameinfo(sin, salen, host->addr, sizeof(host->addr),
158146187Sume		NULL, 0, NI_NUMERICHOST);
15963158Sume#else
16044743Smarkm    struct sockaddr_in *sin = host->sin;
16144743Smarkm
16244743Smarkm    if (sin != 0)
16344743Smarkm	STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr));
16456977Sshin#endif
16544743Smarkm}
16644743Smarkm
16744743Smarkm/* sock_hostname - map endpoint address to host name */
16844743Smarkm
16944743Smarkmvoid    sock_hostname(host)
17044743Smarkmstruct host_info *host;
17144743Smarkm{
17256977Sshin#ifdef INET6
17356977Sshin    struct sockaddr *sin = host->sin;
17463158Sume    struct sockaddr_in sin4;
17563158Sume    struct addrinfo hints, *res, *res0 = NULL;
17663158Sume    int salen, alen, err = 1;
17763158Sume    char *ap = NULL, *rap, hname[NI_MAXHOST];
17863158Sume
17963158Sume    if (sin != NULL) {
18063158Sume	if (sin->sa_family == AF_INET6) {
18163158Sume	    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin;
18263158Sume
18363158Sume	    if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
18463158Sume		memset(&sin4, 0, sizeof(sin4));
18563158Sume#ifdef SIN6_LEN
18663158Sume		sin4.sin_len = sizeof(sin4);
18756977Sshin#endif
18863158Sume		sin4.sin_family = AF_INET;
18963158Sume		sin4.sin_port = sin6->sin6_port;
19063158Sume		sin4.sin_addr.s_addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
19163158Sume		sin = (struct sockaddr *)&sin4;
19263158Sume	    }
19363158Sume	}
19463158Sume	switch (sin->sa_family) {
19563158Sume	case AF_INET:
19663158Sume	    ap = (char *)&((struct sockaddr_in *)sin)->sin_addr;
19763158Sume	    alen = sizeof(struct in_addr);
19863158Sume	    salen = sizeof(struct sockaddr_in);
19963158Sume	    break;
20063158Sume	case AF_INET6:
20163158Sume	    ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr;
20263158Sume	    alen = sizeof(struct in6_addr);
20363158Sume	    salen = sizeof(struct sockaddr_in6);
20463158Sume	    break;
20563158Sume	default:
20663158Sume	    break;
20763158Sume	}
20863158Sume	if (ap)
20963158Sume	    err = getnameinfo(sin, salen, hname, sizeof(hname),
210146187Sume			      NULL, 0, NI_NAMEREQD);
21163158Sume    }
21263158Sume    if (!err) {
21363158Sume
21463158Sume	STRN_CPY(host->name, hname, sizeof(host->name));
21563158Sume
21666329Sume	/* reject numeric addresses */
21766329Sume	memset(&hints, 0, sizeof(hints));
21866329Sume	hints.ai_family = sin->sa_family;
21966329Sume	hints.ai_socktype = SOCK_STREAM;
22066329Sume	hints.ai_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST;
22179249Skris	if ((err = getaddrinfo(host->name, NULL, &hints, &res0)) == 0) {
22266329Sume	    freeaddrinfo(res0);
22366329Sume	    tcpd_warn("host name/name mismatch: "
22466329Sume		      "reverse lookup results in non-FQDN %s",
22566329Sume		      host->name);
22666329Sume	    strcpy(host->name, paranoid);	/* name is bad, clobber it */
22766329Sume	}
22866329Sume	err = !err;
22966329Sume    }
23066329Sume    if (!err) {
23166329Sume	/* we are now sure that this is non-numeric */
23266329Sume
23363158Sume	/*
23463158Sume	 * Verify that the address is a member of the address list returned
23563158Sume	 * by gethostbyname(hostname).
23663158Sume	 *
23763158Sume	 * Verify also that gethostbyaddr() and gethostbyname() return the same
23863158Sume	 * hostname, or rshd and rlogind may still end up being spoofed.
23963158Sume	 *
24063158Sume	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
24163158Sume	 * This is a DNS artefact. We treat it as a special case. When we
24263158Sume	 * can't believe the address list from gethostbyname("localhost")
24363158Sume	 * we're in big trouble anyway.
24463158Sume	 */
24563158Sume
24663158Sume	memset(&hints, 0, sizeof(hints));
24763158Sume	hints.ai_family = sin->sa_family;
24863158Sume	hints.ai_socktype = SOCK_STREAM;
24963158Sume	hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
25063158Sume	if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) {
25163158Sume
25263158Sume	    /*
25363158Sume	     * Unable to verify that the host name matches the address. This
25463158Sume	     * may be a transient problem or a botched name server setup.
25563158Sume	     */
25663158Sume
25763158Sume	    tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed",
25863158Sume		      host->name,
25963158Sume		      (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6");
26063158Sume
26166297Sume	} else if ((res0->ai_canonname == NULL
26266297Sume		    || STR_NE(host->name, res0->ai_canonname))
26363158Sume		   && STR_NE(host->name, "localhost")) {
26463158Sume
26563158Sume	    /*
26663158Sume	     * The gethostbyaddr() and gethostbyname() calls did not return
26763158Sume	     * the same hostname. This could be a nameserver configuration
26863158Sume	     * problem. It could also be that someone is trying to spoof us.
26963158Sume	     */
27063158Sume
27163158Sume	    tcpd_warn("host name/name mismatch: %s != %.*s",
27266297Sume		      host->name, STRING_LENGTH,
27366297Sume		      (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
27463158Sume
27563158Sume	} else {
27663158Sume
27763158Sume	    /*
27863158Sume	     * The address should be a member of the address list returned by
27963158Sume	     * gethostbyname(). We should first verify that the h_addrtype
28063158Sume	     * field is AF_INET, but this program has already caused too much
28163158Sume	     * grief on systems with broken library code.
28263158Sume	     */
28363158Sume
28463158Sume	    for (res = res0; res; res = res->ai_next) {
28563158Sume		if (res->ai_family != sin->sa_family)
28663158Sume		    continue;
28763158Sume		switch (res->ai_family) {
28863158Sume		case AF_INET:
28963158Sume		    rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
29063158Sume		    break;
29163158Sume		case AF_INET6:
29266329Sume		    /* need to check scope_id */
29366329Sume		    if (((struct sockaddr_in6 *)sin)->sin6_scope_id !=
29466329Sume		        ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) {
29566329Sume			continue;
29666329Sume		    }
29763158Sume		    rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
29863158Sume		    break;
29963158Sume		default:
30063158Sume		    continue;
30163158Sume		}
30263158Sume		if (memcmp(rap, ap, alen) == 0) {
30363158Sume		    freeaddrinfo(res0);
30463158Sume		    return;			/* name is good, keep it */
30563158Sume		}
30663158Sume	    }
30763158Sume
30863158Sume	    /*
30963158Sume	     * The host name does not map to the initial address. Perhaps
31063158Sume	     * someone has messed up. Perhaps someone compromised a name
31163158Sume	     * server.
31263158Sume	     */
31363158Sume
31463158Sume	    getnameinfo(sin, salen, hname, sizeof(hname),
315146187Sume			NULL, 0, NI_NUMERICHOST);
31663158Sume	    tcpd_warn("host name/address mismatch: %s != %.*s",
31766297Sume		      hname, STRING_LENGTH,
31866297Sume		      (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
31963158Sume	}
32063158Sume	strcpy(host->name, paranoid);		/* name is bad, clobber it */
32163158Sume	if (res0)
32263158Sume	    freeaddrinfo(res0);
32363158Sume    }
32463158Sume#else /* INET6 */
32544743Smarkm    struct sockaddr_in *sin = host->sin;
32644743Smarkm    struct hostent *hp;
32744743Smarkm    int     i;
32844743Smarkm
32944743Smarkm    /*
33044743Smarkm     * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
33144743Smarkm     * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
33244743Smarkm     * not work the other way around: gethostbyname("INADDR_ANY") fails. We
33344743Smarkm     * have to special-case 0.0.0.0, in order to avoid false alerts from the
33444743Smarkm     * host name/address checking code below.
33544743Smarkm     */
33644743Smarkm    if (sin != 0 && sin->sin_addr.s_addr != 0
33744743Smarkm	&& (hp = gethostbyaddr((char *) &(sin->sin_addr),
33844743Smarkm			       sizeof(sin->sin_addr), AF_INET)) != 0) {
33944743Smarkm
34044743Smarkm	STRN_CPY(host->name, hp->h_name, sizeof(host->name));
34144743Smarkm
34244743Smarkm	/*
34344743Smarkm	 * Verify that the address is a member of the address list returned
34444743Smarkm	 * by gethostbyname(hostname).
34544743Smarkm	 *
34644743Smarkm	 * Verify also that gethostbyaddr() and gethostbyname() return the same
34744743Smarkm	 * hostname, or rshd and rlogind may still end up being spoofed.
34844743Smarkm	 *
34944743Smarkm	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
35044743Smarkm	 * This is a DNS artefact. We treat it as a special case. When we
35144743Smarkm	 * can't believe the address list from gethostbyname("localhost")
35244743Smarkm	 * we're in big trouble anyway.
35344743Smarkm	 */
35444743Smarkm
35544743Smarkm	if ((hp = gethostbyname(host->name)) == 0) {
35644743Smarkm
35744743Smarkm	    /*
35844743Smarkm	     * Unable to verify that the host name matches the address. This
35944743Smarkm	     * may be a transient problem or a botched name server setup.
36044743Smarkm	     */
36144743Smarkm
36244743Smarkm	    tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
36344743Smarkm		      host->name);
36444743Smarkm
36544743Smarkm	} else if (STR_NE(host->name, hp->h_name)
36644743Smarkm		   && STR_NE(host->name, "localhost")) {
36744743Smarkm
36844743Smarkm	    /*
36944743Smarkm	     * The gethostbyaddr() and gethostbyname() calls did not return
37044743Smarkm	     * the same hostname. This could be a nameserver configuration
37144743Smarkm	     * problem. It could also be that someone is trying to spoof us.
37244743Smarkm	     */
37344743Smarkm
37444743Smarkm	    tcpd_warn("host name/name mismatch: %s != %.*s",
37544743Smarkm		      host->name, STRING_LENGTH, hp->h_name);
37644743Smarkm
37744743Smarkm	} else {
37844743Smarkm
37944743Smarkm	    /*
38044743Smarkm	     * The address should be a member of the address list returned by
38144743Smarkm	     * gethostbyname(). We should first verify that the h_addrtype
38244743Smarkm	     * field is AF_INET, but this program has already caused too much
38344743Smarkm	     * grief on systems with broken library code.
38444743Smarkm	     */
38544743Smarkm
38644743Smarkm	    for (i = 0; hp->h_addr_list[i]; i++) {
38744743Smarkm		if (memcmp(hp->h_addr_list[i],
38844743Smarkm			   (char *) &sin->sin_addr,
38944743Smarkm			   sizeof(sin->sin_addr)) == 0)
39044743Smarkm		    return;			/* name is good, keep it */
39144743Smarkm	    }
39244743Smarkm
39344743Smarkm	    /*
39444743Smarkm	     * The host name does not map to the initial address. Perhaps
39544743Smarkm	     * someone has messed up. Perhaps someone compromised a name
39644743Smarkm	     * server.
39744743Smarkm	     */
39844743Smarkm
39944743Smarkm	    tcpd_warn("host name/address mismatch: %s != %.*s",
40044743Smarkm		      inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name);
40144743Smarkm	}
40244743Smarkm	strcpy(host->name, paranoid);		/* name is bad, clobber it */
40344743Smarkm    }
40463158Sume#endif /* INET6 */
40544743Smarkm}
40644743Smarkm
40744743Smarkm/* sock_sink - absorb unreceived IP datagram */
40844743Smarkm
40944743Smarkmstatic void sock_sink(fd)
41044743Smarkmint     fd;
41144743Smarkm{
41244743Smarkm    char    buf[BUFSIZ];
41356977Sshin#ifdef INET6
41456977Sshin    struct sockaddr_storage sin;
41556977Sshin#else
41644743Smarkm    struct sockaddr_in sin;
41756977Sshin#endif
41844743Smarkm    int     size = sizeof(sin);
41944743Smarkm
42044743Smarkm    /*
42144743Smarkm     * Eat up the not-yet received datagram. Some systems insist on a
42244743Smarkm     * non-zero source address argument in the recvfrom() call below.
42344743Smarkm     */
42444743Smarkm
42544743Smarkm    (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);
42644743Smarkm}
427