OCV: Include and verify OCI in WNM-Sleep Exit frames

Include and verify the OCI element in WNM-Sleep Exit Request and
Response frames. In case verification fails, the frame is silently
ignored.

Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
This commit is contained in:
Mathy Vanhoef 2018-08-06 15:46:33 -04:00 committed by Jouni Malinen
parent 0059625b77
commit fa97981265
2 changed files with 156 additions and 9 deletions

View file

@ -12,6 +12,7 @@
#include "utils/eloop.h" #include "utils/eloop.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h" #include "common/wpa_ctrl.h"
#include "common/ocv.h"
#include "ap/hostapd.h" #include "ap/hostapd.h"
#include "ap/sta_info.h" #include "ap/sta_info.h"
#include "ap/ap_config.h" #include "ap/ap_config.h"
@ -54,8 +55,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
size_t gtk_elem_len = 0; size_t gtk_elem_len = 0;
size_t igtk_elem_len = 0; size_t igtk_elem_len = 0;
struct wnm_sleep_element wnmsleep_ie; struct wnm_sleep_element wnmsleep_ie;
u8 *wnmtfs_ie; u8 *wnmtfs_ie, *oci_ie;
u8 wnmsleep_ie_len; u8 wnmsleep_ie_len, oci_ie_len;
u16 wnmtfs_ie_len; u16 wnmtfs_ie_len;
u8 *pos; u8 *pos;
struct sta_info *sta; struct sta_info *sta;
@ -88,10 +89,42 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
wnmtfs_ie = NULL; wnmtfs_ie = NULL;
} }
oci_ie = NULL;
oci_ie_len = 0;
#ifdef CONFIG_OCV
if (action_type == WNM_SLEEP_MODE_EXIT &&
wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in WNM-Sleep Mode frame");
os_free(wnmtfs_ie);
return -1;
}
oci_ie_len = OCV_OCI_EXTENDED_LEN;
oci_ie = os_zalloc(oci_ie_len);
if (!oci_ie) {
wpa_printf(MSG_WARNING,
"Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
os_free(wnmtfs_ie);
return -1;
}
if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
os_free(wnmtfs_ie);
os_free(oci_ie);
return -1;
}
}
#endif /* CONFIG_OCV */
#define MAX_GTK_SUBELEM_LEN 45 #define MAX_GTK_SUBELEM_LEN 45
#define MAX_IGTK_SUBELEM_LEN 26 #define MAX_IGTK_SUBELEM_LEN 26
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
oci_ie_len);
if (mgmt == NULL) { if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"WNM-Sleep Response action frame"); "WNM-Sleep Response action frame");
@ -134,11 +167,18 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
/* copy TFS IE here */ /* copy TFS IE here */
pos += wnmsleep_ie_len; pos += wnmsleep_ie_len;
if (wnmtfs_ie) if (wnmtfs_ie) {
os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
pos += wnmtfs_ie_len;
}
#ifdef CONFIG_OCV
/* copy OCV OCI here */
if (oci_ie_len > 0)
os_memcpy(pos, oci_ie, oci_ie_len);
#endif /* CONFIG_OCV */
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
/* In driver, response frame should be forced to sent when STA is in /* In driver, response frame should be forced to sent when STA is in
* PS mode */ * PS mode */
@ -185,6 +225,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#undef MAX_IGTK_SUBELEM_LEN #undef MAX_IGTK_SUBELEM_LEN
fail: fail:
os_free(wnmtfs_ie); os_free(wnmtfs_ie);
os_free(oci_ie);
os_free(mgmt); os_free(mgmt);
return res; return res;
} }
@ -201,6 +242,11 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
u8 *tfsreq_ie_start = NULL; u8 *tfsreq_ie_start = NULL;
u8 *tfsreq_ie_end = NULL; u8 *tfsreq_ie_end = NULL;
u16 tfsreq_ie_len = 0; u16 tfsreq_ie_len = 0;
#ifdef CONFIG_OCV
struct sta_info *sta;
const u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
#endif /* CONFIG_OCV */
if (!hapd->conf->wnm_sleep_mode) { if (!hapd->conf->wnm_sleep_mode) {
wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from " wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
@ -221,6 +267,12 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
if (!tfsreq_ie_start) if (!tfsreq_ie_start)
tfsreq_ie_start = (u8 *) pos; tfsreq_ie_start = (u8 *) pos;
tfsreq_ie_end = (u8 *) pos; tfsreq_ie_end = (u8 *) pos;
#ifdef CONFIG_OCV
} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
pos[2] == WLAN_EID_EXT_OCV_OCI) {
oci_ie = pos + 3;
oci_ie_len = ie_len - 1;
#endif /* CONFIG_OCV */
} else } else
wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
*pos); *pos);
@ -232,6 +284,27 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
return; return;
} }
#ifdef CONFIG_OCV
sta = ap_get_sta(hapd, addr);
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
return;
}
if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx) != 0) {
wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
tfsreq_ie_start && tfsreq_ie_end && tfsreq_ie_start && tfsreq_ie_end &&
tfsreq_ie_end - tfsreq_ie_start >= 0) { tfsreq_ie_end - tfsreq_ie_start >= 0) {

View file

@ -12,6 +12,7 @@
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h" #include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h" #include "common/wpa_ctrl.h"
#include "common/ocv.h"
#include "rsn_supp/wpa.h" #include "rsn_supp/wpa.h"
#include "config.h" #include "config.h"
#include "wpa_supplicant_i.h" #include "wpa_supplicant_i.h"
@ -58,8 +59,8 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
int res; int res;
size_t len; size_t len;
struct wnm_sleep_element *wnmsleep_ie; struct wnm_sleep_element *wnmsleep_ie;
u8 *wnmtfs_ie; u8 *wnmtfs_ie, *oci_ie;
u8 wnmsleep_ie_len; u8 wnmsleep_ie_len, oci_ie_len;
u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
WNM_SLEEP_TFS_REQ_IE_NONE; WNM_SLEEP_TFS_REQ_IE_NONE;
@ -106,7 +107,41 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
(u8 *) wnmtfs_ie, wnmtfs_ie_len); (u8 *) wnmtfs_ie, wnmtfs_ie_len);
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); oci_ie = NULL;
oci_ie_len = 0;
#ifdef CONFIG_OCV
if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) {
struct wpa_channel_info ci;
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in WNM-Sleep Mode frame");
os_free(wnmsleep_ie);
os_free(wnmtfs_ie);
return -1;
}
oci_ie_len = OCV_OCI_EXTENDED_LEN;
oci_ie = os_zalloc(oci_ie_len);
if (!oci_ie) {
wpa_printf(MSG_WARNING,
"Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame");
os_free(wnmsleep_ie);
os_free(wnmtfs_ie);
return -1;
}
if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
os_free(wnmsleep_ie);
os_free(wnmtfs_ie);
os_free(oci_ie);
return -1;
}
}
#endif /* CONFIG_OCV */
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len +
oci_ie_len);
if (mgmt == NULL) { if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"WNM-Sleep Request action frame"); "WNM-Sleep Request action frame");
@ -131,8 +166,16 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
} }
#ifdef CONFIG_OCV
/* copy OCV OCI here */
if (oci_ie_len > 0) {
os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len);
}
#endif /* CONFIG_OCV */
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
wnmtfs_ie_len; wnmtfs_ie_len + oci_ie_len;
res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
@ -145,6 +188,7 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
os_free(wnmsleep_ie); os_free(wnmsleep_ie);
os_free(wnmtfs_ie); os_free(wnmtfs_ie);
os_free(oci_ie);
os_free(mgmt); os_free(mgmt);
return res; return res;
@ -256,6 +300,10 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
/* multiple TFS Resp IE (assuming consecutive) */ /* multiple TFS Resp IE (assuming consecutive) */
const u8 *tfsresp_ie_start = NULL; const u8 *tfsresp_ie_start = NULL;
const u8 *tfsresp_ie_end = NULL; const u8 *tfsresp_ie_end = NULL;
#ifdef CONFIG_OCV
const u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
#endif /* CONFIG_OCV */
size_t left; size_t left;
if (!wpa_s->wnmsleep_used) { if (!wpa_s->wnmsleep_used) {
@ -289,6 +337,12 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
if (!tfsresp_ie_start) if (!tfsresp_ie_start)
tfsresp_ie_start = pos; tfsresp_ie_start = pos;
tfsresp_ie_end = pos; tfsresp_ie_end = pos;
#ifdef CONFIG_OCV
} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
pos[2] == WLAN_EID_EXT_OCV_OCI) {
oci_ie = pos + 3;
oci_ie_len = ie_len - 1;
#endif /* CONFIG_OCV */
} else } else
wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
pos += ie_len + 2; pos += ie_len + 2;
@ -299,6 +353,26 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
return; return;
} }
#ifdef CONFIG_OCV
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
wpa_sm_ocv_enabled(wpa_s->wpa)) {
struct wpa_channel_info ci;
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
wpa_msg(wpa_s, MSG_WARNING,
"Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
return;
}
if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx) != 0) {
wpa_msg(wpa_s, MSG_WARNING, "WNM: %s", ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
wpa_s->wnmsleep_used = 0; wpa_s->wnmsleep_used = 0;
if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||