nl80211: Generate deauth/disassoc event for locally generated frames
[pandora-kernel.git] / net / wireless / nl80211.c
index 353e1a4..195424e 100644 (file)
@@ -118,6 +118,36 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
 };
 
+/* IE validation */
+static bool is_valid_ie_attr(const struct nlattr *attr)
+{
+       const u8 *pos;
+       int len;
+
+       if (!attr)
+               return true;
+
+       pos = nla_data(attr);
+       len = nla_len(attr);
+
+       while (len) {
+               u8 elemlen;
+
+               if (len < 2)
+                       return false;
+               len -= 2;
+
+               elemlen = pos[1];
+               if (elemlen > len)
+                       return false;
+
+               len -= elemlen;
+               pos += 2 + elemlen;
+       }
+
+       return true;
+}
+
 /* message building helper */
 static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
                                   int flags, u8 cmd)
@@ -1069,6 +1099,9 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        struct beacon_parameters params;
        int haveinfo = 0;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2442,6 +2475,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        enum ieee80211_band band;
        size_t ie_len;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2710,6 +2746,15 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        struct wiphy *wiphy;
        int err;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2731,11 +2776,6 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC]) {
-               err = -EINVAL;
-               goto out;
-       }
-
        wiphy = &drv->wiphy;
        memset(&req, 0, sizeof(req));
 
@@ -2761,13 +2801,10 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
-               req.auth_type =
-                       nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
-               if (!nl80211_valid_auth_type(req.auth_type)) {
-                       err = -EINVAL;
-                       goto out;
-               }
+       req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+       if (!nl80211_valid_auth_type(req.auth_type)) {
+               err = -EINVAL;
+               goto out;
        }
 
        err = drv->ops->auth(&drv->wiphy, dev, &req);
@@ -2788,6 +2825,13 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        struct wiphy *wiphy;
        int err;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC] ||
+           !info->attrs[NL80211_ATTR_SSID])
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2809,12 +2853,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC] ||
-           !info->attrs[NL80211_ATTR_SSID]) {
-               err = -EINVAL;
-               goto out;
-       }
-
        wiphy = &drv->wiphy;
        memset(&req, 0, sizeof(req));
 
@@ -2856,6 +2894,15 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
        struct wiphy *wiphy;
        int err;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_REASON_CODE])
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2877,24 +2924,16 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC]) {
-               err = -EINVAL;
-               goto out;
-       }
-
        wiphy = &drv->wiphy;
        memset(&req, 0, sizeof(req));
 
        req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
-               req.reason_code =
-                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
-               if (req.reason_code == 0) {
-                       /* Reason Code 0 is reserved */
-                       err = -EINVAL;
-                       goto out;
-               }
+       req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+       if (req.reason_code == 0) {
+               /* Reason Code 0 is reserved */
+               err = -EINVAL;
+               goto out;
        }
 
        if (info->attrs[NL80211_ATTR_IE]) {
@@ -2920,6 +2959,15 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
        struct wiphy *wiphy;
        int err;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_REASON_CODE])
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2941,24 +2989,16 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC]) {
-               err = -EINVAL;
-               goto out;
-       }
-
        wiphy = &drv->wiphy;
        memset(&req, 0, sizeof(req));
 
        req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
-               req.reason_code =
-                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
-               if (req.reason_code == 0) {
-                       /* Reason Code 0 is reserved */
-                       err = -EINVAL;
-                       goto out;
-               }
+       req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+       if (req.reason_code == 0) {
+               /* Reason Code 0 is reserved */
+               err = -EINVAL;
+               goto out;
        }
 
        if (info->attrs[NL80211_ATTR_IE]) {
@@ -3334,7 +3374,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
        if (!msg)
                return;
 
@@ -3353,7 +3393,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
        return;
 
  nla_put_failure:
@@ -3375,17 +3415,16 @@ void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
        nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
 }
 
-void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
-                           struct net_device *netdev, const u8 *buf,
-                           size_t len)
+void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+                        struct net_device *netdev, const u8 *buf, size_t len)
 {
        nl80211_send_mlme_event(rdev, netdev, buf, len,
                                NL80211_CMD_DEAUTHENTICATE);
 }
 
-void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev, const u8 *buf,
-                             size_t len)
+void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+                          struct net_device *netdev, const u8 *buf,
+                          size_t len)
 {
        nl80211_send_mlme_event(rdev, netdev, buf, len,
                                NL80211_CMD_DISASSOCIATE);