]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Ensure we can always read the DHCP option length when applicable.
authorRoy Marples <roy@marples.name>
Fri, 19 Jun 2015 11:31:50 +0000 (11:31 +0000)
committerRoy Marples <roy@marples.name>
Fri, 19 Jun 2015 11:31:50 +0000 (11:31 +0000)
Fixes CVE-2014-7912.

dhcp.c

diff --git a/dhcp.c b/dhcp.c
index bbacd7e003c17cb167f1457f8fdd189052b7a271..28c4ca597a539c9013c3aa6f8f894a90b5e3c089 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -168,30 +168,9 @@ get_option(struct dhcpcd_ctx *ctx,
 
        while (p < e) {
                o = *p++;
-               if (o == opt) {
-                       if (op) {
-                               if (!ctx->opt_buffer) {
-                                       ctx->opt_buffer =
-                                           malloc(DHCP_OPTION_LEN +
-                                           BOOTFILE_LEN + SERVERNAME_LEN);
-                                       if (ctx->opt_buffer == NULL)
-                                               return NULL;
-                               }
-                               if (!bp)
-                                       bp = ctx->opt_buffer;
-                               memcpy(bp, op, ol);
-                               bp += ol;
-                       }
-                       ol = *p;
-                       if (p + ol > e) {
-                               errno = EINVAL;
-                               return NULL;
-                       }
-                       op = p + 1;
-                       bl += ol;
-               }
                switch (o) {
                case DHO_PAD:
+                       /* No length to read */
                        continue;
                case DHO_END:
                        if (overl & 1) {
@@ -206,17 +185,48 @@ get_option(struct dhcpcd_ctx *ctx,
                                e = p + sizeof(dhcp->servername);
                        } else
                                goto exit;
-                       break;
-               case DHO_OPTIONSOVERLOADED:
+                       /* No length to read */
+                       continue;
+               }
+
+               /* Check we can read the length */
+               if (p == e) {
+                       errno = EINVAL;
+                       return NULL;
+               }
+               l = *p++;
+
+               if (o == DHO_OPTIONSOVERLOADED) {
                        /* Ensure we only get this option once by setting
                         * the last bit as well as the value.
                         * This is valid because only the first two bits
                         * actually mean anything in RFC2132 Section 9.3 */
-                       if (!overl)
-                               overl = 0x80 | p[1];
-                       break;
+                       if (l == 1 && !overl)
+                               overl = 0x80 | p[0];
+               }
+
+               if (o == opt) {
+                       if (op) {
+                               if (!ctx->opt_buffer) {
+                                       ctx->opt_buffer =
+                                           malloc(DHCP_OPTION_LEN +
+                                           BOOTFILE_LEN + SERVERNAME_LEN);
+                                       if (ctx->opt_buffer == NULL)
+                                               return NULL;
+                               }
+                               if (!bp)
+                                       bp = ctx->opt_buffer;
+                               memcpy(bp, op, ol);
+                               bp += ol;
+                       }
+                       ol = l;
+                       if (p + ol >= e) {
+                               errno = EINVAL;
+                               return NULL;
+                       }
+                       op = p;
+                       bl += ol;
                }
-               l = *p++;
                p += l;
        }