xref: /freebsd/contrib/tcp_wrappers/socket.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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 USE_GETIPNODEBY
37 #include <resolv.h>
38 #endif
39 #endif
40 
41 extern char *inet_ntoa();
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     char *ap;
152     int alen;
153 
154     if (!sin)
155 	return;
156     switch (sin->sa_family) {
157     case AF_INET:
158 	ap = (char *)&((struct sockaddr_in *)sin)->sin_addr;
159 	alen = sizeof(struct in_addr);
160 	break;
161     case AF_INET6:
162 	ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr;
163 	alen = sizeof(struct in6_addr);
164 	break;
165     default:
166 	return;
167     }
168     host->addr[0] = '\0';
169     inet_ntop(sin->sa_family, ap, host->addr, sizeof(host->addr));
170 #else
171     struct sockaddr_in *sin = host->sin;
172 
173     if (sin != 0)
174 	STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr));
175 #endif
176 }
177 
178 /* sock_hostname - map endpoint address to host name */
179 
180 void    sock_hostname(host)
181 struct host_info *host;
182 {
183 #ifdef INET6
184     struct sockaddr *sin = host->sin;
185     char addr[128];
186 #ifdef USE_GETIPNODEBY
187     int h_error;
188 #else
189     u_long res_options;
190 #endif
191     struct hostent *hp = NULL;
192     char *ap;
193     int alen;
194 #else
195     struct sockaddr_in *sin = host->sin;
196     struct hostent *hp;
197 #endif
198     int     i;
199 
200     /*
201      * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
202      * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
203      * not work the other way around: gethostbyname("INADDR_ANY") fails. We
204      * have to special-case 0.0.0.0, in order to avoid false alerts from the
205      * host name/address checking code below.
206      */
207 #ifdef INET6
208     if (sin != NULL) {
209 	switch (sin->sa_family) {
210 	case AF_INET:
211 	    if (((struct sockaddr_in *)sin)->sin_addr.s_addr == 0) {
212 		strcpy(host->name, paranoid);	/* name is bad, clobber it */
213 		return;
214 	    }
215 	    ap = (char *) &((struct sockaddr_in *)sin)->sin_addr;
216 	    alen = sizeof(struct in_addr);
217 	    break;
218 	case AF_INET6:
219 	    ap = (char *) &((struct sockaddr_in6 *)sin)->sin6_addr;
220 	    alen = sizeof(struct in6_addr);
221 	    break;
222 	defalut:
223 	    strcpy(host->name, paranoid);	/* name is bad, clobber it */
224 	    return;
225 	}
226 #ifdef USE_GETIPNODEBY
227 	hp = getipnodebyaddr(ap, alen, sin->sa_family, &h_error);
228 #else
229 	hp = gethostbyaddr(ap, alen, sin->sa_family);
230 #endif
231     }
232     if (hp) {
233 #else
234     if (sin != 0 && sin->sin_addr.s_addr != 0
235 	&& (hp = gethostbyaddr((char *) &(sin->sin_addr),
236 			       sizeof(sin->sin_addr), AF_INET)) != 0) {
237 #endif
238 
239 	STRN_CPY(host->name, hp->h_name, sizeof(host->name));
240 #if defined(INET6) && defined(USE_GETIPNODEBY)
241 	freehostent(hp);
242 #endif
243 
244 	/*
245 	 * Verify that the address is a member of the address list returned
246 	 * by gethostbyname(hostname).
247 	 *
248 	 * Verify also that gethostbyaddr() and gethostbyname() return the same
249 	 * hostname, or rshd and rlogind may still end up being spoofed.
250 	 *
251 	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
252 	 * This is a DNS artefact. We treat it as a special case. When we
253 	 * can't believe the address list from gethostbyname("localhost")
254 	 * we're in big trouble anyway.
255 	 */
256 
257 #ifdef INET6
258 #ifdef USE_GETIPNODEBY
259 	hp = getipnodebyname(host->name, sin->sa_family,
260 			     AI_V4MAPPED | AI_ADDRCONFIG | AI_ALL, &h_error);
261 #else
262 	if ((_res.options & RES_INIT) == 0) {
263 	    if (res_init() < 0) {
264 		inet_ntop(sin->sa_family, ap, addr, sizeof(addr));
265 		tcpd_warn("can't verify hostname: res_init() for %s failed",
266 			  addr);
267 		strcpy(host->name, paranoid);	/* name is bad, clobber it */
268 		return;
269 	    }
270 	}
271 	res_options = _res.options;
272 	if (sin->sa_family == AF_INET6)
273 	    _res.options |= RES_USE_INET6;
274 	else
275 	    _res.options &= ~RES_USE_INET6;
276 	hp = gethostbyname2(host->name,
277 			    (sin->sa_family == AF_INET6 &&
278 			     IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sin)->sin6_addr)) ?
279 				AF_INET : sin->sa_family);
280 	_res.options = res_options;
281 #endif
282 	if (!hp) {
283 #else
284 	if ((hp = gethostbyname(host->name)) == 0) {
285 #endif
286 
287 	    /*
288 	     * Unable to verify that the host name matches the address. This
289 	     * may be a transient problem or a botched name server setup.
290 	     */
291 
292 #ifdef INET6
293 #ifdef USE_GETIPNODEBY
294 	    tcpd_warn("can't verify hostname: getipnodebyname(%s, %s) failed",
295 #else
296 	    tcpd_warn("can't verify hostname: gethostbyname2(%s, %s) failed",
297 #endif
298 		      host->name,
299 		      (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6");
300 #else
301 	    tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
302 		      host->name);
303 #endif
304 
305 	} else if (STR_NE(host->name, hp->h_name)
306 		   && STR_NE(host->name, "localhost")) {
307 
308 	    /*
309 	     * The gethostbyaddr() and gethostbyname() calls did not return
310 	     * the same hostname. This could be a nameserver configuration
311 	     * problem. It could also be that someone is trying to spoof us.
312 	     */
313 
314 	    tcpd_warn("host name/name mismatch: %s != %.*s",
315 		      host->name, STRING_LENGTH, hp->h_name);
316 
317 	} else {
318 
319 	    /*
320 	     * The address should be a member of the address list returned by
321 	     * gethostbyname(). We should first verify that the h_addrtype
322 	     * field is AF_INET, but this program has already caused too much
323 	     * grief on systems with broken library code.
324 	     */
325 
326 	    for (i = 0; hp->h_addr_list[i]; i++) {
327 #ifdef INET6
328 		if (memcmp(hp->h_addr_list[i], ap, alen) == 0) {
329 #ifdef USE_GETIPNODEBY
330 		    freehostent(hp);
331 #endif
332 		    return;			/* name is good, keep it */
333 		}
334 #else
335 		if (memcmp(hp->h_addr_list[i],
336 			   (char *) &sin->sin_addr,
337 			   sizeof(sin->sin_addr)) == 0)
338 		    return;			/* name is good, keep it */
339 #endif
340 	    }
341 
342 	    /*
343 	     * The host name does not map to the initial address. Perhaps
344 	     * someone has messed up. Perhaps someone compromised a name
345 	     * server.
346 	     */
347 
348 #ifdef INET6
349 	    inet_ntop(sin->sa_family, ap, addr, sizeof(addr));
350 	    tcpd_warn("host name/address mismatch: %s != %.*s",
351 		      addr, STRING_LENGTH, hp->h_name);
352 #else
353 	    tcpd_warn("host name/address mismatch: %s != %.*s",
354 		      inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name);
355 #endif
356 	}
357 	strcpy(host->name, paranoid);		/* name is bad, clobber it */
358 #if defined(INET6) && defined(USE_GETIPNODEBY)
359 	if (hp)
360 	    freehostent(hp);
361 #endif
362     }
363 }
364 
365 /* sock_sink - absorb unreceived IP datagram */
366 
367 static void sock_sink(fd)
368 int     fd;
369 {
370     char    buf[BUFSIZ];
371 #ifdef INET6
372     struct sockaddr_storage sin;
373 #else
374     struct sockaddr_in sin;
375 #endif
376     int     size = sizeof(sin);
377 
378     /*
379      * Eat up the not-yet received datagram. Some systems insist on a
380      * non-zero source address argument in the recvfrom() call below.
381      */
382 
383     (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);
384 }
385