# # This patch makes tcpd/libwrap work with IPv6, IPv4 and # mapped IPv4 addresses. # # The approach is "convert everything to IPv6". It means that # any IPv4 address I ever met either in /etc/hosts.* or that I get from # the socket is first converted to v4-mapped and then handled as if it was # IPv6. This simplifies the logic very much and makes the code much # cleaner. Prefixes are also supported in the form [3ffe:ffff::/48] as # well as for IPv4 addresses. # # Made by Michal Ludvig , # November , 2002 # ================================================================================ --- Makefile.orig +++ Makefile @@ -1,5 +1,7 @@ # @(#) Makefile 1.23 97/03/21 19:27:20 +really-all: linux + what: @echo @echo "Usage: edit the REAL_DAEMON_DIR definition in the Makefile then:" @@ -670,7 +672,7 @@ CFLAGS = -O2 -pipe -DFACILITY=$(FACILITY LIB_OBJ= hosts_access.o options.o shell_cmd.o rfc931.o eval.o \ hosts_ctl.o refuse.o percent_x.o clean_exit.o $(AUX_OBJ) \ $(FROM_OBJ) fix_options.o socket.o tli.o workarounds.o \ - update.o misc.o diag.o percent_m.o myvsyslog.o + update.o misc.o diag.o percent_m.o myvsyslog.o ip6utils.o FROM_OBJ= fromhost.o @@ -683,6 +685,7 @@ KIT = README miscd.c tcpd.c fromhost.c h tli-sequent.h misc.c diag.c ncr.c tcpdchk.c percent_m.c \ myvsyslog.c mystdarg.h printf.ck README.IRIX Banners.Makefile \ refuse.c tcpdchk.8 setenv.c inetcf.c inetcf.h scaffold.c \ + ip6utils.c ip6utils.h \ scaffold.h tcpdmatch.8 README.NIS LIB = libwrap.a @@ -812,6 +815,7 @@ printfck: # Internal compilation dependencies. +tcpd.h: ip6utils.h clean_exit.o: cflags clean_exit.o: tcpd.h diag.o: cflags --- hosts_access.c.orig +++ hosts_access.c @@ -85,11 +85,6 @@ static int server_match(); static int client_match(); static int host_match(); static int string_match(); -static int masked_match(); -#ifdef INET6 -static int masked_match4(); -static int masked_match6(); -#endif /* Size of logical line buffer. */ @@ -308,15 +303,17 @@ struct host_info *host; } else if (STR_EQ(tok, "LOCAL")) { /* local: no dots in name */ char *name = eval_hostname(host); return (strchr(name, '.') == 0 && HOSTNAME_KNOWN(name)); - } else if ((mask = split_at(tok, '/')) != 0) { /* net/mask */ - return (masked_match(tok, mask, eval_hostaddr(host))); } else { /* anything else */ return (string_match(tok, eval_hostaddr(host)) || (NOT_INADDR(tok) && string_match(tok, eval_hostname(host)))); } } -/* string_match - match string against pattern */ +/* string_match - match string against pattern + * + * tok = data read from /etc/hosts.* + * string = textual data of actual client + */ static int string_match(tok, string) char *tok; @@ -324,13 +321,6 @@ char *string; { int n; -#ifdef INET6 - /* convert IPv4 mapped IPv6 address to IPv4 address */ - if (STRN_EQ(string, "::ffff:", 7) - && dot_quad_addr(string + 7) != INADDR_NONE) { - string += 7; - } -#endif if (tok[0] == '.') { /* suffix */ n = strlen(string) - strlen(tok); return (n > 0 && STR_EQ(tok, string + n)); @@ -340,122 +330,65 @@ char *string; return (STR_NE(string, unknown)); } else if (tok[(n = strlen(tok)) - 1] == '.') { /* prefix */ return (STRN_EQ(tok, string, n)); - } else { /* exact match */ + } else if (STR_EQ(tok, string)) /* exact match */ + return (YES); #ifdef INET6 + else /* IP addresses match - not needed for IPv4 */ + { + /* For simplicity we convert everything to IPv6 (or v4 mapped) */ struct in6_addr pat, addr; - int len, ret; - char ch; - + int len, ret, prefixlen=128; + char ch, token[INET6_ADDRSTRLEN+1], *mask; + len = strlen(tok); - if (*tok == '[' && tok[len - 1] == ']') { - ch = tok[len - 1]; - tok[len - 1] = '\0'; - ret = inet_pton(AF_INET6, tok + 1, pat.s6_addr); - tok[len - 1] = ch; - if (ret != 1 || inet_pton(AF_INET6, string, addr.s6_addr) != 1) - return NO; - return (!memcmp(&pat, &addr, sizeof(struct in6_addr))); + if (*tok == '[' && tok[len - 1] == ']') + { + ch = tok[len - 1]; + tok[len - 1] = '\0'; + snprintf(token, sizeof(token), "%s", tok+1); + tok[len - 1] = ch; + } + else + snprintf(token, sizeof(token), "%s", tok); + + /* If prefix was given, handle it */ + if ((mask = split_at(token, '/')) != 0) + { + if (sscanf(mask, "%d", &prefixlen) != 1 || prefixlen < 0) + { + tcpd_warn ("Wrong prefix length in %s", tok); + return (NO); + } + + if (is_v4_string (token)) + prefixlen += 96; /* extend to v4mapped */ + + if (prefixlen > 128) + { + tcpd_warn ("Prefix too long in %s", tok); + return (NO); + } + } + + memset (&pat, 0, sizeof(pat)); + memset (&addr, 0, sizeof(addr)); + + if (inet_pton_mapped(AF_INET6, token, &pat) != 1) + return (NO); + + if (inet_pton_mapped(AF_INET6, string, &addr) != 1) + { + tcpd_warn("Unable to handle client address: %s", string); + return (NO); } -#endif - return (STR_EQ(tok, string)); - } -} - -/* masked_match - match address against netnumber/netmask */ - -#ifdef INET6 -static int masked_match(net_tok, mask_tok, string) -char *net_tok; -char *mask_tok; -char *string; -{ - return (masked_match4(net_tok, mask_tok, string) || - masked_match6(net_tok, mask_tok, string)); -} - -static int masked_match4(net_tok, mask_tok, string) -#else -static int masked_match(net_tok, mask_tok, string) -#endif -char *net_tok; -char *mask_tok; -char *string; -{ -#ifdef INET6 - u_int32_t net; - u_int32_t mask; - u_int32_t addr; -#else - unsigned long net; - unsigned long mask; - unsigned long addr; -#endif - - /* - * Disallow forms other than dotted quad: the treatment that inet_addr() - * gives to forms with less than four components is inconsistent with the - * access control language. John P. Rouillard . - */ - - if ((addr = dot_quad_addr(string)) == INADDR_NONE) - return (NO); - if ((net = dot_quad_addr(net_tok)) == INADDR_NONE - || (mask = dot_quad_addr(mask_tok)) == INADDR_NONE) { -#ifndef INET6 - tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok); -#endif - return (NO); /* not tcpd_jump() */ - } - return ((addr & mask) == net); -} - -#ifdef INET6 -static int masked_match6(net_tok, mask_tok, string) -char *net_tok; -char *mask_tok; -char *string; -{ - struct in6_addr net, addr; - u_int32_t mask; - int len, mask_len, i = 0; - char ch; - - if (inet_pton(AF_INET6, string, addr.s6_addr) != 1) - return NO; - - if (IN6_IS_ADDR_V4MAPPED(&addr)) { - if ((*(u_int32_t *)&net.s6_addr[12] = dot_quad_addr(net_tok)) == INADDR_NONE - || (mask = dot_quad_addr(mask_tok)) == INADDR_NONE) - return (NO); - return ((*(u_int32_t *)&addr.s6_addr[12] & mask) == *(u_int32_t *)&net.s6_addr[12]); - } - /* match IPv6 address against netnumber/prefixlen */ - len = strlen(net_tok); - if (*net_tok != '[' || net_tok[len - 1] != ']') - return NO; - ch = net_tok[len - 1]; - net_tok[len - 1] = '\0'; - if (inet_pton(AF_INET6, net_tok + 1, net.s6_addr) != 1) { - net_tok[len - 1] = ch; - return NO; - } - net_tok[len - 1] = ch; - if ((mask_len = atoi(mask_tok)) < 0 || mask_len > 128) - return NO; - - while (mask_len > 0) { - if (mask_len < 32) { - mask = htonl(~(0xffffffff >> mask_len)); - if ((*(u_int32_t *)&addr.s6_addr[i] & mask) != (*(u_int32_t *)&net.s6_addr[i] & mask)) - return NO; - break; + if (prefixlen < 128) + { + apply_v6_prefix (&pat, prefixlen); + apply_v6_prefix (&addr, prefixlen); } - if (*(u_int32_t *)&addr.s6_addr[i] != *(u_int32_t *)&net.s6_addr[i]) - return NO; - i += 4; - mask_len -= 32; + + return (!memcmp(&pat, &addr, sizeof(struct in6_addr))); } - return YES; +#endif } -#endif /* INET6 */ --- /dev/null +++ ip6utils.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include + +#include "ip6utils.h" + +/* inet_pton_mapped() + - works like inet_pton(3) but always returns IPv6 address + in dst - either "real" or v4mapped (::ffff:1.2.3.4) in + the case, when src points to IPv4 address (eg. to 1.2.3.4). */ +int +inet_pton_mapped (int af, const char *src, void *dst) +{ + int ret; + + /* Mapped address is v6. */ + if (af != AF_INET6) + { + errno = EAFNOSUPPORT; + return -1; + } + + /* We must put the result somewhere. */ + if (!dst) + { + errno = EFAULT; + return -1; + } + + /* First try whether the address IPv6. */ + ret = inet_pton (AF_INET6, src, dst); + if (ret > 0) + return ret; + + /* Because we're here, it apparently wasn't IPv6. Try IPv4 now. */ + ret = inet_pton (AF_INET, src, &((struct in6_addr *)dst)->s6_addr32[3]); + if (ret > 0) + { + /* Good, it was IPv4, map it now. */ + ((struct in6_addr *)dst)->s6_addr32[0] = 0; + ((struct in6_addr *)dst)->s6_addr32[1] = 0; + ((struct in6_addr *)dst)->s6_addr32[2] = htonl(0x0000ffffL); + } + return ret; +} + +/* inet_ntop2() + - works like inet_ntop(3) but doesn't need an external + buffer. Usefull eg. for printing addresses via printf(). */ +const char * +inet_ntop2 (int af, const void *src) +{ + static char address[INET6_ADDRSTRLEN]; + + return inet_ntop(af, src, address, sizeof(address)); +} + +/* sa_map_v4_to_v6() + - Take an IPv4 address in first argument and map it to + IPv4-mapped (::ffff:1.2.3.4) IPv6 address. */ +struct sockaddr_in6 * +sa_map_v4_to_v6 (struct sockaddr_in *sin, struct sockaddr_in6 *sin6) +{ + /* Both pointers must be not-NULL or we'll segfault. */ + if (!sin || !sin6) + { + errno = EFAULT; + return NULL; + } + + /* We can map only IPv4 addresses. */ + if (sin->sin_family != AF_INET) + return NULL; + + /* Map it now... */ + memset(sin6, 0, sizeof(*sin6)); + + sin6->sin6_family = AF_INET6; + sin6->sin6_port = sin->sin_port; + sin6->sin6_addr.s6_addr16[5] = 0xffff; + sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; + + return sin6; +} + +/* is_v4_string(), is_v6_string() + - Return 1 when src is a string representing a valid + IPv4, resp. IPv6 address. Return 0 otherwise. */ +int +is_v4_string (const char *src) +{ + struct in_addr result; + + return (inet_pton (AF_INET, src, &result) > 0); +} + +int +is_v6_string (const char *src) +{ + struct in6_addr result; + + return (inet_pton (AF_INET6, src, &result) > 0); +} + +/* apply_v6_prefix() + - mask the address given in 'src' with 'prefixlen' netmask. Clear + all bits not covered by prefixlen. */ +int +apply_v6_prefix (struct in6_addr *src, int prefixlen) +{ + int i; + + /* Check prefix for a valid length. */ + if (prefixlen < 0 || prefixlen > 128) + return -1; + + /* Prefixes will quite often end up on 16b boundary, + so we'll walk thorugh 16b blocks and possibly avoid + creating bitmasks. */ + for (i=0; i<8; i++) + { + /* Prefix fully covers this block -> leave as is. */ + if (prefixlen >= (i+1)*16) + continue; + /* Prefix doesn't cover this block -> zero it. */ + if (prefixlen <= i*16) + { + src->s6_addr16[i] = 0; + continue; + } + /* Prefix ends somewhere inside in this block. Let's + build and apply a bitmask for this block. */ + { + uint16_t mask=0; + int bits; + + bits = prefixlen - i*16; + + while (bits) + { + mask |= (1 << (16-bits)); + bits --; + } + + src->s6_addr16[i] &= htons(mask); + } + } + + return 0; +} --- /dev/null +++ ip6utils.h @@ -0,0 +1,33 @@ +#ifndef IP6UTILS_H +#define IP6UTILS_H + +/* inet_pton_mapped() + - works like inet_pton(3) but always returns IPv6 address + in dst - either "real" or v4mapped (::ffff:1.2.3.4) in + the case, when src points to IPv4 address (eg. to 1.2.3.4). + Return value is as with inet_pton(), dst remains untouched on + an address translation failure. */ +int inet_pton_mapped (int af, const char *src, void *dst); + +/* inet_ntop2() + - works like inet_ntop(3) but doesn't need an external + buffer. Usefull eg. for printing addresses via printf(). */ +const char *inet_ntop2 (int af, const void *src); + +/* sa_map_v4_to_v6() + - Take an IPv4 address in form 1.2.3.4 and map it to + IPv4-mapped form ::ffff:1.2.3.4 */ +struct sockaddr_in6 *sa_map_v4_to_v6 (struct sockaddr_in *sin, struct sockaddr_in6 *sin6); + +/* is_v4_string(), is_v6_string() + - Return 1 when src is a string representing a valid + IPv4, resp. IPv6 address. Return 0 otherwise. */ +int is_v4_string (const char *src); +int is_v6_string (const char *src); + +/* apply_v6_prefix() + - mask the address given in 'src' with 'prefixlen' netmask. Clear + all bits not covered by prefixlen. Return -1 on a failure, else 0. */ +int apply_v6_prefix (struct in6_addr *src, int prefixlen); + +#endif /* IP6UTILS_H */ --- socket.c.orig +++ socket.c @@ -25,16 +25,12 @@ static char sccsid[] = "@(#) socket.c 1. #include #include #include +#include #include #include #include #include - -#ifdef INET6 -#ifndef USE_GETIPNODEBY -#include -#endif -#endif +#include extern char *inet_ntoa(); @@ -65,10 +61,10 @@ char *name; */ if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) { - return (gethostbyname(name)); + return (gethostbyname(name)); } else { - sprintf(dot_name, "%s.", name); - return (gethostbyname(dot_name)); + sprintf(dot_name, "%s.", name); + return (gethostbyname(dot_name)); } } @@ -104,15 +100,15 @@ struct request_info *request; len = sizeof(client); if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) { - request->sink = sock_sink; - len = sizeof(client); - if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK, - (struct sockaddr *) & client, &len) < 0) { - tcpd_warn("can't get client address: %m"); - return; /* give up */ - } + request->sink = sock_sink; + len = sizeof(client); + if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK, + (struct sockaddr *) & client, &len) < 0) { + tcpd_warn("can't get client address: %m"); + return; /* give up */ + } #ifdef really_paranoid - memset(buf, 0 sizeof(buf)); + memset(buf, 0 sizeof(buf)); #endif } #ifdef INET6 @@ -129,8 +125,8 @@ struct request_info *request; len = sizeof(server); if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) { - tcpd_warn("getsockname: %m"); - return; + tcpd_warn("getsockname: %m"); + return; } #ifdef INET6 request->server->sin = (struct sockaddr *)&server; @@ -150,18 +146,18 @@ struct host_info *host; int alen; if (!sin) - return; + return; switch (sin->sa_family) { - case AF_INET: - ap = (char *)&((struct sockaddr_in *)sin)->sin_addr; - alen = sizeof(struct in_addr); - break; - case AF_INET6: - ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr; - alen = sizeof(struct in6_addr); - break; - default: - return; + case AF_INET: + ap = (char *)&((struct sockaddr_in *)sin)->sin_addr; + alen = sizeof(struct in_addr); + break; + case AF_INET6: + ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr; + alen = sizeof(struct in6_addr); + break; + default: + return; } host->addr[0] = '\0'; inet_ntop(sin->sa_family, ap, host->addr, sizeof(host->addr)); @@ -169,30 +165,139 @@ struct host_info *host; struct sockaddr_in *sin = host->sin; if (sin != 0) - STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr)); + STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr)); #endif } +#ifdef INET6 /* sock_hostname - map endpoint address to host name */ +void +sock_hostname(struct host_info *host) +{ + struct addrinfo hints, *res, *resbase; + struct sockaddr *sa = host->sin; + struct sockaddr_in6 *sin6, sin6buf; + int errcode; + + if (!sa) + { + /* Unknown sockaddr => unable to verify */ + tcpd_warn ("can't verify hostname: sockaddr == NULL"); + strncpy(host->name, paranoid, sizeof(host->name)); + return; + } + + switch (sa->sa_family) + { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_addr.s_addr == 0) + { + /* Address 0.0.0.0 is invalid. */ + tcpd_warn ("can't verify hostname of address %s", + inet_ntop2(sa->sa_family, + &((struct sockaddr_in *)sa)->sin_addr)); + strncpy(host->name, paranoid, sizeof(host->name)); + return; + } + sin6 = sa_map_v4_to_v6 ((struct sockaddr_in *)sa, + &sin6buf); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + break; + default: + /* Unknown protocol family. */ + strncpy(host->name, paranoid, sizeof(host->name)); + return; + } + + /* First resolve address to name... */ + if (getnameinfo ((struct sockaddr *)sin6, sizeof(*sin6), + host->name, sizeof(host->name), + NULL, 0, 0) < 0) + { + tcpd_warn ("can't verify hostname: getnameinfo(%s): %s", + inet_ntop2(sin6->sin6_family, &sin6->sin6_addr), + strerror(errno)); + strncpy(host->name, paranoid, sizeof(host->name)); + return; + } + + /* Now resolve the name back to the address. Hopefully we'll + get the same one... */ + + memset (&hints, 0, sizeof(hints)); + + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + errcode = getaddrinfo(host->name, NULL, &hints, &resbase); + if(errcode) + { + tcpd_warn ("can't verify hostname: getaddrinfo(%s): %s", + host->name, + gai_strerror(errcode)); + strncpy(host->name, paranoid, sizeof(host->name)); + return; + } + + res = resbase; + + /* Now walk through all reutrned addresses and see if at least one + is the same (or mmapped-same) as the incoming one. */ + while(res) + { + struct sockaddr_in6 *sin6res, sin6resbuf; + + switch (res->ai_family) + { + case AF_INET: + sin6res = sa_map_v4_to_v6 ((struct sockaddr_in *)res->ai_addr, &sin6resbuf); + break; + case AF_INET6: + sin6res = (struct sockaddr_in6 *)res->ai_addr; + break; + default: + res = res->ai_next; + continue; + } + + if (memcmp (&sin6->sin6_addr, &sin6res->sin6_addr, + sizeof(sin6->sin6_addr)) == 0) + break; + + res = res->ai_next; + } + + freeaddrinfo (resbase); + + if (res == NULL) + { + /* We walked through the list but didn't find a matching address. */ + tcpd_warn ("can't verify hostname: getaddrinfo(%s) didn't return %s", + host->name, + inet_ntop2 (sin6->sin6_family, &sin6->sin6_addr)); + strncpy(host->name, paranoid, sizeof(host->name)); + return; + } -void sock_hostname(host) + if (STR_NE (host->name, res->ai_canonname) && STR_NE(host->name, "localhost")) + { + /* We don't treat this as an error, though... */ + tcpd_warn("host name mismatch: %s != %s (%s)", + host->name, res->ai_canonname, + inet_ntop2 (sin6->sin6_family, &sin6->sin6_addr)); + } + + return; +} +#else /* INET6 */ +void sock_hostname(host) struct host_info *host; { -#ifdef INET6 - struct sockaddr *sin = host->sin; - char addr[128]; -#ifdef USE_GETIPNODEBY - int h_error; -#else - u_long res_options; -#endif - struct hostent *hp = NULL; - char *ap; - int alen; -#else struct sockaddr_in *sin = host->sin; struct hostent *hp; -#endif int i; /* @@ -202,163 +307,76 @@ struct host_info *host; * have to special-case 0.0.0.0, in order to avoid false alerts from the * host name/address checking code below. */ -#ifdef INET6 - if (sin != NULL) { - switch (sin->sa_family) { - case AF_INET: - if (((struct sockaddr_in *)sin)->sin_addr.s_addr == 0) { - strcpy(host->name, paranoid); /* name is bad, clobber it */ - return; - } - ap = (char *) &((struct sockaddr_in *)sin)->sin_addr; - alen = sizeof(struct in_addr); - break; - case AF_INET6: - ap = (char *) &((struct sockaddr_in6 *)sin)->sin6_addr; - alen = sizeof(struct in6_addr); - break; - defalut: - strcpy(host->name, paranoid); /* name is bad, clobber it */ - return; - } -#ifdef USE_GETIPNODEBY - hp = getipnodebyaddr(ap, alen, sin->sa_family, &h_error); -#else - hp = gethostbyaddr(ap, alen, sin->sa_family); -#endif - } - if (hp) { -#else if (sin != 0 && sin->sin_addr.s_addr != 0 - && (hp = gethostbyaddr((char *) &(sin->sin_addr), - sizeof(sin->sin_addr), AF_INET)) != 0) { -#endif - - STRN_CPY(host->name, hp->h_name, sizeof(host->name)); -#if defined(INET6) && defined(USE_GETIPNODEBY) - freehostent(hp); -#endif + && (hp = gethostbyaddr((char *) &(sin->sin_addr), + sizeof(sin->sin_addr), AF_INET)) != 0) { - /* - * Verify that the address is a member of the address list returned - * by gethostbyname(hostname). - * - * Verify also that gethostbyaddr() and gethostbyname() return the same - * hostname, or rshd and rlogind may still end up being spoofed. - * - * On some sites, gethostbyname("localhost") returns "localhost.domain". - * This is a DNS artefact. We treat it as a special case. When we - * can't believe the address list from gethostbyname("localhost") - * we're in big trouble anyway. - */ + STRN_CPY(host->name, hp->h_name, sizeof(host->name)); -#ifdef INET6 -#ifdef USE_GETIPNODEBY - hp = getipnodebyname(host->name, sin->sa_family, - AI_V4MAPPED | AI_ADDRCONFIG | AI_ALL, &h_error); -#else - if ((_res.options & RES_INIT) == 0) { - if (res_init() < 0) { - inet_ntop(sin->sa_family, ap, addr, sizeof(addr)); - tcpd_warn("can't verify hostname: res_init() for %s failed", - addr); - strcpy(host->name, paranoid); /* name is bad, clobber it */ - return; - } - } - res_options = _res.options; - if (sin->sa_family == AF_INET6) - _res.options |= RES_USE_INET6; - else - _res.options &= ~RES_USE_INET6; - hp = gethostbyname2(host->name, - (sin->sa_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sin)->sin6_addr)) ? - AF_INET : sin->sa_family); - _res.options = res_options; -#endif - if (!hp) { -#else - if ((hp = gethostbyname(host->name)) == 0) { -#endif - - /* - * Unable to verify that the host name matches the address. This - * may be a transient problem or a botched name server setup. - */ + /* + * Verify that the address is a member of the address list returned + * by gethostbyname(hostname). + * + * Verify also that gethostbyaddr() and gethostbyname() return the same + * hostname, or rshd and rlogind may still end up being spoofed. + * + * On some sites, gethostbyname("localhost") returns "localhost.domain". + * This is a DNS artefact. We treat it as a special case. When we + * can't believe the address list from gethostbyname("localhost") + * we're in big trouble anyway. + */ -#ifdef INET6 -#ifdef USE_GETIPNODEBY - tcpd_warn("can't verify hostname: getipnodebyname(%s, %s) failed", -#else - tcpd_warn("can't verify hostname: gethostbyname2(%s, %s) failed", -#endif - host->name, - (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6"); -#else - tcpd_warn("can't verify hostname: gethostbyname(%s) failed", - host->name); -#endif + if ((hp = gethostbyname(host->name)) == 0) { - } else if (STR_NE(host->name, hp->h_name) - && STR_NE(host->name, "localhost")) { + /* + * Unable to verify that the host name matches the address. This + * may be a transient problem or a botched name server setup. + */ + + tcpd_warn("can't verify hostname: gethostbyname(%s) failed", + host->name); + + } else if (STR_NE(host->name, hp->h_name) + && STR_NE(host->name, "localhost")) { + + /* + * The gethostbyaddr() and gethostbyname() calls did not return + * the same hostname. This could be a nameserver configuration + * problem. It could also be that someone is trying to spoof us. + */ - /* - * The gethostbyaddr() and gethostbyname() calls did not return - * the same hostname. This could be a nameserver configuration - * problem. It could also be that someone is trying to spoof us. - */ - - tcpd_warn("host name/name mismatch: %s != %.*s", - host->name, STRING_LENGTH, hp->h_name); - - } else { - - /* - * The address should be a member of the address list returned by - * gethostbyname(). We should first verify that the h_addrtype - * field is AF_INET, but this program has already caused too much - * grief on systems with broken library code. - */ + tcpd_warn("host name/name mismatch: %s != %.*s", + host->name, STRING_LENGTH, hp->h_name); - for (i = 0; hp->h_addr_list[i]; i++) { -#ifdef INET6 - if (memcmp(hp->h_addr_list[i], ap, alen) == 0) { -#ifdef USE_GETIPNODEBY - freehostent(hp); -#endif - return; /* name is good, keep it */ - } -#else - if (memcmp(hp->h_addr_list[i], - (char *) &sin->sin_addr, - sizeof(sin->sin_addr)) == 0) - return; /* name is good, keep it */ -#endif - } + } else { - /* - * The host name does not map to the initial address. Perhaps - * someone has messed up. Perhaps someone compromised a name - * server. - */ + /* + * The address should be a member of the address list returned by + * gethostbyname(). We should first verify that the h_addrtype + * field is AF_INET, but this program has already caused too much + * grief on systems with broken library code. + */ + + for (i = 0; hp->h_addr_list[i]; i++) { + if (memcmp(hp->h_addr_list[i], + (char *) &sin->sin_addr, + sizeof(sin->sin_addr)) == 0) + return; /* name is good, keep it */ + } + + /* + * The host name does not map to the initial address. Perhaps + * someone has messed up. Perhaps someone compromised a name + * server. + */ -#ifdef INET6 - inet_ntop(sin->sa_family, ap, addr, sizeof(addr)); - tcpd_warn("host name/address mismatch: %s != %.*s", - addr, STRING_LENGTH, hp->h_name); -#else - tcpd_warn("host name/address mismatch: %s != %.*s", - inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name); -#endif - } - strcpy(host->name, paranoid); /* name is bad, clobber it */ -#if defined(INET6) && defined(USE_GETIPNODEBY) - if (hp) - freehostent(hp); -#endif + tcpd_warn("host name/address mismatch: %s != %.*s", + inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name); + } + strcpy(host->name, paranoid); /* name is bad, clobber it */ } } +#endif /* INET6 */ /* sock_sink - absorb unreceived IP datagram */ --- tcpd.h.orig +++ tcpd.h @@ -10,6 +10,8 @@ #include #endif +#include "ip6utils.h" + /* Structure to describe one communications endpoint. */ #define STRING_LENGTH 128 /* hosts, users, processes */