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