xref: /netbsd-src/lib/libwrap/socket.c (revision e1a2f47f1264dd9755e9e747d45ba016dff2334f)
1*e1a2f47fSmatt /*	$NetBSD: socket.c,v 1.19 2012/03/21 10:10:37 matt Exp $	*/
2d6ddaab4Schristos 
3541be36cSmrg  /*
4541be36cSmrg   * This module determines the type of socket (datagram, stream), the client
5541be36cSmrg   * socket address and port, the server socket address and port. In addition,
6541be36cSmrg   * it provides methods to map a transport address to a printable host name
7541be36cSmrg   * or address. Socket address information results are in static memory.
8541be36cSmrg   *
9541be36cSmrg   * The result from the hostname lookup method is STRING_PARANOID when a host
10541be36cSmrg   * pretends to have someone elses name, or when a host name is available but
11541be36cSmrg   * could not be verified.
12541be36cSmrg   *
13541be36cSmrg   * When lookup or conversion fails the result is set to STRING_UNKNOWN.
14541be36cSmrg   *
15541be36cSmrg   * Diagnostics are reported through syslog(3).
16541be36cSmrg   *
17541be36cSmrg   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
18541be36cSmrg   */
19541be36cSmrg 
20d6ddaab4Schristos #include <sys/cdefs.h>
21541be36cSmrg #ifndef lint
22d6ddaab4Schristos #if 0
23b98c2633Sitojun static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
24d6ddaab4Schristos #else
25*e1a2f47fSmatt __RCSID("$NetBSD: socket.c,v 1.19 2012/03/21 10:10:37 matt Exp $");
26d6ddaab4Schristos #endif
27541be36cSmrg #endif
28541be36cSmrg 
29541be36cSmrg /* System libraries. */
30541be36cSmrg 
31541be36cSmrg #include <sys/types.h>
32541be36cSmrg #include <sys/param.h>
33541be36cSmrg #include <sys/socket.h>
34541be36cSmrg #include <netinet/in.h>
35541be36cSmrg #include <netdb.h>
36541be36cSmrg #include <stdio.h>
37541be36cSmrg #include <syslog.h>
38541be36cSmrg #include <string.h>
39d6ddaab4Schristos #include <arpa/inet.h>
40541be36cSmrg 
41541be36cSmrg /* Local stuff. */
42541be36cSmrg 
43541be36cSmrg #include "tcpd.h"
44541be36cSmrg 
45541be36cSmrg /* Forward declarations. */
46541be36cSmrg 
4744772e4bSitojun #ifdef APPEND_DOT
4844772e4bSitojun static const char *append_dot __P((const char *));
4944772e4bSitojun #endif
50d6ddaab4Schristos static void sock_sink __P((int));
51541be36cSmrg 
5244772e4bSitojun #ifdef APPEND_DOT
5344772e4bSitojun  /*
5444772e4bSitojun   * Speed up DNS lookups by terminating the host name with a dot. Should be
5544772e4bSitojun   * done with care. The speedup can give problems with lookups from sources
5644772e4bSitojun   * that lack DNS-style trailing dot magic, such as local files or NIS maps.
5744772e4bSitojun   */
5844772e4bSitojun 
5944772e4bSitojun static const char *
append_dot(name)6044772e4bSitojun append_dot(name)
61d2503f64Sitojun const char *name;
6244772e4bSitojun {
6344772e4bSitojun     static char hbuf[MAXHOSTNAMELEN + 1];
6444772e4bSitojun 
6544772e4bSitojun     /*
6644772e4bSitojun      * Don't append dots to unqualified names. Such names are likely to come
6744772e4bSitojun      * from local hosts files or from NIS.
6844772e4bSitojun      */
6944772e4bSitojun 
7044772e4bSitojun     if (strchr(name, '.') == 0 || strlen(name) + 2 > sizeof(hbuf))
7144772e4bSitojun 	strlcpy(hbuf, name, sizeof(hbuf));
7244772e4bSitojun     else
7344772e4bSitojun 	(void)snprintf(hbuf, sizeof(hbuf), "%s.", name);
7444772e4bSitojun     return hbuf;
7544772e4bSitojun }
7644772e4bSitojun #endif
7744772e4bSitojun 
78541be36cSmrg /* sock_host - look up endpoint addresses and install conversion methods */
79541be36cSmrg 
80*e1a2f47fSmatt void
sock_host(struct request_info * request)81*e1a2f47fSmatt sock_host(struct request_info *request)
82541be36cSmrg {
831e8c736aSitojun     static struct sockaddr_storage client;
841e8c736aSitojun     static struct sockaddr_storage server;
850c37c63eSmrg     socklen_t     len;
86541be36cSmrg     char    buf[BUFSIZ];
87541be36cSmrg     int     fd = request->fd;
88541be36cSmrg 
89541be36cSmrg     sock_methods(request);
90541be36cSmrg 
91541be36cSmrg     /*
92541be36cSmrg      * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
93541be36cSmrg      * suggested how to get the client host info in case of UDP connections:
94541be36cSmrg      * peek at the first message without actually looking at its contents. We
95541be36cSmrg      * really should verify that client.sin_family gets the value AF_INET,
96541be36cSmrg      * but this program has already caused too much grief on systems with
97541be36cSmrg      * broken library code.
981e8c736aSitojun      *
991e8c736aSitojun      * XXX the last sentence is untrue as we support AF_INET6 as well :-)
100541be36cSmrg      */
101541be36cSmrg 
10240e1466eSthorpej     if (request->client->sin == NULL) {
103541be36cSmrg         len = sizeof(client);
1040c37c63eSmrg         if (getpeername(fd, (struct sockaddr *)(void *)& client, &len) < 0) {
105541be36cSmrg 	    request->sink = sock_sink;
106541be36cSmrg 	    len = sizeof(client);
107541be36cSmrg 	    if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK,
108541be36cSmrg 		         (struct sockaddr *) & client, &len) < 0) {
109541be36cSmrg 	        tcpd_warn("can't get client address: %m");
110541be36cSmrg 	        return;				/* give up */
111541be36cSmrg 	    }
112541be36cSmrg #ifdef really_paranoid
113c4557301Smjl 	    memset(buf, 0, sizeof(buf));
114541be36cSmrg #endif
115541be36cSmrg         }
1161e8c736aSitojun         request->client->sin = (struct sockaddr *)&client;
11740e1466eSthorpej     }
118541be36cSmrg 
119541be36cSmrg     /*
120541be36cSmrg      * Determine the server binding. This is used for client username
121541be36cSmrg      * lookups, and for access control rules that trigger on the server
122541be36cSmrg      * address or name.
123541be36cSmrg      */
124541be36cSmrg 
12540e1466eSthorpej     if (request->server->sin == NULL) {
126541be36cSmrg         len = sizeof(server);
127541be36cSmrg         if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) {
128541be36cSmrg 	    tcpd_warn("getsockname: %m");
129541be36cSmrg 	    return;
130541be36cSmrg         }
1311e8c736aSitojun         request->server->sin = (struct sockaddr *)&server;
132541be36cSmrg     }
13340e1466eSthorpej }
134541be36cSmrg 
135541be36cSmrg /* sock_hostaddr - map endpoint address to printable form */
136541be36cSmrg 
137*e1a2f47fSmatt void
sock_hostaddr(struct host_info * host)138*e1a2f47fSmatt sock_hostaddr(struct host_info *host)
139541be36cSmrg {
1401e8c736aSitojun     struct sockaddr *sa = host->sin;
141541be36cSmrg 
1421e8c736aSitojun     if (!sa)
1431e8c736aSitojun 	return;
1441e8c736aSitojun     host->addr[0] = '\0';
1452f7d82e6Sitojun     getnameinfo(sa, sa->sa_len, host->addr, sizeof(host->addr),
1462f7d82e6Sitojun 	NULL, 0, NI_NUMERICHOST);
147541be36cSmrg }
148541be36cSmrg 
149541be36cSmrg /* sock_hostname - map endpoint address to host name */
150541be36cSmrg 
151*e1a2f47fSmatt void
sock_hostname(struct host_info * host)152*e1a2f47fSmatt sock_hostname(struct host_info *host)
153541be36cSmrg {
1542f7d82e6Sitojun     struct sockaddr *sa = host->sin;
1552f7d82e6Sitojun     char h1[NI_MAXHOST], h2[NI_MAXHOST];
1562f7d82e6Sitojun     struct addrinfo hints, *res, *res0;
1571e8c736aSitojun #ifdef INET6
1582f7d82e6Sitojun     struct sockaddr_in tmp;
1591e8c736aSitojun #endif
1602f7d82e6Sitojun 
1612f7d82e6Sitojun     if (!sa)
1622f7d82e6Sitojun 	return;
1632f7d82e6Sitojun #ifdef INET6
1642f7d82e6Sitojun     /* special case on reverse lookup: mapped addr.  I hate it */
1652f7d82e6Sitojun     if (sa->sa_family == AF_INET6 &&
1662f7d82e6Sitojun         IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr)) {
1672f7d82e6Sitojun 	memset(&tmp, 0, sizeof(tmp));
1682f7d82e6Sitojun 	tmp.sin_family = AF_INET;
1692f7d82e6Sitojun 	tmp.sin_len = sizeof(struct sockaddr_in);
1702f7d82e6Sitojun 	memcpy(&tmp.sin_addr,
1712f7d82e6Sitojun 	    &((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12], 4);
1722f7d82e6Sitojun 	sa = (struct sockaddr *)&tmp;
1732f7d82e6Sitojun     }
1742f7d82e6Sitojun #endif
1752f7d82e6Sitojun     if (getnameinfo(sa, sa->sa_len, h1, sizeof(h1), NULL, 0,
1762f7d82e6Sitojun         NI_NUMERICHOST) != 0) {
1771e8c736aSitojun 	return;
1781e8c736aSitojun     }
1792f7d82e6Sitojun     if (getnameinfo(sa, sa->sa_len, host->name, sizeof(host->name), NULL, 0,
1802f7d82e6Sitojun         NI_NAMEREQD) == 0) {
181541be36cSmrg 	/*
18274a87116Sitojun 	 * if reverse lookup result looks like a numeric hostname,
18374a87116Sitojun 	 * someone is trying to trick us by PTR record like following:
18474a87116Sitojun 	 *	1.1.1.10.in-addr.arpa.  IN PTR  2.3.4.5
18574a87116Sitojun 	 */
18674a87116Sitojun 	memset(&hints, 0, sizeof(hints));
18774a87116Sitojun 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
18874a87116Sitojun 	hints.ai_flags = AI_NUMERICHOST;
18974a87116Sitojun #ifdef APPEND_DOT
19074a87116Sitojun 	if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) == 0)
19174a87116Sitojun #else
19274a87116Sitojun 	if (getaddrinfo(host->name, "0", &hints, &res0) == 0)
19374a87116Sitojun #endif
19474a87116Sitojun 	{
19574a87116Sitojun 	    tcpd_warn("Nasty PTR record is configured");
19674a87116Sitojun 	    freeaddrinfo(res0);
19774a87116Sitojun 	    /* name is bad, clobber it */
19874a87116Sitojun 	    (void)strlcpy(host->name, paranoid, sizeof(host->name));
19974a87116Sitojun 	    return;
20074a87116Sitojun 	}
20174a87116Sitojun 
20274a87116Sitojun 	/*
203541be36cSmrg 	 * Verify that the address is a member of the address list returned
2042f7d82e6Sitojun 	 * by getaddrinfo(hostname).
205541be36cSmrg 	 *
2062f7d82e6Sitojun 	 * Verify also that getnameinfo() and getaddrinfo() return the same
207541be36cSmrg 	 * hostname, or rshd and rlogind may still end up being spoofed.
208541be36cSmrg 	 *
2092f7d82e6Sitojun 	 * On some sites, getaddrinfo("localhost") returns "localhost.domain".
210541be36cSmrg 	 * This is a DNS artefact. We treat it as a special case. When we
2112f7d82e6Sitojun 	 * can't believe the address list from getaddrinfo("localhost")
212541be36cSmrg 	 * we're in big trouble anyway.
213541be36cSmrg 	 */
2142f7d82e6Sitojun 	memset(&hints, 0, sizeof(hints));
2152f7d82e6Sitojun 	hints.ai_family = sa->sa_family;
2162f7d82e6Sitojun 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
2172f7d82e6Sitojun 	hints.ai_flags = AI_CANONNAME;
21844772e4bSitojun #ifdef APPEND_DOT
21944772e4bSitojun 	if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) != 0)
22044772e4bSitojun #else
22144772e4bSitojun 	if (getaddrinfo(host->name, "0", &hints, &res0) != 0)
22244772e4bSitojun #endif
22344772e4bSitojun 	{
224541be36cSmrg 	    /*
225541be36cSmrg 	     * Unable to verify that the host name matches the address. This
226541be36cSmrg 	     * may be a transient problem or a botched name server setup.
227541be36cSmrg 	     */
228541be36cSmrg 
2292f7d82e6Sitojun 	    tcpd_warn("can't verify hostname: getaddrinfo(%s, %d) failed",
2302f7d82e6Sitojun 	        host->name, hints.ai_family);
2312f7d82e6Sitojun 	} else if (res0->ai_canonname &&
2322f7d82e6Sitojun 	    STR_NE(host->name, res0->ai_canonname) &&
2332f7d82e6Sitojun 	    STR_NE(host->name, "localhost")) {
234541be36cSmrg 	    /*
2352f7d82e6Sitojun 	     * The getnameinfo() and getaddrinfo() calls did not return
236541be36cSmrg 	     * the same hostname. This could be a nameserver configuration
237541be36cSmrg 	     * problem. It could also be that someone is trying to spoof us.
238541be36cSmrg 	     */
239541be36cSmrg 
2402f7d82e6Sitojun 	    tcpd_warn("host name/name mismatch: %s != %s",
2412f7d82e6Sitojun 		host->name, res0->ai_canonname);
2422f7d82e6Sitojun 	    freeaddrinfo(res0);
243541be36cSmrg 	} else {
244541be36cSmrg 	    /*
245541be36cSmrg 	     * The address should be a member of the address list returned by
2462f7d82e6Sitojun 	     * getaddrinfo().
247541be36cSmrg 	     */
248541be36cSmrg 
2492f7d82e6Sitojun 	    for (res = res0; res; res = res->ai_next) {
2502f7d82e6Sitojun 		if (getnameinfo(res->ai_addr, res->ai_addrlen, h2, sizeof(h2),
2512f7d82e6Sitojun 		    NULL, 0, NI_NUMERICHOST) != 0) {
2522f7d82e6Sitojun 		    continue;
2532f7d82e6Sitojun 		}
2542f7d82e6Sitojun 		if (STR_EQ(h1, h2)) {
2552f7d82e6Sitojun 		    freeaddrinfo(res0);
2562f7d82e6Sitojun 		    return;
2572f7d82e6Sitojun 		}
258541be36cSmrg 	    }
259541be36cSmrg 
260541be36cSmrg 	    /*
261541be36cSmrg 	     * The host name does not map to the initial address. Perhaps
262541be36cSmrg 	     * someone has messed up. Perhaps someone compromised a name
263541be36cSmrg 	     * server.
264541be36cSmrg 	     */
265541be36cSmrg 
2662f7d82e6Sitojun 	    tcpd_warn("host name/address mismatch: %s != %s", h1,
2672f7d82e6Sitojun 		res0->ai_canonname ? res0->ai_canonname : "?");
2682f7d82e6Sitojun 
2692f7d82e6Sitojun 	    freeaddrinfo(res0);
270541be36cSmrg 	}
2719cd5492cSmrg 	/* name is bad, clobber it */
2723ba1803eSitojun 	(void)strlcpy(host->name, paranoid, sizeof(host->name));
273541be36cSmrg     }
274541be36cSmrg }
275541be36cSmrg 
276541be36cSmrg /* sock_sink - absorb unreceived IP datagram */
277541be36cSmrg 
278*e1a2f47fSmatt static void
sock_sink(int fd)279*e1a2f47fSmatt sock_sink(int fd)
280541be36cSmrg {
281541be36cSmrg     char    buf[BUFSIZ];
2823ba1803eSitojun     struct sockaddr_storage ss;
2830c37c63eSmrg     socklen_t size = sizeof(ss);
284541be36cSmrg 
285541be36cSmrg     /*
286541be36cSmrg      * Eat up the not-yet received datagram. Some systems insist on a
287541be36cSmrg      * non-zero source address argument in the recvfrom() call below.
288541be36cSmrg      */
289541be36cSmrg 
2903ba1803eSitojun     (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & ss, &size);
291541be36cSmrg }
292