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