bpf: fix arraymap NULL deref and missing overflow and zero size checks
[deliverable/linux.git] / net / wireless / nl80211.c
index cb9f5a44ffadf7175d109cadefb091bbfdbb41e4..1a31736914e5ca9a9ac2cd2083deb43bfb7e6fc3 100644 (file)
@@ -884,7 +884,12 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
                if (!wdev->current_bss)
                        return -ENOLINK;
                break;
-       default:
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_OCB:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_WDS:
+       case NUM_NL80211_IFTYPES:
                return -EINVAL;
        }
 
@@ -1514,8 +1519,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                        if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
                                CMD(channel_switch, CHANNEL_SWITCH);
                        CMD(set_qos_map, SET_QOS_MAP);
-                       if (rdev->wiphy.flags &
-                                       WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
+                       if (rdev->wiphy.features &
+                                       NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
                                CMD(add_tx_ts, ADD_TX_TS);
                }
                /* add into the if now */
@@ -2605,7 +2610,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
            !(rdev->wiphy.interface_modes & (1 << type)))
                return -EOPNOTSUPP;
 
-       if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
+       if ((type == NL80211_IFTYPE_P2P_DEVICE ||
+            rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
+           info->attrs[NL80211_ATTR_MAC]) {
                nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
                           ETH_ALEN);
                if (!is_valid_ether_addr(params.macaddr))
@@ -4398,10 +4405,12 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
-       u8 *mac_addr = NULL;
+       struct station_del_parameters params;
+
+       memset(&params, 0, sizeof(params));
 
        if (info->attrs[NL80211_ATTR_MAC])
-               mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+               params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
@@ -4412,7 +4421,28 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->del_station)
                return -EOPNOTSUPP;
 
-       return rdev_del_station(rdev, dev, mac_addr);
+       if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
+               params.subtype =
+                       nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
+               if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
+                   params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
+                       return -EINVAL;
+       } else {
+               /* Default to Deauthentication frame */
+               params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
+       }
+
+       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+               params.reason_code =
+                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+               if (params.reason_code == 0)
+                       return -EINVAL; /* 0 is reserved */
+       } else {
+               /* Default to reason code 2 */
+               params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
+       }
+
+       return rdev_del_station(rdev, dev, &params);
 }
 
 static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
@@ -4624,6 +4654,96 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
        return rdev_del_mpath(rdev, dev, dst);
 }
 
+static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       int err;
+       struct net_device *dev = info->user_ptr[1];
+       struct mpath_info pinfo;
+       struct sk_buff *msg;
+       u8 *dst = NULL;
+       u8 mpp[ETH_ALEN];
+
+       memset(&pinfo, 0, sizeof(pinfo));
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (!rdev->ops->get_mpp)
+               return -EOPNOTSUPP;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+
+       err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
+       if (err)
+               return err;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
+                              dev, dst, mpp, &pinfo) < 0) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int nl80211_dump_mpp(struct sk_buff *skb,
+                           struct netlink_callback *cb)
+{
+       struct mpath_info pinfo;
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+       u8 dst[ETH_ALEN];
+       u8 mpp[ETH_ALEN];
+       int path_idx = cb->args[2];
+       int err;
+
+       err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+       if (err)
+               return err;
+
+       if (!rdev->ops->dump_mpp) {
+               err = -EOPNOTSUPP;
+               goto out_err;
+       }
+
+       if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out_err;
+       }
+
+       while (1) {
+               err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
+                                   mpp, &pinfo);
+               if (err == -ENOENT)
+                       break;
+               if (err)
+                       goto out_err;
+
+               if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
+                                      cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                      wdev->netdev, dst, mpp,
+                                      &pinfo) < 0)
+                       goto out;
+
+               path_idx++;
+       }
+
+ out:
+       cb->args[2] = path_idx;
+       err = skb->len;
+ out_err:
+       nl80211_finish_wdev_dump(rdev);
+       return err;
+}
+
 static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5923,10 +6043,10 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
         * function is called under RTNL lock, so this should not be a problem.
         */
        static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
-       u8 radar_detect_width = 0;
        int err;
        bool need_new_beacon = false;
        int len, i;
+       u32 cs_count;
 
        if (!rdev->ops->channel_switch ||
            !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
@@ -5963,7 +6083,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
                return -EINVAL;
 
-       params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+       /* Even though the attribute is u32, the specification says
+        * u8, so let's make sure we don't overflow.
+        */
+       cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+       if (cs_count > 255)
+               return -EINVAL;
+
+       params.count = cs_count;
 
        if (!need_new_beacon)
                goto skip_beacons;
@@ -6051,10 +6178,8 @@ skip_beacons:
        if (err < 0)
                return err;
 
-       if (err > 0) {
-               radar_detect_width = BIT(params.chandef.width);
+       if (err > 0)
                params.radar_required = true;
-       }
 
        if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
                params.block_tx = true;
@@ -8151,6 +8276,28 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
        return -EINVAL;
 }
 
+static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct ocb_setup setup = {};
+       int err;
+
+       err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+       if (err)
+               return err;
+
+       return cfg80211_join_ocb(rdev, dev, &setup);
+}
+
+static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+
+       return cfg80211_leave_ocb(rdev, dev);
+}
+
 static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9436,7 +9583,7 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
        u16 admitted_time = 0;
        int err;
 
-       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
+       if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
                return -EOPNOTSUPP;
 
        if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
@@ -9452,12 +9599,10 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        /* WMM uses TIDs 0-7 even for TSPEC */
-       if (tsid < IEEE80211_FIRST_TSPEC_TSID) {
-               if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
-                       return -EINVAL;
-       } else {
+       if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
                /* TODO: handle 802.11 TSPEC/admission control
-                * need more attributes for that (e.g. BA session requirement)
+                * need more attributes for that (e.g. BA session requirement);
+                * change the WMM adminssion test above to allow both then
                 */
                return -EINVAL;
        }
@@ -9773,6 +9918,15 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_GET_MPP,
+               .doit = nl80211_get_mpp,
+               .dumpit = nl80211_dump_mpp,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
        {
                .cmd = NL80211_CMD_SET_MPATH,
                .doit = nl80211_set_mpath,
@@ -10087,6 +10241,22 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_JOIN_OCB,
+               .doit = nl80211_join_ocb,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_LEAVE_OCB,
+               .doit = nl80211_leave_ocb,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 #ifdef CONFIG_PM
        {
                .cmd = NL80211_CMD_GET_WOWLAN,
This page took 0.028926 seconds and 5 git commands to generate.