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