From d65602ca6f9ded344bdc7b86208b78fd7c396cd9 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 13 Aug 2007 16:29:25 +0000 Subject: [PATCH] Add IPv6 support; adapted from patches by YOSHIFUJI Hideaki --- gram.y | 2 +- interfaces.c | 46 ++++++++++++++-- interfaces.h | 15 ++++- match.c | 152 +++++++++++++++++++++++++++++++++++++++++++-------- sudoers.pod | 5 +- toke.l | 17 ++++++ 6 files changed, 204 insertions(+), 33 deletions(-) diff --git a/gram.y b/gram.y index bfc14938a..53cb4222d 100644 --- a/gram.y +++ b/gram.y @@ -113,7 +113,7 @@ yyerror(s) %token COMMAND /* absolute pathname w/ optional args */ %token ALIAS /* an UPPERCASE alias name */ %token DEFVAR /* a Defaults variable name */ -%token NTWKADDR /* w.x.y.z */ +%token NTWKADDR /* ipv4 or ipv6 address */ %token NETGROUP /* a netgroup (+NAME) */ %token USERGROUP /* a usergroup (%NAME) */ %token WORD /* a word */ diff --git a/interfaces.c b/interfaces.c index 49d298ecc..d7e777d1f 100644 --- a/interfaces.c +++ b/interfaces.c @@ -98,8 +98,10 @@ void load_interfaces() { struct ifaddrs *ifa, *ifaddrs; - /* XXX - sockaddr_in6 sin6; */ struct sockaddr_in *sin; +#ifdef AF_INET6 + struct sockaddr_in6 *sin6; +#endif int i; if (getifaddrs(&ifaddrs)) @@ -113,8 +115,10 @@ load_interfaces() continue; switch(ifa->ifa_addr->sa_family) { - /* XXX - AF_INET6 */ case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif num_interfaces++; break; } @@ -132,7 +136,6 @@ load_interfaces() continue; switch(ifa->ifa_addr->sa_family) { - /* XXX - AF_INET6 */ case AF_INET: sin = (struct sockaddr_in *)ifa->ifa_addr; memcpy(&interfaces[i].addr, &sin->sin_addr, @@ -140,8 +143,21 @@ load_interfaces() sin = (struct sockaddr_in *)ifa->ifa_netmask; memcpy(&interfaces[i].netmask, &sin->sin_addr, sizeof(struct in_addr)); + interfaces[i].family = AF_INET; i++; break; +#ifdef AF_INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + memcpy(&interfaces[i].addr, &sin6->sin6_addr, + sizeof(struct in6_addr)); + sin6 = (struct sockaddr_in6 *)ifa->ifa_netmask; + memcpy(&interfaces[i].netmask, &sin6->sin6_addr, + sizeof(struct in6_addr)); + interfaces[i].family = AF_INET6; + i++; + break; +#endif /* AF_INET6 */ } } #ifdef HAVE_FREEIFADDRS @@ -271,6 +287,7 @@ load_interfaces() } /* Only now can we be sure it was a good/interesting interface. */ + interfaces[num_interfaces].family = AF_INET; num_interfaces++; } @@ -303,9 +320,26 @@ void dump_interfaces() { int i; +#ifdef AF_INET6 + char addrbuf[INET6_ADDRSTRLEN], maskbuf[INET6_ADDRSTRLEN]; +#endif puts("Local IP address and netmask pairs:"); - for (i = 0; i < num_interfaces; i++) - printf("\t%s / 0x%x\n", inet_ntoa(interfaces[i].addr), - (unsigned int)ntohl(interfaces[i].netmask.s_addr)); + for (i = 0; i < num_interfaces; i++) { + switch(interfaces[i].family) { + case AF_INET: + printf("\t%s / ", inet_ntoa(interfaces[i].addr.ip4)); + puts(inet_ntoa(interfaces[i].netmask.ip4)); + break; +#ifdef AF_INET6 + case AF_INET6: + inet_ntop(AF_INET6, &interfaces[i].addr.ip6, + addrbuf, sizeof(addrbuf)); + inet_ntop(AF_INET6, &interfaces[i].netmask.ip6, + maskbuf, sizeof(maskbuf)); + printf("\t%s / %s\n", addrbuf, maskbuf); + break; +#endif /* AF_INET6 */ + } + } } diff --git a/interfaces.h b/interfaces.h index 2bbaa0b70..8b6f1675c 100644 --- a/interfaces.h +++ b/interfaces.h @@ -27,8 +27,19 @@ * IP address and netmask pairs for checking against local interfaces. */ struct interface { - struct in_addr addr; - struct in_addr netmask; + int family; /* AF_INET or AF_INET6 */ + union { + struct in_addr ip4; +#ifdef AF_INET6 + struct in6_addr ip6; +#endif + } addr; + union { + struct in_addr ip4; +#ifdef AF_INET6 + struct in6_addr ip6; +#endif + } netmask; }; /* diff --git a/match.c b/match.c index ded638144..09ae958e1 100644 --- a/match.c +++ b/match.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #ifdef STDC_HEADERS @@ -418,22 +419,85 @@ command_matches(sudoers_cmnd, sudoers_args) } } -/* - * Returns TRUE if "n" is one of our ip addresses or if - * "n" is a network that we are on, else returns FALSE. - */ -int -addr_matches(n) +static int +addr_matches_if(n) char *n; { int i; + struct in_addr addr; + struct interface *ifp; +#ifdef AF_INET6 + struct in6_addr addr6; + int j; +#endif + int family = AF_UNSPEC; + +#ifdef AF_INET6 + if (inet_pton(AF_INET6, n, &addr6) > 0) { + family = AF_INET6; + } else +#else + { + family = AF_INET; + addr.s_addr = inet_addr(n); + } +#endif + + for (i = 0; i < num_interfaces; i++) { + ifp = &interfaces[i]; + if (ifp->family != family) + continue; + switch(family) { + case AF_INET: + if (ifp->addr.ip4.s_addr == addr.s_addr || + (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr) + == addr.s_addr) + return(TRUE); + break; +#ifdef AF_INET6 + case AF_INET6: + if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr, + sizeof(addr6.s6_addr)) == 0) + return(TRUE); + for (j = 0; j < sizeof(addr6.s6_addr); j++) { + if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j]) + break; + } + if (j == sizeof(addr6.s6_addr)) + return(TRUE); +#endif + } + } + + return(FALSE); +} + +static int +addr_matches_if_netmask(n, m) + char *n; char *m; +{ + int i; struct in_addr addr, mask; + struct interface *ifp; +#ifdef AF_INET6 + struct in6_addr addr6, mask6; + int j; +#endif + int family = AF_UNSPEC; - /* If there's an explicit netmask, use it. */ - if ((m = strchr(n, '/'))) { - *m++ = '\0'; +#ifdef AF_INET6 + if (inet_pton(AF_INET6, n, &addr6) > 0) + family = AF_INET6; + else +#else + { + family = AF_INET; addr.s_addr = inet_addr(n); + } +#endif + + if (family == AF_INET) { if (strchr(m, '.')) mask.s_addr = inet_addr(m); else { @@ -443,24 +507,68 @@ addr_matches(n) mask.s_addr <<= i; mask.s_addr = htonl(mask.s_addr); } - *(m - 1) = '/'; - - for (i = 0; i < num_interfaces; i++) - if ((interfaces[i].addr.s_addr & mask.s_addr) == addr.s_addr) - return(TRUE); - } else { - addr.s_addr = inet_addr(n); - - for (i = 0; i < num_interfaces; i++) - if (interfaces[i].addr.s_addr == addr.s_addr || - (interfaces[i].addr.s_addr & interfaces[i].netmask.s_addr) - == addr.s_addr) - return(TRUE); + } +#ifdef AF_INET6 + else { + if (inet_pton(AF_INET6, m, &mask6) <= 0) { + j = atoi(m); + for (i = 0; i < 16; i++) { + if (j < i * 8) + mask6.s6_addr[i] = 0; + else if (i * 8 + 8 <= j) + mask6.s6_addr[i] = 0xff; + else + mask6.s6_addr[i] = 0xff00 >> (j - i * 8); + } + } + } +#endif /* AF_INET6 */ + + for (i = 0; i < num_interfaces; i++) { + ifp = &interfaces[i]; + if (ifp->family != family) + continue; + switch(family) { + case AF_INET: + if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr) + return(TRUE); +#ifdef AF_INET6 + case AF_INET6: + for (j = 0; j < sizeof(addr6.s6_addr); j++) { + if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j]) + break; + } + if (j == sizeof(addr6.s6_addr)) + return(TRUE); +#endif /* AF_INET6 */ + } } return(FALSE); } +/* + * Returns TRUE if "n" is one of our ip addresses or if + * "n" is a network that we are on, else returns FALSE. + */ +int +addr_matches(n) + char *n; +{ + char *m; + int retval; + + /* If there's an explicit netmask, use it. */ + if ((m = strchr(n, '/'))) { + *m++ = '\0'; + retval = addr_matches_if_netmask(n, m); + *(m - 1) = '/'; + } else + retval = addr_matches_if(n); + + return(retval); +} + /* * Returns TRUE if the hostname matches the pattern, else FALSE */ diff --git a/sudoers.pod b/sudoers.pod index 8ada44d9b..978acd66e 100644 --- a/sudoers.pod +++ b/sudoers.pod @@ -157,8 +157,9 @@ If you do not specify a netmask along with the network number, B will query each of the local host's network interfaces and, if the network number corresponds to one of the hosts's network interfaces, the corresponding netmask will be used. The netmask -may be specified either in dotted quad notation (e.g.E255.255.255.0) -or CIDR notation (number of bits, e.g.E24). A hostname may +may be specified either in standard IP address notation +(e.g.E255.255.255.0 or ffff:ffff:ffff:ffff::), +or CIDR notation (number of bits, e.g.E24 or 64). A hostname may include shell-style wildcards (see the L section below), but unless the C command on your machine returns the fully qualified hostname, you'll need to use the I option for diff --git a/toke.l b/toke.l index 087413e49..48de6bb53 100644 --- a/toke.l +++ b/toke.l @@ -84,8 +84,11 @@ extern void yyerror __P((const char *)); #endif %} +HEXDIGIT [0-9A-Fa-f]{1,4} OCTET (1?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]) DOTTEDQUAD {OCTET}(\.{OCTET}){3} +IPV6ADDR \:\:|({HEXDIGIT}\:){7}{HEXDIGIT}|({HEXDIGIT}\:){5}{HEXDIGIT}\:{DOTTEDQUAD}|({HEXDIGIT}\:){1,7}\:|({HEXDIGIT}\:){1,6}(\:{HEXDIGIT}){1}|({HEXDIGIT}\:){1,5}(\:{HEXDIGIT}){2}|({HEXDIGIT}\:){1,2}\:{DOTTEDQUAD}|({HEXDIGIT}\:){1,4}(\:{HEXDIGIT}){3}|({HEXDIGIT}\:){1,4}(\:{HEXDIGIT}){1}\:{DOTTEDQUAD}|({HEXDIGIT}\:){1,3}(\:{HEXDIGIT}){4}|({HEXDIGIT}\:){1,3}(\:{HEXDIGIT}){2}\:{DOTTEDQUAD}|({HEXDIGIT}\:){1,2}(\:{HEXDIGIT}){5}|({HEXDIGIT}\:){1,2}(\:{HEXDIGIT}){3}\:{DOTTEDQUAD}|({HEXDIGIT}\:){1}(\:{HEXDIGIT}){6}|({HEXDIGIT}\:){1}(\:{HEXDIGIT}){4}\:{DOTTEDQUAD}|\:(\:{HEXDIGIT}){1,7}|\:(\:{HEXDIGIT}){1,5}\:{DOTTEDQUAD} + HOSTNAME [[:alnum:]_-]+ WORD ([^#>!=:,\(\) \t\n\\]|\\[^\n])+ PATH \/(\\[\,:= \t#]|[^\,:=\\ \t\n#])+ @@ -323,6 +326,20 @@ MONITOR[[:blank:]]*: { return(NTWKADDR); } +{IPV6ADDR}(\/{IPV6ADDR})? { + if (!fill(yytext, yyleng)) + yyterminate(); + LEXTRACE("NTWKADDR "); + return(NTWKADDR); + } + +{IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) { + if (!fill(yytext, yyleng)) + yyterminate(); + LEXTRACE("NTWKADDR "); + return(NTWKADDR); + } + \( { BEGIN GOTRUNAS; LEXTRACE("RUNAS "); -- 2.40.0