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