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