socket.c revision 63158
144743Smarkm /* 244743Smarkm * This module determines the type of socket (datagram, stream), the client 344743Smarkm * socket address and port, the server socket address and port. In addition, 444743Smarkm * it provides methods to map a transport address to a printable host name 544743Smarkm * or address. Socket address information results are in static memory. 644743Smarkm * 744743Smarkm * The result from the hostname lookup method is STRING_PARANOID when a host 844743Smarkm * pretends to have someone elses name, or when a host name is available but 944743Smarkm * could not be verified. 1044743Smarkm * 1144743Smarkm * When lookup or conversion fails the result is set to STRING_UNKNOWN. 1244743Smarkm * 1344743Smarkm * Diagnostics are reported through syslog(3). 1444743Smarkm * 1544743Smarkm * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 1656977Sshin * 1756977Sshin * $FreeBSD: head/contrib/tcp_wrappers/socket.c 63158 2000-07-14 17:15:34Z ume $ 1844743Smarkm */ 1944743Smarkm 2044743Smarkm#ifndef lint 2144743Smarkmstatic char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24"; 2244743Smarkm#endif 2344743Smarkm 2444743Smarkm/* System libraries. */ 2544743Smarkm 2644743Smarkm#include <sys/types.h> 2744743Smarkm#include <sys/param.h> 2844743Smarkm#include <sys/socket.h> 2944743Smarkm#include <netinet/in.h> 3044743Smarkm#include <netdb.h> 3144743Smarkm#include <stdio.h> 3244743Smarkm#include <syslog.h> 3344743Smarkm#include <string.h> 3444743Smarkm 3556977Sshin#ifdef INET6 3663158Sume#ifndef NI_WITHSCOPEID 3763158Sume#define NI_WITHSCOPEID 0 3856977Sshin#endif 3963158Sume#else 4063158Sumeextern char *inet_ntoa(); 4156977Sshin#endif 4256977Sshin 4344743Smarkm/* Local stuff. */ 4444743Smarkm 4544743Smarkm#include "tcpd.h" 4644743Smarkm 4744743Smarkm/* Forward declarations. */ 4844743Smarkm 4944743Smarkmstatic void sock_sink(); 5044743Smarkm 5144743Smarkm#ifdef APPEND_DOT 5244743Smarkm 5344743Smarkm /* 5444743Smarkm * Speed up DNS lookups by terminating the host name with a dot. Should be 5544743Smarkm * done with care. The speedup can give problems with lookups from sources 5644743Smarkm * that lack DNS-style trailing dot magic, such as local files or NIS maps. 5744743Smarkm */ 5844743Smarkm 5944743Smarkmstatic struct hostent *gethostbyname_dot(name) 6044743Smarkmchar *name; 6144743Smarkm{ 6244743Smarkm char dot_name[MAXHOSTNAMELEN + 1]; 6344743Smarkm 6444743Smarkm /* 6544743Smarkm * Don't append dots to unqualified names. Such names are likely to come 6644743Smarkm * from local hosts files or from NIS. 6744743Smarkm */ 6844743Smarkm 6944743Smarkm if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) { 7044743Smarkm return (gethostbyname(name)); 7144743Smarkm } else { 7244743Smarkm sprintf(dot_name, "%s.", name); 7344743Smarkm return (gethostbyname(dot_name)); 7444743Smarkm } 7544743Smarkm} 7644743Smarkm 7744743Smarkm#define gethostbyname gethostbyname_dot 7844743Smarkm#endif 7944743Smarkm 8044743Smarkm/* sock_host - look up endpoint addresses and install conversion methods */ 8144743Smarkm 8244743Smarkmvoid sock_host(request) 8344743Smarkmstruct request_info *request; 8444743Smarkm{ 8556977Sshin#ifdef INET6 8656977Sshin static struct sockaddr_storage client; 8756977Sshin static struct sockaddr_storage server; 8856977Sshin#else 8944743Smarkm static struct sockaddr_in client; 9044743Smarkm static struct sockaddr_in server; 9156977Sshin#endif 9244743Smarkm int len; 9344743Smarkm char buf[BUFSIZ]; 9444743Smarkm int fd = request->fd; 9544743Smarkm 9644743Smarkm sock_methods(request); 9744743Smarkm 9844743Smarkm /* 9944743Smarkm * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov> 10044743Smarkm * suggested how to get the client host info in case of UDP connections: 10144743Smarkm * peek at the first message without actually looking at its contents. We 10244743Smarkm * really should verify that client.sin_family gets the value AF_INET, 10344743Smarkm * but this program has already caused too much grief on systems with 10444743Smarkm * broken library code. 10544743Smarkm */ 10644743Smarkm 10744743Smarkm len = sizeof(client); 10844743Smarkm if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) { 10944743Smarkm request->sink = sock_sink; 11044743Smarkm len = sizeof(client); 11144743Smarkm if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK, 11244743Smarkm (struct sockaddr *) & client, &len) < 0) { 11344743Smarkm tcpd_warn("can't get client address: %m"); 11444743Smarkm return; /* give up */ 11544743Smarkm } 11644743Smarkm#ifdef really_paranoid 11744743Smarkm memset(buf, 0 sizeof(buf)); 11844743Smarkm#endif 11944743Smarkm } 12056977Sshin#ifdef INET6 12156977Sshin request->client->sin = (struct sockaddr *)&client; 12256977Sshin#else 12344743Smarkm request->client->sin = &client; 12456977Sshin#endif 12544743Smarkm 12644743Smarkm /* 12744743Smarkm * Determine the server binding. This is used for client username 12844743Smarkm * lookups, and for access control rules that trigger on the server 12944743Smarkm * address or name. 13044743Smarkm */ 13144743Smarkm 13244743Smarkm len = sizeof(server); 13344743Smarkm if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) { 13444743Smarkm tcpd_warn("getsockname: %m"); 13544743Smarkm return; 13644743Smarkm } 13756977Sshin#ifdef INET6 13856977Sshin request->server->sin = (struct sockaddr *)&server; 13956977Sshin#else 14044743Smarkm request->server->sin = &server; 14156977Sshin#endif 14244743Smarkm} 14344743Smarkm 14444743Smarkm/* sock_hostaddr - map endpoint address to printable form */ 14544743Smarkm 14644743Smarkmvoid sock_hostaddr(host) 14744743Smarkmstruct host_info *host; 14844743Smarkm{ 14956977Sshin#ifdef INET6 15056977Sshin struct sockaddr *sin = host->sin; 15163158Sume int salen; 15256977Sshin 15356977Sshin if (!sin) 15456977Sshin return; 15563158Sume#ifdef SIN6_LEN 15663158Sume salen = sin->sa_len; 15756977Sshin#else 15863158Sume salen = (sin->sa_family == AF_INET) ? sizeof(struct sockaddr_in) 15963158Sume : sizeof(struct sockaddr_in6); 16063158Sume#endif 16163158Sume getnameinfo(sin, salen, host->addr, sizeof(host->addr), 16263158Sume NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); 16363158Sume#else 16444743Smarkm struct sockaddr_in *sin = host->sin; 16544743Smarkm 16644743Smarkm if (sin != 0) 16744743Smarkm STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr)); 16856977Sshin#endif 16944743Smarkm} 17044743Smarkm 17144743Smarkm/* sock_hostname - map endpoint address to host name */ 17244743Smarkm 17344743Smarkmvoid sock_hostname(host) 17444743Smarkmstruct host_info *host; 17544743Smarkm{ 17656977Sshin#ifdef INET6 17756977Sshin struct sockaddr *sin = host->sin; 17863158Sume struct sockaddr_in sin4; 17963158Sume struct addrinfo hints, *res, *res0 = NULL; 18063158Sume int salen, alen, err = 1; 18163158Sume char *ap = NULL, *rap, hname[NI_MAXHOST]; 18263158Sume 18363158Sume if (sin != NULL) { 18463158Sume if (sin->sa_family == AF_INET6) { 18563158Sume struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin; 18663158Sume 18763158Sume if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 18863158Sume memset(&sin4, 0, sizeof(sin4)); 18963158Sume#ifdef SIN6_LEN 19063158Sume sin4.sin_len = sizeof(sin4); 19156977Sshin#endif 19263158Sume sin4.sin_family = AF_INET; 19363158Sume sin4.sin_port = sin6->sin6_port; 19463158Sume sin4.sin_addr.s_addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 19563158Sume sin = (struct sockaddr *)&sin4; 19663158Sume } 19763158Sume } 19863158Sume switch (sin->sa_family) { 19963158Sume case AF_INET: 20063158Sume ap = (char *)&((struct sockaddr_in *)sin)->sin_addr; 20163158Sume alen = sizeof(struct in_addr); 20263158Sume salen = sizeof(struct sockaddr_in); 20363158Sume break; 20463158Sume case AF_INET6: 20563158Sume ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr; 20663158Sume alen = sizeof(struct in6_addr); 20763158Sume salen = sizeof(struct sockaddr_in6); 20863158Sume break; 20963158Sume default: 21063158Sume break; 21163158Sume } 21263158Sume if (ap) 21363158Sume err = getnameinfo(sin, salen, hname, sizeof(hname), 21463158Sume NULL, 0, NI_WITHSCOPEID | NI_NAMEREQD); 21563158Sume } 21663158Sume if (!err) { 21763158Sume 21863158Sume STRN_CPY(host->name, hname, sizeof(host->name)); 21963158Sume 22063158Sume /* 22163158Sume * Verify that the address is a member of the address list returned 22263158Sume * by gethostbyname(hostname). 22363158Sume * 22463158Sume * Verify also that gethostbyaddr() and gethostbyname() return the same 22563158Sume * hostname, or rshd and rlogind may still end up being spoofed. 22663158Sume * 22763158Sume * On some sites, gethostbyname("localhost") returns "localhost.domain". 22863158Sume * This is a DNS artefact. We treat it as a special case. When we 22963158Sume * can't believe the address list from gethostbyname("localhost") 23063158Sume * we're in big trouble anyway. 23163158Sume */ 23263158Sume 23363158Sume memset(&hints, 0, sizeof(hints)); 23463158Sume hints.ai_family = sin->sa_family; 23563158Sume hints.ai_socktype = SOCK_STREAM; 23663158Sume hints.ai_flags = AI_PASSIVE | AI_CANONNAME; 23763158Sume if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) { 23863158Sume 23963158Sume /* 24063158Sume * Unable to verify that the host name matches the address. This 24163158Sume * may be a transient problem or a botched name server setup. 24263158Sume */ 24363158Sume 24463158Sume tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed", 24563158Sume host->name, 24663158Sume (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6"); 24763158Sume 24863158Sume } else if (STR_NE(host->name, res0->ai_canonname) 24963158Sume && STR_NE(host->name, "localhost")) { 25063158Sume 25163158Sume /* 25263158Sume * The gethostbyaddr() and gethostbyname() calls did not return 25363158Sume * the same hostname. This could be a nameserver configuration 25463158Sume * problem. It could also be that someone is trying to spoof us. 25563158Sume */ 25663158Sume 25763158Sume tcpd_warn("host name/name mismatch: %s != %.*s", 25863158Sume host->name, STRING_LENGTH, res0->ai_canonname); 25963158Sume 26063158Sume } else { 26163158Sume 26263158Sume /* 26363158Sume * The address should be a member of the address list returned by 26463158Sume * gethostbyname(). We should first verify that the h_addrtype 26563158Sume * field is AF_INET, but this program has already caused too much 26663158Sume * grief on systems with broken library code. 26763158Sume */ 26863158Sume 26963158Sume for (res = res0; res; res = res->ai_next) { 27063158Sume if (res->ai_family != sin->sa_family) 27163158Sume continue; 27263158Sume switch (res->ai_family) { 27363158Sume case AF_INET: 27463158Sume rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr; 27563158Sume break; 27663158Sume case AF_INET6: 27763158Sume rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; 27863158Sume break; 27963158Sume default: 28063158Sume continue; 28163158Sume } 28263158Sume if (memcmp(rap, ap, alen) == 0) { 28363158Sume freeaddrinfo(res0); 28463158Sume return; /* name is good, keep it */ 28563158Sume } 28663158Sume } 28763158Sume 28863158Sume /* 28963158Sume * The host name does not map to the initial address. Perhaps 29063158Sume * someone has messed up. Perhaps someone compromised a name 29163158Sume * server. 29263158Sume */ 29363158Sume 29463158Sume getnameinfo(sin, salen, hname, sizeof(hname), 29563158Sume NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); 29663158Sume tcpd_warn("host name/address mismatch: %s != %.*s", 29763158Sume hname, STRING_LENGTH, res0->ai_canonname); 29863158Sume } 29963158Sume strcpy(host->name, paranoid); /* name is bad, clobber it */ 30063158Sume if (res0) 30163158Sume freeaddrinfo(res0); 30263158Sume } 30363158Sume#else /* INET6 */ 30444743Smarkm struct sockaddr_in *sin = host->sin; 30544743Smarkm struct hostent *hp; 30644743Smarkm int i; 30744743Smarkm 30844743Smarkm /* 30944743Smarkm * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does 31044743Smarkm * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does 31144743Smarkm * not work the other way around: gethostbyname("INADDR_ANY") fails. We 31244743Smarkm * have to special-case 0.0.0.0, in order to avoid false alerts from the 31344743Smarkm * host name/address checking code below. 31444743Smarkm */ 31544743Smarkm if (sin != 0 && sin->sin_addr.s_addr != 0 31644743Smarkm && (hp = gethostbyaddr((char *) &(sin->sin_addr), 31744743Smarkm sizeof(sin->sin_addr), AF_INET)) != 0) { 31844743Smarkm 31944743Smarkm STRN_CPY(host->name, hp->h_name, sizeof(host->name)); 32044743Smarkm 32144743Smarkm /* 32244743Smarkm * Verify that the address is a member of the address list returned 32344743Smarkm * by gethostbyname(hostname). 32444743Smarkm * 32544743Smarkm * Verify also that gethostbyaddr() and gethostbyname() return the same 32644743Smarkm * hostname, or rshd and rlogind may still end up being spoofed. 32744743Smarkm * 32844743Smarkm * On some sites, gethostbyname("localhost") returns "localhost.domain". 32944743Smarkm * This is a DNS artefact. We treat it as a special case. When we 33044743Smarkm * can't believe the address list from gethostbyname("localhost") 33144743Smarkm * we're in big trouble anyway. 33244743Smarkm */ 33344743Smarkm 33444743Smarkm if ((hp = gethostbyname(host->name)) == 0) { 33544743Smarkm 33644743Smarkm /* 33744743Smarkm * Unable to verify that the host name matches the address. This 33844743Smarkm * may be a transient problem or a botched name server setup. 33944743Smarkm */ 34044743Smarkm 34144743Smarkm tcpd_warn("can't verify hostname: gethostbyname(%s) failed", 34244743Smarkm host->name); 34344743Smarkm 34444743Smarkm } else if (STR_NE(host->name, hp->h_name) 34544743Smarkm && STR_NE(host->name, "localhost")) { 34644743Smarkm 34744743Smarkm /* 34844743Smarkm * The gethostbyaddr() and gethostbyname() calls did not return 34944743Smarkm * the same hostname. This could be a nameserver configuration 35044743Smarkm * problem. It could also be that someone is trying to spoof us. 35144743Smarkm */ 35244743Smarkm 35344743Smarkm tcpd_warn("host name/name mismatch: %s != %.*s", 35444743Smarkm host->name, STRING_LENGTH, hp->h_name); 35544743Smarkm 35644743Smarkm } else { 35744743Smarkm 35844743Smarkm /* 35944743Smarkm * The address should be a member of the address list returned by 36044743Smarkm * gethostbyname(). We should first verify that the h_addrtype 36144743Smarkm * field is AF_INET, but this program has already caused too much 36244743Smarkm * grief on systems with broken library code. 36344743Smarkm */ 36444743Smarkm 36544743Smarkm for (i = 0; hp->h_addr_list[i]; i++) { 36644743Smarkm if (memcmp(hp->h_addr_list[i], 36744743Smarkm (char *) &sin->sin_addr, 36844743Smarkm sizeof(sin->sin_addr)) == 0) 36944743Smarkm return; /* name is good, keep it */ 37044743Smarkm } 37144743Smarkm 37244743Smarkm /* 37344743Smarkm * The host name does not map to the initial address. Perhaps 37444743Smarkm * someone has messed up. Perhaps someone compromised a name 37544743Smarkm * server. 37644743Smarkm */ 37744743Smarkm 37844743Smarkm tcpd_warn("host name/address mismatch: %s != %.*s", 37944743Smarkm inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name); 38044743Smarkm } 38144743Smarkm strcpy(host->name, paranoid); /* name is bad, clobber it */ 38244743Smarkm } 38363158Sume#endif /* INET6 */ 38444743Smarkm} 38544743Smarkm 38644743Smarkm/* sock_sink - absorb unreceived IP datagram */ 38744743Smarkm 38844743Smarkmstatic void sock_sink(fd) 38944743Smarkmint fd; 39044743Smarkm{ 39144743Smarkm char buf[BUFSIZ]; 39256977Sshin#ifdef INET6 39356977Sshin struct sockaddr_storage sin; 39456977Sshin#else 39544743Smarkm struct sockaddr_in sin; 39656977Sshin#endif 39744743Smarkm int size = sizeof(sin); 39844743Smarkm 39944743Smarkm /* 40044743Smarkm * Eat up the not-yet received datagram. Some systems insist on a 40144743Smarkm * non-zero source address argument in the recvfrom() call below. 40244743Smarkm */ 40344743Smarkm 40444743Smarkm (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size); 40544743Smarkm} 406