de-errno
[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.3 2005/04/29 01:00:27 joerg 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 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 */