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