diff options
author | 2002-06-07 03:32:04 +0000 | |
---|---|---|
committer | 2002-06-07 03:32:04 +0000 | |
commit | 67e33fe46ba81e4790a09ef4032bb26535108cbc (patch) | |
tree | b8ac4b99c4cb69588c57c201224e268c0bcc3e11 /lib/libwrap | |
parent | Backout previous change. Since it requires people to upgrade gcc, we want (diff) | |
download | wireguard-openbsd-67e33fe46ba81e4790a09ef4032bb26535108cbc.tar.xz wireguard-openbsd-67e33fe46ba81e4790a09ef4032bb26535108cbc.zip |
support scoped IPv6 address.
no visible API change, old config files work just fine.
now you can use expressions like "ALL: [fe80::%lo0/64]". theo ok
Diffstat (limited to 'lib/libwrap')
-rw-r--r-- | lib/libwrap/hosts_access.c | 220 | ||||
-rw-r--r-- | lib/libwrap/misc.c | 16 | ||||
-rw-r--r-- | lib/libwrap/socket.c | 177 |
3 files changed, 227 insertions, 186 deletions
diff --git a/lib/libwrap/hosts_access.c b/lib/libwrap/hosts_access.c index b1d183b06f6..1043d6d811f 100644 --- a/lib/libwrap/hosts_access.c +++ b/lib/libwrap/hosts_access.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hosts_access.c,v 1.7 2001/12/13 17:44:47 beck Exp $ */ +/* $OpenBSD: hosts_access.c,v 1.8 2002/06/07 03:32:04 itojun Exp $ */ /* * This module implements a simple access control language that is based on @@ -23,7 +23,7 @@ #if 0 static char sccsid[] = "@(#) hosts_access.c 1.21 97/02/12 02:13:22"; #else -static char rcsid[] = "$OpenBSD: hosts_access.c,v 1.7 2001/12/13 17:44:47 beck Exp $"; +static char rcsid[] = "$OpenBSD: hosts_access.c,v 1.8 2002/06/07 03:32:04 itojun Exp $"; #endif #endif @@ -45,12 +45,9 @@ static char rcsid[] = "$OpenBSD: hosts_access.c,v 1.7 2001/12/13 17:44:47 beck E #ifdef NETGROUP #include <netgroup.h> #endif +#include <netdb.h> -#ifndef INADDR_NONE -#define INADDR_NONE (-1) /* XXX should be 0xffffffff */ -#endif - /* Local stuff. */ #include "tcpd.h" @@ -85,16 +82,17 @@ int resident = (-1); /* -1, 0: unknown; +1: yes */ /* Forward declarations. */ -static int table_match(); -static int list_match(); -static int server_match(); -static int client_match(); -static int host_match(); -static int string_match(); -static int masked_match(); -static int masked_match4(); +static int table_match(char *, struct request_info *); +static int list_match(char *, struct request_info *, + int (*)(char *, struct request_info *)); +static int server_match(char *, struct request_info *); +static int client_match(char *, struct request_info *); +static int host_match(char *, struct host_info *); +static int string_match(char *, char *); +static int masked_match(char *, char *, char *); +static int masked_match4(char *, char *, char *); #ifdef INET6 -static int masked_match6(); +static int masked_match6(char *, char *, char *); #endif /* Size of logical line buffer. */ @@ -199,7 +197,7 @@ struct request_info *request; static int list_match(list, request, match_fn) char *list; struct request_info *request; -int (*match_fn) (); +int (*match_fn)(char *, struct request_info *); { char *tok; int l; @@ -333,9 +331,14 @@ char *string; #ifndef INET6 return masked_match4(net_tok, mask_tok, string); #else - if (dot_quad_addr_new(net_tok, NULL) - && dot_quad_addr_new(mask_tok, NULL) - && dot_quad_addr_new(string, NULL)) { + /* + * masked_match4() is kept just for supporting shortened IPv4 address form. + * If we could get rid of shortened IPv4 form, we could just always use + * masked_match6(). + */ + if (dot_quad_addr_new(net_tok, NULL) && + dot_quad_addr_new(mask_tok, NULL) && + dot_quad_addr_new(string, NULL)) { return masked_match4(net_tok, mask_tok, string); } else return masked_match6(net_tok, mask_tok, string); @@ -360,81 +363,152 @@ char *string; if (!dot_quad_addr_new(string, &addr)) return (NO); if (!dot_quad_addr_new(net_tok, &net) || - !dot_quad_addr_new(mask_tok, &mask)) { + !dot_quad_addr_new(mask_tok, &mask)) { tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok); return (NO); /* not tcpd_jump() */ } + + if ((net & ~mask) != 0) + tcpd_warn("host bits not all zero in %s/%s", net_tok, mask_tok); + return ((addr & mask) == net); } #ifdef INET6 -/* Ugly because it covers IPv4 mapped address. I hate mapped addresses. */ static int masked_match6(net_tok, mask_tok, string) char *net_tok; char *mask_tok; char *string; { - struct in6_addr net; - struct in6_addr mask; - struct in6_addr addr; - u_long masklen; - int fail; + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } net, mask, addr; + struct addrinfo hints, *res; + unsigned long masklen; + char *ep; int i; - int maskoff; - int netaf; - char *p; - const int sizoff64 = sizeof(struct in6_addr) - sizeof(struct in_addr); - - memset(&addr, 0, sizeof(addr)); - if (inet_pton(AF_INET6, string, &addr) == 1) - ; /* okay */ - else if (inet_pton(AF_INET, string, &addr.s6_addr[sizoff64]) == 1) - addr.s6_addr[10] = addr.s6_addr[11] = 0xff; - else - return NO; - - memset(&net, 0, sizeof(net)); - if (inet_pton(AF_INET6, net_tok, &net) == 1) { - netaf = AF_INET6; - maskoff = 0; - } else if (inet_pton(AF_INET, net_tok, &net.s6_addr[sizoff64]) == 1) { - netaf = AF_INET; - maskoff = sizoff64; - net.s6_addr[10] = net.s6_addr[11] = 0xff; + char *np, *mp, *ap; + int alen; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(net_tok, "0", &hints, &res) == 0) { + if (res->ai_addrlen > sizeof(net) || res->ai_next) { + freeaddrinfo(res); + return NO; + } + memcpy(&net, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } else return NO; - fail = 0; - if (mask_tok[strspn(mask_tok, "0123456789")] == '\0') { - errno = 0; - masklen = strtoul(mask_tok, &p, 10); - if (!*mask_tok || *p || (errno == ERANGE && masklen == ULONG_MAX)) - goto bogusmask; - masklen += maskoff * 8; - if (0 <= masklen && masklen <= 128) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = net.sa.sa_family; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + ep = NULL; + if (getaddrinfo(mask_tok, "0", &hints, &res) == 0) { + if (res->ai_family == AF_INET6 && + ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) { + freeaddrinfo(res); + return NO; + } + if (res->ai_addrlen > sizeof(mask) || res->ai_next) { + freeaddrinfo(res); + return NO; + } + memcpy(&mask, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } else { + ep = NULL; + masklen = strtoul(mask_tok, &ep, 10); + if (ep && !*ep) { memset(&mask, 0, sizeof(mask)); - memset(&mask, 0xff, masklen / 8); - if (masklen % 8) { - ((u_char *)&mask)[masklen / 8] = - (0xff00 >> (masklen % 8)) & 0xff; + mask.sa.sa_family = net.sa.sa_family; + mask.sa.sa_len = net.sa.sa_len; + switch (mask.sa.sa_family) { + case AF_INET: + mp = (char *)&mask.sin.sin_addr; + alen = sizeof(mask.sin.sin_addr); + break; + case AF_INET6: + mp = (char *)&mask.sin6.sin6_addr; + alen = sizeof(mask.sin6.sin6_addr); + break; + default: + return NO; } + if (masklen / 8 > alen) + return NO; + memset(mp, 0xff, masklen / 8); + if (masklen % 8) + mp[masklen / 8] = 0xff00 >> (masklen % 8); + } else + return NO; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(string, "0", &hints, &res) == 0) { + if (res->ai_addrlen > sizeof(addr) || res->ai_next) { + freeaddrinfo(res); + return NO; + } + /* special case - IPv4 mapped address */ + if (net.sa.sa_family == AF_INET && res->ai_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) { + memset(&addr, 0, sizeof(addr)); + addr.sa.sa_family = net.sa.sa_family; + addr.sa.sa_len = net.sa.sa_len; + memcpy(&addr.sin.sin_addr, + &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr.s6_addr[12], + sizeof(addr.sin.sin_addr)); } else - fail++; - } else if (netaf == AF_INET6 && inet_pton(AF_INET6, mask_tok, &mask) == 1) - ; /* okay */ - else if (netaf == AF_INET - && inet_pton(AF_INET, mask_tok, &mask.s6_addr[12]) == 1) { - memset(&mask, 0xff, sizoff64); + memcpy(&addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } else -bogusmask: - fail++; - if (fail) { - tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok); - return (NO); /* not tcpd_jump() */ + return NO; + + if (net.sa.sa_family != mask.sa.sa_family || + net.sa.sa_family != addr.sa.sa_family) { + return NO; } + + switch (net.sa.sa_family) { + case AF_INET: + np = (char *)&net.sin.sin_addr; + mp = (char *)&mask.sin.sin_addr; + ap = (char *)&addr.sin.sin_addr; + alen = sizeof(net.sin.sin_addr); + break; + case AF_INET6: + np = (char *)&net.sin6.sin6_addr; + mp = (char *)&mask.sin6.sin6_addr; + ap = (char *)&addr.sin6.sin6_addr; + alen = sizeof(net.sin6.sin6_addr); + break; + default: + return NO; + } + + for (i = 0; i < alen; i++) + if (np[i] & ~mp[i]) { + tcpd_warn("host bits not all zero in %s/%s", net_tok, mask_tok); + break; + } + + for (i = 0; i < alen; i++) + ap[i] &= mp[i]; - for (i = 0; i < sizeof(addr); i++) - addr.s6_addr[i] &= mask.s6_addr[i]; - return (memcmp(&addr, &net, sizeof(addr)) == 0); + if (addr.sa.sa_family == AF_INET6 && addr.sin6.sin6_scope_id && + addr.sin6.sin6_scope_id != net.sin6.sin6_scope_id) + return NO; + return (memcmp(ap, np, alen) == 0); } #endif diff --git a/lib/libwrap/misc.c b/lib/libwrap/misc.c index 3ba0caaf023..8c5294cc795 100644 --- a/lib/libwrap/misc.c +++ b/lib/libwrap/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.4 2000/10/14 00:56:15 itojun Exp $ */ +/* $OpenBSD: misc.c,v 1.5 2002/06/07 03:32:04 itojun Exp $ */ /* * Misc routines that are used by tcpd and by tcpdchk. @@ -10,7 +10,7 @@ #if 0 static char sccsic[] = "@(#) misc.c 1.2 96/02/11 17:01:29"; #else -static char rcsid[] = "$OpenBSD: misc.c,v 1.4 2000/10/14 00:56:15 itojun Exp $"; +static char rcsid[] = "$OpenBSD: misc.c,v 1.5 2002/06/07 03:32:04 itojun Exp $"; #endif #endif @@ -23,10 +23,6 @@ static char rcsid[] = "$OpenBSD: misc.c,v 1.4 2000/10/14 00:56:15 itojun Exp $"; #include "tcpd.h" -#ifndef INADDR_NONE -#define INADDR_NONE (-1) /* XXX should be 0xffffffff */ -#endif - /* xgets - fgets() with backslash-newline stripping */ char *xgets(ptr, len, fp) @@ -60,13 +56,6 @@ char *split_at(string, delimiter) char *string; int delimiter; { -#if 0 - char *cp; - - if ((cp = strchr(string, delimiter)) != 0) - *cp++ = 0; - return (cp); -#else char *cp; int bracket; @@ -88,7 +77,6 @@ int delimiter; } } return NULL; -#endif } /* dot_quad_addr_new - convert dotted quad to internal form */ diff --git a/lib/libwrap/socket.c b/lib/libwrap/socket.c index 6e5532f8401..4ca3461c466 100644 --- a/lib/libwrap/socket.c +++ b/lib/libwrap/socket.c @@ -1,4 +1,4 @@ -/* $OpenBSD: socket.c,v 1.4 2001/11/07 18:49:21 deraadt Exp $ */ +/* $OpenBSD: socket.c,v 1.5 2002/06/07 03:32:04 itojun Exp $ */ /* * This module determines the type of socket (datagram, stream), the client @@ -21,7 +21,7 @@ #if 0 static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24"; #else -static char rcsid[] = "$OpenBSD: socket.c,v 1.4 2001/11/07 18:49:21 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: socket.c,v 1.5 2002/06/07 03:32:04 itojun Exp $"; #endif #endif @@ -43,35 +43,35 @@ static char rcsid[] = "$OpenBSD: socket.c,v 1.4 2001/11/07 18:49:21 deraadt Exp /* Forward declarations. */ -static void sock_sink(); - #ifdef APPEND_DOT +static const char *append_dot(const char *); +#endif +static void sock_sink(int); +#ifdef APPEND_DOT /* * Speed up DNS lookups by terminating the host name with a dot. Should be * done with care. The speedup can give problems with lookups from sources * that lack DNS-style trailing dot magic, such as local files or NIS maps. */ -static struct hostent *gethostbyname_dot(name) -char *name; +static const char * +append_dot(name) +const char *name; { - char dot_name[MAXHOSTNAMELEN + 1]; + static char hbuf[MAXHOSTNAMELEN + 1]; /* * Don't append dots to unqualified names. Such names are likely to come * from local hosts files or from NIS. */ - if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) { - return (gethostbyname(name)); - } else { - snprintf(dot_name, sizeof(dot_name), "%s.", name); - return (gethostbyname(dot_name)); - } + if (strchr(name, '.') == 0 || strlen(name) + 2 > sizeof(hbuf)) + strlcpy(hbuf, name, sizeof(hbuf)); + else + snprintf(hbuf, sizeof(hbuf), "%s.", name); + return hbuf; } - -#define gethostbyname gethostbyname_dot #endif /* sock_host - look up endpoint addresses and install conversion methods */ @@ -133,27 +133,12 @@ void sock_hostaddr(host) struct host_info *host; { struct sockaddr *sa = host->sin; - int alen, af; - char *ap; if (!sa) return; - switch (af = sa->sa_family) { - case AF_INET: - ap = (char *)&((struct sockaddr_in *)sa)->sin_addr; - alen = sizeof(struct in_addr); - break; -#ifdef INET6 - case AF_INET6: - ap = (char *)&((struct sockaddr_in6 *)sa)->sin6_addr; - alen = sizeof(struct in6_addr); - break; -#endif - default: - return; - } host->addr[0] = '\0'; - inet_ntop(af, ap, host->addr, sizeof(host->addr)); + getnameinfo(sa, sa->sa_len, host->addr, sizeof(host->addr), + NULL, 0, NI_NUMERICHOST); } /* sock_hostname - map endpoint address to host name */ @@ -161,95 +146,89 @@ struct host_info *host; void sock_hostname(host) struct host_info *host; { - struct sockaddr *sin = host->sin; - struct hostent *hp; - int i; - int af, alen; - char *ap; - char hbuf[MAXHOSTNAMELEN]; + struct sockaddr *sa = host->sin; + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + struct addrinfo hints, *res, *res0; +#ifdef INET6 + struct sockaddr_in tmp; +#endif - /* - * 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) + if (!sa) return; - switch (af = sin->sa_family) { - case AF_INET: - if (((struct sockaddr_in *)sin)->sin_addr.s_addr == 0) - return; - ap = (char *)&((struct sockaddr_in *)sin)->sin_addr; - alen = sizeof(struct in_addr); - break; #ifdef INET6 - case AF_INET6: - ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr; - alen = sizeof(struct in6_addr); - /* special case on reverse lookup: mapped addr. I hate it */ - if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { - af = AF_INET; - ap += (sizeof(struct in6_addr) - sizeof(struct in_addr)); - alen = sizeof(struct in_addr); - } - break; + /* special case on reverse lookup: mapped addr. I hate it */ + if (sa->sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr)) { + memset(&tmp, 0, sizeof(tmp)); + tmp.sin_family = AF_INET; + tmp.sin_len = sizeof(struct sockaddr_in); + memcpy(&tmp.sin_addr, + &((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12], 4); + sa = (struct sockaddr *)&tmp; + } #endif - default: + if (getnameinfo(sa, sa->sa_len, h1, sizeof(h1), NULL, 0, + NI_NUMERICHOST) != 0) { return; } - if ((hp = gethostbyaddr(ap, alen, af)) != 0) { - - strlcpy(host->name, hp->h_name, sizeof(host->name)); - + if (getnameinfo(sa, sa->sa_len, host->name, sizeof(host->name), NULL, 0, + NI_NAMEREQD) == 0) { /* * Verify that the address is a member of the address list returned - * by gethostbyname(hostname). + * by getaddrinfo(hostname). * - * Verify also that gethostbyaddr() and gethostbyname() return the same + * Verify also that getnameinfo() and getaddrinfo() return the same * hostname, or rshd and rlogind may still end up being spoofed. * - * On some sites, gethostbyname("localhost") returns "localhost.domain". + * On some sites, getaddrinfo("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") + * can't believe the address list from getaddrinfo("localhost") * we're in big trouble anyway. */ - - if ((hp = gethostbyname2(host->name, af)) == 0) { - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = sa->sa_family; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_CANONNAME; +#ifdef APPEND_DOT + if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) != 0) +#else + if (getaddrinfo(host->name, "0", &hints, &res0) != 0) +#endif + { /* * 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: gethostbyname2(%s, %d) failed", - host->name, af); - - } else if (STR_NE(host->name, hp->h_name) - && STR_NE(host->name, "localhost")) { - + tcpd_warn("can't verify hostname: getaddrinfo(%s, %d) failed", + host->name, hints.ai_family); + } else if (res0->ai_canonname && + STR_NE(host->name, res0->ai_canonname) && + STR_NE(host->name, "localhost")) { /* - * The gethostbyaddr() and gethostbyname() calls did not return + * The getnameinfo() and getaddrinfo() 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); - + tcpd_warn("host name/name mismatch: %s != %s", + host->name, res0->ai_canonname); + freeaddrinfo(res0); } 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. + * getaddrinfo(). */ - for (i = 0; hp->h_addr_list[i]; i++) { - if (memcmp(hp->h_addr_list[i], (char *) ap, alen) == 0) - return; /* name is good, keep it */ + for (res = res0; res; res = res->ai_next) { + if (getnameinfo(res->ai_addr, res->ai_addrlen, h2, sizeof(h2), + NULL, 0, NI_NUMERICHOST) != 0) { + continue; + } + if (STR_EQ(h1, h2)) { + freeaddrinfo(res0); + return; + } } /* @@ -258,11 +237,11 @@ struct host_info *host; * server. */ - tcpd_warn("host name/address mismatch: %s != %.*s", - inet_ntop(af, ap, hbuf, sizeof(hbuf)), - STRING_LENGTH, hp->h_name); - } + tcpd_warn("host name/address mismatch: %s != %s", h1, + res0->ai_canonname ? res0->ai_canonname : "?"); + freeaddrinfo(res0); + } /* name is bad, clobber it */ strlcpy(host->name, paranoid, sizeof(host->name)); } @@ -274,13 +253,13 @@ static void sock_sink(fd) int fd; { char buf[BUFSIZ]; - struct sockaddr_storage sin; - int size = sizeof(sin); + struct sockaddr_storage ss; + int size = sizeof(ss); /* * Eat up the not-yet received datagram. Some systems insist on a * non-zero source address argument in the recvfrom() call below. */ - (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size); + (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & ss, &size); } |