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: head/contrib/tcp_wrappers/tli.c 56977 2000-02-03 10:27:03Z shin $
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 */
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: head/contrib/tcp_wrappers/tli.c 56977 2000-02-03 10:27:03Z shin $
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 */