socket.c revision 63158
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: head/contrib/tcp_wrappers/socket.c 63158 2000-07-14 17:15:34Z 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
3556977Sshin#ifdef INET6
3663158Sume#ifndef NI_WITHSCOPEID
3763158Sume#define NI_WITHSCOPEID	0
3856977Sshin#endif
3963158Sume#else
4063158Sumeextern char *inet_ntoa();
4156977Sshin#endif
4256977Sshin
4344743Smarkm/* Local stuff. */
4444743Smarkm
4544743Smarkm#include "tcpd.h"
4644743Smarkm
4744743Smarkm/* Forward declarations. */
4844743Smarkm
4944743Smarkmstatic void sock_sink();
5044743Smarkm
5144743Smarkm#ifdef APPEND_DOT
5244743Smarkm
5344743Smarkm /*
5444743Smarkm  * Speed up DNS lookups by terminating the host name with a dot. Should be
5544743Smarkm  * done with care. The speedup can give problems with lookups from sources
5644743Smarkm  * that lack DNS-style trailing dot magic, such as local files or NIS maps.
5744743Smarkm  */
5844743Smarkm
5944743Smarkmstatic struct hostent *gethostbyname_dot(name)
6044743Smarkmchar   *name;
6144743Smarkm{
6244743Smarkm    char    dot_name[MAXHOSTNAMELEN + 1];
6344743Smarkm
6444743Smarkm    /*
6544743Smarkm     * Don't append dots to unqualified names. Such names are likely to come
6644743Smarkm     * from local hosts files or from NIS.
6744743Smarkm     */
6844743Smarkm
6944743Smarkm    if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) {
7044743Smarkm	return (gethostbyname(name));
7144743Smarkm    } else {
7244743Smarkm	sprintf(dot_name, "%s.", name);
7344743Smarkm	return (gethostbyname(dot_name));
7444743Smarkm    }
7544743Smarkm}
7644743Smarkm
7744743Smarkm#define gethostbyname gethostbyname_dot
7844743Smarkm#endif
7944743Smarkm
8044743Smarkm/* sock_host - look up endpoint addresses and install conversion methods */
8144743Smarkm
8244743Smarkmvoid    sock_host(request)
8344743Smarkmstruct request_info *request;
8444743Smarkm{
8556977Sshin#ifdef INET6
8656977Sshin    static struct sockaddr_storage client;
8756977Sshin    static struct sockaddr_storage server;
8856977Sshin#else
8944743Smarkm    static struct sockaddr_in client;
9044743Smarkm    static struct sockaddr_in server;
9156977Sshin#endif
9244743Smarkm    int     len;
9344743Smarkm    char    buf[BUFSIZ];
9444743Smarkm    int     fd = request->fd;
9544743Smarkm
9644743Smarkm    sock_methods(request);
9744743Smarkm
9844743Smarkm    /*
9944743Smarkm     * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
10044743Smarkm     * suggested how to get the client host info in case of UDP connections:
10144743Smarkm     * peek at the first message without actually looking at its contents. We
10244743Smarkm     * really should verify that client.sin_family gets the value AF_INET,
10344743Smarkm     * but this program has already caused too much grief on systems with
10444743Smarkm     * broken library code.
10544743Smarkm     */
10644743Smarkm
10744743Smarkm    len = sizeof(client);
10844743Smarkm    if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) {
10944743Smarkm	request->sink = sock_sink;
11044743Smarkm	len = sizeof(client);
11144743Smarkm	if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK,
11244743Smarkm		     (struct sockaddr *) & client, &len) < 0) {
11344743Smarkm	    tcpd_warn("can't get client address: %m");
11444743Smarkm	    return;				/* give up */
11544743Smarkm	}
11644743Smarkm#ifdef really_paranoid
11744743Smarkm	memset(buf, 0 sizeof(buf));
11844743Smarkm#endif
11944743Smarkm    }
12056977Sshin#ifdef INET6
12156977Sshin    request->client->sin = (struct sockaddr *)&client;
12256977Sshin#else
12344743Smarkm    request->client->sin = &client;
12456977Sshin#endif
12544743Smarkm
12644743Smarkm    /*
12744743Smarkm     * Determine the server binding. This is used for client username
12844743Smarkm     * lookups, and for access control rules that trigger on the server
12944743Smarkm     * address or name.
13044743Smarkm     */
13144743Smarkm
13244743Smarkm    len = sizeof(server);
13344743Smarkm    if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) {
13444743Smarkm	tcpd_warn("getsockname: %m");
13544743Smarkm	return;
13644743Smarkm    }
13756977Sshin#ifdef INET6
13856977Sshin    request->server->sin = (struct sockaddr *)&server;
13956977Sshin#else
14044743Smarkm    request->server->sin = &server;
14156977Sshin#endif
14244743Smarkm}
14344743Smarkm
14444743Smarkm/* sock_hostaddr - map endpoint address to printable form */
14544743Smarkm
14644743Smarkmvoid    sock_hostaddr(host)
14744743Smarkmstruct host_info *host;
14844743Smarkm{
14956977Sshin#ifdef INET6
15056977Sshin    struct sockaddr *sin = host->sin;
15163158Sume    int salen;
15256977Sshin
15356977Sshin    if (!sin)
15456977Sshin	return;
15563158Sume#ifdef SIN6_LEN
15663158Sume    salen = sin->sa_len;
15756977Sshin#else
15863158Sume    salen = (sin->sa_family == AF_INET) ? sizeof(struct sockaddr_in)
15963158Sume					: sizeof(struct sockaddr_in6);
16063158Sume#endif
16163158Sume    getnameinfo(sin, salen, host->addr, sizeof(host->addr),
16263158Sume		NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
16363158Sume#else
16444743Smarkm    struct sockaddr_in *sin = host->sin;
16544743Smarkm
16644743Smarkm    if (sin != 0)
16744743Smarkm	STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr));
16856977Sshin#endif
16944743Smarkm}
17044743Smarkm
17144743Smarkm/* sock_hostname - map endpoint address to host name */
17244743Smarkm
17344743Smarkmvoid    sock_hostname(host)
17444743Smarkmstruct host_info *host;
17544743Smarkm{
17656977Sshin#ifdef INET6
17756977Sshin    struct sockaddr *sin = host->sin;
17863158Sume    struct sockaddr_in sin4;
17963158Sume    struct addrinfo hints, *res, *res0 = NULL;
18063158Sume    int salen, alen, err = 1;
18163158Sume    char *ap = NULL, *rap, hname[NI_MAXHOST];
18263158Sume
18363158Sume    if (sin != NULL) {
18463158Sume	if (sin->sa_family == AF_INET6) {
18563158Sume	    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin;
18663158Sume
18763158Sume	    if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
18863158Sume		memset(&sin4, 0, sizeof(sin4));
18963158Sume#ifdef SIN6_LEN
19063158Sume		sin4.sin_len = sizeof(sin4);
19156977Sshin#endif
19263158Sume		sin4.sin_family = AF_INET;
19363158Sume		sin4.sin_port = sin6->sin6_port;
19463158Sume		sin4.sin_addr.s_addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
19563158Sume		sin = (struct sockaddr *)&sin4;
19663158Sume	    }
19763158Sume	}
19863158Sume	switch (sin->sa_family) {
19963158Sume	case AF_INET:
20063158Sume	    ap = (char *)&((struct sockaddr_in *)sin)->sin_addr;
20163158Sume	    alen = sizeof(struct in_addr);
20263158Sume	    salen = sizeof(struct sockaddr_in);
20363158Sume	    break;
20463158Sume	case AF_INET6:
20563158Sume	    ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr;
20663158Sume	    alen = sizeof(struct in6_addr);
20763158Sume	    salen = sizeof(struct sockaddr_in6);
20863158Sume	    break;
20963158Sume	default:
21063158Sume	    break;
21163158Sume	}
21263158Sume	if (ap)
21363158Sume	    err = getnameinfo(sin, salen, hname, sizeof(hname),
21463158Sume			      NULL, 0, NI_WITHSCOPEID | NI_NAMEREQD);
21563158Sume    }
21663158Sume    if (!err) {
21763158Sume
21863158Sume	STRN_CPY(host->name, hname, sizeof(host->name));
21963158Sume
22063158Sume	/*
22163158Sume	 * Verify that the address is a member of the address list returned
22263158Sume	 * by gethostbyname(hostname).
22363158Sume	 *
22463158Sume	 * Verify also that gethostbyaddr() and gethostbyname() return the same
22563158Sume	 * hostname, or rshd and rlogind may still end up being spoofed.
22663158Sume	 *
22763158Sume	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
22863158Sume	 * This is a DNS artefact. We treat it as a special case. When we
22963158Sume	 * can't believe the address list from gethostbyname("localhost")
23063158Sume	 * we're in big trouble anyway.
23163158Sume	 */
23263158Sume
23363158Sume	memset(&hints, 0, sizeof(hints));
23463158Sume	hints.ai_family = sin->sa_family;
23563158Sume	hints.ai_socktype = SOCK_STREAM;
23663158Sume	hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
23763158Sume	if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) {
23863158Sume
23963158Sume	    /*
24063158Sume	     * Unable to verify that the host name matches the address. This
24163158Sume	     * may be a transient problem or a botched name server setup.
24263158Sume	     */
24363158Sume
24463158Sume	    tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed",
24563158Sume		      host->name,
24663158Sume		      (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6");
24763158Sume
24863158Sume	} else if (STR_NE(host->name, res0->ai_canonname)
24963158Sume		   && STR_NE(host->name, "localhost")) {
25063158Sume
25163158Sume	    /*
25263158Sume	     * The gethostbyaddr() and gethostbyname() calls did not return
25363158Sume	     * the same hostname. This could be a nameserver configuration
25463158Sume	     * problem. It could also be that someone is trying to spoof us.
25563158Sume	     */
25663158Sume
25763158Sume	    tcpd_warn("host name/name mismatch: %s != %.*s",
25863158Sume		      host->name, STRING_LENGTH, res0->ai_canonname);
25963158Sume
26063158Sume	} else {
26163158Sume
26263158Sume	    /*
26363158Sume	     * The address should be a member of the address list returned by
26463158Sume	     * gethostbyname(). We should first verify that the h_addrtype
26563158Sume	     * field is AF_INET, but this program has already caused too much
26663158Sume	     * grief on systems with broken library code.
26763158Sume	     */
26863158Sume
26963158Sume	    for (res = res0; res; res = res->ai_next) {
27063158Sume		if (res->ai_family != sin->sa_family)
27163158Sume		    continue;
27263158Sume		switch (res->ai_family) {
27363158Sume		case AF_INET:
27463158Sume		    rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
27563158Sume		    break;
27663158Sume		case AF_INET6:
27763158Sume		    rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
27863158Sume		    break;
27963158Sume		default:
28063158Sume		    continue;
28163158Sume		}
28263158Sume		if (memcmp(rap, ap, alen) == 0) {
28363158Sume		    freeaddrinfo(res0);
28463158Sume		    return;			/* name is good, keep it */
28563158Sume		}
28663158Sume	    }
28763158Sume
28863158Sume	    /*
28963158Sume	     * The host name does not map to the initial address. Perhaps
29063158Sume	     * someone has messed up. Perhaps someone compromised a name
29163158Sume	     * server.
29263158Sume	     */
29363158Sume
29463158Sume	    getnameinfo(sin, salen, hname, sizeof(hname),
29563158Sume			NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
29663158Sume	    tcpd_warn("host name/address mismatch: %s != %.*s",
29763158Sume		      hname, STRING_LENGTH, res0->ai_canonname);
29863158Sume	}
29963158Sume	strcpy(host->name, paranoid);		/* name is bad, clobber it */
30063158Sume	if (res0)
30163158Sume	    freeaddrinfo(res0);
30263158Sume    }
30363158Sume#else /* INET6 */
30444743Smarkm    struct sockaddr_in *sin = host->sin;
30544743Smarkm    struct hostent *hp;
30644743Smarkm    int     i;
30744743Smarkm
30844743Smarkm    /*
30944743Smarkm     * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
31044743Smarkm     * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
31144743Smarkm     * not work the other way around: gethostbyname("INADDR_ANY") fails. We
31244743Smarkm     * have to special-case 0.0.0.0, in order to avoid false alerts from the
31344743Smarkm     * host name/address checking code below.
31444743Smarkm     */
31544743Smarkm    if (sin != 0 && sin->sin_addr.s_addr != 0
31644743Smarkm	&& (hp = gethostbyaddr((char *) &(sin->sin_addr),
31744743Smarkm			       sizeof(sin->sin_addr), AF_INET)) != 0) {
31844743Smarkm
31944743Smarkm	STRN_CPY(host->name, hp->h_name, sizeof(host->name));
32044743Smarkm
32144743Smarkm	/*
32244743Smarkm	 * Verify that the address is a member of the address list returned
32344743Smarkm	 * by gethostbyname(hostname).
32444743Smarkm	 *
32544743Smarkm	 * Verify also that gethostbyaddr() and gethostbyname() return the same
32644743Smarkm	 * hostname, or rshd and rlogind may still end up being spoofed.
32744743Smarkm	 *
32844743Smarkm	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
32944743Smarkm	 * This is a DNS artefact. We treat it as a special case. When we
33044743Smarkm	 * can't believe the address list from gethostbyname("localhost")
33144743Smarkm	 * we're in big trouble anyway.
33244743Smarkm	 */
33344743Smarkm
33444743Smarkm	if ((hp = gethostbyname(host->name)) == 0) {
33544743Smarkm
33644743Smarkm	    /*
33744743Smarkm	     * Unable to verify that the host name matches the address. This
33844743Smarkm	     * may be a transient problem or a botched name server setup.
33944743Smarkm	     */
34044743Smarkm
34144743Smarkm	    tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
34244743Smarkm		      host->name);
34344743Smarkm
34444743Smarkm	} else if (STR_NE(host->name, hp->h_name)
34544743Smarkm		   && STR_NE(host->name, "localhost")) {
34644743Smarkm
34744743Smarkm	    /*
34844743Smarkm	     * The gethostbyaddr() and gethostbyname() calls did not return
34944743Smarkm	     * the same hostname. This could be a nameserver configuration
35044743Smarkm	     * problem. It could also be that someone is trying to spoof us.
35144743Smarkm	     */
35244743Smarkm
35344743Smarkm	    tcpd_warn("host name/name mismatch: %s != %.*s",
35444743Smarkm		      host->name, STRING_LENGTH, hp->h_name);
35544743Smarkm
35644743Smarkm	} else {
35744743Smarkm
35844743Smarkm	    /*
35944743Smarkm	     * The address should be a member of the address list returned by
36044743Smarkm	     * gethostbyname(). We should first verify that the h_addrtype
36144743Smarkm	     * field is AF_INET, but this program has already caused too much
36244743Smarkm	     * grief on systems with broken library code.
36344743Smarkm	     */
36444743Smarkm
36544743Smarkm	    for (i = 0; hp->h_addr_list[i]; i++) {
36644743Smarkm		if (memcmp(hp->h_addr_list[i],
36744743Smarkm			   (char *) &sin->sin_addr,
36844743Smarkm			   sizeof(sin->sin_addr)) == 0)
36944743Smarkm		    return;			/* name is good, keep it */
37044743Smarkm	    }
37144743Smarkm
37244743Smarkm	    /*
37344743Smarkm	     * The host name does not map to the initial address. Perhaps
37444743Smarkm	     * someone has messed up. Perhaps someone compromised a name
37544743Smarkm	     * server.
37644743Smarkm	     */
37744743Smarkm
37844743Smarkm	    tcpd_warn("host name/address mismatch: %s != %.*s",
37944743Smarkm		      inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name);
38044743Smarkm	}
38144743Smarkm	strcpy(host->name, paranoid);		/* name is bad, clobber it */
38244743Smarkm    }
38363158Sume#endif /* INET6 */
38444743Smarkm}
38544743Smarkm
38644743Smarkm/* sock_sink - absorb unreceived IP datagram */
38744743Smarkm
38844743Smarkmstatic void sock_sink(fd)
38944743Smarkmint     fd;
39044743Smarkm{
39144743Smarkm    char    buf[BUFSIZ];
39256977Sshin#ifdef INET6
39356977Sshin    struct sockaddr_storage sin;
39456977Sshin#else
39544743Smarkm    struct sockaddr_in sin;
39656977Sshin#endif
39744743Smarkm    int     size = sizeof(sin);
39844743Smarkm
39944743Smarkm    /*
40044743Smarkm     * Eat up the not-yet received datagram. Some systems insist on a
40144743Smarkm     * non-zero source address argument in the recvfrom() call below.
40244743Smarkm     */
40344743Smarkm
40444743Smarkm    (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);
40544743Smarkm}
406