diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 60516aecb..75a7c0499 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -262,6 +262,7 @@ struct hostapd_data { struct sta_info *sta); struct wpabuf *mesh_pending_auth; struct os_reltime mesh_pending_auth_time; + u8 mesh_required_peer[ETH_ALEN]; #endif /* CONFIG_MESH */ #ifdef CONFIG_SQLITE diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 134ae0620..6a373c54c 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -554,6 +554,18 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, } +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->sae->state = SAE_ACCEPTED; + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + sta->sae->pmk, sta->sae->pmkid); +} + + static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid, u8 auth_transaction) { @@ -676,13 +688,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, sae_set_retransmit_timer(hapd, sta); } else { - sta->flags |= WLAN_STA_AUTH; - sta->auth_alg = WLAN_AUTH_SAE; - mlme_authenticate_indication(hapd, sta); - wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->sae->state = SAE_ACCEPTED; - wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, - sta->sae->pmk, sta->sae->pmkid); + sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: @@ -691,6 +697,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, ") doing reauthentication", MAC2STR(sta->addr)); ap_free_sta(hapd, sta); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); } else { if (sae_check_big_sync(sta)) return WLAN_STATUS_SUCCESS; @@ -733,6 +740,13 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, sta->sae->sync = 0; } + if (sta->mesh_sae_pmksa_caching) { + wpa_printf(MSG_DEBUG, + "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication"); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + sta->mesh_sae_pmksa_caching = 0; + } + if (auth_transaction == 1) { const u8 *token = NULL, *pos, *end; size_t token_len = 0; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 1a96b3317..71b3b497a 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -102,6 +102,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta); #ifdef CONFIG_SAE void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta); +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta); #else /* CONFIG_SAE */ static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 70a59ccd7..3d9a928aa 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -183,6 +183,7 @@ struct sta_info { #ifdef CONFIG_SAE struct sae_data *sae; + unsigned int mesh_sae_pmksa_caching:1; #endif /* CONFIG_SAE */ u32 session_timeout; /* valid only if session_timeout_set == 1 */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index ad1a7bcc8..779a40dba 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -3375,6 +3375,34 @@ void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) } +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return NULL; + return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); +} + + +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk) +{ + if (!sm) + return; + + sm->pmksa = pmksa; + if (sm->pmksa->pmk) + os_memcpy(pmk, sm->pmksa->pmk, PMK_LEN); + if (sm->pmksa->pmkid) { + os_memcpy(pmkid, sm->pmksa->pmkid, PMKID_LEN); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, + sm->pmksa->pmkid, PMKID_LEN); + } +} + + /* * Remove and free the group from wpa_authenticator. This is triggered by a * callback to make sure nobody is currently iterating the group list while it diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 166976648..0de8d976e 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -301,6 +301,12 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, size_t len); void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 381d5210c..a79200b54 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -2744,11 +2744,21 @@ static int wpa_supplicant_ctrl_iface_mesh_peer_add( struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; + int duration; + char *pos; + + pos = os_strstr(cmd, " duration="); + if (pos) { + *pos = '\0'; + duration = atoi(pos + 10); + } else { + duration = -1; + } if (hwaddr_aton(cmd, addr)) return -1; - return wpas_mesh_peer_add(wpa_s, addr); + return wpas_mesh_peer_add(wpa_s, addr, duration); } #endif /* CONFIG_MESH */ diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index cdb5eb0be..89b033bc8 100644 --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c @@ -605,7 +605,8 @@ int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr) } -int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr) +int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration) { - return mesh_mpm_connect_peer(wpa_s, addr); + return mesh_mpm_connect_peer(wpa_s, addr, duration); } diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h index b40ae6c32..7317083c9 100644 --- a/wpa_supplicant/mesh.h +++ b/wpa_supplicant/mesh.h @@ -19,7 +19,8 @@ int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, size_t len); int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr); -int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr); +int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration); #ifdef CONFIG_MESH diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index c470d1046..c014eaf86 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -14,6 +14,7 @@ #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ieee802_11.h" +#include "ap/wpa_auth.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "mesh_mpm.h" @@ -419,6 +420,7 @@ static void plink_timer(void *eloop_ctx, void *user_data) struct sta_info *sta = user_data; u16 reason = 0; struct mesh_conf *conf = wpa_s->ifmsh->mconf; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; switch (sta->plink_state) { case PLINK_OPEN_RCVD: @@ -448,6 +450,13 @@ static void plink_timer(void *eloop_ctx, void *user_data) break; case PLINK_HOLDING: /* holding timer */ + + if (sta->mesh_sae_pmksa_caching) { + wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR + " looks like it does not support mesh SAE PMKSA caching, so remove the cached entry for it", + MAC2STR(sta->addr)); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + } mesh_mpm_fsm_restart(wpa_s, sta); break; default: @@ -512,7 +521,17 @@ int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr) } -int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr) +static void peer_add_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + os_memset(hapd->mesh_required_peer, 0, ETH_ALEN); +} + + +int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration) { struct wpa_ssid *ssid = wpa_s->current_ssid; struct hostapd_data *hapd; @@ -547,10 +566,14 @@ int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr) return -1; } - if (conf->security == MESH_CONF_SEC_NONE) + if (conf->security == MESH_CONF_SEC_NONE) { mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); - else + } else { mesh_rsn_auth_sae_sta(wpa_s, sta); + os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN); + eloop_register_timeout(duration == -1 ? 10 : duration, 0, + peer_add_timer, wpa_s, NULL); + } return 0; } @@ -565,6 +588,7 @@ void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) hapd->num_plinks = 0; hostapd_free_stas(hapd); + eloop_cancel_timeout(peer_add_timer, wpa_s, NULL); } @@ -705,7 +729,9 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, if (!sta) return; - if (ssid && ssid->no_auto_peer) { + if (ssid && ssid->no_auto_peer && + (is_zero_ether_addr(data->mesh_required_peer) || + os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) { wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " MACSTR " because of no_auto_peer", MAC2STR(addr)); if (data->mesh_pending_auth) { @@ -785,7 +811,10 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, hapd->num_plinks++; sta->flags |= WLAN_STA_ASSOC; + sta->mesh_sae_pmksa_caching = 0; + eloop_cancel_timeout(peer_add_timer, wpa_s, NULL); + peer_add_timer(wpa_s, NULL); eloop_cancel_timeout(plink_timer, wpa_s, sta); /* Send ctrl event */ @@ -1065,7 +1094,8 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, * open mesh, then go ahead and add the peer before proceeding. */ if (!sta && action_field == PLINK_OPEN && - !(mconf->security & MESH_CONF_SEC_AMPE)) + (!(mconf->security & MESH_CONF_SEC_AMPE) || + wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa))) sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems); if (!sta) { diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h index cbecd3714..5fc1e6184 100644 --- a/wpa_supplicant/mesh_mpm.h +++ b/wpa_supplicant/mesh_mpm.h @@ -19,7 +19,8 @@ void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta, enum mesh_plink_state state); int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr); -int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr); +int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration); #ifdef CONFIG_MESH diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index 5d88274b8..7077cc6c4 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/wpa_supplicant/mesh_rsn.c @@ -291,6 +291,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, { struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct wpa_ssid *ssid = wpa_s->current_ssid; + struct rsn_pmksa_cache_entry *pmksa; unsigned int rnd; int ret; @@ -306,6 +307,29 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, return -1; } + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr); + if (pmksa) { + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_ERROR, + "mesh: Failed to initialize RSN state machine"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "AUTH: Mesh PMKSA cache entry found for " MACSTR + " - try to use PMKSA caching instead of new SAE authentication", + MAC2STR(sta->addr)); + wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth, + sta->sae->pmkid, sta->sae->pmk); + sae_accept_sta(hapd, sta); + sta->mesh_sae_pmksa_caching = 1; + return 0; + } + sta->mesh_sae_pmksa_caching = 0; + if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta)) return -1; @@ -514,6 +538,17 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, (elems->mic - 2) - cat }; + if (!sta->sae) { + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) { + wpa_printf(MSG_INFO, + "Mesh RSN: SAE is not prepared yet"); + return -1; + } + mesh_rsn_auth_sae_sta(wpa_s, sta); + } + if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index bd669e339..36a7a4ebd 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -3229,7 +3229,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { " = Remove a mesh peer" }, { "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL, cli_cmd_flag_none, - " = Add a mesh peer" }, + " [duration=] = Add a mesh peer" }, #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,