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: releng/10.3/contrib/tcp_wrappers/socket.c 146187 2005-05-13 16:31:11Z 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 35146187Sume#ifndef INET6 3663158Sumeextern char *inet_ntoa(); 3756977Sshin#endif 3856977Sshin 3944743Smarkm/* Local stuff. */ 4044743Smarkm 4144743Smarkm#include "tcpd.h" 4244743Smarkm 4344743Smarkm/* Forward declarations. */ 4444743Smarkm 4544743Smarkmstatic void sock_sink(); 4644743Smarkm 4744743Smarkm#ifdef APPEND_DOT 4844743Smarkm 4944743Smarkm /* 5044743Smarkm * Speed up DNS lookups by terminating the host name with a dot. Should be 5144743Smarkm * done with care. The speedup can give problems with lookups from sources 5244743Smarkm * that lack DNS-style trailing dot magic, such as local files or NIS maps. 5344743Smarkm */ 5444743Smarkm 5544743Smarkmstatic struct hostent *gethostbyname_dot(name) 5644743Smarkmchar *name; 5744743Smarkm{ 5844743Smarkm char dot_name[MAXHOSTNAMELEN + 1]; 5944743Smarkm 6044743Smarkm /* 6144743Smarkm * Don't append dots to unqualified names. Such names are likely to come 6244743Smarkm * from local hosts files or from NIS. 6344743Smarkm */ 6444743Smarkm 6544743Smarkm if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) { 6644743Smarkm return (gethostbyname(name)); 6744743Smarkm } else { 6844743Smarkm sprintf(dot_name, "%s.", name); 6944743Smarkm return (gethostbyname(dot_name)); 7044743Smarkm } 7144743Smarkm} 7244743Smarkm 7344743Smarkm#define gethostbyname gethostbyname_dot 7444743Smarkm#endif 7544743Smarkm 7644743Smarkm/* sock_host - look up endpoint addresses and install conversion methods */ 7744743Smarkm 7844743Smarkmvoid sock_host(request) 7944743Smarkmstruct request_info *request; 8044743Smarkm{ 8156977Sshin#ifdef INET6 8256977Sshin static struct sockaddr_storage client; 8356977Sshin static struct sockaddr_storage server; 8456977Sshin#else 8544743Smarkm static struct sockaddr_in client; 8644743Smarkm static struct sockaddr_in server; 8756977Sshin#endif 8844743Smarkm int len; 8944743Smarkm char buf[BUFSIZ]; 9044743Smarkm int fd = request->fd; 9144743Smarkm 9244743Smarkm sock_methods(request); 9344743Smarkm 9444743Smarkm /* 9544743Smarkm * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov> 9644743Smarkm * suggested how to get the client host info in case of UDP connections: 9744743Smarkm * peek at the first message without actually looking at its contents. We 9844743Smarkm * really should verify that client.sin_family gets the value AF_INET, 9944743Smarkm * but this program has already caused too much grief on systems with 10044743Smarkm * broken library code. 10144743Smarkm */ 10244743Smarkm 10344743Smarkm len = sizeof(client); 10444743Smarkm if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) { 10544743Smarkm request->sink = sock_sink; 10644743Smarkm len = sizeof(client); 10744743Smarkm if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK, 10844743Smarkm (struct sockaddr *) & client, &len) < 0) { 10944743Smarkm tcpd_warn("can't get client address: %m"); 11044743Smarkm return; /* give up */ 11144743Smarkm } 11244743Smarkm#ifdef really_paranoid 113123895Sceri memset(buf, 0, sizeof(buf)); 11444743Smarkm#endif 11544743Smarkm } 11656977Sshin#ifdef INET6 11756977Sshin request->client->sin = (struct sockaddr *)&client; 11856977Sshin#else 11944743Smarkm request->client->sin = &client; 12056977Sshin#endif 12144743Smarkm 12244743Smarkm /* 12344743Smarkm * Determine the server binding. This is used for client username 12444743Smarkm * lookups, and for access control rules that trigger on the server 12544743Smarkm * address or name. 12644743Smarkm */ 12744743Smarkm 12844743Smarkm len = sizeof(server); 12944743Smarkm if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) { 13044743Smarkm tcpd_warn("getsockname: %m"); 13144743Smarkm return; 13244743Smarkm } 13356977Sshin#ifdef INET6 13456977Sshin request->server->sin = (struct sockaddr *)&server; 13556977Sshin#else 13644743Smarkm request->server->sin = &server; 13756977Sshin#endif 13844743Smarkm} 13944743Smarkm 14044743Smarkm/* sock_hostaddr - map endpoint address to printable form */ 14144743Smarkm 14244743Smarkmvoid sock_hostaddr(host) 14344743Smarkmstruct host_info *host; 14444743Smarkm{ 14556977Sshin#ifdef INET6 14656977Sshin struct sockaddr *sin = host->sin; 14763158Sume int salen; 14856977Sshin 14956977Sshin if (!sin) 15056977Sshin return; 15163158Sume#ifdef SIN6_LEN 15263158Sume salen = sin->sa_len; 15356977Sshin#else 15463158Sume salen = (sin->sa_family == AF_INET) ? sizeof(struct sockaddr_in) 15563158Sume : sizeof(struct sockaddr_in6); 15663158Sume#endif 15763158Sume getnameinfo(sin, salen, host->addr, sizeof(host->addr), 158146187Sume NULL, 0, NI_NUMERICHOST); 15963158Sume#else 16044743Smarkm struct sockaddr_in *sin = host->sin; 16144743Smarkm 16244743Smarkm if (sin != 0) 16344743Smarkm STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr)); 16456977Sshin#endif 16544743Smarkm} 16644743Smarkm 16744743Smarkm/* sock_hostname - map endpoint address to host name */ 16844743Smarkm 16944743Smarkmvoid sock_hostname(host) 17044743Smarkmstruct host_info *host; 17144743Smarkm{ 17256977Sshin#ifdef INET6 17356977Sshin struct sockaddr *sin = host->sin; 17463158Sume struct sockaddr_in sin4; 17563158Sume struct addrinfo hints, *res, *res0 = NULL; 17663158Sume int salen, alen, err = 1; 17763158Sume char *ap = NULL, *rap, hname[NI_MAXHOST]; 17863158Sume 17963158Sume if (sin != NULL) { 18063158Sume if (sin->sa_family == AF_INET6) { 18163158Sume struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin; 18263158Sume 18363158Sume if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 18463158Sume memset(&sin4, 0, sizeof(sin4)); 18563158Sume#ifdef SIN6_LEN 18663158Sume sin4.sin_len = sizeof(sin4); 18756977Sshin#endif 18863158Sume sin4.sin_family = AF_INET; 18963158Sume sin4.sin_port = sin6->sin6_port; 19063158Sume sin4.sin_addr.s_addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 19163158Sume sin = (struct sockaddr *)&sin4; 19263158Sume } 19363158Sume } 19463158Sume switch (sin->sa_family) { 19563158Sume case AF_INET: 19663158Sume ap = (char *)&((struct sockaddr_in *)sin)->sin_addr; 19763158Sume alen = sizeof(struct in_addr); 19863158Sume salen = sizeof(struct sockaddr_in); 19963158Sume break; 20063158Sume case AF_INET6: 20163158Sume ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr; 20263158Sume alen = sizeof(struct in6_addr); 20363158Sume salen = sizeof(struct sockaddr_in6); 20463158Sume break; 20563158Sume default: 20663158Sume break; 20763158Sume } 20863158Sume if (ap) 20963158Sume err = getnameinfo(sin, salen, hname, sizeof(hname), 210146187Sume NULL, 0, NI_NAMEREQD); 21163158Sume } 21263158Sume if (!err) { 21363158Sume 21463158Sume STRN_CPY(host->name, hname, sizeof(host->name)); 21563158Sume 21666329Sume /* reject numeric addresses */ 21766329Sume memset(&hints, 0, sizeof(hints)); 21866329Sume hints.ai_family = sin->sa_family; 21966329Sume hints.ai_socktype = SOCK_STREAM; 22066329Sume hints.ai_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST; 22179249Skris if ((err = getaddrinfo(host->name, NULL, &hints, &res0)) == 0) { 22266329Sume freeaddrinfo(res0); 22366329Sume tcpd_warn("host name/name mismatch: " 22466329Sume "reverse lookup results in non-FQDN %s", 22566329Sume host->name); 22666329Sume strcpy(host->name, paranoid); /* name is bad, clobber it */ 22766329Sume } 22866329Sume err = !err; 22966329Sume } 23066329Sume if (!err) { 23166329Sume /* we are now sure that this is non-numeric */ 23266329Sume 23363158Sume /* 23463158Sume * Verify that the address is a member of the address list returned 23563158Sume * by gethostbyname(hostname). 23663158Sume * 23763158Sume * Verify also that gethostbyaddr() and gethostbyname() return the same 23863158Sume * hostname, or rshd and rlogind may still end up being spoofed. 23963158Sume * 24063158Sume * On some sites, gethostbyname("localhost") returns "localhost.domain". 24163158Sume * This is a DNS artefact. We treat it as a special case. When we 24263158Sume * can't believe the address list from gethostbyname("localhost") 24363158Sume * we're in big trouble anyway. 24463158Sume */ 24563158Sume 24663158Sume memset(&hints, 0, sizeof(hints)); 24763158Sume hints.ai_family = sin->sa_family; 24863158Sume hints.ai_socktype = SOCK_STREAM; 24963158Sume hints.ai_flags = AI_PASSIVE | AI_CANONNAME; 25063158Sume if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) { 25163158Sume 25263158Sume /* 25363158Sume * Unable to verify that the host name matches the address. This 25463158Sume * may be a transient problem or a botched name server setup. 25563158Sume */ 25663158Sume 25763158Sume tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed", 25863158Sume host->name, 25963158Sume (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6"); 26063158Sume 26166297Sume } else if ((res0->ai_canonname == NULL 26266297Sume || STR_NE(host->name, res0->ai_canonname)) 26363158Sume && STR_NE(host->name, "localhost")) { 26463158Sume 26563158Sume /* 26663158Sume * The gethostbyaddr() and gethostbyname() calls did not return 26763158Sume * the same hostname. This could be a nameserver configuration 26863158Sume * problem. It could also be that someone is trying to spoof us. 26963158Sume */ 27063158Sume 27163158Sume tcpd_warn("host name/name mismatch: %s != %.*s", 27266297Sume host->name, STRING_LENGTH, 27366297Sume (res0->ai_canonname == NULL) ? "" : res0->ai_canonname); 27463158Sume 27563158Sume } else { 27663158Sume 27763158Sume /* 27863158Sume * The address should be a member of the address list returned by 27963158Sume * gethostbyname(). We should first verify that the h_addrtype 28063158Sume * field is AF_INET, but this program has already caused too much 28163158Sume * grief on systems with broken library code. 28263158Sume */ 28363158Sume 28463158Sume for (res = res0; res; res = res->ai_next) { 28563158Sume if (res->ai_family != sin->sa_family) 28663158Sume continue; 28763158Sume switch (res->ai_family) { 28863158Sume case AF_INET: 28963158Sume rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr; 29063158Sume break; 29163158Sume case AF_INET6: 29266329Sume /* need to check scope_id */ 29366329Sume if (((struct sockaddr_in6 *)sin)->sin6_scope_id != 29466329Sume ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) { 29566329Sume continue; 29666329Sume } 29763158Sume rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; 29863158Sume break; 29963158Sume default: 30063158Sume continue; 30163158Sume } 30263158Sume if (memcmp(rap, ap, alen) == 0) { 30363158Sume freeaddrinfo(res0); 30463158Sume return; /* name is good, keep it */ 30563158Sume } 30663158Sume } 30763158Sume 30863158Sume /* 30963158Sume * The host name does not map to the initial address. Perhaps 31063158Sume * someone has messed up. Perhaps someone compromised a name 31163158Sume * server. 31263158Sume */ 31363158Sume 31463158Sume getnameinfo(sin, salen, hname, sizeof(hname), 315146187Sume NULL, 0, NI_NUMERICHOST); 31663158Sume tcpd_warn("host name/address mismatch: %s != %.*s", 31766297Sume hname, STRING_LENGTH, 31866297Sume (res0->ai_canonname == NULL) ? "" : res0->ai_canonname); 31963158Sume } 32063158Sume strcpy(host->name, paranoid); /* name is bad, clobber it */ 32163158Sume if (res0) 32263158Sume freeaddrinfo(res0); 32363158Sume } 32463158Sume#else /* INET6 */ 32544743Smarkm struct sockaddr_in *sin = host->sin; 32644743Smarkm struct hostent *hp; 32744743Smarkm int i; 32844743Smarkm 32944743Smarkm /* 33044743Smarkm * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does 33144743Smarkm * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does 33244743Smarkm * not work the other way around: gethostbyname("INADDR_ANY") fails. We 33344743Smarkm * have to special-case 0.0.0.0, in order to avoid false alerts from the 33444743Smarkm * host name/address checking code below. 33544743Smarkm */ 33644743Smarkm if (sin != 0 && sin->sin_addr.s_addr != 0 33744743Smarkm && (hp = gethostbyaddr((char *) &(sin->sin_addr), 33844743Smarkm sizeof(sin->sin_addr), AF_INET)) != 0) { 33944743Smarkm 34044743Smarkm STRN_CPY(host->name, hp->h_name, sizeof(host->name)); 34144743Smarkm 34244743Smarkm /* 34344743Smarkm * Verify that the address is a member of the address list returned 34444743Smarkm * by gethostbyname(hostname). 34544743Smarkm * 34644743Smarkm * Verify also that gethostbyaddr() and gethostbyname() return the same 34744743Smarkm * hostname, or rshd and rlogind may still end up being spoofed. 34844743Smarkm * 34944743Smarkm * On some sites, gethostbyname("localhost") returns "localhost.domain". 35044743Smarkm * This is a DNS artefact. We treat it as a special case. When we 35144743Smarkm * can't believe the address list from gethostbyname("localhost") 35244743Smarkm * we're in big trouble anyway. 35344743Smarkm */ 35444743Smarkm 35544743Smarkm if ((hp = gethostbyname(host->name)) == 0) { 35644743Smarkm 35744743Smarkm /* 35844743Smarkm * Unable to verify that the host name matches the address. This 35944743Smarkm * may be a transient problem or a botched name server setup. 36044743Smarkm */ 36144743Smarkm 36244743Smarkm tcpd_warn("can't verify hostname: gethostbyname(%s) failed", 36344743Smarkm host->name); 36444743Smarkm 36544743Smarkm } else if (STR_NE(host->name, hp->h_name) 36644743Smarkm && STR_NE(host->name, "localhost")) { 36744743Smarkm 36844743Smarkm /* 36944743Smarkm * The gethostbyaddr() and gethostbyname() calls did not return 37044743Smarkm * the same hostname. This could be a nameserver configuration 37144743Smarkm * problem. It could also be that someone is trying to spoof us. 37244743Smarkm */ 37344743Smarkm 37444743Smarkm tcpd_warn("host name/name mismatch: %s != %.*s", 37544743Smarkm host->name, STRING_LENGTH, hp->h_name); 37644743Smarkm 37744743Smarkm } else { 37844743Smarkm 37944743Smarkm /* 38044743Smarkm * The address should be a member of the address list returned by 38144743Smarkm * gethostbyname(). We should first verify that the h_addrtype 38244743Smarkm * field is AF_INET, but this program has already caused too much 38344743Smarkm * grief on systems with broken library code. 38444743Smarkm */ 38544743Smarkm 38644743Smarkm for (i = 0; hp->h_addr_list[i]; i++) { 38744743Smarkm if (memcmp(hp->h_addr_list[i], 38844743Smarkm (char *) &sin->sin_addr, 38944743Smarkm sizeof(sin->sin_addr)) == 0) 39044743Smarkm return; /* name is good, keep it */ 39144743Smarkm } 39244743Smarkm 39344743Smarkm /* 39444743Smarkm * The host name does not map to the initial address. Perhaps 39544743Smarkm * someone has messed up. Perhaps someone compromised a name 39644743Smarkm * server. 39744743Smarkm */ 39844743Smarkm 39944743Smarkm tcpd_warn("host name/address mismatch: %s != %.*s", 40044743Smarkm inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name); 40144743Smarkm } 40244743Smarkm strcpy(host->name, paranoid); /* name is bad, clobber it */ 40344743Smarkm } 40463158Sume#endif /* INET6 */ 40544743Smarkm} 40644743Smarkm 40744743Smarkm/* sock_sink - absorb unreceived IP datagram */ 40844743Smarkm 40944743Smarkmstatic void sock_sink(fd) 41044743Smarkmint fd; 41144743Smarkm{ 41244743Smarkm char buf[BUFSIZ]; 41356977Sshin#ifdef INET6 41456977Sshin struct sockaddr_storage sin; 41556977Sshin#else 41644743Smarkm struct sockaddr_in sin; 41756977Sshin#endif 41844743Smarkm int size = sizeof(sin); 41944743Smarkm 42044743Smarkm /* 42144743Smarkm * Eat up the not-yet received datagram. Some systems insist on a 42244743Smarkm * non-zero source address argument in the recvfrom() call below. 42344743Smarkm */ 42444743Smarkm 42544743Smarkm (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size); 42644743Smarkm} 427