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