]> git.decadent.org.uk Git - odhcp6c.git/commitdiff
Merge pull request #41 from bwhacks/security-fixes
authorSteven Barth <steven@midlink.org>
Thu, 28 Jan 2016 15:59:12 +0000 (16:59 +0100)
committerSteven Barth <steven@midlink.org>
Thu, 28 Jan 2016 15:59:12 +0000 (16:59 +0100)
Security fixes

src/dhcpv6.c
src/odhcp6c.c
src/odhcp6c.h
src/script.c

index cfa3f29e17f5ec39415572cc89207b88730d2dcd..e27d8995a40102a5446816334fb3e3843819e311 100644 (file)
@@ -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)
index 133ade707a788efa57e9431d5c094f59728b3ca2..1714d629b7cc38a7b3816cfaac58af5a2420baa0 100644 (file)
@@ -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)
index d202790a1115e96efb5c1c142e64157d8b28a8b4..928f82f32e1004502ecaf2b61e8439af9e9716d5 100644 (file)
@@ -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);
index f272c190afdaa3eb07a20c6c8c9a001557b717de..357933105d35d56f5718fbad56b30595dbe80557 100644 (file)
@@ -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);