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 
  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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 */