summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Barth <steven@midlink.org>2016-01-28 16:59:12 +0100
committerSteven Barth <steven@midlink.org>2016-01-28 16:59:12 +0100
commit23aa0c218808361c1252b73a8ba82a91ac9039e7 (patch)
tree4e97bf8c749db4f4a5e1af2fa72fff19514560ba
parent4f9eded5cff092772fdc2bdeafc8647dae826390 (diff)
parent78bc7d9c835404ad1b5d2eb26de390bd45e26d2a (diff)
downloadodhcp6c-23aa0c218808361c1252b73a8ba82a91ac9039e7.tar.gz
Merge pull request #41 from bwhacks/security-fixes
Security fixes
-rw-r--r--src/dhcpv6.c33
-rw-r--r--src/odhcp6c.c6
-rw-r--r--src/odhcp6c.h2
-rw-r--r--src/script.c45
4 files changed, 57 insertions, 29 deletions
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index cfa3f29..e27d899 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <syslog.h>
#include <stdbool.h>
+#include <ctype.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -812,7 +813,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
if (inf_max_rt >= DHCPV6_INF_MAX_RT_MIN &&
inf_max_rt <= DHCPV6_INF_MAX_RT_MAX)
cand.inf_max_rt = inf_max_rt;
- } else if (otype == DHCPV6_OPT_IA_PD && request_prefix) {
+ } else if (otype == DHCPV6_OPT_IA_PD && request_prefix &&
+ olen >= -4 + sizeof(struct dhcpv6_ia_hdr)) {
struct dhcpv6_ia_hdr *h = (struct dhcpv6_ia_hdr*)&odata[-4];
uint8_t *oend = odata + olen, *d;
dhcpv6_for_each_option(&h[1], oend, otype, olen, d) {
@@ -822,7 +824,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
have_pd = p->prefix;
}
}
- } else if (otype == DHCPV6_OPT_IA_NA) {
+ } else if (otype == DHCPV6_OPT_IA_NA &&
+ olen >= -4 + sizeof(struct dhcpv6_ia_hdr)) {
struct dhcpv6_ia_hdr *h = (struct dhcpv6_ia_hdr*)&odata[-4];
uint8_t *oend = odata + olen, *d;
dhcpv6_for_each_option(&h[1], oend, otype, olen, d)
@@ -1185,7 +1188,7 @@ static int dhcpv6_parse_ia(void *opt, void *end)
if (elen > 64)
elen = 64;
- if (elen <= 32 || elen <= entry.length) {
+ if (entry.length < 32 || elen <= entry.length) {
ok = false;
continue;
}
@@ -1290,16 +1293,22 @@ static int dhcpv6_calc_refresh_timers(void)
static void dhcpv6_log_status_code(const uint16_t code, const char *scope,
- const void *status_msg, const int len)
+ const void *status_msg, int len)
{
- uint8_t buf[len + 3];
+ const char *src = status_msg;
+ char buf[len + 3];
+ char *dst = buf;
- memset(buf, 0, sizeof(buf));
if (len) {
- buf[0] = '(';
- memcpy(&buf[1], status_msg, len);
- buf[len + 1] = ')';
+ *dst++ = '(';
+ while (len--) {
+ *dst = isprint((unsigned char)*src) ? *src : '?';
+ src++;
+ dst++;
+ }
+ *dst++ = ')';
}
+ *dst = 0;
syslog(LOG_WARNING, "Server returned %s status %i %s",
scope, code, buf);
@@ -1362,6 +1371,7 @@ static void dhcpv6_handle_ia_status_code(const enum dhcpv6_msg orig,
}
}
+// Note this always takes ownership of cand->ia_na and cand->ia_pd
static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand)
{
size_t cand_len, i;
@@ -1384,7 +1394,10 @@ static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand)
break;
}
- odhcp6c_insert_state(STATE_SERVER_CAND, i * sizeof(*c), cand, sizeof(*cand));
+ if (odhcp6c_insert_state(STATE_SERVER_CAND, i * sizeof(*c), cand, sizeof(*cand))) {
+ free(cand->ia_na);
+ free(cand->ia_pd);
+ }
}
static void dhcpv6_clear_all_server_cand(void)
diff --git a/src/odhcp6c.c b/src/odhcp6c.c
index 133ade7..1714d62 100644
--- a/src/odhcp6c.c
+++ b/src/odhcp6c.c
@@ -516,11 +516,11 @@ void odhcp6c_add_state(enum odhcp6c_state state, const void *data, size_t len)
memcpy(n, data, len);
}
-void odhcp6c_insert_state(enum odhcp6c_state state, size_t offset, const void *data, size_t len)
+int odhcp6c_insert_state(enum odhcp6c_state state, size_t offset, const void *data, size_t len)
{
ssize_t len_after = state_len[state] - offset;
if (len_after < 0)
- return;
+ return -1;
uint8_t *n = odhcp6c_resize_state(state, len);
if (n) {
@@ -529,6 +529,8 @@ void odhcp6c_insert_state(enum odhcp6c_state state, size_t offset, const void *d
memmove(sdata + offset + len, sdata + offset, len_after);
memcpy(sdata + offset, data, len);
}
+
+ return 0;
}
size_t odhcp6c_remove_state(enum odhcp6c_state state, size_t offset, size_t len)
diff --git a/src/odhcp6c.h b/src/odhcp6c.h
index d202790..928f82f 100644
--- a/src/odhcp6c.h
+++ b/src/odhcp6c.h
@@ -335,7 +335,7 @@ bool odhcp6c_is_bound(void);
void odhcp6c_clear_state(enum odhcp6c_state state);
void odhcp6c_add_state(enum odhcp6c_state state, const void *data, size_t len);
void odhcp6c_append_state(enum odhcp6c_state state, const void *data, size_t len);
-void odhcp6c_insert_state(enum odhcp6c_state state, size_t offset, const void *data, size_t len);
+int odhcp6c_insert_state(enum odhcp6c_state state, size_t offset, const void *data, size_t len);
size_t odhcp6c_remove_state(enum odhcp6c_state state, size_t offset, size_t len);
void* odhcp6c_move_state(enum odhcp6c_state state, size_t *len);
void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len);
diff --git a/src/script.c b/src/script.c
index f272c19..3579331 100644
--- a/src/script.c
+++ b/src/script.c
@@ -118,9 +118,10 @@ static void fqdn_to_env(const char *name, const uint8_t *fqdn, size_t len)
char *buf = realloc(NULL, len + buf_len + 2);
memcpy(buf, name, buf_len);
buf[buf_len++] = '=';
- int l = 1;
- while (l > 0 && fqdn < fqdn_end) {
- l = dn_expand(fqdn, fqdn_end, fqdn, &buf[buf_len], buf_size - buf_len);
+ while (fqdn < fqdn_end) {
+ int l = dn_expand(fqdn, fqdn_end, fqdn, &buf[buf_len], buf_size - buf_len);
+ if (l <= 0)
+ break;
fqdn += l;
buf_len += strlen(&buf[buf_len]);
buf[buf_len++] = ' ';
@@ -156,7 +157,10 @@ static void entry_to_env(const char *name, const void *data, size_t len, enum en
{
size_t buf_len = strlen(name);
const struct odhcp6c_entry *e = data;
- char *buf = realloc(NULL, buf_len + 2 + (len / sizeof(*e)) * 144);
+ // Worst case: ENTRY_PREFIX with iaid != 1 and exclusion
+ const size_t max_entry_len = (INET6_ADDRSTRLEN-1 + 5 + 22 + 15 + 10 +
+ INET6_ADDRSTRLEN-1 + 11 + 1);
+ char *buf = realloc(NULL, buf_len + 2 + (len / sizeof(*e)) * max_entry_len);
memcpy(buf, name, buf_len);
buf[buf_len++] = '=';
@@ -164,28 +168,34 @@ static void entry_to_env(const char *name, const void *data, size_t len, enum en
inet_ntop(AF_INET6, &e[i].target, &buf[buf_len], INET6_ADDRSTRLEN);
buf_len += strlen(&buf[buf_len]);
if (type != ENTRY_HOST) {
- buf_len += snprintf(&buf[buf_len], 6, "/%"PRIu16, e[i].length);
+ snprintf(&buf[buf_len], 6, "/%"PRIu16, e[i].length);
+ buf += strlen(&buf[buf_len]);
if (type == ENTRY_ROUTE) {
buf[buf_len++] = ',';
if (!IN6_IS_ADDR_UNSPECIFIED(&e[i].router)) {
inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN);
buf_len += strlen(&buf[buf_len]);
}
- buf_len += snprintf(&buf[buf_len], 24, ",%u", e[i].valid);
- buf_len += snprintf(&buf[buf_len], 12, ",%u", e[i].priority);
+ snprintf(&buf[buf_len], 23, ",%u,%u", e[i].valid, e[i].priority);
+ buf += strlen(&buf[buf_len]);
} else {
- buf_len += snprintf(&buf[buf_len], 24, ",%u,%u", e[i].preferred, e[i].valid);
+ snprintf(&buf[buf_len], 23, ",%u,%u", e[i].preferred, e[i].valid);
+ buf += strlen(&buf[buf_len]);
}
- if (type == ENTRY_PREFIX && ntohl(e[i].iaid) != 1)
- buf_len += snprintf(&buf[buf_len], 16, ",class=%08x", ntohl(e[i].iaid));
+ if (type == ENTRY_PREFIX && ntohl(e[i].iaid) != 1) {
+ snprintf(&buf[buf_len], 16, ",class=%08x", ntohl(e[i].iaid));
+ buf += strlen(&buf[buf_len]);
+ }
if (type == ENTRY_PREFIX && e[i].priority) {
// priority and router are abused for prefix exclusion
- buf_len += snprintf(&buf[buf_len], 12, ",excluded=");
+ snprintf(&buf[buf_len], 11, ",excluded=");
+ buf_len += strlen(&buf[buf_len]);
inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN);
buf_len += strlen(&buf[buf_len]);
- buf_len += snprintf(&buf[buf_len], 24, "/%u", e[i].priority);
+ snprintf(&buf[buf_len], 12, "/%u", e[i].priority);
+ buf_len += strlen(&buf[buf_len]);
}
}
buf[buf_len++] = ' ';
@@ -217,7 +227,7 @@ static void search_to_env(const char *name, const uint8_t *start, size_t len)
static void int_to_env(const char *name, int value)
{
- size_t len = 12 + strlen(name);
+ size_t len = 13 + strlen(name);
char *buf = realloc(NULL, len);
snprintf(buf, len, "%s=%d", name, value);
putenv(buf);
@@ -272,7 +282,8 @@ static void s46_to_env(enum odhcp6c_state state, const uint8_t *data, size_t len
size_t prefix6len = rule->prefix6_len;
prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1;
- if (olen < sizeof(struct dhcpv6_s46_rule) + prefix6len)
+ if (prefix6len > sizeof(in6) ||
+ olen < sizeof(struct dhcpv6_s46_rule) + prefix6len)
continue;
memcpy(&in6, rule->ipv6_prefix, prefix6len);
@@ -301,7 +312,8 @@ static void s46_to_env(enum odhcp6c_state state, const uint8_t *data, size_t len
size_t prefix6len = dmr->dmr_prefix6_len;
prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1;
- if (olen < sizeof(struct dhcpv6_s46_dmr) + prefix6len)
+ if (prefix6len > sizeof(in6) ||
+ olen < sizeof(struct dhcpv6_s46_dmr) + prefix6len)
continue;
memcpy(&in6, dmr->dmr_ipv6_prefix, prefix6len);
@@ -320,7 +332,8 @@ static void s46_to_env(enum odhcp6c_state state, const uint8_t *data, size_t len
size_t prefix6len = bind->bindprefix6_len;
prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1;
- if (olen < sizeof(struct dhcpv6_s46_v4v6bind) + prefix6len)
+ if (prefix6len > sizeof(in6) ||
+ olen < sizeof(struct dhcpv6_s46_v4v6bind) + prefix6len)
continue;
memcpy(&in6, bind->bind_ipv6_prefix, prefix6len);