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