1 /*
2  * This module determines the type of socket (datagram, stream), the client
3  * socket address and port, the server socket address and port. In addition,
4  * it provides methods to map a transport address to a printable host name
5  * or address. Socket address information results are in static memory.
6  *
7  * The result from the hostname lookup method is STRING_PARANOID when a host
8  * pretends to have someone elses name, or when a host name is available but
9  * could not be verified.
10  *
11  * When lookup or conversion fails the result is set to STRING_UNKNOWN.
12  *
13  * Diagnostics are reported through syslog(3).
14  *
15  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
16  *
17  * $FreeBSD$
18  */
19
20#ifndef lint
21static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
22#endif
23
24/* System libraries. */
25
26#include <sys/types.h>
27#include <sys/param.h>
28#include <sys/socket.h>
29#include <netinet/in.h>
30#include <netdb.h>
31#include <stdio.h>
32#include <syslog.h>
33#include <string.h>
34
35#ifndef INET6
36extern char *inet_ntoa();
37#endif
38
39/* Local stuff. */
40
41#include "tcpd.h"
42
43/* Forward declarations. */
44
45static void sock_sink(int);
46
47#ifdef APPEND_DOT
48
49 /*
50  * Speed up DNS lookups by terminating the host name with a dot. Should be
51  * done with care. The speedup can give problems with lookups from sources
52  * that lack DNS-style trailing dot magic, such as local files or NIS maps.
53  */
54
55static struct hostent *gethostbyname_dot(char *name)
56{
57    char    dot_name[MAXHOSTNAMELEN + 1];
58
59    /*
60     * Don't append dots to unqualified names. Such names are likely to come
61     * from local hosts files or from NIS.
62     */
63
64    if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) {
65	return (gethostbyname(name));
66    } else {
67	sprintf(dot_name, "%s.", name);
68	return (gethostbyname(dot_name));
69    }
70}
71
72#define gethostbyname gethostbyname_dot
73#endif
74
75/* sock_host - look up endpoint addresses and install conversion methods */
76
77void    sock_host(struct request_info *request)
78{
79#ifdef INET6
80    static struct sockaddr_storage client;
81    static struct sockaddr_storage server;
82#else
83    static struct sockaddr_in client;
84    static struct sockaddr_in server;
85#endif
86    int     len;
87    char    buf[BUFSIZ];
88    int     fd = request->fd;
89
90    sock_methods(request);
91
92    /*
93     * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
94     * suggested how to get the client host info in case of UDP connections:
95     * peek at the first message without actually looking at its contents. We
96     * really should verify that client.sin_family gets the value AF_INET,
97     * but this program has already caused too much grief on systems with
98     * broken library code.
99     */
100
101    len = sizeof(client);
102    if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) {
103	request->sink = sock_sink;
104	len = sizeof(client);
105	if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK,
106		     (struct sockaddr *) & client, &len) < 0) {
107	    tcpd_warn("can't get client address: %m");
108	    return;				/* give up */
109	}
110#ifdef really_paranoid
111	memset(buf, 0, sizeof(buf));
112#endif
113    }
114#ifdef INET6
115    request->client->sin = (struct sockaddr *)&client;
116#else
117    request->client->sin = &client;
118#endif
119
120    /*
121     * Determine the server binding. This is used for client username
122     * lookups, and for access control rules that trigger on the server
123     * address or name.
124     */
125
126    len = sizeof(server);
127    if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) {
128	tcpd_warn("getsockname: %m");
129	return;
130    }
131#ifdef INET6
132    request->server->sin = (struct sockaddr *)&server;
133#else
134    request->server->sin = &server;
135#endif
136}
137
138/* sock_hostaddr - map endpoint address to printable form */
139
140void    sock_hostaddr(struct host_info *host)
141{
142#ifdef INET6
143    struct sockaddr *sin = host->sin;
144    int salen;
145
146    if (!sin)
147	return;
148#ifdef SIN6_LEN
149    salen = sin->sa_len;
150#else
151    salen = (sin->sa_family == AF_INET) ? sizeof(struct sockaddr_in)
152					: sizeof(struct sockaddr_in6);
153#endif
154    getnameinfo(sin, salen, host->addr, sizeof(host->addr),
155		NULL, 0, NI_NUMERICHOST);
156#else
157    struct sockaddr_in *sin = host->sin;
158
159    if (sin != 0)
160	STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr));
161#endif
162}
163
164/* sock_hostname - map endpoint address to host name */
165
166void    sock_hostname(struct host_info *host)
167{
168#ifdef INET6
169    struct sockaddr *sin = host->sin;
170    struct sockaddr_in sin4;
171    struct addrinfo hints, *res, *res0 = NULL;
172    int salen, alen, err = 1;
173    char *ap = NULL, *rap, hname[NI_MAXHOST];
174
175    if (sin != NULL) {
176	if (sin->sa_family == AF_INET6) {
177	    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin;
178
179	    if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
180		memset(&sin4, 0, sizeof(sin4));
181#ifdef SIN6_LEN
182		sin4.sin_len = sizeof(sin4);
183#endif
184		sin4.sin_family = AF_INET;
185		sin4.sin_port = sin6->sin6_port;
186		sin4.sin_addr.s_addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
187		sin = (struct sockaddr *)&sin4;
188	    }
189	}
190	switch (sin->sa_family) {
191	case AF_INET:
192	    ap = (char *)&((struct sockaddr_in *)sin)->sin_addr;
193	    alen = sizeof(struct in_addr);
194	    salen = sizeof(struct sockaddr_in);
195	    break;
196	case AF_INET6:
197	    ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr;
198	    alen = sizeof(struct in6_addr);
199	    salen = sizeof(struct sockaddr_in6);
200	    break;
201	default:
202	    break;
203	}
204	if (ap)
205	    err = getnameinfo(sin, salen, hname, sizeof(hname),
206			      NULL, 0, NI_NAMEREQD);
207    }
208    if (!err) {
209
210	STRN_CPY(host->name, hname, sizeof(host->name));
211
212	/* reject numeric addresses */
213	memset(&hints, 0, sizeof(hints));
214	hints.ai_family = sin->sa_family;
215	hints.ai_socktype = SOCK_STREAM;
216	hints.ai_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST;
217	if ((err = getaddrinfo(host->name, NULL, &hints, &res0)) == 0) {
218	    freeaddrinfo(res0);
219	    tcpd_warn("host name/name mismatch: "
220		      "reverse lookup results in non-FQDN %s",
221		      host->name);
222	    strcpy(host->name, paranoid);	/* name is bad, clobber it */
223	}
224	err = !err;
225    }
226    if (!err) {
227	/* we are now sure that this is non-numeric */
228
229	/*
230	 * Verify that the address is a member of the address list returned
231	 * by gethostbyname(hostname).
232	 *
233	 * Verify also that gethostbyaddr() and gethostbyname() return the same
234	 * hostname, or rshd and rlogind may still end up being spoofed.
235	 *
236	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
237	 * This is a DNS artefact. We treat it as a special case. When we
238	 * can't believe the address list from gethostbyname("localhost")
239	 * we're in big trouble anyway.
240	 */
241
242	memset(&hints, 0, sizeof(hints));
243	hints.ai_family = sin->sa_family;
244	hints.ai_socktype = SOCK_STREAM;
245	hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
246	if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) {
247
248	    /*
249	     * Unable to verify that the host name matches the address. This
250	     * may be a transient problem or a botched name server setup.
251	     */
252
253	    tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed",
254		      host->name,
255		      (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6");
256
257	} else if ((res0->ai_canonname == NULL
258		    || STR_NE(host->name, res0->ai_canonname))
259		   && STR_NE(host->name, "localhost")) {
260
261	    /*
262	     * The gethostbyaddr() and gethostbyname() calls did not return
263	     * the same hostname. This could be a nameserver configuration
264	     * problem. It could also be that someone is trying to spoof us.
265	     */
266
267	    tcpd_warn("host name/name mismatch: %s != %.*s",
268		      host->name, STRING_LENGTH,
269		      (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
270
271	} else {
272
273	    /*
274	     * The address should be a member of the address list returned by
275	     * gethostbyname(). We should first verify that the h_addrtype
276	     * field is AF_INET, but this program has already caused too much
277	     * grief on systems with broken library code.
278	     */
279
280	    for (res = res0; res; res = res->ai_next) {
281		if (res->ai_family != sin->sa_family)
282		    continue;
283		switch (res->ai_family) {
284		case AF_INET:
285		    rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
286		    break;
287		case AF_INET6:
288		    /* need to check scope_id */
289		    if (((struct sockaddr_in6 *)sin)->sin6_scope_id !=
290		        ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) {
291			continue;
292		    }
293		    rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
294		    break;
295		default:
296		    continue;
297		}
298		if (memcmp(rap, ap, alen) == 0) {
299		    freeaddrinfo(res0);
300		    return;			/* name is good, keep it */
301		}
302	    }
303
304	    /*
305	     * The host name does not map to the initial address. Perhaps
306	     * someone has messed up. Perhaps someone compromised a name
307	     * server.
308	     */
309
310	    getnameinfo(sin, salen, hname, sizeof(hname),
311			NULL, 0, NI_NUMERICHOST);
312	    tcpd_warn("host name/address mismatch: %s != %.*s",
313		      hname, STRING_LENGTH,
314		      (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
315	}
316	strcpy(host->name, paranoid);		/* name is bad, clobber it */
317	if (res0)
318	    freeaddrinfo(res0);
319    }
320#else /* INET6 */
321    struct sockaddr_in *sin = host->sin;
322    struct hostent *hp;
323    int     i;
324
325    /*
326     * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
327     * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
328     * not work the other way around: gethostbyname("INADDR_ANY") fails. We
329     * have to special-case 0.0.0.0, in order to avoid false alerts from the
330     * host name/address checking code below.
331     */
332    if (sin != 0 && sin->sin_addr.s_addr != 0
333	&& (hp = gethostbyaddr((char *) &(sin->sin_addr),
334			       sizeof(sin->sin_addr), AF_INET)) != 0) {
335
336	STRN_CPY(host->name, hp->h_name, sizeof(host->name));
337
338	/*
339	 * Verify that the address is a member of the address list returned
340	 * by gethostbyname(hostname).
341	 *
342	 * Verify also that gethostbyaddr() and gethostbyname() return the same
343	 * hostname, or rshd and rlogind may still end up being spoofed.
344	 *
345	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
346	 * This is a DNS artefact. We treat it as a special case. When we
347	 * can't believe the address list from gethostbyname("localhost")
348	 * we're in big trouble anyway.
349	 */
350
351	if ((hp = gethostbyname(host->name)) == 0) {
352
353	    /*
354	     * Unable to verify that the host name matches the address. This
355	     * may be a transient problem or a botched name server setup.
356	     */
357
358	    tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
359		      host->name);
360
361	} else if (STR_NE(host->name, hp->h_name)
362		   && STR_NE(host->name, "localhost")) {
363
364	    /*
365	     * The gethostbyaddr() and gethostbyname() calls did not return
366	     * the same hostname. This could be a nameserver configuration
367	     * problem. It could also be that someone is trying to spoof us.
368	     */
369
370	    tcpd_warn("host name/name mismatch: %s != %.*s",
371		      host->name, STRING_LENGTH, hp->h_name);
372
373	} else {
374
375	    /*
376	     * The address should be a member of the address list returned by
377	     * gethostbyname(). We should first verify that the h_addrtype
378	     * field is AF_INET, but this program has already caused too much
379	     * grief on systems with broken library code.
380	     */
381
382	    for (i = 0; hp->h_addr_list[i]; i++) {
383		if (memcmp(hp->h_addr_list[i],
384			   (char *) &sin->sin_addr,
385			   sizeof(sin->sin_addr)) == 0)
386		    return;			/* name is good, keep it */
387	    }
388
389	    /*
390	     * The host name does not map to the initial address. Perhaps
391	     * someone has messed up. Perhaps someone compromised a name
392	     * server.
393	     */
394
395	    tcpd_warn("host name/address mismatch: %s != %.*s",
396		      inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name);
397	}
398	strcpy(host->name, paranoid);		/* name is bad, clobber it */
399    }
400#endif /* INET6 */
401}
402
403/* sock_sink - absorb unreceived IP datagram */
404
405static void sock_sink(int fd)
406{
407    char    buf[BUFSIZ];
408#ifdef INET6
409    struct sockaddr_storage sin;
410#else
411    struct sockaddr_in sin;
412#endif
413    int     size = sizeof(sin);
414
415    /*
416     * Eat up the not-yet received datagram. Some systems insist on a
417     * non-zero source address argument in the recvfrom() call below.
418     */
419
420    (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);
421}
422