]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Set NLA_F_NESTED flag with NL80211_ATTR_VENDOR_DATA conditionally
authorVinita S. Maloo <vmaloo@codeaurora.org>
Tue, 27 Oct 2020 18:03:59 +0000 (23:33 +0530)
committerJouni Malinen <j@w1.fi>
Tue, 10 Nov 2020 21:48:00 +0000 (23:48 +0200)
The newer kernel versions enforce strict netlink attribute policy
validation and will cause cfg80211 to reject vendor commands with
NL80211_ATTR_VENDOR_DATA if NLA_F_NESTED attribute is not set but
if the vendor command is expecting nested data within
NL80211_ATTR_VENDOR_DATA attribute.

Most of the earlier instances were addressed by adding NLA_F_NESTED
flag in nla_nest_start(). This commit addresses the remaining
instance in which NL80211_ATTR_VENDOR_DATA is populated using data
set by user through the control interface.

Enhance the control interface VENDOR command to indicate whether the
vendor subcommand uses nested attributes within NL80211_ATTR_VENDOR_DATA
attribute or not.

Set NLA_F_NESTED flag for existing QCA vendor commands which use nested
attributes within the NL80211_ATTR_VENDOR_DATA attributes so that the
old frameworks implementations for already existing commands work
without any issues.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
hostapd/ctrl_iface.c
hostapd/hostapd_cli.c
src/ap/ap_drv_ops.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/driver_i.h

index 30fa47f2db7f5803dc555cd9a119c1cea8ae987d..e2ae0ad445dd641c0a002b1aad35932bae1de1de 100644 (file)
@@ -2707,13 +2707,17 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
                                     char *buf, size_t buflen)
 {
        int ret;
-       char *pos;
+       char *pos, *temp = NULL;
        u8 *data = NULL;
        unsigned int vendor_id, subcmd;
+       enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
        struct wpabuf *reply;
        size_t data_len = 0;
 
-       /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+       /**
+        * cmd: <vendor id> <subcommand id> [<hex formatted data>]
+        * [nested=<0|1>]
+        */
        vendor_id = strtoul(cmd, &pos, 16);
        if (!isblank((unsigned char) *pos))
                return -EINVAL;
@@ -2723,7 +2727,9 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
        if (*pos != '\0') {
                if (!isblank((unsigned char) *pos++))
                        return -EINVAL;
-               data_len = os_strlen(pos);
+
+               temp = os_strchr(pos, ' ');
+               data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
        }
 
        if (data_len) {
@@ -2740,6 +2746,11 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
                }
        }
 
+       pos = os_strstr(cmd, "nested=");
+       if (pos)
+               nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED :
+                       NESTED_ATTR_NOT_USED;
+
        reply = wpabuf_alloc((buflen - 1) / 2);
        if (!reply) {
                os_free(data);
@@ -2747,7 +2758,7 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
        }
 
        ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
-                                    reply);
+                                    nested_attr_flag, reply);
 
        if (ret == 0)
                ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
index ad4398175e829067f6091f98b6f52dd33ab0ce94..dac00e01abfdf09ea459540616184d969a661b03 100644 (file)
@@ -1227,14 +1227,15 @@ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
        char cmd[256];
        int res;
 
-       if (argc < 2 || argc > 3) {
+       if (argc < 2 || argc > 4) {
                printf("Invalid vendor command\n"
-                      "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
+                      "usage: <vendor id> <command id> [<hex formatted command argument>] [nested=<0|1>]\n");
                return -1;
        }
 
-       res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
-                         argc == 3 ? argv[2] : "");
+       res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s%s%s", argv[0],
+                         argv[1], argc >= 3 ? argv[2] : "",
+                         argc == 4 ? " " : "", argc == 4 ? argv[3] : "");
        if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long VENDOR command.\n");
                return -1;
index 5738c1c97192f8a433a5717e52e12160ef190223..cc7ea07a2f0b530b6b803b212da1d51b5a1a6d34 100644 (file)
@@ -350,12 +350,13 @@ static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
 static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
                                         int vendor_id, int subcmd,
                                         const u8 *data, size_t data_len,
+                                        enum nested_attr nested_attr_flag,
                                         struct wpabuf *buf)
 {
        if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
                return -1;
        return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
-                                       data_len, buf);
+                                       data_len, nested_attr_flag, buf);
 }
 
 static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
index e8defaba2118913d2bef5c59743568ebf7ff510e..0019f54f9aca531ee4cb97b2e75b9a06999c0920 100644 (file)
@@ -2432,6 +2432,13 @@ struct external_auth {
        const u8 *pmkid;
 };
 
+/* enum nested_attr - Used to specify if subcommand uses nested attributes */
+enum nested_attr {
+       NESTED_ATTR_NOT_USED = 0,
+       NESTED_ATTR_USED = 1,
+       NESTED_ATTR_UNSPECIFIED = 2,
+};
+
 /**
  * struct wpa_driver_ops - Driver interface API definition
  *
@@ -3717,6 +3724,8 @@ struct wpa_driver_ops {
         * @priv: Private driver interface data
         * @vendor_id: Vendor id
         * @subcmd: Vendor command id
+        * @nested_attr_flag: Specifies if vendor subcommand uses nested
+        *      attributes or not
         * @data: Vendor command parameters (%NULL if no parameters)
         * @data_len: Data length
         * @buf: Return buffer (%NULL to ignore reply)
@@ -3724,9 +3733,10 @@ struct wpa_driver_ops {
         *
         * This function handles vendor specific commands that are passed to
         * the driver/device. The command is identified by vendor id and
-        * command id. Parameters can be passed as argument to the command
-        * in the data buffer. Reply (if any) will be filled in the supplied
-        * return buffer.
+        * command id. The nested_attr_flag specifies whether the subcommand
+        * uses nested attributes or not. Parameters can be passed
+        * as argument to the command in the data buffer. Reply (if any) will be
+        * filled in the supplied return buffer.
         *
         * The exact driver behavior is driver interface and vendor specific. As
         * an example, this will be converted to a vendor specific cfg80211
@@ -3734,6 +3744,7 @@ struct wpa_driver_ops {
         */
        int (*vendor_cmd)(void *priv, unsigned int vendor_id,
                          unsigned int subcmd, const u8 *data, size_t data_len,
+                         enum nested_attr nested_attr_flag,
                          struct wpabuf *buf);
 
        /**
index ffa0c467e40d6bb3aa6a6a0f501cc8c922bf031e..4774430500f1d9c05d5370f732c2a6eb1279e8f9 100644 (file)
@@ -9704,14 +9704,35 @@ static int vendor_reply_handler(struct nl_msg *msg, void *arg)
 }
 
 
+static bool is_cmd_with_nested_attrs(unsigned int vendor_id,
+                                    unsigned int subcmd)
+{
+       if (vendor_id != OUI_QCA)
+               return true;
+
+       switch (subcmd) {
+       case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+       case QCA_NL80211_VENDOR_SUBCMD_STATS_EXT:
+       case QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI:
+       case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
+       case QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS:
+       case QCA_NL80211_VENDOR_SUBCMD_NAN:
+               return false;
+       default:
+               return true;
+       }
+}
+
+
 static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
                              unsigned int subcmd, const u8 *data,
-                             size_t data_len, struct wpabuf *buf)
+                             size_t data_len, enum nested_attr nested_attr,
+                             struct wpabuf *buf)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int ret;
+       int ret, nla_flag;
 
 #ifdef CONFIG_TESTING_OPTIONS
        if (vendor_id == 0xffffffff) {
@@ -9737,11 +9758,20 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
        }
 #endif /* CONFIG_TESTING_OPTIONS */
 
+       if (nested_attr == NESTED_ATTR_USED)
+               nla_flag = NLA_F_NESTED;
+       else if (nested_attr == NESTED_ATTR_UNSPECIFIED &&
+                is_cmd_with_nested_attrs(vendor_id, subcmd))
+               nla_flag = NLA_F_NESTED;
+       else
+               nla_flag = 0;
+
        if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
            nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
            nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
            (data &&
-            nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data)))
+            nla_put(msg, nla_flag | NL80211_ATTR_VENDOR_DATA,
+                    data_len, data)))
                goto fail;
 
        ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
index 56a6aaaad768422ab764f1b0a646459e9ca1ddac..aeea5ddbc0168c31bf4caa57d509e23399fdf1c1 100644 (file)
@@ -8236,13 +8236,17 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
                                     char *buf, size_t buflen)
 {
        int ret;
-       char *pos;
+       char *pos, *temp = NULL;
        u8 *data = NULL;
        unsigned int vendor_id, subcmd;
+       enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
        struct wpabuf *reply;
        size_t data_len = 0;
 
-       /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+       /**
+        * cmd: <vendor id> <subcommand id> [<hex formatted data>]
+        * [nested=<0|1>]
+        */
        vendor_id = strtoul(cmd, &pos, 16);
        if (!isblank((unsigned char) *pos))
                return -EINVAL;
@@ -8252,7 +8256,9 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
        if (*pos != '\0') {
                if (!isblank((unsigned char) *pos++))
                        return -EINVAL;
-               data_len = os_strlen(pos);
+
+               temp = os_strchr(pos, ' ');
+               data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
        }
 
        if (data_len) {
@@ -8269,6 +8275,11 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
                }
        }
 
+       pos = os_strstr(cmd, "nested=");
+       if (pos)
+               nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED :
+                       NESTED_ATTR_NOT_USED;
+
        reply = wpabuf_alloc((buflen - 1) / 2);
        if (!reply) {
                os_free(data);
@@ -8276,7 +8287,7 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
        }
 
        ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
-                                reply);
+                                nested_attr_flag, reply);
 
        if (ret == 0)
                ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
index ba8cc552b9aad6e0f4c87096595423a8417f1b90..3a2828982a242d4cccca20faabcd039760e2ed23 100644 (file)
@@ -719,12 +719,14 @@ static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s,
 
 static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
                                     int vendor_id, int subcmd, const u8 *data,
-                                    size_t data_len, struct wpabuf *buf)
+                                    size_t data_len,
+                                    enum nested_attr nested_attr_flag,
+                                    struct wpabuf *buf)
 {
        if (!wpa_s->driver->vendor_cmd)
                return -1;
        return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
-                                        data, data_len, buf);
+                                        data, data_len, nested_attr_flag, buf);
 }
 
 static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,