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