xref: /freebsd/contrib/tcp_wrappers/tli.c (revision 38f0b757fd84d17d0fc24739a7cda160c4516d81)
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 int errno;
45 extern char *sys_errlist[];
46 extern int sys_nerr;
47 extern int t_errno;
48 extern char *t_errlist[];
49 extern int t_nerr;
50 
51 /* Local stuff. */
52 
53 #include "tcpd.h"
54 
55 /* Forward declarations. */
56 
57 static void tli_endpoints();
58 static struct netconfig *tli_transport();
59 static void tli_hostname();
60 static void tli_hostaddr();
61 static void tli_cleanup();
62 static char *tli_error();
63 static void tli_sink();
64 
65 /* tli_host - look up endpoint addresses and install conversion methods */
66 
67 void    tli_host(request)
68 struct 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 
122 static void tli_cleanup(request)
123 struct 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 
135 static void tli_endpoints(request)
136 struct 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 
189 static struct netconfig *tli_transport(fd)
190 int     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 
244 static void tli_hostaddr(host)
245 struct 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 
261 static void tli_hostname(host)
262 struct 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 
327 static 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 
350 static void tli_sink(fd)
351 int     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