xref: /freebsd/contrib/tcp_wrappers/socket.c (revision 69c9999d0ca45b210e75706ab4952ad5a33ce6ec)
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 	/* reject numeric addresses */
221 	memset(&hints, 0, sizeof(hints));
222 	hints.ai_family = sin->sa_family;
223 	hints.ai_socktype = SOCK_STREAM;
224 	hints.ai_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST;
225 	if ((err = getaddrinfo(host->name, NULL, &hints, &res0)) == 0) {
226 	    freeaddrinfo(res0);
227 	    tcpd_warn("host name/name mismatch: "
228 		      "reverse lookup results in non-FQDN %s",
229 		      host->name);
230 	    strcpy(host->name, paranoid);	/* name is bad, clobber it */
231 	}
232 	err = !err;
233     }
234     if (!err) {
235 	/* we are now sure that this is non-numeric */
236 
237 	/*
238 	 * Verify that the address is a member of the address list returned
239 	 * by gethostbyname(hostname).
240 	 *
241 	 * Verify also that gethostbyaddr() and gethostbyname() return the same
242 	 * hostname, or rshd and rlogind may still end up being spoofed.
243 	 *
244 	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
245 	 * This is a DNS artefact. We treat it as a special case. When we
246 	 * can't believe the address list from gethostbyname("localhost")
247 	 * we're in big trouble anyway.
248 	 */
249 
250 	memset(&hints, 0, sizeof(hints));
251 	hints.ai_family = sin->sa_family;
252 	hints.ai_socktype = SOCK_STREAM;
253 	hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
254 	if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) {
255 
256 	    /*
257 	     * Unable to verify that the host name matches the address. This
258 	     * may be a transient problem or a botched name server setup.
259 	     */
260 
261 	    tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed",
262 		      host->name,
263 		      (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6");
264 
265 	} else if ((res0->ai_canonname == NULL
266 		    || STR_NE(host->name, res0->ai_canonname))
267 		   && STR_NE(host->name, "localhost")) {
268 
269 	    /*
270 	     * The gethostbyaddr() and gethostbyname() calls did not return
271 	     * the same hostname. This could be a nameserver configuration
272 	     * problem. It could also be that someone is trying to spoof us.
273 	     */
274 
275 	    tcpd_warn("host name/name mismatch: %s != %.*s",
276 		      host->name, STRING_LENGTH,
277 		      (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
278 
279 	} else {
280 
281 	    /*
282 	     * The address should be a member of the address list returned by
283 	     * gethostbyname(). We should first verify that the h_addrtype
284 	     * field is AF_INET, but this program has already caused too much
285 	     * grief on systems with broken library code.
286 	     */
287 
288 	    for (res = res0; res; res = res->ai_next) {
289 		if (res->ai_family != sin->sa_family)
290 		    continue;
291 		switch (res->ai_family) {
292 		case AF_INET:
293 		    rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
294 		    break;
295 		case AF_INET6:
296 		    /* need to check scope_id */
297 		    if (((struct sockaddr_in6 *)sin)->sin6_scope_id !=
298 		        ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) {
299 			continue;
300 		    }
301 		    rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
302 		    break;
303 		default:
304 		    continue;
305 		}
306 		if (memcmp(rap, ap, alen) == 0) {
307 		    freeaddrinfo(res0);
308 		    return;			/* name is good, keep it */
309 		}
310 	    }
311 
312 	    /*
313 	     * The host name does not map to the initial address. Perhaps
314 	     * someone has messed up. Perhaps someone compromised a name
315 	     * server.
316 	     */
317 
318 	    getnameinfo(sin, salen, hname, sizeof(hname),
319 			NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
320 	    tcpd_warn("host name/address mismatch: %s != %.*s",
321 		      hname, STRING_LENGTH,
322 		      (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
323 	}
324 	strcpy(host->name, paranoid);		/* name is bad, clobber it */
325 	if (res0)
326 	    freeaddrinfo(res0);
327     }
328 #else /* INET6 */
329     struct sockaddr_in *sin = host->sin;
330     struct hostent *hp;
331     int     i;
332 
333     /*
334      * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
335      * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
336      * not work the other way around: gethostbyname("INADDR_ANY") fails. We
337      * have to special-case 0.0.0.0, in order to avoid false alerts from the
338      * host name/address checking code below.
339      */
340     if (sin != 0 && sin->sin_addr.s_addr != 0
341 	&& (hp = gethostbyaddr((char *) &(sin->sin_addr),
342 			       sizeof(sin->sin_addr), AF_INET)) != 0) {
343 
344 	STRN_CPY(host->name, hp->h_name, sizeof(host->name));
345 
346 	/*
347 	 * Verify that the address is a member of the address list returned
348 	 * by gethostbyname(hostname).
349 	 *
350 	 * Verify also that gethostbyaddr() and gethostbyname() return the same
351 	 * hostname, or rshd and rlogind may still end up being spoofed.
352 	 *
353 	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
354 	 * This is a DNS artefact. We treat it as a special case. When we
355 	 * can't believe the address list from gethostbyname("localhost")
356 	 * we're in big trouble anyway.
357 	 */
358 
359 	if ((hp = gethostbyname(host->name)) == 0) {
360 
361 	    /*
362 	     * Unable to verify that the host name matches the address. This
363 	     * may be a transient problem or a botched name server setup.
364 	     */
365 
366 	    tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
367 		      host->name);
368 
369 	} else if (STR_NE(host->name, hp->h_name)
370 		   && STR_NE(host->name, "localhost")) {
371 
372 	    /*
373 	     * The gethostbyaddr() and gethostbyname() calls did not return
374 	     * the same hostname. This could be a nameserver configuration
375 	     * problem. It could also be that someone is trying to spoof us.
376 	     */
377 
378 	    tcpd_warn("host name/name mismatch: %s != %.*s",
379 		      host->name, STRING_LENGTH, hp->h_name);
380 
381 	} else {
382 
383 	    /*
384 	     * The address should be a member of the address list returned by
385 	     * gethostbyname(). We should first verify that the h_addrtype
386 	     * field is AF_INET, but this program has already caused too much
387 	     * grief on systems with broken library code.
388 	     */
389 
390 	    for (i = 0; hp->h_addr_list[i]; i++) {
391 		if (memcmp(hp->h_addr_list[i],
392 			   (char *) &sin->sin_addr,
393 			   sizeof(sin->sin_addr)) == 0)
394 		    return;			/* name is good, keep it */
395 	    }
396 
397 	    /*
398 	     * The host name does not map to the initial address. Perhaps
399 	     * someone has messed up. Perhaps someone compromised a name
400 	     * server.
401 	     */
402 
403 	    tcpd_warn("host name/address mismatch: %s != %.*s",
404 		      inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name);
405 	}
406 	strcpy(host->name, paranoid);		/* name is bad, clobber it */
407     }
408 #endif /* INET6 */
409 }
410 
411 /* sock_sink - absorb unreceived IP datagram */
412 
413 static void sock_sink(fd)
414 int     fd;
415 {
416     char    buf[BUFSIZ];
417 #ifdef INET6
418     struct sockaddr_storage sin;
419 #else
420     struct sockaddr_in sin;
421 #endif
422     int     size = sizeof(sin);
423 
424     /*
425      * Eat up the not-yet received datagram. Some systems insist on a
426      * non-zero source address argument in the recvfrom() call below.
427      */
428 
429     (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);
430 }
431