Commit 9b395bc3 authored by Johannes Berg's avatar Johannes Berg

mac80211: verify that skb data is present

A number of places in the mesh code don't check that
the frame data is present and in the skb header when
trying to access. Add those checks and the necessary
pskb_may_pull() calls. This prevents accessing data
that doesn't actually exist.

To do this, export ieee80211_get_mesh_hdrlen() to be
able to use it in mac80211.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 4a4f1a58
...@@ -2651,6 +2651,15 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); ...@@ -2651,6 +2651,15 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb);
*/ */
unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc); unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc);
/**
* ieee80211_get_mesh_hdrlen - get mesh extension header length
* @meshhdr: the mesh extension header, only the flags field
* (first byte) will be accessed
* Returns the length of the extension header, which is always at
* least 6 bytes and at most 18 if address 5 and 6 are present.
*/
unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
/** /**
* DOC: Data path helpers * DOC: Data path helpers
* *
......
...@@ -531,6 +531,11 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ...@@ -531,6 +531,11 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_action(hdr->frame_control)) { if (ieee80211_is_action(hdr->frame_control)) {
u8 category; u8 category;
/* make sure category field is present */
if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE)
return RX_DROP_MONITOR;
mgmt = (struct ieee80211_mgmt *)hdr; mgmt = (struct ieee80211_mgmt *)hdr;
category = mgmt->u.action.category; category = mgmt->u.action.category;
if (category != WLAN_CATEGORY_MESH_ACTION && if (category != WLAN_CATEGORY_MESH_ACTION &&
...@@ -1892,6 +1897,20 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ...@@ -1892,6 +1897,20 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
hdr = (struct ieee80211_hdr *) skb->data; hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control); hdrlen = ieee80211_hdrlen(hdr->frame_control);
/* make sure fixed part of mesh header is there, also checks skb len */
if (!pskb_may_pull(rx->skb, hdrlen + 6))
return RX_DROP_MONITOR;
mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
/* make sure full mesh header is there, also checks skb len */
if (!pskb_may_pull(rx->skb,
hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr)))
return RX_DROP_MONITOR;
/* reload pointers */
hdr = (struct ieee80211_hdr *) skb->data;
mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
/* frame is in RMC, don't forward */ /* frame is in RMC, don't forward */
...@@ -1915,9 +1934,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ...@@ -1915,9 +1934,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
if (is_multicast_ether_addr(hdr->addr1)) { if (is_multicast_ether_addr(hdr->addr1)) {
mpp_addr = hdr->addr3; mpp_addr = hdr->addr3;
proxied_addr = mesh_hdr->eaddr1; proxied_addr = mesh_hdr->eaddr1;
} else { } else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) {
/* has_a4 already checked in ieee80211_rx_mesh_check */
mpp_addr = hdr->addr4; mpp_addr = hdr->addr4;
proxied_addr = mesh_hdr->eaddr2; proxied_addr = mesh_hdr->eaddr2;
} else {
return RX_DROP_MONITOR;
} }
rcu_read_lock(); rcu_read_lock();
...@@ -2354,6 +2376,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -2354,6 +2376,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
} }
break; break;
case WLAN_CATEGORY_SELF_PROTECTED: case WLAN_CATEGORY_SELF_PROTECTED:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.self_prot.action_code)))
break;
switch (mgmt->u.action.u.self_prot.action_code) { switch (mgmt->u.action.u.self_prot.action_code) {
case WLAN_SP_MESH_PEERING_OPEN: case WLAN_SP_MESH_PEERING_OPEN:
case WLAN_SP_MESH_PEERING_CLOSE: case WLAN_SP_MESH_PEERING_CLOSE:
...@@ -2372,6 +2398,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -2372,6 +2398,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
} }
break; break;
case WLAN_CATEGORY_MESH_ACTION: case WLAN_CATEGORY_MESH_ACTION:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.mesh_action.action_code)))
break;
if (!ieee80211_vif_is_mesh(&sdata->vif)) if (!ieee80211_vif_is_mesh(&sdata->vif))
break; break;
if (mesh_action_is_path_sel(mgmt) && if (mesh_action_is_path_sel(mgmt) &&
......
...@@ -309,7 +309,7 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) ...@@ -309,7 +309,7 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
} }
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
{ {
int ae = meshhdr->flags & MESH_FLAGS_AE; int ae = meshhdr->flags & MESH_FLAGS_AE;
/* 802.11-2012, 8.2.4.7.3 */ /* 802.11-2012, 8.2.4.7.3 */
...@@ -323,6 +323,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) ...@@ -323,6 +323,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
return 18; return 18;
} }
} }
EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
enum nl80211_iftype iftype) enum nl80211_iftype iftype)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment