]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Check return value of snprintf and ipv6_printaddr.
authorRoy Marples <roy@marples.name>
Fri, 22 Jan 2016 10:05:47 +0000 (10:05 +0000)
committerRoy Marples <roy@marples.name>
Fri, 22 Jan 2016 10:05:47 +0000 (10:05 +0000)
This is not really needed (unless you have a buggy libc) as the correct bounds
are checked elsewhere, but it's hard to see that, hence this change.
Fixes CVE-2014-7913.

dhcp-common.c
ipv6.c
ipv6.h

index 24703d3321a3d4b65dbfa2ed891d654b01dd6f74..ff8a959ea8bbcf6799c39ee0bcf129f23097e1d3 100644 (file)
@@ -618,6 +618,39 @@ dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
        return (ssize_t)sz;
 }
 
+/* It's possible for DHCPv4 to contain an IPv6 address */
+static ssize_t
+ipv6_printaddr(char *s, size_t sl, const uint8_t *d, const char *ifname)
+{
+       char buf[INET6_ADDRSTRLEN];
+       const char *p;
+       size_t l;
+
+       p = inet_ntop(AF_INET6, d, buf, sizeof(buf));
+       if (p == NULL)
+               return -1;
+
+       l = strlen(p);
+       if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80)
+               l += 1 + strlen(ifname);
+
+       if (s == NULL)
+               return (ssize_t)l;
+
+       if (sl < l) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       s += strlcpy(s, p, sl);
+       if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) {
+               *s++ = '%';
+               s += strlcpy(s, ifname, sl);
+       }
+       *s = '\0';
+       return (ssize_t)l;
+}
+
 static ssize_t
 print_option(char *s, size_t len, const struct dhcp_opt *opt,
     const uint8_t *data, size_t dl, const char *ifname)
@@ -632,10 +665,6 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt,
        size_t l;
        char *tmp;
 
-#ifndef INET6
-       UNUSED(ifname);
-#endif
-
        if (opt->type & RFC1035) {
                sl = decode_rfc1035(NULL, 0, data, dl);
                if (sl == 0 || sl == -1)
@@ -716,23 +745,20 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt,
                } else if (opt->type & ADDRIPV4) {
                        l = 16;
                        dl /= 4;
-               }
-#ifdef INET6
-               else if (opt->type & ADDRIPV6) {
+               } else if (opt->type & ADDRIPV6) {
                        e = data + dl;
                        l = 0;
                        while (data < e) {
                                if (l)
                                        l++; /* space */
                                sl = ipv6_printaddr(NULL, 0, data, ifname);
-                               if (sl != -1)
-                                       l += (size_t)sl;
+                               if (sl == -1)
+                                       return l == 0 ? -1 : (ssize_t)l;
+                               l += (size_t)sl;
                                data += 16;
                        }
                        return (ssize_t)l;
-               }
-#endif
-               else {
+               } else {
                        errno = EINVAL;
                        return -1;
                }
@@ -774,21 +800,15 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt,
                        memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
                        sl = snprintf(s, len, "%s", inet_ntoa(addr));
                        data += sizeof(addr.s_addr);
-               }
-#ifdef INET6
-               else if (opt->type & ADDRIPV6) {
-                       ssize_t r;
-
-                       r = ipv6_printaddr(s, len, data, ifname);
-                       if (r != -1)
-                               sl = r;
-                       else
-                               sl = 0;
+               } else if (opt->type & ADDRIPV6) {
+                       sl = ipv6_printaddr(s, len, data, ifname);
                        data += 16;
+               } else {
+                       errno = EINVAL;
+                       return -1;
                }
-#endif
-               else
-                       sl = 0;
+               if (sl == -1)
+                       return bytes == 0 ? -1 : bytes;
                len -= (size_t)sl;
                bytes += sl;
                s += sl;
diff --git a/ipv6.c b/ipv6.c
index 8835f6208fd396c78c5c224881e312ac735af116..2b383bd2db752c7551ce161df94a5203e7deb770 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -181,38 +181,6 @@ ipv6_init(struct dhcpcd_ctx *dhcpcd_ctx)
        return ctx;
 }
 
-ssize_t
-ipv6_printaddr(char *s, size_t sl, const uint8_t *d, const char *ifname)
-{
-       char buf[INET6_ADDRSTRLEN];
-       const char *p;
-       size_t l;
-
-       p = inet_ntop(AF_INET6, d, buf, sizeof(buf));
-       if (p == NULL)
-               return -1;
-
-       l = strlen(p);
-       if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80)
-               l += 1 + strlen(ifname);
-
-       if (s == NULL)
-               return (ssize_t)l;
-
-       if (sl < l) {
-               errno = ENOMEM;
-               return -1;
-       }
-
-       s += strlcpy(s, p, sl);
-       if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) {
-               *s++ = '%';
-               s += strlcpy(s, ifname, sl);
-       }
-       *s = '\0';
-       return (ssize_t)l;
-}
-
 static ssize_t
 ipv6_readsecret(struct dhcpcd_ctx *ctx)
 {
diff --git a/ipv6.h b/ipv6.h
index cdf525dd8f5d5907fedcb7866a2d9217d3b5bf50..c01e05b80ee1486d09b81dbf8429949ac7bce9e6 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -236,7 +236,6 @@ struct ipv6_ctx {
 };
 
 struct ipv6_ctx *ipv6_init(struct dhcpcd_ctx *);
-ssize_t ipv6_printaddr(char *, size_t, const uint8_t *, const char *);
 int ipv6_makestableprivate(struct in6_addr *addr,
     const struct in6_addr *prefix, int prefix_len,
     const struct interface *ifp, int *dad_counter);