xref: /freebsd/contrib/tcp_wrappers/tli.c (revision 5956d97f4b3204318ceb6aa9c77bd0bc6ea87a41)
1  /*
2   * tli_host() determines the type of transport (connected, connectionless),
3   * the transport address of a client host, and the transport address of a
4   * server endpoint. In addition, it provides methods to map a transport
5   * address to a printable host name or address. Socket address results are
6   * in static memory; tli structures are allocated from the heap.
7   *
8   * The result from the hostname lookup method is STRING_PARANOID when a host
9   * pretends to have someone elses name, or when a host name is available but
10   * could not be verified.
11   *
12   * Diagnostics are reported through syslog(3).
13   *
14   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
15   *
16   * $FreeBSD$
17   */
18 
19 #ifndef lint
20 static char sccsid[] = "@(#) tli.c 1.15 97/03/21 19:27:25";
21 #endif
22 
23 #ifdef TLI
24 
25 /* System libraries. */
26 
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/stream.h>
30 #include <sys/stat.h>
31 #include <sys/mkdev.h>
32 #include <sys/tiuser.h>
33 #include <sys/timod.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <stdio.h>
37 #include <syslog.h>
38 #include <errno.h>
39 #include <netconfig.h>
40 #include <netdir.h>
41 #include <string.h>
42 
43 extern char *nc_sperror();
44 extern char *sys_errlist[];
45 extern int sys_nerr;
46 extern int t_errno;
47 extern char *t_errlist[];
48 extern int t_nerr;
49 
50 /* Local stuff. */
51 
52 #include "tcpd.h"
53 
54 /* Forward declarations. */
55 
56 static void tli_endpoints();
57 static struct netconfig *tli_transport();
58 static void tli_hostname();
59 static void tli_hostaddr();
60 static void tli_cleanup();
61 static char *tli_error();
62 static void tli_sink();
63 
64 /* tli_host - look up endpoint addresses and install conversion methods */
65 
66 void    tli_host(request)
67 struct request_info *request;
68 {
69 #ifdef INET6
70     static struct sockaddr_storage client;
71     static struct sockaddr_storage server;
72 #else
73     static struct sockaddr_in client;
74     static struct sockaddr_in server;
75 #endif
76 
77     /*
78      * If we discover that we are using an IP transport, pretend we never
79      * were here. Otherwise, use the transport-independent method and stick
80      * to generic network addresses. XXX hard-coded protocol family name.
81      */
82 
83     tli_endpoints(request);
84 #ifdef INET6
85     if ((request->config = tli_transport(request->fd)) != 0
86 	&& (STR_EQ(request->config->nc_protofmly, "inet") ||
87 	    STR_EQ(request->config->nc_protofmly, "inet6"))) {
88 #else
89     if ((request->config = tli_transport(request->fd)) != 0
90         && STR_EQ(request->config->nc_protofmly, "inet")) {
91 #endif
92 	if (request->client->unit != 0) {
93 #ifdef INET6
94 	    client = *(struct sockaddr_storage *) request->client->unit->addr.buf;
95 	    request->client->sin = (struct sockaddr *) &client;
96 #else
97 	    client = *(struct sockaddr_in *) request->client->unit->addr.buf;
98 	    request->client->sin = &client;
99 #endif
100 	}
101 	if (request->server->unit != 0) {
102 #ifdef INET6
103             server = *(struct sockaddr_storage *) request->server->unit->addr.buf;
104             request->server->sin = (struct sockaddr *) &server;
105 #else
106             server = *(struct sockaddr_in *) request->server->unit->addr.buf;
107             request->server->sin = &server;
108 #endif
109 	}
110 	tli_cleanup(request);
111 	sock_methods(request);
112     } else {
113 	request->hostname = tli_hostname;
114 	request->hostaddr = tli_hostaddr;
115 	request->cleanup = tli_cleanup;
116     }
117 }
118 
119 /* tli_cleanup - cleanup some dynamically-allocated data structures */
120 
121 static void tli_cleanup(request)
122 struct request_info *request;
123 {
124     if (request->config != 0)
125 	freenetconfigent(request->config);
126     if (request->client->unit != 0)
127 	t_free((char *) request->client->unit, T_UNITDATA);
128     if (request->server->unit != 0)
129 	t_free((char *) request->server->unit, T_UNITDATA);
130 }
131 
132 /* tli_endpoints - determine TLI client and server endpoint information */
133 
134 static void tli_endpoints(request)
135 struct request_info *request;
136 {
137     struct t_unitdata *server;
138     struct t_unitdata *client;
139     int     fd = request->fd;
140     int     flags;
141 
142     /*
143      * Determine the client endpoint address. With unconnected services, peek
144      * at the sender address of the pending protocol data unit without
145      * popping it off the receive queue. This trick works because only the
146      * address member of the unitdata structure has been allocated.
147      *
148      * Beware of successful returns with zero-length netbufs (for example,
149      * Solaris 2.3 with ticlts transport). The netdir(3) routines can't
150      * handle that. Assume connection-less transport when TI_GETPEERNAME
151      * produces no usable result, even when t_rcvudata() is unable to figure
152      * out the peer address. Better to hang than to loop.
153      */
154 
155     if ((client = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ADDR)) == 0) {
156 	tcpd_warn("t_alloc: %s", tli_error());
157 	return;
158     }
159     if (ioctl(fd, TI_GETPEERNAME, &client->addr) < 0 || client->addr.len == 0) {
160 	request->sink = tli_sink;
161 	if (t_rcvudata(fd, client, &flags) < 0 || client->addr.len == 0) {
162 	    tcpd_warn("can't get client address: %s", tli_error());
163 	    t_free((void *) client, T_UNITDATA);
164 	    return;
165 	}
166     }
167     request->client->unit = client;
168 
169     /*
170      * Look up the server endpoint address. This can be used for filtering on
171      * server address or name, or to look up the client user.
172      */
173 
174     if ((server = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ADDR)) == 0) {
175 	tcpd_warn("t_alloc: %s", tli_error());
176 	return;
177     }
178     if (ioctl(fd, TI_GETMYNAME, &server->addr) < 0) {
179 	tcpd_warn("TI_GETMYNAME: %m");
180 	t_free((void *) server, T_UNITDATA);
181 	return;
182     }
183     request->server->unit = server;
184 }
185 
186 /* tli_transport - find out TLI transport type */
187 
188 static struct netconfig *tli_transport(fd)
189 int     fd;
190 {
191     struct stat from_client;
192     struct stat from_config;
193     void   *handlep;
194     struct netconfig *config;
195 
196     /*
197      * Assuming that the network device is a clone device, we must compare
198      * the major device number of stdin to the minor device number of the
199      * devices listed in the netconfig table.
200      */
201 
202     if (fstat(fd, &from_client) != 0) {
203 	tcpd_warn("fstat(fd %d): %m", fd);
204 	return (0);
205     }
206     if ((handlep = setnetconfig()) == 0) {
207 	tcpd_warn("setnetconfig: %m");
208 	return (0);
209     }
210     while (config = getnetconfig(handlep)) {
211 	if (stat(config->nc_device, &from_config) == 0) {
212 #ifdef NO_CLONE_DEVICE
213 	/*
214 	 * If the network devices are not cloned (as is the case for
215 	 * Solaris 8 Beta), we must compare the major device numbers.
216 	 */
217 	    if (major(from_config.st_rdev) == major(from_client.st_rdev))
218 #else
219 	    if (minor(from_config.st_rdev) == major(from_client.st_rdev))
220 #endif
221 		break;
222 	}
223     }
224     if (config == 0) {
225 	tcpd_warn("unable to identify transport protocol");
226 	return (0);
227     }
228 
229     /*
230      * Something else may clobber our getnetconfig() result, so we'd better
231      * acquire our private copy.
232      */
233 
234     if ((config = getnetconfigent(config->nc_netid)) == 0) {
235 	tcpd_warn("getnetconfigent(%s): %s", config->nc_netid, nc_sperror());
236 	return (0);
237     }
238     return (config);
239 }
240 
241 /* tli_hostaddr - map TLI transport address to printable address */
242 
243 static void tli_hostaddr(host)
244 struct host_info *host;
245 {
246     struct request_info *request = host->request;
247     struct netconfig *config = request->config;
248     struct t_unitdata *unit = host->unit;
249     char   *uaddr;
250 
251     if (config != 0 && unit != 0
252 	&& (uaddr = taddr2uaddr(config, &unit->addr)) != 0) {
253 	STRN_CPY(host->addr, uaddr, sizeof(host->addr));
254 	free(uaddr);
255     }
256 }
257 
258 /* tli_hostname - map TLI transport address to hostname */
259 
260 static void tli_hostname(host)
261 struct host_info *host;
262 {
263     struct request_info *request = host->request;
264     struct netconfig *config = request->config;
265     struct t_unitdata *unit = host->unit;
266     struct nd_hostservlist *servlist;
267 
268     if (config != 0 && unit != 0
269 	&& netdir_getbyaddr(config, &servlist, &unit->addr) == ND_OK) {
270 
271 	struct nd_hostserv *service = servlist->h_hostservs;
272 	struct nd_addrlist *addr_list;
273 	int     found = 0;
274 
275 	if (netdir_getbyname(config, service, &addr_list) != ND_OK) {
276 
277 	    /*
278 	     * Unable to verify that the name matches the address. This may
279 	     * be a transient problem or a botched name server setup. We
280 	     * decide to play safe.
281 	     */
282 
283 	    tcpd_warn("can't verify hostname: netdir_getbyname(%.*s) failed",
284 		      STRING_LENGTH, service->h_host);
285 
286 	} else {
287 
288 	    /*
289 	     * Look up the host address in the address list we just got. The
290 	     * comparison is done on the textual representation, because the
291 	     * transport address is an opaque structure that may have holes
292 	     * with uninitialized garbage. This approach obviously loses when
293 	     * the address does not have a textual representation.
294 	     */
295 
296 	    char   *uaddr = eval_hostaddr(host);
297 	    char   *ua;
298 	    int     i;
299 
300 	    for (i = 0; found == 0 && i < addr_list->n_cnt; i++) {
301 		if ((ua = taddr2uaddr(config, &(addr_list->n_addrs[i]))) != 0) {
302 		    found = !strcmp(ua, uaddr);
303 		    free(ua);
304 		}
305 	    }
306 	    netdir_free((void *) addr_list, ND_ADDRLIST);
307 
308 	    /*
309 	     * When the host name does not map to the initial address, assume
310 	     * someone has compromised a name server. More likely someone
311 	     * botched it, but that could be dangerous, too.
312 	     */
313 
314 	    if (found == 0)
315 		tcpd_warn("host name/address mismatch: %s != %.*s",
316 			  host->addr, STRING_LENGTH, service->h_host);
317 	}
318 	STRN_CPY(host->name, found ? service->h_host : paranoid,
319 		 sizeof(host->name));
320 	netdir_free((void *) servlist, ND_HOSTSERVLIST);
321     }
322 }
323 
324 /* tli_error - convert tli error number to text */
325 
326 static char *tli_error()
327 {
328     static char buf[40];
329 
330     if (t_errno != TSYSERR) {
331 	if (t_errno < 0 || t_errno >= t_nerr) {
332 	    sprintf(buf, "Unknown TLI error %d", t_errno);
333 	    return (buf);
334 	} else {
335 	    return (t_errlist[t_errno]);
336 	}
337     } else {
338 	if (errno < 0 || errno >= sys_nerr) {
339 	    sprintf(buf, "Unknown UNIX error %d", errno);
340 	    return (buf);
341 	} else {
342 	    return (sys_errlist[errno]);
343 	}
344     }
345 }
346 
347 /* tli_sink - absorb unreceived datagram */
348 
349 static void tli_sink(fd)
350 int     fd;
351 {
352     struct t_unitdata *unit;
353     int     flags;
354 
355     /*
356      * Something went wrong. Absorb the datagram to keep inetd from looping.
357      * Allocate storage for address, control and data. If that fails, sleep
358      * for a couple of seconds in an attempt to keep inetd from looping too
359      * fast.
360      */
361 
362     if ((unit = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ALL)) == 0) {
363 	tcpd_warn("t_alloc: %s", tli_error());
364 	sleep(5);
365     } else {
366 	(void) t_rcvudata(fd, unit, &flags);
367 	t_free((void *) unit, T_UNITDATA);
368     }
369 }
370 
371 #endif /* TLI */
372