net: wireless: bcmdhd_88: security enhancement for BRCM ether type
Ramanathan R [Mon, 24 Oct 2016 08:06:34 +0000 (13:06 +0530)]
Strict checkup for type and event data length
Patch to fix CVE-2016-0801 in DHD driver

Bug 200240360

Change-Id: I67cab7809740c1cba9079fbab0a3ad1848cd7586
Signed-off-by: Ramanathan R <ramanathan.r@broadcom.com>
Signed-off-by: Mohan Thadikamalla <mohant@nvidia.com>
Reviewed-on: http://git-master/r/1241426
GVS: Gerrit_Virtual_Submit
Reviewed-by: Om Prakash Singh <omp@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>

drivers/net/wireless/bcmdhd_88/bcmevent.c
drivers/net/wireless/bcmdhd_88/dhd.h
drivers/net/wireless/bcmdhd_88/dhd_common.c
drivers/net/wireless/bcmdhd_88/dhd_linux.c
drivers/net/wireless/bcmdhd_88/include/dnglevent.h [new file with mode: 0644]
drivers/net/wireless/bcmdhd_88/include/proto/bcmeth.h
drivers/net/wireless/bcmdhd_88/include/proto/bcmevent.h

index e66cc1a..0c12f9c 100644 (file)
 
 #include <typedefs.h>
 #include <bcmutils.h>
+#include <bcmendian.h>
 #include <proto/ethernet.h>
 #include <proto/bcmeth.h>
 #include <proto/bcmevent.h>
+#include <proto/802.11.h>
 
 #if WLC_E_LAST != 130
 #error "You need to add an entry to bcmevent_names[] for the new event"
@@ -156,3 +158,99 @@ const bcmevent_name_t bcmevent_names[] = {
 };
 
 const int bcmevent_names_size = ARRAYSIZE(bcmevent_names);
+
+/*
+ * Validate if the event is proper and if valid copy event header to event.
+ * If proper event pointer is passed, to just validate, pass NULL to event.
+ *
+ * Return values are
+ *     BCME_OK - It is a BRCM event or BRCM dongle event
+ *     BCME_NOTFOUND - Not BRCM, not an event, may be okay
+ *     BCME_BADLEN - Bad length, should not process, just drop
+ */
+int
+is_wlc_event_frame(void *pktdata, uint pktlen, uint16 exp_usr_subtype,
+       bcm_event_msg_u_t *out_event)
+{
+       uint16 len;
+       uint16 subtype;
+       uint16 usr_subtype;
+       bcm_event_t *bcm_event;
+       uint8 *pktend;
+       int err = BCME_OK;
+
+       pktend = (uint8 *)pktdata + pktlen;
+       bcm_event = (bcm_event_t *)pktdata;
+
+       /* only care about 16-bit subtype / length versions */
+       if ((uint8 *)&bcm_event->bcm_hdr < pktend) {
+               uint8 short_subtype = *(uint8 *)&bcm_event->bcm_hdr;
+               if (!(short_subtype & 0x80)) {
+                       err = BCME_NOTFOUND;
+                       goto done;
+               }
+       }
+
+       /* must have both ether_header and bcmeth_hdr */
+       if (pktlen < OFFSETOF(bcm_event_t, event)) {
+               err = BCME_BADLEN;
+               goto done;
+       }
+
+       /* check length in bcmeth_hdr */
+       len = ntoh16_ua((void *)&bcm_event->bcm_hdr.length);
+#ifdef EXT_STA
+       if (((uint8 *)&bcm_event->bcm_hdr.version + len) > pktend) {
+               err = BCME_BADLEN;
+               goto done;
+       }
+#endif /* EXT_STA */
+
+       /* match on subtype, oui and usr subtype for BRCM events */
+       subtype = ntoh16_ua((void *)&bcm_event->bcm_hdr.subtype);
+       if (subtype != BCMILCP_SUBTYPE_VENDOR_LONG) {
+               err = BCME_NOTFOUND;
+               goto done;
+       }
+
+       if (bcmp(BRCM_OUI, &bcm_event->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
+               err = BCME_NOTFOUND;
+               goto done;
+       }
+
+       /* if it is a bcm_event or bcm_dngl_event_t, validate it */
+       usr_subtype = ntoh16_ua((void *)&bcm_event->bcm_hdr.usr_subtype);
+       switch (usr_subtype) {
+       case BCMILCP_BCM_SUBTYPE_EVENT:
+               if (pktlen < sizeof(bcm_event_t)) {
+                       err = BCME_BADLEN;
+                       goto done;
+               }
+
+               len = (uint16)sizeof(bcm_event_t) +
+                       (uint16)ntoh32_ua((void *)&bcm_event->event.datalen);
+               if ((uint8 *)pktdata + len > pktend) {
+                       err = BCME_BADLEN;
+                       goto done;
+               }
+
+               if (exp_usr_subtype && (exp_usr_subtype != usr_subtype)) {
+                       err = BCME_NOTFOUND;
+                       goto done;
+               }
+
+               if (out_event) {
+                       /* ensure BRCM event pkt aligned */
+                       memcpy(&out_event->event, &bcm_event->event, sizeof(wl_event_msg_t));
+               }
+
+               break;
+
+       default:
+               err = BCME_NOTFOUND;
+               goto done;
+       }
+
+done:
+       return err;
+}
index 4db45f3..b774314 100644 (file)
@@ -628,6 +628,7 @@ extern struct net_device * dhd_idx2net(void *pub, int ifidx);
 extern int net_os_send_hang_message(struct net_device *dev);
 extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, size_t pktlen,
                          wl_event_msg_t *, void **data_ptr);
+extern int wl_host_event_get_data(void *pktdata, uint pktlen, bcm_event_msg_u_t *evu);
 extern void wl_event_to_host_order(wl_event_msg_t * evt);
 
 extern int dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len);
index cadce99..506a84e 100644 (file)
@@ -1169,46 +1169,72 @@ wl_show_host_event(wl_event_msg_t *event, void *event_data)
 }
 #endif /* SHOW_EVENTS */
 
+/* Check whether packet is a BRCM event pkt. If it is, record event data. */
+int
+wl_host_event_get_data(void *pktdata, uint pktlen, bcm_event_msg_u_t *evu)
+{
+       int ret;
+
+       ret = is_wlc_event_frame(pktdata, pktlen, 0, evu);
+       if (ret != BCME_OK) {
+               DHD_ERROR(("%s: Invalid event frame, err = %d\n",
+                       __FUNCTION__, ret));
+               return ret;
+       }
+
+       return ret;
+}
+
 int
 wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, size_t pktlen,
               wl_event_msg_t *event, void **data_ptr)
 {
        /* check whether packet is a BRCM event pkt */
        bcm_event_t *pvt_data = (bcm_event_t *)pktdata;
+       bcm_event_msg_u_t evu;
        uint8 *event_data;
        uint32 type, status, datalen;
        uint16 flags;
        int evlen;
+       int ret;
+       uint16 usr_subtype;
 
-       if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
-               DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__));
-               return (BCME_ERROR);
+       /* make sure it is a BRCM event pkt and record event data */
+       ret = wl_host_event_get_data(pktdata, pktlen, &evu);
+       if (ret != BCME_OK) {
+               return ret;
        }
 
-       /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
-       if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) {
-               DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__));
-               return (BCME_ERROR);
+       usr_subtype = ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype);
+       switch (usr_subtype) {
+       case BCMILCP_BCM_SUBTYPE_EVENT:
+               memcpy(event, &evu.event, sizeof(wl_event_msg_t));
+               *data_ptr = &pvt_data[1];
+               break;
+       case BCMILCP_BCM_SUBTYPE_DNGLEVENT:
+#ifdef DNGL_EVENT_SUPPORT
+       /* If it is a DNGL event process it first */
+       if (dngl_host_event(dhd_pub, pktdata, &evu.dngl_event, pktlen) == BCME_OK) {
+               /*
+                * Return error purposely to prevent DNGL event being processed
+                * as BRCM event
+                */
+                return BCME_ERROR;
+       }
+#endif /* DNGL_EVENT_SUPPORT */
+               return BCME_NOTFOUND;
+       default:
+               return BCME_NOTFOUND;
        }
 
-       if (pktlen < sizeof(bcm_event_t))
-               return (BCME_ERROR);
-
-       *data_ptr = &pvt_data[1];
+       /* start wl_event_msg process */
        event_data = *data_ptr;
 
-       /* memcpy since BRCM event pkt may be unaligned. */
-       memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t));
-
        type = ntoh32_ua((void *)&event->event_type);
        flags = ntoh16_ua((void *)&event->flags);
        status = ntoh32_ua((void *)&event->status);
        datalen = ntoh32_ua((void *)&event->datalen);
-       if (datalen > pktlen)
-               return (BCME_ERROR);
        evlen = datalen + sizeof(bcm_event_t);
-       if (evlen > pktlen)
-               return (BCME_ERROR);
 
        switch (type) {
 #ifdef PROP_TXSTATUS
index 261d9c7..28d110a 100644 (file)
@@ -2182,16 +2182,23 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
                /* Process special event packets and then discard them */
                memset(&event, 0, sizeof(event));
                if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {
-                       dhd_wl_host_event(dhd, &ifidx,
+                       int ret_event;
+
+                       ret_event = dhd_wl_host_event(dhd, &ifidx,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
                        skb_mac_header(skb),
 #else
                        skb->mac.raw,
 #endif
-                       len - 2,
+                       len,
                        &event,
                        &data);
 
+                       if (ret_event != BCME_OK) {
+                               PKTFREE(dhdp->osh, pktbuf, FALSE);
+                               continue;
+                       }
+
                        wl_event_to_host_order(&event);
                        if (!tout_ctrl)
                                tout_ctrl = DHD_PACKET_TIMEOUT_MS;
diff --git a/drivers/net/wireless/bcmdhd_88/include/dnglevent.h b/drivers/net/wireless/bcmdhd_88/include/dnglevent.h
new file mode 100644 (file)
index 0000000..c391552
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Broadcom Event  protocol definitions
+ *
+ * Copyright (C) 1999-2015, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * Broadcom Event  protocol definitions
+ *
+ * Dependencies: proto/bcmeth.h
+ *
+ * $Id: dnglevent.h $
+ *
+ * <<Broadcom-WL-IPTag/Open:>>
+ *
+ * -----------------------------------------------------------------------------
+ *
+ */
+
+/*
+ * Broadcom dngl Ethernet Events protocol defines
+ *
+ */
+
+#ifndef _DNGLEVENT_H_
+#define _DNGLEVENT_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+#include <proto/bcmeth.h>
+#include <proto/ethernet.h>
+#include <dngl_defs.h>
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+#define BCM_DNGL_EVENT_MSG_VERSION             1
+#define DNGL_E_RSRVD_1                         0x0
+#define DNGL_E_RSRVD_2                         0x1
+#define DNGL_E_SOCRAM_IND                      0x2
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+       uint16  version; /* Current version is 1 */
+       uint16  reserved; /* reserved for any future extension */
+       uint16  event_type; /* DNGL_E_SOCRAM_IND */
+       uint16  datalen; /* Length of the event payload */
+} BWL_POST_PACKED_STRUCT bcm_dngl_event_msg_t;
+
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_event {
+       struct ether_header eth;
+       bcmeth_hdr_t        bcm_hdr;
+       bcm_dngl_event_msg_t      dngl_event;
+       /* data portion follows */
+} BWL_POST_PACKED_STRUCT bcm_dngl_event_t;
+
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_socramind {
+       uint16                  tag;    /* data tag */
+       uint16                  length; /* data length */
+       uint8                   value[1]; /* data value with variable length specified by length */
+} BWL_POST_PACKED_STRUCT bcm_dngl_socramind_t;
+
+/* SOCRAM_IND type tags */
+#define SOCRAM_IND_ASSERT_TAG          0x1
+/* Health check top level module tags */
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_healthcheck {
+       uint16                  top_module_tag; /* top level module tag */
+       uint16                  top_module_len; /* Type of PCIE issue indication */
+       uint8                   value[1]; /* data value with variable length specified by length */
+} BWL_POST_PACKED_STRUCT bcm_dngl_healthcheck_t;
+
+#define HC_PCIEDEV_CONFIG_REGLIST_MAX  20
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_pcie_hc {
+       uint16                  reserved;
+       uint16                  pcie_err_ind_type; /* PCIE Module TAGs */
+       uint16                  pcie_flag;
+       uint32                  pcie_control_reg;
+       uint32                  pcie_config_regs[HC_PCIEDEV_CONFIG_REGLIST_MAX];
+} BWL_POST_PACKED_STRUCT bcm_dngl_pcie_hc_t;
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif /* _DNGLEVENT_H_ */
index 94ba246..ce255c1 100644 (file)
@@ -90,6 +90,7 @@
  */
 /* #define BCMILCP_BCM_SUBTYPE_EAPOL           3 */
 #define BCMILCP_BCM_SUBTYPE_DPT                        4
+#define BCMILCP_BCM_SUBTYPE_DNGLEVENT          5
 
 #define BCMILCP_BCM_SUBTYPEHDR_MINLENGTH       8
 #define BCMILCP_BCM_SUBTYPEHDR_VERSION         0
index 6474ecb..39c3ffe 100644 (file)
@@ -94,6 +94,11 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event {
        /* data portion follows */
 } BWL_POST_PACKED_STRUCT bcm_event_t;
 
+typedef union bcm_event_msg_u {
+       wl_event_msg_t  event;
+       /* add new event here */
+} bcm_event_msg_u_t;
+
 #define BCM_MSG_LEN    (sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header))
 
 /* Event messages */
@@ -233,6 +238,9 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event {
 #define WLC_E_CCX_S69_RESP_RX  129
 #define WLC_E_LAST                     130     /* highest val + 1 for range checking */
 
+/* validate if the event is proper and if valid copy event header to event */
+extern int is_wlc_event_frame(void *pktdata, uint pktlen, uint16 exp_usr_subtype,
+       bcm_event_msg_u_t *out_event);
 
 /* Table of event name strings for UIs and debugging dumps */
 typedef struct {