From de981e6070d168b58ec1bb0713ded77ed4ad87f4 Mon Sep 17 00:00:00 2001
From: Guy Harris <guy@alum.mit.edu>
Date: Sat, 4 Feb 2017 15:33:12 -0800
Subject: [PATCH] CVE-2017-12901/EIGRP: Do more length checks.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes a buffer over-read discovered by Forcepoint's security
researchers Otto Airamo & Antti Levomäki.

Add a test using the capture file supplied by the reporter(s).
---
 print-eigrp.c             |   47 +
 tests/TESTLIST            |    1 +
 tests/eigrp-tlv-oobr.out  | 4136 +++++++++++++++++++++++++++++++++++++
 tests/eigrp-tlv-oobr.pcap |  Bin 0 -> 65575 bytes
 4 files changed, 4184 insertions(+)
 create mode 100644 tests/eigrp-tlv-oobr.out
 create mode 100644 tests/eigrp-tlv-oobr.pcap

Index: tcpdump-3.9.8/print-eigrp.c
===================================================================
--- tcpdump-3.9.8.orig/print-eigrp.c
+++ tcpdump-3.9.8/print-eigrp.c
@@ -36,6 +36,7 @@ static const char rcsid[] _U_ =
 /*
  * packet format documented at
  * http://www.rhyshaden.com/eigrp.htm
+ * RFC 7868
  */
 
 struct eigrp_common_header {
@@ -251,6 +252,12 @@ eigrp_print(register const u_char *pptr,
 
     /* ok they seem to want to know everything - lets fully decode it */
 
+    if (len < sizeof(struct eigrp_common_header)) {
+        printf("EIGRP %s, length: %u (too short, < %u)",
+               tok2str(eigrp_opcode_values, "unknown (%u)",eigrp_com_header->opcode),
+               len, (u_int) sizeof(struct eigrp_common_header));
+        return;
+    }
     tlen=len-sizeof(struct eigrp_common_header);
 
     /* FIXME print other header info */
@@ -291,6 +298,11 @@ eigrp_print(register const u_char *pptr,
                eigrp_tlv_type,
                eigrp_tlv_len);
 
+        if (eigrp_tlv_len < sizeof(struct eigrp_tlv_header)) {
+                printf(" (too short, < %u)",
+                        (u_int) sizeof(struct eigrp_tlv_header));
+                break;
+        }
         tlv_tptr=tptr+sizeof(struct eigrp_tlv_header);
         tlv_tlen=eigrp_tlv_len-sizeof(struct eigrp_tlv_header);
 
@@ -301,6 +313,11 @@ eigrp_print(register const u_char *pptr,
 
         case EIGRP_TLV_GENERAL_PARM:
             tlv_ptr.eigrp_tlv_general_parm = (const struct eigrp_tlv_general_parm_t *)tlv_tptr;
+            if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_general_parm)) {
+                printf(" (too short, < %u)",
+                    (u_int) (sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_general_parm)));
+                break;
+            }
 
             printf("\n\t    holdtime: %us, k1 %u, k2 %u, k3 %u, k4 %u, k5 %u",
                    EXTRACT_16BITS(tlv_ptr.eigrp_tlv_general_parm->holdtime),
@@ -313,6 +330,11 @@ eigrp_print(register const u_char *pptr,
 
         case EIGRP_TLV_SW_VERSION:
             tlv_ptr.eigrp_tlv_sw_version = (const struct eigrp_tlv_sw_version_t *)tlv_tptr;
+            if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_sw_version)) {
+                printf(" (too short, < %u)",
+                    (u_int) (sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_sw_version)));
+                break;
+            }
 
             printf("\n\t    IOS version: %u.%u, EIGRP version %u.%u",
                    tlv_ptr.eigrp_tlv_sw_version->ios_major,
@@ -323,6 +345,11 @@ eigrp_print(register const u_char *pptr,
 
         case EIGRP_TLV_IP_INT:
             tlv_ptr.eigrp_tlv_ip_int = (const struct eigrp_tlv_ip_int_t *)tlv_tptr;
+            if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_ip_int)) {
+                printf(" (too short, < %u)",
+                    (u_int) (sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_ip_int)));
+                break;
+            }
 
             bit_length = tlv_ptr.eigrp_tlv_ip_int->plen;
             if (bit_length > 32) {
@@ -352,6 +379,11 @@ eigrp_print(register const u_char *pptr,
 
         case EIGRP_TLV_IP_EXT:
             tlv_ptr.eigrp_tlv_ip_ext = (const struct eigrp_tlv_ip_ext_t *)tlv_tptr;
+            if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_ip_ext)) {
+                printf(" (too short, < %u)",
+                    (u_int) (sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_ip_ext)));
+                break;
+            }
 
             bit_length = tlv_ptr.eigrp_tlv_ip_ext->plen;
             if (bit_length > 32) {
@@ -389,6 +421,11 @@ eigrp_print(register const u_char *pptr,
 
         case EIGRP_TLV_AT_CABLE_SETUP:
             tlv_ptr.eigrp_tlv_at_cable_setup = (const struct eigrp_tlv_at_cable_setup_t *)tlv_tptr;
+            if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_cable_setup)) {
+                printf(" (too short, < %u)",
+                    (u_int) (sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_cable_setup)));
+                break;
+            }
 
             printf("\n\t    Cable-range: %u-%u, Router-ID %u",
                    EXTRACT_16BITS(&tlv_ptr.eigrp_tlv_at_cable_setup->cable_start),
@@ -398,6 +435,11 @@ eigrp_print(register const u_char *pptr,
 
         case EIGRP_TLV_AT_INT:
             tlv_ptr.eigrp_tlv_at_int = (const struct eigrp_tlv_at_int_t *)tlv_tptr;
+            if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_int)) {
+                printf(" (too short, < %u)",
+                    (u_int) (sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_int)));
+                break;
+            }
 
             printf("\n\t     Cable-Range: %u-%u, nexthop: ",
                    EXTRACT_16BITS(&tlv_ptr.eigrp_tlv_at_int->cable_start),
@@ -421,6 +463,11 @@ eigrp_print(register const u_char *pptr,
 
         case EIGRP_TLV_AT_EXT:
             tlv_ptr.eigrp_tlv_at_ext = (const struct eigrp_tlv_at_ext_t *)tlv_tptr;
+            if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_ext)) {
+                printf(" (too short, < %u)",
+                    (u_int) (sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_ext)));
+                break;
+            }
 
             printf("\n\t     Cable-Range: %u-%u, nexthop: ",
                    EXTRACT_16BITS(&tlv_ptr.eigrp_tlv_at_ext->cable_start),
