]> granicus.if.org Git - sudo/commitdiff
Add IPv6 support; adapted from patches by YOSHIFUJI Hideaki
authorTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 13 Aug 2007 16:29:25 +0000 (16:29 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 13 Aug 2007 16:29:25 +0000 (16:29 +0000)
gram.y
interfaces.c
interfaces.h
match.c
sudoers.pod
toke.l

diff --git a/gram.y b/gram.y
index bfc14938a013a51fd69ab01c98715ceb8fe01079..53cb4222dbb885713799bfc6fb99e1eb0fc6294a 100644 (file)
--- a/gram.y
+++ b/gram.y
@@ -113,7 +113,7 @@ yyerror(s)
 %token <command> COMMAND               /* absolute pathname w/ optional args */
 %token <string>  ALIAS                 /* an UPPERCASE alias name */
 %token <string>         DEFVAR                 /* a Defaults variable name */
-%token <string>  NTWKADDR              /* w.x.y.z */
+%token <string>  NTWKADDR              /* ipv4 or ipv6 address */
 %token <string>  NETGROUP              /* a netgroup (+NAME) */
 %token <string>  USERGROUP             /* a usergroup (%NAME) */
 %token <string>  WORD                  /* a word */
index 49d298ecc4abffa002b05d49d807912872d13d49..d7e777d1f02af3c3be841f2250ac796f29e8b09a 100644 (file)
@@ -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 */
+       }
+    }
 }
index 2bbaa0b706f7dc73a6e9f6bb314fe3ef5a913566..8b6f1675c6a21251d7ad2cd5e0ccfadbb41e1adc 100644 (file)
  * 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 ded6381445c34d10bd151a4f612769da54e86337..09ae958e18b7f9c19f21092b3f0ce72b0fa51a57 100644 (file)
--- a/match.c
+++ b/match.c
@@ -24,6 +24,7 @@
 
 #include <sys/types.h>
 #include <sys/param.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #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
  */
index 8ada44d9b8ea498964d8c8c4cf82e9413882bfc8..978acd66e2166d0b7700f76fd34c93de88703a75 100644 (file)
@@ -157,8 +157,9 @@ If you do not specify a netmask along with the network number,
 B<sudo> 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.E<nbsp>255.255.255.0)
-or CIDR notation (number of bits, e.g.E<nbsp>24).  A hostname may
+may be specified either in standard IP address notation
+(e.g.E<nbsp>255.255.255.0 or ffff:ffff:ffff:ffff::),
+or CIDR notation (number of bits, e.g.E<nbsp>24 or 64).  A hostname may
 include shell-style wildcards (see the L<Wildcards> section below),
 but unless the C<hostname> command on your machine returns the fully
 qualified hostname, you'll need to use the I<fqdn> option for
diff --git a/toke.l b/toke.l
index 087413e497274fbab3bdec52ca36bf3f5f8bfc1d..48de6bb53d848507a0129cd65bef4661e96994b3 100644 (file)
--- 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);
+                       }
+
 <INITIAL>\(            {
                                BEGIN GOTRUNAS;
                                LEXTRACE("RUNAS ");