]> git.decadent.org.uk Git - odhcp6c.git/blobdiff - src/dhcpv6.c
Fix alignment of hash buffer in dhcpv6_response_is_valid
[odhcp6c.git] / src / dhcpv6.c
index a3e0a18e50ec1644b07110e371ac52bd6283ce27..f91f2cf16b8c8ff6fe881310433b98b68e2d8780 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>
@@ -109,6 +110,14 @@ static uint8_t reconf_key[16];
 static unsigned int client_options = 0;
 
 
+static uint32_t ntohl_unaligned(const uint8_t *data)
+{
+       uint32_t buf;
+
+       memcpy(&buf, data, sizeof(buf));
+       return ntohl(buf);
+}
+
 int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
 {
        client_options = options;
@@ -576,7 +585,9 @@ int dhcpv6_request(enum dhcpv6_msg type)
                // Receive rounds
                for (; len < 0 && (round_start < round_end);
                                round_start = odhcp6c_get_milli_time()) {
-                       uint8_t buf[1536], cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+                       uint8_t buf[1536];
+                       uint8_t cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]
+                               __aligned(__alignof__(struct cmsghdr));
                        struct iovec iov = {buf, sizeof(buf)};
                        struct sockaddr_in6 addr;
                        struct msghdr msg = {.msg_name = &addr, .msg_namelen = sizeof(addr),
@@ -691,7 +702,8 @@ static bool dhcpv6_response_is_valid(const void *buf, ssize_t len,
                                continue;
 
                        md5_ctx_t md5;
-                       uint8_t serverhash[16], secretbytes[64], hash[16];
+                       uint8_t serverhash[16], secretbytes[64];
+                       uint32_t hash[4];
                        memcpy(serverhash, r->key, sizeof(serverhash));
                        memset(r->key, 0, sizeof(r->key));
 
@@ -797,34 +809,23 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
                if (otype == DHCPV6_OPT_SERVERID && olen <= 130) {
                        memcpy(cand.duid, odata, olen);
                        cand.duid_len = olen;
-               } else if (otype == DHCPV6_OPT_STATUS && olen >= 2) {
-                       int error = ((int)odata[0] << 8 | (int)odata[1]);
-
-                       switch (error) {
-                       case DHCPV6_NoPrefixAvail:
-                               // Status code on global level
-                               cand.preference -= 2000;
-                               break;
-
-                       default :
-                               break;
-                       }
                } else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
                                cand.preference >= 0) {
                        cand.preference = pref = odata[0];
                } else if (otype == DHCPV6_OPT_RECONF_ACCEPT) {
                        cand.wants_reconfigure = true;
                } else if (otype == DHCPV6_OPT_SOL_MAX_RT && olen == 4) {
-                       uint32_t sol_max_rt = ntohl(*((uint32_t *)odata));
+                       uint32_t sol_max_rt = ntohl_unaligned(odata);
                        if (sol_max_rt >= DHCPV6_SOL_MAX_RT_MIN &&
                                        sol_max_rt <= DHCPV6_SOL_MAX_RT_MAX)
                                cand.sol_max_rt = sol_max_rt;
                } else if (otype == DHCPV6_OPT_INF_MAX_RT && olen == 4) {
-                       uint32_t inf_max_rt = ntohl(*((uint32_t *)odata));
+                       uint32_t inf_max_rt = ntohl_unaligned(odata);
                        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) {
@@ -834,7 +835,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)
@@ -1036,7 +1038,7 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                        } else if (otype == DHCPV6_OPT_SIP_SERVER_D) {
                                odhcp6c_add_state(STATE_SIP_FQDN, odata, olen);
                        } else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4) {
-                               refresh = ntohl(*((uint32_t*)odata));
+                               refresh = ntohl_unaligned(odata);
                                passthru = false;
                        } else if (otype == DHCPV6_OPT_AUTH) {
                                if (olen == -4 + sizeof(struct dhcpv6_auth_reconfigure)) {
@@ -1053,13 +1055,13 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                                        odhcp6c_add_state(STATE_AFTR_NAME, odata, olen);
                                passthru = false;
                        } else if (otype == DHCPV6_OPT_SOL_MAX_RT && olen == 4) {
-                               uint32_t sol_max_rt = ntohl(*((uint32_t *)odata));
+                               uint32_t sol_max_rt = ntohl_unaligned(odata);
                                if (sol_max_rt >= DHCPV6_SOL_MAX_RT_MIN &&
                                                sol_max_rt <= DHCPV6_SOL_MAX_RT_MAX)
                                        dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_max_rt;
                                passthru = false;
                        } else if (otype == DHCPV6_OPT_INF_MAX_RT && olen == 4) {
-                               uint32_t inf_max_rt = ntohl(*((uint32_t *)odata));
+                               uint32_t inf_max_rt = ntohl_unaligned(odata);
                                if (inf_max_rt >= DHCPV6_INF_MAX_RT_MIN &&
                                                inf_max_rt <= DHCPV6_INF_MAX_RT_MAX)
                                        dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = inf_max_rt;
@@ -1197,7 +1199,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;
                                }
@@ -1302,16 +1304,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);
@@ -1374,6 +1382,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;
@@ -1396,7 +1405,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)