Set NLA_F_NESTED flag with NL80211_ATTR_VENDOR_DATA conditionally
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>
This commit is contained in:
parent
cd3aa54a37
commit
c2c4686228
7 changed files with 88 additions and 21 deletions
|
@ -2707,13 +2707,17 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
|
||||||
char *buf, size_t buflen)
|
char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
char *pos;
|
char *pos, *temp = NULL;
|
||||||
u8 *data = NULL;
|
u8 *data = NULL;
|
||||||
unsigned int vendor_id, subcmd;
|
unsigned int vendor_id, subcmd;
|
||||||
|
enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
|
||||||
struct wpabuf *reply;
|
struct wpabuf *reply;
|
||||||
size_t data_len = 0;
|
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);
|
vendor_id = strtoul(cmd, &pos, 16);
|
||||||
if (!isblank((unsigned char) *pos))
|
if (!isblank((unsigned char) *pos))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -2723,7 +2727,9 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
|
||||||
if (*pos != '\0') {
|
if (*pos != '\0') {
|
||||||
if (!isblank((unsigned char) *pos++))
|
if (!isblank((unsigned char) *pos++))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
data_len = os_strlen(pos);
|
|
||||||
|
temp = os_strchr(pos, ' ');
|
||||||
|
data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_len) {
|
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);
|
reply = wpabuf_alloc((buflen - 1) / 2);
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
os_free(data);
|
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,
|
ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
|
||||||
reply);
|
nested_attr_flag, reply);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
|
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
|
||||||
|
|
|
@ -1227,14 +1227,15 @@ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
||||||
char cmd[256];
|
char cmd[256];
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (argc < 2 || argc > 3) {
|
if (argc < 2 || argc > 4) {
|
||||||
printf("Invalid vendor command\n"
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
|
res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s%s%s", argv[0],
|
||||||
argc == 3 ? argv[2] : "");
|
argv[1], argc >= 3 ? argv[2] : "",
|
||||||
|
argc == 4 ? " " : "", argc == 4 ? argv[3] : "");
|
||||||
if (os_snprintf_error(sizeof(cmd), res)) {
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
||||||
printf("Too long VENDOR command.\n");
|
printf("Too long VENDOR command.\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -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,
|
static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
|
||||||
int vendor_id, int subcmd,
|
int vendor_id, int subcmd,
|
||||||
const u8 *data, size_t data_len,
|
const u8 *data, size_t data_len,
|
||||||
|
enum nested_attr nested_attr_flag,
|
||||||
struct wpabuf *buf)
|
struct wpabuf *buf)
|
||||||
{
|
{
|
||||||
if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
|
if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
|
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)
|
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
|
||||||
|
|
|
@ -2432,6 +2432,13 @@ struct external_auth {
|
||||||
const u8 *pmkid;
|
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
|
* struct wpa_driver_ops - Driver interface API definition
|
||||||
*
|
*
|
||||||
|
@ -3717,6 +3724,8 @@ struct wpa_driver_ops {
|
||||||
* @priv: Private driver interface data
|
* @priv: Private driver interface data
|
||||||
* @vendor_id: Vendor id
|
* @vendor_id: Vendor id
|
||||||
* @subcmd: Vendor command 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: Vendor command parameters (%NULL if no parameters)
|
||||||
* @data_len: Data length
|
* @data_len: Data length
|
||||||
* @buf: Return buffer (%NULL to ignore reply)
|
* @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
|
* This function handles vendor specific commands that are passed to
|
||||||
* the driver/device. The command is identified by vendor id and
|
* the driver/device. The command is identified by vendor id and
|
||||||
* command id. Parameters can be passed as argument to the command
|
* command id. The nested_attr_flag specifies whether the subcommand
|
||||||
* in the data buffer. Reply (if any) will be filled in the supplied
|
* uses nested attributes or not. Parameters can be passed
|
||||||
* return buffer.
|
* 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
|
* The exact driver behavior is driver interface and vendor specific. As
|
||||||
* an example, this will be converted to a vendor specific cfg80211
|
* 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,
|
int (*vendor_cmd)(void *priv, unsigned int vendor_id,
|
||||||
unsigned int subcmd, const u8 *data, size_t data_len,
|
unsigned int subcmd, const u8 *data, size_t data_len,
|
||||||
|
enum nested_attr nested_attr_flag,
|
||||||
struct wpabuf *buf);
|
struct wpabuf *buf);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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,
|
static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
|
||||||
unsigned int subcmd, const u8 *data,
|
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 i802_bss *bss = priv;
|
||||||
struct wpa_driver_nl80211_data *drv = bss->drv;
|
struct wpa_driver_nl80211_data *drv = bss->drv;
|
||||||
struct nl_msg *msg;
|
struct nl_msg *msg;
|
||||||
int ret;
|
int ret, nla_flag;
|
||||||
|
|
||||||
#ifdef CONFIG_TESTING_OPTIONS
|
#ifdef CONFIG_TESTING_OPTIONS
|
||||||
if (vendor_id == 0xffffffff) {
|
if (vendor_id == 0xffffffff) {
|
||||||
|
@ -9737,11 +9758,20 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_TESTING_OPTIONS */
|
#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)) ||
|
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_ID, vendor_id) ||
|
||||||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
|
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
|
||||||
(data &&
|
(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;
|
goto fail;
|
||||||
|
|
||||||
ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
|
ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
|
||||||
|
|
|
@ -8236,13 +8236,17 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
|
||||||
char *buf, size_t buflen)
|
char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
char *pos;
|
char *pos, *temp = NULL;
|
||||||
u8 *data = NULL;
|
u8 *data = NULL;
|
||||||
unsigned int vendor_id, subcmd;
|
unsigned int vendor_id, subcmd;
|
||||||
|
enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
|
||||||
struct wpabuf *reply;
|
struct wpabuf *reply;
|
||||||
size_t data_len = 0;
|
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);
|
vendor_id = strtoul(cmd, &pos, 16);
|
||||||
if (!isblank((unsigned char) *pos))
|
if (!isblank((unsigned char) *pos))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -8252,7 +8256,9 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
|
||||||
if (*pos != '\0') {
|
if (*pos != '\0') {
|
||||||
if (!isblank((unsigned char) *pos++))
|
if (!isblank((unsigned char) *pos++))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
data_len = os_strlen(pos);
|
|
||||||
|
temp = os_strchr(pos, ' ');
|
||||||
|
data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_len) {
|
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);
|
reply = wpabuf_alloc((buflen - 1) / 2);
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
os_free(data);
|
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,
|
ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
|
||||||
reply);
|
nested_attr_flag, reply);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
|
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
|
||||||
|
|
|
@ -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,
|
static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
|
||||||
int vendor_id, int subcmd, const u8 *data,
|
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)
|
if (!wpa_s->driver->vendor_cmd)
|
||||||
return -1;
|
return -1;
|
||||||
return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
|
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,
|
static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,
|
||||||
|
|
Loading…
Reference in a new issue