summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2016-01-22 10:05:47 +0000
committerRoy Marples <roy@marples.name>2016-01-22 10:05:47 +0000
commit93f3066bb0bc0974eab1943543205312a6b512ad (patch)
tree50b30ff6a197b805618fc7f99cc1f95b716c8fdd
parent4507db941053c5af9608b7884f6abe1150cc5fa2 (diff)
downloaddhcpcd-93f3066bb0bc0974eab1943543205312a6b512ad.tar.xz
Check return value of snprintf and ipv6_printaddr.
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.
-rw-r--r--dhcp-common.c70
-rw-r--r--ipv6.c32
-rw-r--r--ipv6.h1
3 files changed, 45 insertions, 58 deletions
diff --git a/dhcp-common.c b/dhcp-common.c
index 24703d33..ff8a959e 100644
--- a/dhcp-common.c
+++ b/dhcp-common.c
@@ -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 8835f620..2b383bd2 100644
--- 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 cdf525dd..c01e05b8 100644
--- 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);