144743Smarkm /*
244743Smarkm  * tli_host() determines the type of transport (connected, connectionless),
344743Smarkm  * the transport address of a client host, and the transport address of a
444743Smarkm  * server endpoint. In addition, it provides methods to map a transport
544743Smarkm  * address to a printable host name or address. Socket address results are
644743Smarkm  * in static memory; tli structures are allocated from the heap.
744743Smarkm  *
844743Smarkm  * The result from the hostname lookup method is STRING_PARANOID when a host
944743Smarkm  * pretends to have someone elses name, or when a host name is available but
1044743Smarkm  * could not be verified.
1144743Smarkm  *
1244743Smarkm  * Diagnostics are reported through syslog(3).
1344743Smarkm  *
1444743Smarkm  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
1556977Sshin  *
1656977Sshin  * $FreeBSD$
1744743Smarkm  */
1844743Smarkm
1944743Smarkm#ifndef lint
2044743Smarkmstatic char sccsid[] = "@(#) tli.c 1.15 97/03/21 19:27:25";
2144743Smarkm#endif
2244743Smarkm
2344743Smarkm#ifdef TLI
2444743Smarkm
2544743Smarkm/* System libraries. */
2644743Smarkm
2744743Smarkm#include <sys/types.h>
2844743Smarkm#include <sys/param.h>
2944743Smarkm#include <sys/stream.h>
3044743Smarkm#include <sys/stat.h>
3144743Smarkm#include <sys/mkdev.h>
3244743Smarkm#include <sys/tiuser.h>
3344743Smarkm#include <sys/timod.h>
3444743Smarkm#include <sys/socket.h>
3544743Smarkm#include <netinet/in.h>
3644743Smarkm#include <stdio.h>
3744743Smarkm#include <syslog.h>
3844743Smarkm#include <errno.h>
3944743Smarkm#include <netconfig.h>
4044743Smarkm#include <netdir.h>
4144743Smarkm#include <string.h>
4244743Smarkm
4344743Smarkmextern char *nc_sperror();
4444743Smarkmextern int errno;
4544743Smarkmextern char *sys_errlist[];
4644743Smarkmextern int sys_nerr;
4744743Smarkmextern int t_errno;
4844743Smarkmextern char *t_errlist[];
4944743Smarkmextern int t_nerr;
5044743Smarkm
5144743Smarkm/* Local stuff. */
5244743Smarkm
5344743Smarkm#include "tcpd.h"
5444743Smarkm
5544743Smarkm/* Forward declarations. */
5644743Smarkm
5744743Smarkmstatic void tli_endpoints();
5844743Smarkmstatic struct netconfig *tli_transport();
5944743Smarkmstatic void tli_hostname();
6044743Smarkmstatic void tli_hostaddr();
6144743Smarkmstatic void tli_cleanup();
6244743Smarkmstatic char *tli_error();
6344743Smarkmstatic void tli_sink();
6444743Smarkm
6544743Smarkm/* tli_host - look up endpoint addresses and install conversion methods */
6644743Smarkm
6744743Smarkmvoid    tli_host(request)
6844743Smarkmstruct request_info *request;
6944743Smarkm{
7056977Sshin#ifdef INET6
7156977Sshin    static struct sockaddr_storage client;
7256977Sshin    static struct sockaddr_storage server;
7356977Sshin#else
7444743Smarkm    static struct sockaddr_in client;
7544743Smarkm    static struct sockaddr_in server;
7656977Sshin#endif
7744743Smarkm
7844743Smarkm    /*
7944743Smarkm     * If we discover that we are using an IP transport, pretend we never
8044743Smarkm     * were here. Otherwise, use the transport-independent method and stick
8144743Smarkm     * to generic network addresses. XXX hard-coded protocol family name.
8244743Smarkm     */
8344743Smarkm
8444743Smarkm    tli_endpoints(request);
8556977Sshin#ifdef INET6
8644743Smarkm    if ((request->config = tli_transport(request->fd)) != 0
8756977Sshin	&& (STR_EQ(request->config->nc_protofmly, "inet") ||
8856977Sshin	    STR_EQ(request->config->nc_protofmly, "inet6"))) {
8956977Sshin#else
9056977Sshin    if ((request->config = tli_transport(request->fd)) != 0
9156977Sshin        && STR_EQ(request->config->nc_protofmly, "inet")) {
9256977Sshin#endif
9344743Smarkm	if (request->client->unit != 0) {
9456977Sshin#ifdef INET6
9556977Sshin	    client = *(struct sockaddr_storage *) request->client->unit->addr.buf;
9656977Sshin	    request->client->sin = (struct sockaddr *) &client;
9756977Sshin#else
9844743Smarkm	    client = *(struct sockaddr_in *) request->client->unit->addr.buf;
9944743Smarkm	    request->client->sin = &client;
10056977Sshin#endif
10144743Smarkm	}
10244743Smarkm	if (request->server->unit != 0) {
10356977Sshin#ifdef INET6
10456977Sshin            server = *(struct sockaddr_storage *) request->server->unit->addr.buf;
10556977Sshin            request->server->sin = (struct sockaddr *) &server;
10656977Sshin#else
10756977Sshin            server = *(struct sockaddr_in *) request->server->unit->addr.buf;
10856977Sshin            request->server->sin = &server;
10956977Sshin#endif
11044743Smarkm	}
11144743Smarkm	tli_cleanup(request);
11244743Smarkm	sock_methods(request);
11344743Smarkm    } else {
11444743Smarkm	request->hostname = tli_hostname;
11544743Smarkm	request->hostaddr = tli_hostaddr;
11644743Smarkm	request->cleanup = tli_cleanup;
11744743Smarkm    }
11844743Smarkm}
11944743Smarkm
12044743Smarkm/* tli_cleanup - cleanup some dynamically-allocated data structures */
12144743Smarkm
12244743Smarkmstatic void tli_cleanup(request)
12344743Smarkmstruct request_info *request;
12444743Smarkm{
12544743Smarkm    if (request->config != 0)
12644743Smarkm	freenetconfigent(request->config);
12744743Smarkm    if (request->client->unit != 0)
12844743Smarkm	t_free((char *) request->client->unit, T_UNITDATA);
12944743Smarkm    if (request->server->unit != 0)
13044743Smarkm	t_free((char *) request->server->unit, T_UNITDATA);
13144743Smarkm}
13244743Smarkm
13344743Smarkm/* tli_endpoints - determine TLI client and server endpoint information */
13444743Smarkm
13544743Smarkmstatic void tli_endpoints(request)
13644743Smarkmstruct request_info *request;
13744743Smarkm{
13844743Smarkm    struct t_unitdata *server;
13944743Smarkm    struct t_unitdata *client;
14044743Smarkm    int     fd = request->fd;
14144743Smarkm    int     flags;
14244743Smarkm
14344743Smarkm    /*
14444743Smarkm     * Determine the client endpoint address. With unconnected services, peek
14544743Smarkm     * at the sender address of the pending protocol data unit without
14644743Smarkm     * popping it off the receive queue. This trick works because only the
14744743Smarkm     * address member of the unitdata structure has been allocated.
14844743Smarkm     *
14944743Smarkm     * Beware of successful returns with zero-length netbufs (for example,
15044743Smarkm     * Solaris 2.3 with ticlts transport). The netdir(3) routines can't
15144743Smarkm     * handle that. Assume connection-less transport when TI_GETPEERNAME
15244743Smarkm     * produces no usable result, even when t_rcvudata() is unable to figure
15344743Smarkm     * out the peer address. Better to hang than to loop.
15444743Smarkm     */
15544743Smarkm
15644743Smarkm    if ((client = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ADDR)) == 0) {
15744743Smarkm	tcpd_warn("t_alloc: %s", tli_error());
15844743Smarkm	return;
15944743Smarkm    }
16044743Smarkm    if (ioctl(fd, TI_GETPEERNAME, &client->addr) < 0 || client->addr.len == 0) {
16144743Smarkm	request->sink = tli_sink;
16244743Smarkm	if (t_rcvudata(fd, client, &flags) < 0 || client->addr.len == 0) {
16344743Smarkm	    tcpd_warn("can't get client address: %s", tli_error());
16444743Smarkm	    t_free((void *) client, T_UNITDATA);
16544743Smarkm	    return;
16644743Smarkm	}
16744743Smarkm    }
16844743Smarkm    request->client->unit = client;
16944743Smarkm
17044743Smarkm    /*
17144743Smarkm     * Look up the server endpoint address. This can be used for filtering on
17244743Smarkm     * server address or name, or to look up the client user.
17344743Smarkm     */
17444743Smarkm
17544743Smarkm    if ((server = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ADDR)) == 0) {
17644743Smarkm	tcpd_warn("t_alloc: %s", tli_error());
17744743Smarkm	return;
17844743Smarkm    }
17944743Smarkm    if (ioctl(fd, TI_GETMYNAME, &server->addr) < 0) {
18044743Smarkm	tcpd_warn("TI_GETMYNAME: %m");
18144743Smarkm	t_free((void *) server, T_UNITDATA);
18244743Smarkm	return;
18344743Smarkm    }
18444743Smarkm    request->server->unit = server;
18544743Smarkm}
18644743Smarkm
18744743Smarkm/* tli_transport - find out TLI transport type */
18844743Smarkm
18944743Smarkmstatic struct netconfig *tli_transport(fd)
19044743Smarkmint     fd;
19144743Smarkm{
19244743Smarkm    struct stat from_client;
19344743Smarkm    struct stat from_config;
19444743Smarkm    void   *handlep;
19544743Smarkm    struct netconfig *config;
19644743Smarkm
19744743Smarkm    /*
19844743Smarkm     * Assuming that the network device is a clone device, we must compare
19944743Smarkm     * the major device number of stdin to the minor device number of the
20044743Smarkm     * devices listed in the netconfig table.
20144743Smarkm     */
20244743Smarkm
20344743Smarkm    if (fstat(fd, &from_client) != 0) {
20444743Smarkm	tcpd_warn("fstat(fd %d): %m", fd);
20544743Smarkm	return (0);
20644743Smarkm    }
20744743Smarkm    if ((handlep = setnetconfig()) == 0) {
20844743Smarkm	tcpd_warn("setnetconfig: %m");
20944743Smarkm	return (0);
21044743Smarkm    }
21144743Smarkm    while (config = getnetconfig(handlep)) {
21244743Smarkm	if (stat(config->nc_device, &from_config) == 0) {
21356977Sshin#ifdef NO_CLONE_DEVICE
21456977Sshin	/*
21556977Sshin	 * If the network devices are not cloned (as is the case for
21656977Sshin	 * Solaris 8 Beta), we must compare the major device numbers.
21756977Sshin	 */
21856977Sshin	    if (major(from_config.st_rdev) == major(from_client.st_rdev))
21956977Sshin#else
22044743Smarkm	    if (minor(from_config.st_rdev) == major(from_client.st_rdev))
22156977Sshin#endif
22244743Smarkm		break;
22344743Smarkm	}
22444743Smarkm    }
22544743Smarkm    if (config == 0) {
22644743Smarkm	tcpd_warn("unable to identify transport protocol");
22744743Smarkm	return (0);
22844743Smarkm    }
22944743Smarkm
23044743Smarkm    /*
23144743Smarkm     * Something else may clobber our getnetconfig() result, so we'd better
23244743Smarkm     * acquire our private copy.
23344743Smarkm     */
23444743Smarkm
23544743Smarkm    if ((config = getnetconfigent(config->nc_netid)) == 0) {
23644743Smarkm	tcpd_warn("getnetconfigent(%s): %s", config->nc_netid, nc_sperror());
23744743Smarkm	return (0);
23844743Smarkm    }
23944743Smarkm    return (config);
24044743Smarkm}
24144743Smarkm
24244743Smarkm/* tli_hostaddr - map TLI transport address to printable address */
24344743Smarkm
24444743Smarkmstatic void tli_hostaddr(host)
24544743Smarkmstruct host_info *host;
24644743Smarkm{
24744743Smarkm    struct request_info *request = host->request;
24844743Smarkm    struct netconfig *config = request->config;
24944743Smarkm    struct t_unitdata *unit = host->unit;
25044743Smarkm    char   *uaddr;
25144743Smarkm
25244743Smarkm    if (config != 0 && unit != 0
25344743Smarkm	&& (uaddr = taddr2uaddr(config, &unit->addr)) != 0) {
25444743Smarkm	STRN_CPY(host->addr, uaddr, sizeof(host->addr));
25544743Smarkm	free(uaddr);
25644743Smarkm    }
25744743Smarkm}
25844743Smarkm
25944743Smarkm/* tli_hostname - map TLI transport address to hostname */
26044743Smarkm
26144743Smarkmstatic void tli_hostname(host)
26244743Smarkmstruct host_info *host;
26344743Smarkm{
26444743Smarkm    struct request_info *request = host->request;
26544743Smarkm    struct netconfig *config = request->config;
26644743Smarkm    struct t_unitdata *unit = host->unit;
26744743Smarkm    struct nd_hostservlist *servlist;
26844743Smarkm
26944743Smarkm    if (config != 0 && unit != 0
27044743Smarkm	&& netdir_getbyaddr(config, &servlist, &unit->addr) == ND_OK) {
27144743Smarkm
27244743Smarkm	struct nd_hostserv *service = servlist->h_hostservs;
27344743Smarkm	struct nd_addrlist *addr_list;
27444743Smarkm	int     found = 0;
27544743Smarkm
27644743Smarkm	if (netdir_getbyname(config, service, &addr_list) != ND_OK) {
27744743Smarkm
27844743Smarkm	    /*
27944743Smarkm	     * Unable to verify that the name matches the address. This may
28044743Smarkm	     * be a transient problem or a botched name server setup. We
28144743Smarkm	     * decide to play safe.
28244743Smarkm	     */
28344743Smarkm
28444743Smarkm	    tcpd_warn("can't verify hostname: netdir_getbyname(%.*s) failed",
28544743Smarkm		      STRING_LENGTH, service->h_host);
28644743Smarkm
28744743Smarkm	} else {
28844743Smarkm
28944743Smarkm	    /*
29044743Smarkm	     * Look up the host address in the address list we just got. The
29144743Smarkm	     * comparison is done on the textual representation, because the
29244743Smarkm	     * transport address is an opaque structure that may have holes
29344743Smarkm	     * with uninitialized garbage. This approach obviously loses when
29444743Smarkm	     * the address does not have a textual representation.
29544743Smarkm	     */
29644743Smarkm
29744743Smarkm	    char   *uaddr = eval_hostaddr(host);
29844743Smarkm	    char   *ua;
29944743Smarkm	    int     i;
30044743Smarkm
30144743Smarkm	    for (i = 0; found == 0 && i < addr_list->n_cnt; i++) {
30244743Smarkm		if ((ua = taddr2uaddr(config, &(addr_list->n_addrs[i]))) != 0) {
30344743Smarkm		    found = !strcmp(ua, uaddr);
30444743Smarkm		    free(ua);
30544743Smarkm		}
30644743Smarkm	    }
30744743Smarkm	    netdir_free((void *) addr_list, ND_ADDRLIST);
30844743Smarkm
30944743Smarkm	    /*
31044743Smarkm	     * When the host name does not map to the initial address, assume
31144743Smarkm	     * someone has compromised a name server. More likely someone
31244743Smarkm	     * botched it, but that could be dangerous, too.
31344743Smarkm	     */
31444743Smarkm
31544743Smarkm	    if (found == 0)
31644743Smarkm		tcpd_warn("host name/address mismatch: %s != %.*s",
31744743Smarkm			  host->addr, STRING_LENGTH, service->h_host);
31844743Smarkm	}
31944743Smarkm	STRN_CPY(host->name, found ? service->h_host : paranoid,
32044743Smarkm		 sizeof(host->name));
32144743Smarkm	netdir_free((void *) servlist, ND_HOSTSERVLIST);
32244743Smarkm    }
32344743Smarkm}
32444743Smarkm
32544743Smarkm/* tli_error - convert tli error number to text */
32644743Smarkm
32744743Smarkmstatic char *tli_error()
32844743Smarkm{
32944743Smarkm    static char buf[40];
33044743Smarkm
33144743Smarkm    if (t_errno != TSYSERR) {
33244743Smarkm	if (t_errno < 0 || t_errno >= t_nerr) {
33344743Smarkm	    sprintf(buf, "Unknown TLI error %d", t_errno);
33444743Smarkm	    return (buf);
33544743Smarkm	} else {
33644743Smarkm	    return (t_errlist[t_errno]);
33744743Smarkm	}
33844743Smarkm    } else {
33944743Smarkm	if (errno < 0 || errno >= sys_nerr) {
34044743Smarkm	    sprintf(buf, "Unknown UNIX error %d", errno);
34144743Smarkm	    return (buf);
34244743Smarkm	} else {
34344743Smarkm	    return (sys_errlist[errno]);
34444743Smarkm	}
34544743Smarkm    }
34644743Smarkm}
34744743Smarkm
34844743Smarkm/* tli_sink - absorb unreceived datagram */
34944743Smarkm
35044743Smarkmstatic void tli_sink(fd)
35144743Smarkmint     fd;
35244743Smarkm{
35344743Smarkm    struct t_unitdata *unit;
35444743Smarkm    int     flags;
35544743Smarkm
35644743Smarkm    /*
35744743Smarkm     * Something went wrong. Absorb the datagram to keep inetd from looping.
35844743Smarkm     * Allocate storage for address, control and data. If that fails, sleep
35944743Smarkm     * for a couple of seconds in an attempt to keep inetd from looping too
36044743Smarkm     * fast.
36144743Smarkm     */
36244743Smarkm
36344743Smarkm    if ((unit = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ALL)) == 0) {
36444743Smarkm	tcpd_warn("t_alloc: %s", tli_error());
36544743Smarkm	sleep(5);
36644743Smarkm    } else {
36744743Smarkm	(void) t_rcvudata(fd, unit, &flags);
36844743Smarkm	t_free((void *) unit, T_UNITDATA);
36944743Smarkm    }
37044743Smarkm}
37144743Smarkm
37244743Smarkm#endif /* TLI */
373