From b208ff84c19755dc340ca684184834efbad33508 Mon Sep 17 00:00:00 2001 From: Hajimu UMEMOTO Date: Fri, 14 Jul 2000 17:15:34 +0000 Subject: [PATCH] Add IPv6 scoped address support. It enables us to control link-local connections by interface like this: ALL : [fe80::%ed0]/10 : allow ALL : [fe80::]/10 : deny --- contrib/tcp_wrappers/hosts_access.c | 59 +++++-- contrib/tcp_wrappers/scaffold.c | 196 ++++++-------------- contrib/tcp_wrappers/scaffold.h | 6 + contrib/tcp_wrappers/socket.c | 265 +++++++++++++++------------- contrib/tcp_wrappers/tcpdchk.c | 11 +- contrib/tcp_wrappers/tcpdmatch.c | 94 +++++----- lib/libwrap/Makefile | 1 - usr.sbin/tcpdchk/Makefile | 4 +- usr.sbin/tcpdmatch/Makefile | 3 + 9 files changed, 310 insertions(+), 329 deletions(-) diff --git a/contrib/tcp_wrappers/hosts_access.c b/contrib/tcp_wrappers/hosts_access.c index 2b4dcee9ef9a..24f5753fa3bc 100644 --- a/contrib/tcp_wrappers/hosts_access.c +++ b/contrib/tcp_wrappers/hosts_access.c @@ -41,6 +41,9 @@ static char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22"; #include #include #include +#ifdef INET6 +#include +#endif extern char *fgets(); extern int errno; @@ -341,7 +344,8 @@ char *string; return (STRN_EQ(tok, string, n)); } else { /* exact match */ #ifdef INET6 - struct in6_addr pat, addr; + struct addrinfo hints, *res; + struct sockaddr_in6 pat, addr; int len, ret; char ch; @@ -349,11 +353,27 @@ char *string; if (*tok == '[' && tok[len - 1] == ']') { ch = tok[len - 1]; tok[len - 1] = '\0'; - ret = inet_pton(AF_INET6, tok + 1, pat.s6_addr); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + if ((ret = getaddrinfo(tok + 1, NULL, &hints, &res)) == 0) { + memcpy(&pat, res->ai_addr, sizeof(pat)); + freeaddrinfo(res); + } tok[len - 1] = ch; - if (ret != 1 || inet_pton(AF_INET6, string, addr.s6_addr) != 1) + if (ret != 0 || getaddrinfo(string, NULL, &hints, &res) != 0) return NO; - return (!memcmp(&pat, &addr, sizeof(struct in6_addr))); + memcpy(&addr, res->ai_addr, sizeof(addr)); + freeaddrinfo(res); +#ifdef NI_WITHSCOPEID + if (pat.sin6_scope_id != 0 && + addr.sin6_scope_id != pat.sin6_scope_id) + return NO; +#endif + return (!memcmp(&pat.sin6_addr, &addr.sin6_addr, + sizeof(struct in6_addr))); + return (ret); } #endif return (STR_EQ(tok, string)); @@ -414,19 +434,26 @@ char *net_tok; char *mask_tok; char *string; { - struct in6_addr net, addr; + struct addrinfo hints, *res; + struct sockaddr_in6 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; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + if (getaddrinfo(string, NULL, &hints, &res) != 0) + return NO; + memcpy(&addr, res->ai_addr, sizeof(addr)); + freeaddrinfo(res); - if (IN6_IS_ADDR_V4MAPPED(&addr)) { - if ((*(u_int32_t *)&net.s6_addr[12] = dot_quad_addr(net_tok)) == INADDR_NONE + if (IN6_IS_ADDR_V4MAPPED(&addr.sin6_addr)) { + if ((*(u_int32_t *)&net.sin6_addr.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]); + return ((*(u_int32_t *)&addr.sin6_addr.s6_addr[12] & mask) == *(u_int32_t *)&net.sin6_addr.s6_addr[12]); } /* match IPv6 address against netnumber/prefixlen */ @@ -435,22 +462,28 @@ char *string; return NO; ch = net_tok[len - 1]; net_tok[len - 1] = '\0'; - if (inet_pton(AF_INET6, net_tok + 1, net.s6_addr) != 1) { + if (getaddrinfo(net_tok + 1, NULL, &hints, &res) != 0) { net_tok[len - 1] = ch; return NO; } + memcpy(&net, res->ai_addr, sizeof(net)); + freeaddrinfo(res); net_tok[len - 1] = ch; if ((mask_len = atoi(mask_tok)) < 0 || mask_len > 128) return NO; +#ifdef NI_WITHSCOPEID + if (net.sin6_scope_id != 0 && addr.sin6_scope_id != net.sin6_scope_id) + return NO; +#endif 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)) + if ((*(u_int32_t *)&addr.sin6_addr.s6_addr[i] & mask) != (*(u_int32_t *)&net.sin6_addr.s6_addr[i] & mask)) return NO; break; } - if (*(u_int32_t *)&addr.s6_addr[i] != *(u_int32_t *)&net.s6_addr[i]) + if (*(u_int32_t *)&addr.sin6_addr.s6_addr[i] != *(u_int32_t *)&net.sin6_addr.s6_addr[i]) return NO; i += 4; mask_len -= 32; diff --git a/contrib/tcp_wrappers/scaffold.c b/contrib/tcp_wrappers/scaffold.c index ea87c5c65419..8da9df0c6861 100644 --- a/contrib/tcp_wrappers/scaffold.c +++ b/contrib/tcp_wrappers/scaffold.c @@ -22,13 +22,14 @@ static char sccs_id[] = "@(#) scaffold.c 1.6 97/03/21 19:27:24"; #include #include #include -#include #ifndef INADDR_NONE #define INADDR_NONE (-1) /* XXX should be 0xffffffff */ #endif +#ifndef INET6 extern char *malloc(); +#endif /* Application-specific. */ @@ -42,6 +43,7 @@ int allow_severity = SEVERITY; int deny_severity = LOG_WARNING; int rfc931_timeout = RFC931_TIMEOUT; +#ifndef INET6 /* dup_hostent - create hostent in one memory block */ static struct hostent *dup_hostent(hp) @@ -60,9 +62,6 @@ struct hostent *hp; /* void */ ; if ((hb = (struct hostent_block *) malloc(sizeof(struct hostent_block) -#ifdef INET6 - + strlen(hp->h_name) + 1 -#endif + (hp->h_length + sizeof(char *)) * count)) == 0) { fprintf(stderr, "Sorry, out of memory\n"); exit(1); @@ -72,11 +71,6 @@ struct hostent *hp; hb->host.h_addr_list = hb->addr_list; hb->host.h_addr_list[count] = 0; data = (char *) (hb->host.h_addr_list + count + 1); -#ifdef INET6 - hb->host.h_name = data + hp->h_length * count; - strcpy(hb->host.h_name, hp->h_name); - hb->host.h_addrtype = hp->h_addrtype; -#endif for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { hb->host.h_addr_list[count] = data + hp->h_length * count; @@ -84,107 +78,46 @@ struct hostent *hp; } return (&hb->host); } - -#if defined(INET6) && !defined(USE_GETIPNODEBY) -/* merge_hostent - merge hostent in one memory block */ - -static struct hostent *merge_hostent(hp1, hp2) -struct hostent *hp1, *hp2; -{ - struct hostent_block { - struct hostent host; - char *addr_list[1]; - }; - struct hostent_block *hb; - int count, count2; - char *data; - char *addr; - - for (count = 0; hp1->h_addr_list[count] != 0; count++) - /* void */ ; - for (count2 = 0; hp2->h_addr_list[count2] != 0; count2++) - /* void */ ; - count += count2; - - if ((hb = (struct hostent_block *) malloc(sizeof(struct hostent_block) - + strlen(hp1->h_name) + 1 - + (hp1->h_length + sizeof(char *)) * count)) == 0) { - fprintf(stderr, "Sorry, out of memory\n"); - exit(1); - } - memset((char *) &hb->host, 0, sizeof(hb->host)); - hb->host.h_length = hp1->h_length; - hb->host.h_addr_list = hb->addr_list; - hb->host.h_addr_list[count] = 0; - data = (char *) (hb->host.h_addr_list + count + 1); - hb->host.h_name = data + hp1->h_length * count; - strcpy(hb->host.h_name, hp1->h_name); - hb->host.h_addrtype = hp1->h_addrtype; - - for (count = 0; (addr = hp1->h_addr_list[count]) != 0; count++) { - hb->host.h_addr_list[count] = data + hp1->h_length * count; - memcpy(hb->host.h_addr_list[count], addr, hp1->h_length); - } - for (count2 = 0; (addr = hp2->h_addr_list[count2]) != 0; count2++) { - hb->host.h_addr_list[count] = data + hp1->h_length * count; - memcpy(hb->host.h_addr_list[count], addr, hp1->h_length); - ++count; - } - return (&hb->host); -} #endif -static struct hostent *gethostbyname64(host) -char *host; -{ - struct hostent *hp = NULL, *hp2 = NULL; -#ifdef USE_GETIPNODEBY - int h_error; - - if ((hp = getipnodebyname(host, AF_INET6, - AI_V4MAPPED | AI_ADDRCONFIG | AI_ALL, - &h_error)) != 0) { - hp2 = dup_hostent(hp); - freehostent(hp); - return (hp2); - } -#else - struct hostent *hp1; - u_long res_options; - - if ((_res.options & RES_INIT) == 0) { - if (res_init() < 0) { - tcpd_warn("%s: res_init() failed", host); - return (NULL); - } - } - res_options = _res.options; -#ifdef INET6 - _res.options |= RES_USE_INET6; - if ((hp1 = gethostbyname2(host, AF_INET6)) != NULL) - hp1 = dup_hostent(hp1); -#endif - if ((hp2 = gethostbyname2(host, AF_INET)) != NULL) - hp2 = dup_hostent(hp2); - _res.options = res_options; -#ifdef INET6 - if (hp1 && hp2) { - hp = merge_hostent(hp1, hp2); - free((char *) hp1); - free((char *) hp2); - return (hp); - } - if (hp1) - return (hp1); -#endif - if (hp2) - return (hp2); -#endif - return (NULL); -} - /* find_inet_addr - find all addresses for this host, result to free() */ +#ifdef INET6 +struct addrinfo *find_inet_addr(host) +char *host; +{ + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + if (getaddrinfo(host, NULL, &hints, &res) == 0) + return (res); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_CANONNAME; + if (getaddrinfo(host, NULL, &hints, &res) != 0) { + tcpd_warn("%s: host not found", host); + return (0); + } + if (res->ai_family != AF_INET6 && res->ai_family != AF_INET) { + tcpd_warn("%d: not an internet host", res->ai_family); + freeaddrinfo(res); + return (0); + } + if (!res->ai_canonname) { + tcpd_warn("%s: hostname alias", host); + tcpd_warn("(cannot obtain official name)", res->ai_canonname); + } else if (STR_NE(host, res->ai_canonname)) { + tcpd_warn("%s: hostname alias", host); + tcpd_warn("(official name: %.*s)", STRING_LENGTH, res->ai_canonname); + } + return (res); +} +#else struct hostent *find_inet_addr(host) char *host; { @@ -192,7 +125,6 @@ char *host; struct hostent *hp; static struct hostent h; static char *addr_list[2]; - static char hnamebuf[BUFSIZ]; /* * Host address: translate it to internal form. @@ -201,11 +133,6 @@ char *host; h.h_addr_list = addr_list; h.h_addr_list[0] = (char *) &addr; h.h_length = sizeof(addr); -#ifdef INET6 - h.h_addrtype = AF_INET; - h.h_name = hnamebuf; - strcpy(h.h_name, host); -#endif return (dup_hostent(&h)); } @@ -219,34 +146,21 @@ char *host; tcpd_warn("%s: not an internet address", host); return (0); } -#ifdef INET6 - if ((hp = gethostbyname64(host)) == 0) { -#else if ((hp = gethostbyname(host)) == 0) { -#endif tcpd_warn("%s: host not found", host); return (0); } -#ifdef INET6 - if (hp->h_addrtype != AF_INET6) { - tcpd_warn("%d: not an internet host", hp->h_addrtype); - free((char *) hp); -#else if (hp->h_addrtype != AF_INET) { tcpd_warn("%d: not an internet host", hp->h_addrtype); -#endif return (0); } if (STR_NE(host, hp->h_name)) { tcpd_warn("%s: hostname alias", host); tcpd_warn("(official name: %.*s)", STRING_LENGTH, hp->h_name); } -#ifdef INET6 - return (hp); -#else return (dup_hostent(hp)); -#endif } +#endif /* check_dns - give each address thorough workout, return address count */ @@ -256,12 +170,11 @@ char *host; struct request_info request; #ifdef INET6 struct sockaddr_storage sin; - char *ap; - int alen; + struct addrinfo *hp, *res; #else struct sockaddr_in sin; -#endif struct hostent *hp; +#endif int count; char *addr; @@ -269,29 +182,16 @@ char *host; return (0); request_init(&request, RQ_CLIENT_SIN, &sin, 0); sock_methods(&request); +#ifndef INET6 memset((char *) &sin, 0, sizeof(sin)); -#ifdef INET6 - sin.ss_family = hp->h_addrtype; - switch (hp->h_addrtype) { - case AF_INET: - ap = (char *)&((struct sockaddr_in *)&sin)->sin_addr; - alen = sizeof(struct sockaddr_in); - break; - case AF_INET6: - ap = (char *)&((struct sockaddr_in6 *)&sin)->sin6_addr; - alen = sizeof(struct sockaddr_in6); - break; - default: - return (0); - } -#else sin.sin_family = AF_INET; #endif - for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { #ifdef INET6 - memcpy(ap, addr, alen); + for (res = hp, count = 0; res; res = res->ai_next, count++) { + memcpy(&sin, res->ai_addr, res->ai_addrlen); #else + for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { memcpy((char *) &sin.sin_addr, addr, sizeof(sin.sin_addr)); #endif @@ -306,7 +206,11 @@ char *host; tcpd_warn("host address %s->name lookup failed", eval_hostaddr(request.client)); } +#ifdef INET6 + freeaddrinfo(hp); +#else free((char *) hp); +#endif return (count); } diff --git a/contrib/tcp_wrappers/scaffold.h b/contrib/tcp_wrappers/scaffold.h index 2273f4730b3f..ea568b722507 100644 --- a/contrib/tcp_wrappers/scaffold.h +++ b/contrib/tcp_wrappers/scaffold.h @@ -2,8 +2,14 @@ * @(#) scaffold.h 1.3 94/12/31 18:19:19 * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + * + * $FreeBSD$ */ +#ifdef INET6 +extern struct addrinfo *find_inet_addr(); +#else extern struct hostent *find_inet_addr(); +#endif extern int check_dns(); extern int check_path(); diff --git a/contrib/tcp_wrappers/socket.c b/contrib/tcp_wrappers/socket.c index d2370e0e967b..0a24aca40475 100644 --- a/contrib/tcp_wrappers/socket.c +++ b/contrib/tcp_wrappers/socket.c @@ -33,12 +33,12 @@ static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24"; #include #ifdef INET6 -#ifndef USE_GETIPNODEBY -#include +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 #endif -#endif - +#else extern char *inet_ntoa(); +#endif /* Local stuff. */ @@ -148,25 +148,18 @@ struct host_info *host; { #ifdef INET6 struct sockaddr *sin = host->sin; - char *ap; - int alen; + int salen; if (!sin) 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; - } - host->addr[0] = '\0'; - inet_ntop(sin->sa_family, ap, host->addr, sizeof(host->addr)); +#ifdef SIN6_LEN + salen = sin->sa_len; +#else + salen = (sin->sa_family == AF_INET) ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6); +#endif + getnameinfo(sin, salen, host->addr, sizeof(host->addr), + NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); #else struct sockaddr_in *sin = host->sin; @@ -182,64 +175,47 @@ 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; + struct sockaddr_in sin4; + struct addrinfo hints, *res, *res0 = NULL; + int salen, alen, err = 1; + char *ap = NULL, *rap, hname[NI_MAXHOST]; - /* - * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does - * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does - * not work the other way around: gethostbyname("INADDR_ANY") fails. We - * 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) { + if (sin->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin; + + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + memset(&sin4, 0, sizeof(sin4)); +#ifdef SIN6_LEN + sin4.sin_len = sizeof(sin4); +#endif + sin4.sin_family = AF_INET; + sin4.sin_port = sin6->sin6_port; + sin4.sin_addr.s_addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; + sin = (struct sockaddr *)&sin4; + } + } 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; + ap = (char *)&((struct sockaddr_in *)sin)->sin_addr; alen = sizeof(struct in_addr); + salen = sizeof(struct sockaddr_in); break; case AF_INET6: - ap = (char *) &((struct sockaddr_in6 *)sin)->sin6_addr; + ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr; alen = sizeof(struct in6_addr); + salen = sizeof(struct sockaddr_in6); + break; + default: 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 (ap) + err = getnameinfo(sin, salen, hname, sizeof(hname), + NULL, 0, NI_WITHSCOPEID | NI_NAMEREQD); } - 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 + if (!err) { - STRN_CPY(host->name, hp->h_name, sizeof(host->name)); -#if defined(INET6) && defined(USE_GETIPNODEBY) - freehostent(hp); -#endif + STRN_CPY(host->name, hname, sizeof(host->name)); /* * Verify that the address is a member of the address list returned @@ -254,53 +230,116 @@ struct host_info *host; * we're in big trouble anyway. */ -#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 + memset(&hints, 0, sizeof(hints)); + hints.ai_family = sin->sa_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_CANONNAME; + if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) { /* * Unable to verify that the host name matches the address. This * may be a transient problem or a botched name server setup. */ -#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 + tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed", host->name, (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6"); -#else + + } else if (STR_NE(host->name, res0->ai_canonname) + && 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. + */ + + tcpd_warn("host name/name mismatch: %s != %.*s", + host->name, STRING_LENGTH, res0->ai_canonname); + + } 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. + */ + + for (res = res0; res; res = res->ai_next) { + if (res->ai_family != sin->sa_family) + continue; + switch (res->ai_family) { + case AF_INET: + rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + default: + continue; + } + if (memcmp(rap, ap, alen) == 0) { + freeaddrinfo(res0); + 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. + */ + + getnameinfo(sin, salen, hname, sizeof(hname), + NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID); + tcpd_warn("host name/address mismatch: %s != %.*s", + hname, STRING_LENGTH, res0->ai_canonname); + } + strcpy(host->name, paranoid); /* name is bad, clobber it */ + if (res0) + freeaddrinfo(res0); + } +#else /* INET6 */ + struct sockaddr_in *sin = host->sin; + struct hostent *hp; + int i; + + /* + * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does + * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does + * not work the other way around: gethostbyname("INADDR_ANY") fails. We + * have to special-case 0.0.0.0, in order to avoid false alerts from the + * host name/address checking code below. + */ + if (sin != 0 && sin->sin_addr.s_addr != 0 + && (hp = gethostbyaddr((char *) &(sin->sin_addr), + sizeof(sin->sin_addr), AF_INET)) != 0) { + + STRN_CPY(host->name, hp->h_name, sizeof(host->name)); + + /* + * 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. + */ + + if ((hp = gethostbyname(host->name)) == 0) { + + /* + * 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); -#endif } else if (STR_NE(host->name, hp->h_name) && STR_NE(host->name, "localhost")) { @@ -324,19 +363,10 @@ struct host_info *host; */ 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 } /* @@ -345,21 +375,12 @@ struct host_info *host; * 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 } +#endif /* INET6 */ } /* sock_sink - absorb unreceived IP datagram */ diff --git a/contrib/tcp_wrappers/tcpdchk.c b/contrib/tcp_wrappers/tcpdchk.c index a2804a264d6f..99ec49525300 100644 --- a/contrib/tcp_wrappers/tcpdchk.c +++ b/contrib/tcp_wrappers/tcpdchk.c @@ -410,7 +410,7 @@ char *pat; static int is_inet6_addr(pat) char *pat; { - struct in6_addr addr; + struct addrinfo hints, *res; int len, ret; char ch; @@ -420,9 +420,14 @@ static int is_inet6_addr(pat) if ((ch = pat[len - 1]) != ']') return (0); pat[len - 1] = '\0'; - ret = inet_pton(AF_INET6, pat + 1, &addr); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + if ((ret = getaddrinfo(pat + 1, NULL, &hints, &res)) == 0) + freeaddrinfo(res); pat[len - 1] = ch; - return (ret == 1); + return (ret == 0); } #endif diff --git a/contrib/tcp_wrappers/tcpdmatch.c b/contrib/tcp_wrappers/tcpdmatch.c index f8c8dc12456f..94f3e7009606 100644 --- a/contrib/tcp_wrappers/tcpdmatch.c +++ b/contrib/tcp_wrappers/tcpdmatch.c @@ -59,7 +59,11 @@ int main(argc, argv) int argc; char **argv; { +#ifdef INET6 + struct addrinfo hints, *hp, *res; +#else struct hostent *hp; +#endif char *myname = argv[0]; char *client; char *server; @@ -73,8 +77,6 @@ char **argv; #ifdef INET6 struct sockaddr_storage server_sin; struct sockaddr_storage client_sin; - char *ap; - int alen; #else struct sockaddr_in server_sin; struct sockaddr_in client_sin; @@ -181,33 +183,17 @@ char **argv; if (NOT_INADDR(server) == 0 || HOSTNAME_KNOWN(server)) { if ((hp = find_inet_addr(server)) == 0) exit(1); +#ifndef INET6 memset((char *) &server_sin, 0, sizeof(server_sin)); -#ifdef INET6 - server_sin.ss_family = hp->h_addrtype; - switch (hp->h_addrtype) { - case AF_INET: - ap = (char *)&((struct sockaddr_in *)&server_sin)->sin_addr; - alen = sizeof(struct sockaddr_in); - break; - case AF_INET6: - ap = (char *)&((struct sockaddr_in6 *)&server_sin)->sin6_addr; - alen = sizeof(struct sockaddr_in6); - break; - default: - exit(1); - } -#ifdef SIN6_LEN - server_sin.ss_len = alen; -#endif -#else server_sin.sin_family = AF_INET; #endif request_set(&request, RQ_SERVER_SIN, &server_sin, 0); - for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { #ifdef INET6 - memcpy(ap, addr, alen); + for (res = hp, count = 0; res; res = res->ai_next, count++) { + memcpy(&server_sin, res->ai_addr, res->ai_addrlen); #else + for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { memcpy((char *) &server_sin.sin_addr, addr, sizeof(server_sin.sin_addr)); #endif @@ -226,7 +212,11 @@ char **argv; fprintf(stderr, "Please specify an address instead\n"); exit(1); } +#ifdef INET6 + freeaddrinfo(hp); +#else free((char *) hp); +#endif } else { request_set(&request, RQ_SERVER_NAME, server, 0); } @@ -240,6 +230,18 @@ char **argv; tcpdmatch(&request); exit(0); } +#ifdef INET6 + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + if (getaddrinfo(client, NULL, &hints, &res) == 0) { + freeaddrinfo(res); + request_set(&request, RQ_CLIENT_ADDR, client, 0); + tcpdmatch(&request); + exit(0); + } +#endif /* * Perhaps they are testing special client hostname patterns that aren't @@ -261,36 +263,41 @@ char **argv; */ if ((hp = find_inet_addr(client)) == 0) exit(1); - memset((char *) &client_sin, 0, sizeof(client_sin)); #ifdef INET6 - client_sin.ss_family = hp->h_addrtype; - switch (hp->h_addrtype) { - case AF_INET: - ap = (char *)&((struct sockaddr_in *)&client_sin)->sin_addr; - alen = sizeof(struct sockaddr_in); - break; - case AF_INET6: - ap = (char *)&((struct sockaddr_in6 *)&client_sin)->sin6_addr; - alen = sizeof(struct sockaddr_in6); - break; - default: - exit(1); + request_set(&request, RQ_CLIENT_SIN, &client_sin, 0); + + for (res = hp, count = 0; res; res = res->ai_next, count++) { + memcpy(&client_sin, res->ai_addr, res->ai_addrlen); + + /* + * getnameinfo() doesn't do reverse lookup against link-local + * address. So, we pass through host name evaluation against + * such addresses. + */ + if (res->ai_family != AF_INET6 || + !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) { + /* + * Force evaluation of client host name and address. Host name + * conflicts will be reported while eval_hostname() does its job. + */ + request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0); + if (STR_EQ(eval_hostname(request.client), unknown)) + tcpd_warn("host address %s->name lookup failed", + eval_hostaddr(request.client)); + } + tcpdmatch(&request); + if (res->ai_next) + printf("\n"); } -#ifdef SIN6_LEN - client_sin.ss_len = alen; -#endif + freeaddrinfo(hp); #else + memset((char *) &client_sin, 0, sizeof(client_sin)); client_sin.sin_family = AF_INET; -#endif request_set(&request, RQ_CLIENT_SIN, &client_sin, 0); for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { -#ifdef INET6 - memcpy(ap, addr, alen); -#else memcpy((char *) &client_sin.sin_addr, addr, sizeof(client_sin.sin_addr)); -#endif /* * Force evaluation of client host name and address. Host name @@ -305,6 +312,7 @@ char **argv; printf("\n"); } free((char *) hp); +#endif exit(0); } diff --git a/lib/libwrap/Makefile b/lib/libwrap/Makefile index 4422cfaff6a3..022da441d670 100644 --- a/lib/libwrap/Makefile +++ b/lib/libwrap/Makefile @@ -17,7 +17,6 @@ CFLAGS+=-DFACILITY=LOG_AUTH -DHOSTS_ACCESS -DNETGROUP -DDAEMON_UMASK=022 \ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 \ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" \ -DSYS_ERRLIST_DEFINED -DALWAYS_HOSTNAME -CFLAGS+=-DUSE_GETIPNODEBY .if !defined(NOINET6) CFLAGS+=-DINET6 .endif diff --git a/usr.sbin/tcpdchk/Makefile b/usr.sbin/tcpdchk/Makefile index 86639f71fff7..3393011498fe 100644 --- a/usr.sbin/tcpdchk/Makefile +++ b/usr.sbin/tcpdchk/Makefile @@ -11,7 +11,9 @@ SRCS= tcpdchk.c fakelog.c inetcf.c scaffold.c CFLAGS= -DREAL_DAEMON_DIR=\"/usr/libexec\" \ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 \ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" -CFLAGS+=-DINET6 -DUSE_GETIPNODEBY +.if !defined(NOINET6) +CFLAGS+=-DINET6 +.endif DPADD= ${LIBWRAP} LDADD= -lwrap diff --git a/usr.sbin/tcpdmatch/Makefile b/usr.sbin/tcpdmatch/Makefile index c29eb91375df..e69c050f6964 100644 --- a/usr.sbin/tcpdmatch/Makefile +++ b/usr.sbin/tcpdmatch/Makefile @@ -10,6 +10,9 @@ SRCS= tcpdmatch.c fakelog.c inetcf.c scaffold.c CFLAGS= -DREAL_DAEMON_DIR=\"/usr/libexec\" \ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 +.if !defined(NOINET6) +CFLAGS+=-DINET6 +.endif DPADD= ${LIBWRAP} LDADD= -lwrap