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