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