Signed-off-by: Ilan Peer <ilan.peer@xxxxxxxxx> --- src/ap/ieee802_11.c | 282 ++++++++++++++++++++++++++++++++++++++++---- src/ap/sta_info.c | 3 + src/ap/sta_info.h | 5 + 3 files changed, 267 insertions(+), 23 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index ad307c81d5..770c13daab 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -407,27 +407,16 @@ static void sae_set_state(struct sta_info *sta, enum sae_state state, } -static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, - struct sta_info *sta, int update, - int status_code) +static const char *sae_get_password(struct hostapd_data *hapd, + struct sta_info *sta, + const char *rx_id, + struct sae_password_entry **pw_entry, + struct sae_pt **s_pt) { - struct wpabuf *buf; const char *password = NULL; struct sae_password_entry *pw; - const char *rx_id = NULL; - int use_pt = 0; struct sae_pt *pt = NULL; - if (sta->sae->tmp) { - rx_id = sta->sae->tmp->pw_id; - use_pt = sta->sae->tmp->h2e; - } - - if (status_code == WLAN_STATUS_SUCCESS) - use_pt = 0; - else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) - use_pt = 1; - for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { if (!is_broadcast_ether_addr(pw->peer_addr) && os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) @@ -441,11 +430,44 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, pt = pw->pt; break; } + if (!password) { password = hapd->conf->ssid.wpa_passphrase; pt = hapd->conf->ssid.pt; } - if (!password || (use_pt && !pt)) { + + if (pw_entry) + *pw_entry = pw; + if (s_pt) + *s_pt = pt; + + return password; +} + + +static struct wpabuf *auth_build_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta, int update, + int status_code) +{ + struct wpabuf *buf; + const char *password = NULL; + struct sae_password_entry *pw; + const char *rx_id = NULL; + int use_pt = 0; + struct sae_pt *pt = NULL; + + if (sta->sae->tmp) { + rx_id = sta->sae->tmp->pw_id; + use_pt = sta->sae->tmp->h2e; + } + + if (status_code == WLAN_STATUS_SUCCESS) + use_pt = 0; + else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) + use_pt = 1; + + password = sae_get_password(hapd, sta, rx_id, &pw, &pt); + if (!password ||(use_pt && !pt)) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } @@ -2145,6 +2167,181 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_PASN +#ifdef CONFIG_SAE + +static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta, + struct wpabuf *wd) +{ + struct pasn_data *pasn = sta->pasn; + const char *password = NULL; + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + int groups[] = { pasn->group, 0 }; + int ret; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit"); + return -1; + } + + sae_clear_data(&pasn->sae); + pasn->sae.state = SAE_NOTHING; + + ret = sae_set_group(&pasn->sae, pasn->group); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group"); + return -1; + } + + password = sae_get_password(hapd, sta, NULL, NULL, NULL); + if (!password) { + wpa_printf(MSG_DEBUG, "PASN: No SAE password found"); + return -1; + } + + ret = sae_prepare_commit(hapd->own_addr, sta->addr, + (u8 *)password, + os_strlen(password), 0, + &pasn->sae); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); + return -1; + } + + res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0, + groups, 0); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit"); + return -1; + } + + /* process the commit message and derive the PMK */ + ret = sae_process_commit(&pasn->sae); + if (ret) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); + return -1; + } + + pasn->sae.state = SAE_COMMITTED; + + return 0; +} + + +static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd, + struct sta_info *sta, + struct wpabuf *wd) +{ + struct pasn_data *pasn = sta->pasn; + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit"); + return -1; + } + + res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm"); + return -1; + } + + pasn->sae.state = SAE_ACCEPTED; + + /* + * TODO: Based on on P802.11az_D1.5, the PMKSA derived with PASN/SAE + * should only be allowed with future PASN only. For now do not restrict + * this only for PASN. + */ + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + pasn->sae.pmk, pasn->sae.pmkid); + return 0; +} + + +static struct wpabuf *pasn_get_sae_wd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct pasn_data *pasn = sta->pasn; + struct wpabuf *buf = NULL; + u8 *len_ptr; + size_t len; + + /* Need to add the entire authentication frame body */ + buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN); + if (!buf) { + wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); + return NULL; + } + + /* Need to add the entire authentication frame body for the commit */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 1); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* write the actual commit and update the length accordingly */ + sae_write_commit(&pasn->sae, buf, NULL, 0); + len = wpabuf_len(buf); + WPA_PUT_LE16(len_ptr, len - 2); + + /* Need to add the entire authentication frame body for the confirm */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 2); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + sae_write_confirm(&pasn->sae, buf); + WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2); + + pasn->sae.state = SAE_CONFIRMED; + + return buf; +} + +#endif /* CONFIG_SAE */ static struct wpabuf *pasn_get_wrapped_data(struct hostapd_data *hapd, struct sta_info *sta) @@ -2154,6 +2351,10 @@ static struct wpabuf *pasn_get_wrapped_data(struct hostapd_data *hapd, /* no wrapped data */ return NULL; case WPA_KEY_MGMT_SAE: +#ifdef CONFIG_SAE + return pasn_get_sae_wd(hapd, sta); +#endif /* CONFIG_SAE */ + /* fall through */ case WPA_KEY_MGMT_FILS_SHA256: case WPA_KEY_MGMT_FILS_SHA384: case WPA_KEY_MGMT_FT_PSK: @@ -2197,11 +2398,22 @@ pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta, pmk_len = pmksa->pmk_len; os_memcpy(pmk, pmksa->pmk, pmksa->pmk_len); } else { - /* TODO: derive PMK based on wrapped data */ - wpa_printf(MSG_DEBUG, - "PASN: missing implementation to derive PMK"); - - return -1; + switch (sta->pasn->akmp) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + if (sta->pasn->sae.state == SAE_COMMITTED) { + pmk_len = PMK_LEN; + os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN); + break; + } +#endif /* CONFIG_SAE */ + /* fall through */ + default: + /* TODO: derive PMK based on wrapped data */ + wpa_printf(MSG_DEBUG, + "PASN: missing PMK derivation"); + return -1; + } } ret = pasn_pmk_to_ptk(pmk, pmk_len, @@ -2438,6 +2650,19 @@ static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta, status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } + +#ifdef CONFIG_SAE + if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { + ret = pasn_wd_handle_sae_commit(hapd, sta, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: failed processing SAE commit"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + } +#endif /* CONFIG_SAE */ } sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format; @@ -2562,7 +2787,18 @@ static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta, goto fail; } - /* TODO: handle wrapped data */ +#ifdef CONFIG_SAE + if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { + ret = pasn_wd_handle_sae_confirm(hapd, sta, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: failed processing SAE confirm"); + wpabuf_free(wrapped_data); + goto fail; + } + } +#endif /* CONFIG_SAE */ wpabuf_free(wrapped_data); } diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 1d27319654..51474b2a75 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -165,6 +165,9 @@ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta) if (sta->pasn) { if (sta->pasn->ecdh) crypto_ecdh_deinit(sta->pasn->ecdh); +#ifdef CONFIG_SAE + sae_clear_data(&sta->pasn->sae); +#endif /* CONFIG_SAE */ os_free(sta->pasn); sta->pasn = NULL; } diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 4c1c20c150..3666d51b7a 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -15,6 +15,7 @@ #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" #include "crypto/sha384.h" +#include "common/sae.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -74,6 +75,10 @@ struct pasn_data { u8 hash[SHA384_MAC_LEN]; struct wpa_ptk ptk; struct crypto_ecdh *ecdh; + +#ifdef CONFIG_SAE + struct sae_data sae; +#endif /* CONFIG_SAE */ }; struct sta_info { -- 2.17.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap