xref: /titanic_51/usr/src/lib/libwrap/socket.c (revision 31e37bb439502e3f7c4c0a9a77d655ea5d56887a)
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
26  static 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  
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 *tcpd_gethostbyname_dot(name, af)
60  char   *name;
61  int 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  
83  void    sock_host(request)
84  struct 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  
136  void    sock_hostaddr(host)
137  struct 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  
152  void    sock_hostname(host)
153  struct 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  
247  static void sock_sink(fd)
248  int     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   */
266  void sockgen_simplify(sg)
267  sockaddr_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