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;