From ef60f0121ffcc1aed6aed3202a180705bb855221 Mon Sep 17 00:00:00 2001 From: Liangwei Dong Date: Wed, 29 May 2019 05:11:48 -0400 Subject: [PATCH] hostapd: Process OWE IE and update DH IE to the driver if needed This implements the required functionality in hostapd to facilitate OWE connection with the AP SME-in-driver cases. Stations can either send DH IE or PMKID (in RSNE) (or both) in Association Request frame during the OWE handshake. The drivers that use this offload mechanism do not interpret this information and instead, pass the same to hostapd for further processing. hostapd will either validate the PMKID obtained from the STA or generate DH IE and further indicate the same to the driver. The driver further sends this information in the Association Response frame. Signed-off-by: Srinivas Dasari Signed-off-by: Liangwei Dong Signed-off-by: Jouni Malinen --- src/ap/ap_drv_ops.c | 10 ++++ src/ap/ap_drv_ops.h | 2 + src/ap/drv_callbacks.c | 76 +++++++++++++++++++++++++ src/ap/ieee802_11.c | 124 +++++++++++++++++++++++++++++++++++++++++ src/ap/ieee802_11.h | 6 ++ src/ap/sta_info.h | 1 + 6 files changed, 219 insertions(+) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index dced3371d..7c9dcf9b9 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -956,3 +956,13 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd) return ret; } + + +int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, + u16 reason_code, const u8 *ie, size_t ielen) +{ + if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv) + return 0; + return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code, + ie, ielen); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 2a914dc65..ca7f7abe0 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -130,6 +130,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, int sec_channel_offset, int oper_chwidth, int center_segment0, int center_segment1); int hostapd_drv_do_acs(struct hostapd_data *hapd); +int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, + u16 reason_code, const u8 *ie, size_t ielen); #include "drivers/driver.h" diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 104dc1cd7..31587685f 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -1589,6 +1589,73 @@ static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd, } +#ifdef CONFIG_OWE +static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd, + const u8 *peer, const u8 *ie, + size_t ie_len) +{ + u16 status; + struct sta_info *sta; + struct ieee802_11_elems elems; + + if (!hapd || !hapd->wpa_auth) { + wpa_printf(MSG_DEBUG, "OWE: Invalid hapd context"); + return -1; + } + if (!peer) { + wpa_printf(MSG_DEBUG, "OWE: Peer unknown"); + return -1; + } + if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) { + wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured"); + status = WLAN_STATUS_AKMP_NOT_VALID; + goto err; + } + if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "OWE: Failed to parse OWE IE for " + MACSTR, MAC2STR(peer)); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto err; + } + status = owe_validate_request(hapd, peer, elems.rsn_ie, + elems.rsn_ie_len, + elems.owe_dh, elems.owe_dh_len); + if (status != WLAN_STATUS_SUCCESS) + goto err; + + sta = ap_get_sta(hapd, peer); + if (sta) { + ap_sta_no_session_timeout(hapd, sta); + accounting_sta_stop(hapd, sta); + + /* + * Make sure that the previously registered inactivity timer + * will not remove the STA immediately. + */ + sta->timeout_next = STA_NULLFUNC; + } else { + sta = ap_sta_add(hapd, peer); + if (!sta) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto err; + } + } + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + + status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie, + elems.rsn_ie_len, elems.owe_dh, + elems.owe_dh_len); + if (status != WLAN_STATUS_SUCCESS) + ap_free_sta(hapd, sta); + + return 0; +err: + hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0); + return 0; +} +#endif /* CONFIG_OWE */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -1694,6 +1761,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->assoc_info.req_ies_len, data->assoc_info.reassoc); break; +#ifdef CONFIG_OWE + case EVENT_UPDATE_DH: + if (!data) + return; + hostapd_notif_update_dh_ie(hapd, data->update_dh.peer, + data->update_dh.ie, + data->update_dh.ie_len); + break; +#endif /* CONFIG_OWE */ case EVENT_DISASSOC: if (data) hostapd_notif_disassoc(hapd, data->disassoc_info.addr); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 02f56701e..4aef59e88 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -23,6 +23,7 @@ #include "common/sae.h" #include "common/dpp.h" #include "common/ocv.h" +#include "common/wpa_common.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -2795,6 +2796,123 @@ static u16 owe_process_assoc_req(struct hostapd_data *hapd, return WLAN_STATUS_SUCCESS; } + +u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len) +{ + struct wpa_ie_data data; + int res; + + if (!rsn_ie || rsn_ie_len < 2) { + wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR, + MAC2STR(peer)); + return WLAN_STATUS_INVALID_IE; + } + rsn_ie -= 2; + rsn_ie_len += 2; + + res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data); + if (res) { + wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR + " (res=%d)", MAC2STR(peer), res); + wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len); + return wpa_res_to_status_code(res); + } + if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) { + wpa_printf(MSG_DEBUG, + "OWE: Unexpected key mgmt 0x%x from " MACSTR, + (unsigned int) data.key_mgmt, MAC2STR(peer)); + return WLAN_STATUS_AKMP_NOT_VALID; + } + if (!owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: No Diffie-Hellman Parameter element from " + MACSTR, MAC2STR(peer)); + return WLAN_STATUS_AKMP_NOT_VALID; + } + + return WLAN_STATUS_SUCCESS; +} + + +u16 owe_process_rsn_ie(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len) +{ + u16 status; + u8 *owe_buf, ie[256 * 2]; + size_t ie_len = 0; + int res; + + if (!rsn_ie || rsn_ie_len < 2) { + wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq"); + status = WLAN_STATUS_INVALID_IE; + goto end; + } + + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, + NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_WARNING, + "OWE: Failed to initialize WPA state machine"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto end; + } + rsn_ie -= 2; + rsn_ie_len += 2; + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, rsn_ie, rsn_ie_len, + NULL, 0, owe_dh, owe_dh_len); + status = wpa_res_to_status_code(res); + if (status != WLAN_STATUS_SUCCESS) + goto end; + status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); + if (status != WLAN_STATUS_SUCCESS) + goto end; + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie), + NULL, 0); + if (!owe_buf) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto end; + } + + if (sta->owe_ecdh) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto end; + } + + /* OWE Diffie-Hellman Parameter element */ + *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ + *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension + */ + WPA_PUT_LE16(owe_buf, sta->owe_group); + owe_buf += 2; + os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); + owe_buf += wpabuf_len(pub); + wpabuf_free(pub); + sta->external_dh_updated = 1; + } + ie_len = owe_buf - ie; + +end: + wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer " + MACSTR, status, (unsigned int) ie_len, + MAC2STR(sta->addr)); + hostapd_drv_update_dh_ie(hapd, sta->addr, status, + status == WLAN_STATUS_SUCCESS ? ie : NULL, + ie_len); + + return status; +} + #endif /* CONFIG_OWE */ @@ -3648,6 +3766,12 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, return owe_buf; } + if (sta->owe_pmk && sta->external_dh_updated) { + wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK"); + *reason = WLAN_STATUS_SUCCESS; + return owe_buf; + } + *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); if (*reason != WLAN_STATUS_SUCCESS) return NULL; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 914cd1f19..8822a1785 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -160,6 +160,12 @@ void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh, u8 owe_dh_len, u8 *owe_buf, size_t owe_buf_len, u16 *reason); +u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len); +u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len); void fils_hlp_timeout(void *eloop_ctx, void *eloop_data); void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta); void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 9a081cb23..5456a63a7 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -120,6 +120,7 @@ struct sta_info { unsigned int agreed_to_steer:1; unsigned int hs20_t_c_filtering:1; unsigned int ft_over_ds:1; + unsigned int external_dh_updated:1; u16 auth_alg;