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