1/*
2 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6#pragma ident	"%Z%%M%	%I%	%E% SMI"
7
8 /*
9  * This module determines the type of socket (datagram, stream), the client
10  * socket address and port, the server socket address and port. In addition,
11  * it provides methods to map a transport address to a printable host name
12  * or address. Socket address information results are in static memory.
13  *
14  * The result from the hostname lookup method is STRING_PARANOID when a host
15  * pretends to have someone elses name, or when a host name is available but
16  * could not be verified.
17  *
18  * When lookup or conversion fails the result is set to STRING_UNKNOWN.
19  *
20  * Diagnostics are reported through syslog(3).
21  *
22  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
23  */
24
25#ifndef lint
26static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
27#endif
28
29/* System libraries. */
30
31#include <sys/types.h>
32#include <sys/param.h>
33#include <sys/socket.h>
34#include <netinet/in.h>
35#include <arpa/inet.h>
36#include <netdb.h>
37#include <stdio.h>
38#include <syslog.h>
39#include <string.h>
40
41extern char *inet_ntoa();
42
43/* Local stuff. */
44
45#include "tcpd.h"
46
47/* Forward declarations. */
48
49static 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
59static struct hostent *tcpd_gethostbyname_dot(name, af)
60char   *name;
61int af;
62{
63    char    dot_name[MAXHOSTNAMELEN + 1];
64
65    /*
66     * Don't append dots to unqualified names. Such names are likely to come
67     * from local hosts files or from NIS.
68     */
69
70    if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) {
71	return (tcpd_gethostbyname(name, af));
72    } else {
73	sprintf(dot_name, "%s.", name);
74	return (tcpd_gethostbyname(dot_name, af));
75    }
76}
77
78#define tcpd_gethostbyname tcpd_gethostbyname_dot
79#endif
80
81/* sock_host - look up endpoint addresses and install conversion methods */
82
83void    sock_host(request)
84struct request_info *request;
85{
86    static struct sockaddr_gen client;
87    static struct sockaddr_gen server;
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    sockgen_simplify(&client);
117    request->client->sin = &client;
118
119    /*
120     * Determine the server binding. This is used for client username
121     * lookups, and for access control rules that trigger on the server
122     * address or name.
123     */
124
125    len = sizeof(server);
126    if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) {
127	tcpd_warn("getsockname: %m");
128	return;
129    }
130    sockgen_simplify(&server);
131    request->server->sin = &server;
132}
133
134/* sock_hostaddr - map endpoint address to printable form */
135
136void    sock_hostaddr(host)
137struct host_info *host;
138{
139    struct sockaddr_gen *sin = host->sin;
140
141    if (sin != 0)
142#ifdef HAVE_IPV6
143
144	(void) inet_ntop(SGFAM(sin), SGADDRP(sin), host->addr, sizeof(host->addr));
145#else
146	STRN_CPY(host->addr, inet_ntoa(sin->sg_sin.sin_addr), sizeof(host->addr));
147#endif
148}
149
150/* sock_hostname - map endpoint address to host name */
151
152void    sock_hostname(host)
153struct host_info *host;
154{
155    struct sockaddr_gen *sin = host->sin;
156    struct hostent *hp;
157    int     i;
158    int     herr;
159
160    /*
161     * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
162     * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
163     * not work the other way around: gethostbyname("INADDR_ANY") fails. We
164     * have to special-case 0.0.0.0, in order to avoid false alerts from the
165     * host name/address checking code below.
166     */
167    if (sin != 0
168	&& !SG_IS_UNSPECIFIED(sin)
169	&& (hp = gethostbyaddr(SGADDRP(sin), SGADDRSZ(sin), SGFAM(sin))) != 0) {
170
171	STRN_CPY(host->name, hp->h_name, sizeof(host->name));
172
173	/*
174	 * Verify that the address is a member of the address list returned
175	 * by gethostbyname(hostname).
176	 *
177	 * Verify also that gethostbyaddr() and gethostbyname() return the same
178	 * hostname, or rshd and rlogind may still end up being spoofed.
179	 *
180	 * On some sites, gethostbyname("localhost") returns "localhost.domain".
181	 * This is a DNS artefact. We treat it as a special case. When we
182	 * can't believe the address list from gethostbyname("localhost")
183	 * we're in big trouble anyway.
184	 */
185
186	if ((hp = tcpd_gethostbyname(host->name, SGFAM(sin))) == 0) {
187
188	    /*
189	     * Unable to verify that the host name matches the address. This
190	     * may be a transient problem or a botched name server setup.
191	     */
192
193	    tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
194		      host->name);
195
196	} else if (STR_NE(host->name, hp->h_name)
197		   && STR_NE(host->name, "localhost")) {
198
199	    /*
200	     * The gethostbyaddr() and gethostbyname() calls did not return
201	     * the same hostname. This could be a nameserver configuration
202	     * problem. It could also be that someone is trying to spoof us.
203	     */
204
205	    tcpd_warn("host name/name mismatch: %s != %.*s",
206		      host->name, STRING_LENGTH, hp->h_name);
207
208	} else {
209#ifdef HAVE_IPV6
210	    char buf[INET6_ADDRSTRLEN];
211#endif
212
213	    /*
214	     * The address should be a member of the address list returned by
215	     * gethostbyname(). We should first verify that the h_addrtype
216	     * field is AF_INET, but this program has already caused too much
217	     * grief on systems with broken library code.
218	     */
219
220	    for (i = 0; hp->h_addr_list[i]; i++) {
221		if (memcmp(hp->h_addr_list[i],
222			   (char *) SGADDRP(sin),
223			   SGADDRSZ(sin)) == 0) {
224		    return;			/* name is good, keep it */
225		}
226	    }
227
228	    /*
229	     * The host name does not map to the initial address. Perhaps
230	     * someone has messed up. Perhaps someone compromised a name
231	     * server.
232	     */
233	    tcpd_warn("host name/address mismatch: %s != %.*s",
234#ifdef HAVE_IPV6
235		      inet_ntop(SGFAM(sin), SGADDRP(sin), buf, sizeof(buf)),
236#else
237		      inet_ntoa(sin->sg_sin.sin_addr),
238#endif
239		      STRING_LENGTH, hp->h_name);
240	}
241	strcpy(host->name, paranoid);		/* name is bad, clobber it */
242    }
243}
244
245/* sock_sink - absorb unreceived IP datagram */
246
247static void sock_sink(fd)
248int     fd;
249{
250    char    buf[BUFSIZ];
251    struct sockaddr_in sin;
252    int     size = sizeof(sin);
253
254    /*
255     * Eat up the not-yet received datagram. Some systems insist on a
256     * non-zero source address argument in the recvfrom() call below.
257     */
258
259    (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);
260}
261
262/*
263 * If we receive a V4 connection on a V6 socket, we pretend we really
264 * got a V4 connection.
265 */
266void sockgen_simplify(sg)
267sockaddr_gen *sg;
268{
269#ifdef HAVE_IPV6
270    if (sg->sg_family == AF_INET6 &&
271	IN6_IS_ADDR_V4MAPPED(&sg->sg_sin6.sin6_addr)) {
272	    struct sockaddr_in v4_addr;
273
274#ifdef IN6_V4MAPPED_TO_INADDR			/* Solaris 8 */
275	    IN6_V4MAPPED_TO_INADDR(&sg->sg_sin6.sin6_addr, &v4_addr.sin_addr);
276#elif defined(IN6_MAPPED_TO_V4) 		/* Solaris 8 Beta only? */
277	    IN6_MAPPED_TO_V4(&sg->sg_sin6.sin6_addr, &v4_addr.sin_addr);
278#else						/* Do it the hard way */
279	    memcpy(&v4_addr.sin_addr, ((char*) &sg->sg_sin6.sin6_addr) + 12, 4);
280#endif
281	    v4_addr.sin_port = sg->sg_sin6.sin6_port;
282	    v4_addr.sin_family = AF_INET;
283	    memcpy(&sg->sg_sin, &v4_addr, sizeof(v4_addr));
284    }
285#else
286    return;
287#endif /* HAVE_IPV6 */
288}
289