Index: head/contrib/wpa/src/ap/ieee802_11.c =================================================================== --- head/contrib/wpa/src/ap/ieee802_11.c (revision 336498) +++ head/contrib/wpa/src/ap/ieee802_11.c (revision 336499) @@ -1,3179 +1,3189 @@ /* * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include "utils/common.h" #include "utils/eloop.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/sae.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" #include "wps/wps.h" #include "fst/fst.h" #include "hostapd.h" #include "beacon.h" #include "ieee802_11_auth.h" #include "sta_info.h" #include "ieee802_1x.h" #include "wpa_auth.h" #include "pmksa_cache_auth.h" #include "wmm.h" #include "ap_list.h" #include "accounting.h" #include "ap_config.h" #include "ap_mlme.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "wnm_ap.h" #include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" #include "mbo_ap.h" #include "rrm.h" #include "taxonomy.h" u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; if (hapd->iface->current_rates == NULL) return eid; *pos++ = WLAN_EID_SUPP_RATES; num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; if (num > 8) { /* rest of the rates are encoded in Extended supported * rates element */ num = 8; } *pos++ = num; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; i++) { count++; *pos = hapd->iface->current_rates[i].rate / 5; if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) *pos |= 0x80; pos++; } if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } return pos; } u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; if (hapd->iface->current_rates == NULL) return eid; num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; if (num <= 8) return eid; num -= 8; *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = num; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; i++) { count++; if (count <= 8) continue; /* already in SuppRates IE */ *pos = hapd->iface->current_rates[i].rate / 5; if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) *pos |= 0x80; pos++; } if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } return pos; } u16 hostapd_own_capab_info(struct hostapd_data *hapd) { int capab = WLAN_CAPABILITY_ESS; int privacy; int dfs; int i; /* Check if any of configured channels require DFS */ dfs = hostapd_is_dfs_required(hapd->iface); if (dfs < 0) { wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", dfs); dfs = 0; } if (hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; privacy = hapd->conf->ssid.wep.keys_set; if (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)) privacy = 1; if (hapd->conf->wpa) privacy = 1; #ifdef CONFIG_HS20 if (hapd->conf->osen) privacy = 1; #endif /* CONFIG_HS20 */ if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 0) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; /* * Currently, Spectrum Management capability bit is set when directly * requested in configuration by spectrum_mgmt_required or when AP is * running on DFS channel. * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit */ if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && (hapd->iconf->spectrum_mgmt_required || dfs)) capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { if (hapd->conf->radio_measurements[i]) { capab |= IEEE80211_CAP_RRM; break; } } return capab; } #ifndef CONFIG_NO_RC4 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, const u8 *challenge, int iswep) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication (shared key, transaction %d)", auth_transaction); if (auth_transaction == 1) { if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (os_get_random(key, sizeof(key)) < 0) { os_free(sta->challenge); sta->challenge = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); } return 0; } if (auth_transaction != 3) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* Transaction 3 */ if (!iswep || !sta->challenge || !challenge || os_memcmp_const(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "shared key authentication - invalid " "challenge-response"); return WLAN_STATUS_CHALLENGE_FAIL; } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (shared key)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); os_free(sta->challenge); sta->challenge = NULL; return 0; } #endif /* CONFIG_NO_RC4 */ static int send_auth_reply(struct hostapd_data *hapd, const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len) { struct ieee80211_mgmt *reply; u8 *buf; size_t rlen; int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; buf = os_zalloc(rlen); if (buf == NULL) return -1; reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_AUTH); os_memcpy(reply->da, dst, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, bssid, ETH_ALEN); reply->u.auth.auth_alg = host_to_le16(auth_alg); reply->u.auth.auth_transaction = host_to_le16(auth_transaction); reply->u.auth.status_code = host_to_le16(resp); if (ies && ies_len) os_memcpy(reply->u.auth.variable, ies, ies_len); wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); else reply_res = WLAN_STATUS_SUCCESS; os_free(buf); return reply_res; } #ifdef CONFIG_IEEE80211R static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta; int reply_res; reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, status, ies, ies_len); sta = ap_get_sta(hapd, dst); if (sta == NULL) return; if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS || status != WLAN_STATUS_SUCCESS)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; return; } if (status != WLAN_STATUS_SUCCESS) return; hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; mlme_authenticate_indication(hapd, sta); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE #define dot11RSNASAESync 5 /* attempts */ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, int update) { struct wpabuf *buf; if (hapd->conf->ssid.wpa_passphrase == NULL) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } if (update && sae_prepare_commit(hapd->own_addr, sta->addr, (u8 *) hapd->conf->ssid.wpa_passphrase, os_strlen(hapd->conf->ssid.wpa_passphrase), sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); if (buf == NULL) return NULL; sae_write_commit(sta->sae, buf, sta->sae->tmp ? sta->sae->tmp->anti_clogging_token : NULL); return buf; } static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta) { struct wpabuf *buf; buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN); if (buf == NULL) return NULL; sae_write_confirm(sta->sae, buf); return buf; } static int auth_sae_send_commit(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid, int update) { struct wpabuf *data; int reply_res; data = auth_build_sae_commit(hapd, sta, update); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, wpabuf_head(data), wpabuf_len(data)); wpabuf_free(data); return reply_res; } static int auth_sae_send_confirm(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid) { struct wpabuf *data; int reply_res; data = auth_build_sae_confirm(hapd, sta); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, wpabuf_head(data), wpabuf_len(data)); wpabuf_free(data); return reply_res; } static int use_sae_anti_clogging(struct hostapd_data *hapd) { struct sta_info *sta; unsigned int open = 0; if (hapd->conf->sae_anti_clogging_threshold == 0) return 1; for (sta = hapd->sta_list; sta; sta = sta->next) { if (!sta->sae) continue; if (sta->sae->state != SAE_COMMITTED && sta->sae->state != SAE_CONFIRMED) continue; open++; if (open >= hapd->conf->sae_anti_clogging_threshold) return 1; } return 0; } static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, const u8 *token, size_t token_len) { u8 mac[SHA256_MAC_LEN]; if (token_len != SHA256_MAC_LEN) return -1; if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), addr, ETH_ALEN, mac) < 0 || os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0) return -1; return 0; } static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, int group, const u8 *addr) { struct wpabuf *buf; u8 *token; struct os_reltime now; os_get_reltime(&now); if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { if (random_get_bytes(hapd->sae_token_key, sizeof(hapd->sae_token_key)) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", hapd->sae_token_key, sizeof(hapd->sae_token_key)); hapd->last_sae_token_key_update = now; } buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); if (buf == NULL) return NULL; wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ token = wpabuf_put(buf, SHA256_MAC_LEN); hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), addr, ETH_ALEN, token); return buf; } static int sae_check_big_sync(struct sta_info *sta) { if (sta->sae->sync > dot11RSNASAESync) { sta->sae->state = SAE_NOTHING; sta->sae->sync = 0; return -1; } return 0; } static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = eloop_data; int ret; if (sae_check_big_sync(sta)) return; sta->sae->sync++; wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR " (sync=%d state=%d)", MAC2STR(sta->addr), sta->sae->sync, sta->sae->state); switch (sta->sae->state) { case SAE_COMMITTED: ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; case SAE_CONFIRMED: ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr); eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; default: ret = -1; break; } if (ret != WLAN_STATUS_SUCCESS) wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret); } void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) { eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); } static void sae_set_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) { if (!(hapd->conf->mesh & MESH_ENABLED)) return; eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); } 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) { int ret; if (auth_transaction != 1 && auth_transaction != 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; switch (sta->sae->state) { case SAE_NOTHING: if (auth_transaction == 1) { ret = auth_sae_send_commit(hapd, sta, bssid, 1); if (ret) return ret; sta->sae->state = SAE_COMMITTED; if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* * In mesh case, both Commit and Confirm can be sent * immediately. In infrastructure BSS, only a single * Authentication frame (Commit) is expected from the AP * here and the second one (Confirm) will be sent once * the STA has sent its second Authentication frame * (Confirm). */ if (hapd->conf->mesh & MESH_ENABLED) { /* * Send both Commit and Confirm immediately * based on SAE finite state machine * Nothing -> Confirm transition. */ ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sta->sae->state = SAE_CONFIRMED; } else { /* * For infrastructure BSS, send only the Commit * message now to get alternating sequence of * Authentication frames between the AP and STA. * Confirm will be sent in * Committed -> Confirmed/Accepted transition * when receiving Confirm from STA. */ } sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE confirm before commit"); } break; case SAE_COMMITTED: sae_clear_retransmit_timer(hapd, sta); if (auth_transaction == 1) { if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sta->sae->state = SAE_CONFIRMED; sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); } else if (hapd->conf->mesh & MESH_ENABLED) { /* * In mesh case, follow SAE finite state machine and * send Commit now, if sync count allows. */ if (sae_check_big_sync(sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; ret = auth_sae_send_commit(hapd, sta, bssid, 0); if (ret) return ret; sae_set_retransmit_timer(hapd, sta); } else { /* * For instructure BSS, send the postponed Confirm from * Nothing -> Confirmed transition that was reduced to * Nothing -> Committed above. */ ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sta->sae->state = SAE_CONFIRMED; /* * Since this was triggered on Confirm RX, run another * step to get to Accepted without waiting for * additional events. */ return sae_sm_step(hapd, sta, bssid, auth_transaction); } break; case SAE_CONFIRMED: sae_clear_retransmit_timer(hapd, sta); if (auth_transaction == 1) { if (sae_check_big_sync(sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; ret = auth_sae_send_commit(hapd, sta, bssid, 1); if (ret) return ret; if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sae_set_retransmit_timer(hapd, sta); } else { sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: if (auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") 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; sta->sae->sync++; ret = auth_sae_send_confirm(hapd, sta, bssid); sae_clear_temp_data(sta->sae); if (ret) return ret; } break; default: wpa_printf(MSG_ERROR, "SAE: invalid state %d", sta->sae->state); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) { struct sae_data *sae = sta->sae; int i, *groups = hapd->conf->sae_groups; if (sae->state != SAE_COMMITTED) return; wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); for (i = 0; groups && groups[i] > 0; i++) { if (sae->group == groups[i]) break; } if (!groups || groups[i] <= 0) { wpa_printf(MSG_DEBUG, "SAE: Previously selected group not found from the current configuration"); return; } for (;;) { i++; if (groups[i] <= 0) { wpa_printf(MSG_DEBUG, "SAE: No alternative group enabled"); return; } if (sae_set_group(sae, groups[i]) < 0) continue; break; } wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]); } static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 auth_transaction, u16 status_code) { int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; if (!sta->sae) { if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) { resp = -1; goto remove_sta; } sta->sae = os_zalloc(sizeof(*sta->sae)); if (!sta->sae) { resp = -1; goto remove_sta; } sta->sae->state = SAE_NOTHING; 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; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "start SAE authentication (RX commit, status=%u)", status_code); if ((hapd->conf->mesh & MESH_ENABLED) && status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && sta->sae->tmp) { pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; if (pos + sizeof(le16) > end) { wpa_printf(MSG_ERROR, "SAE: Too short anti-clogging token request"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } resp = sae_group_allowed(sta->sae, hapd->conf->sae_groups, WPA_GET_LE16(pos)); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Invalid group in anti-clogging token request"); goto reply; } pos += sizeof(le16); wpabuf_free(sta->sae->tmp->anti_clogging_token); sta->sae->tmp->anti_clogging_token = wpabuf_alloc_copy(pos, end - pos); if (sta->sae->tmp->anti_clogging_token == NULL) { wpa_printf(MSG_ERROR, "SAE: Failed to alloc for anti-clogging token"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto remove_sta; } /* * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code * is 76, a new Commit Message shall be constructed * with the Anti-Clogging Token from the received * Authentication frame, and the commit-scalar and * COMMIT-ELEMENT previously sent. */ resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Failed to send commit message"); goto remove_sta; } sta->sae->state = SAE_COMMITTED; sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); return; } if ((hapd->conf->mesh & MESH_ENABLED) && status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && sta->sae->tmp) { wpa_printf(MSG_DEBUG, "SAE: Peer did not accept our SAE group"); sae_pick_next_group(hapd, sta); goto remove_sta; } if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, &token_len, hapd->conf->sae_groups); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", MAC2STR(sta->addr)); goto remove_sta; } if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, MAC2STR(sta->addr)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto remove_sta; } if (resp != WLAN_STATUS_SUCCESS) goto reply; if (!token && use_sae_anti_clogging(hapd)) { wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); data = auth_build_token_req(hapd, sta->sae->group, sta->addr); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; if (hapd->conf->mesh & MESH_ENABLED) sta->sae->state = SAE_NOTHING; goto reply; } resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE authentication (RX confirm, status=%u)", status_code); if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; if (sta->sae->state >= SAE_CONFIRMED || !(hapd->conf->mesh & MESH_ENABLED)) { if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, ((u8 *) mgmt) + len - mgmt->u.auth.variable) < 0) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } } resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "unexpected SAE authentication transaction %u (status=%u)", auth_transaction, status_code); if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } reply: if (resp != WLAN_STATUS_SUCCESS) { send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0); } remove_sta: if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || status_code != WLAN_STATUS_SUCCESS)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } wpabuf_free(data); } /** * auth_sae_init_committed - Send COMMIT and start SAE in committed state * @hapd: BSS data for the device initiating the authentication * @sta: the peer to which commit authentication frame is sent * * This function implements Init event handling (IEEE Std 802.11-2012, * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the * sta->sae structure should be initialized appropriately via a call to * sae_prepare_commit(). */ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) { int ret; if (!sta->sae || !sta->sae->tmp) return -1; if (sta->sae->state != SAE_NOTHING) return -1; ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); if (ret) return -1; sta->sae->state = SAE_COMMITTED; sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); return 0; } #endif /* CONFIG_SAE */ static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; int res, reply_res; u16 fc; const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; struct vlan_description vlan_id; struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; char *identity = NULL; char *radius_cui = NULL; u16 seq_ctrl; os_memset(&vlan_id, 0, sizeof(vlan_id)); if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", (unsigned long) len); return; } #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_auth_probability > 0.0 && drand48() < hapd->iconf->ignore_auth_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring auth frame from " MACSTR, MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_TESTING_OPTIONS */ auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); fc = le_to_host16(mgmt->frame_control); seq_ctrl = le_to_host16(mgmt->seq_ctrl); if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + WLAN_AUTH_CHALLENGE_LEN && mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE && mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) challenge = &mgmt->u.auth.variable[2]; wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " "auth_transaction=%d status_code=%d wep=%d%s " "seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), challenge ? " challenge" : "", seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); #ifdef CONFIG_NO_RC4 if (auth_alg == WLAN_AUTH_SHARED_KEY) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", auth_alg); resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; goto fail; } #endif /* CONFIG_NO_RC4 */ if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; } if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || #ifdef CONFIG_IEEE80211R (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_SAE) || #endif /* CONFIG_SAE */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", auth_alg); resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; goto fail; } if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", auth_transaction); resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; goto fail; } if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (hapd->conf->no_auth_if_seen_on) { struct hostapd_data *other; other = sta_track_seen_on(hapd->iface, mgmt->sa, hapd->conf->no_auth_if_seen_on); if (other) { u8 *pos; u32 info; u8 op_class, channel, phytype; wpa_printf(MSG_DEBUG, "%s: Reject authentication from " MACSTR " since STA has been seen on %s", hapd->conf->iface, MAC2STR(mgmt->sa), hapd->conf->no_auth_if_seen_on); resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION; pos = &resp_ies[0]; *pos++ = WLAN_EID_NEIGHBOR_REPORT; *pos++ = 13; os_memcpy(pos, other->own_addr, ETH_ALEN); pos += ETH_ALEN; info = 0; /* TODO: BSSID Information */ WPA_PUT_LE32(pos, info); pos += 4; if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) phytype = 8; /* dmg */ else if (other->iconf->ieee80211ac) phytype = 9; /* vht */ else if (other->iconf->ieee80211n) phytype = 7; /* ht */ else if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A) phytype = 4; /* ofdm */ else if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211G) phytype = 6; /* erp */ else phytype = 5; /* hrdsss */ if (ieee80211_freq_to_channel_ext( hostapd_hw_get_freq(other, other->iconf->channel), other->iconf->secondary_channel, other->iconf->ieee80211ac, &op_class, &channel) == NUM_HOSTAPD_MODES) { op_class = 0; channel = other->iconf->channel; } *pos++ = op_class; *pos++ = channel; *pos++ = phytype; resp_ies_len = pos - &resp_ies[0]; goto fail; } } res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui); if (res == HOSTAPD_ACL_REJECT) { wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (res == HOSTAPD_ACL_PENDING) { wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR " waiting for an external authentication", MAC2STR(mgmt->sa)); /* Authentication code will re-send the authentication frame * after it has received (and cached) information from the * external source. */ return; } sta = ap_get_sta(hapd, mgmt->sa); if (sta) { if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && sta->last_subtype == WLAN_FC_STYPE_AUTH) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated authentication frame seq_ctrl=0x%x", seq_ctrl); return; } #ifdef CONFIG_MESH if ((hapd->conf->mesh & MESH_ENABLED) && sta->plink_state == PLINK_BLOCKED) { wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR " is blocked - drop Authentication frame", MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_MESH */ } else { #ifdef CONFIG_MESH if (hapd->conf->mesh & MESH_ENABLED) { /* if the mesh peer is not available, we don't do auth. */ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR " not yet known - drop Authentication frame", MAC2STR(mgmt->sa)); /* * Save a copy of the frame so that it can be processed * if a new peer entry is added shortly after this. */ wpabuf_free(hapd->mesh_pending_auth); hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len); os_get_reltime(&hapd->mesh_pending_auth_time); return; } #endif /* CONFIG_MESH */ sta = ap_sta_add(hapd, mgmt->sa); if (!sta) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; if (vlan_id.notempty && !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Invalid VLAN %d%s received from RADIUS server", vlan_id.untagged, vlan_id.tagged[0] ? "+" : ""); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (sta->vlan_id) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); hostapd_free_psk_list(sta->psk); if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { sta->psk = psk; psk = NULL; } else { sta->psk = NULL; } sta->identity = identity; identity = NULL; sta->radius_cui = radius_cui; radius_cui = NULL; sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) sta->acct_interim_interval = acct_interim_interval; if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) ap_sta_session_timeout(hapd, sta, session_timeout); else ap_sta_no_session_timeout(hapd, sta); /* * If the driver supports full AP client state, add a station to the * driver before sending authentication reply to make sure the driver * has resources, and not to go through the entire authentication and * association handshake, and fail it at the end. * * If this is not the first transaction, in a multi-step authentication * algorithm, the station already exists in the driver * (sta->added_unassoc = 1) so skip it. * * In mesh mode, the station was already added to the driver when the * NEW_PEER_CANDIDATE event is received. */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) { /* * If a station that is already associated to the AP, is trying * to authenticate again, remove the STA entry, in order to make * sure the STA PS state gets cleared and configuration gets * updated. To handle this, station's added_unassoc flag is * cleared once the station has completed association. */ hostapd_drv_sta_remove(hapd, sta->addr); sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED); if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0, NULL, NULL, sta->flags, 0, 0, 0, 0)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } sta->added_unassoc = 1; } switch (auth_alg) { case WLAN_AUTH_OPEN: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (open system)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); break; #ifndef CONFIG_NO_RC4 case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); sta->auth_alg = WLAN_AUTH_SHARED_KEY; mlme_authenticate_indication(hapd, sta); if (sta->challenge && auth_transaction == 1) { resp_ies[0] = WLAN_EID_CHALLENGE; resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN; os_memcpy(resp_ies + 2, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; } break; #endif /* CONFIG_NO_RC4 */ #ifdef CONFIG_IEEE80211R case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " "state machine"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid, auth_transaction, mgmt->u.auth.variable, len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), handle_auth_ft_finish, hapd); /* handle_auth_ft_finish() callback will complete auth. */ return; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: #ifdef CONFIG_MESH if (status_code == WLAN_STATUS_SUCCESS && hapd->conf->mesh & MESH_ENABLED) { if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "SAE: Failed to initialize WPA state machine"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } } #endif /* CONFIG_MESH */ handle_auth_sae(hapd, sta, mgmt, len, auth_transaction, status_code); return; #endif /* CONFIG_SAE */ } fail: os_free(identity); os_free(radius_cui); hostapd_free_psk_list(psk); reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, resp_ies_len); if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || reply_res != WLAN_STATUS_SUCCESS)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } } int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) { int i, j = 32, aid; /* get a unique AID */ if (sta->aid > 0) { wpa_printf(MSG_DEBUG, " old AID %d", sta->aid); return 0; } if (TEST_FAIL()) return -1; for (i = 0; i < AID_WORDS; i++) { if (hapd->sta_aid[i] == (u32) -1) continue; for (j = 0; j < 32; j++) { if (!(hapd->sta_aid[i] & BIT(j))) break; } if (j < 32) break; } if (j == 32) return -1; aid = i * 32 + j + 1; if (aid > 2007) return -1; sta->aid = aid; hapd->sta_aid[i] |= BIT(j); wpa_printf(MSG_DEBUG, " new AID %d", sta->aid); return 0; } static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ssid_ie, size_t ssid_ie_len) { if (ssid_ie == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (ssid_ie_len != hapd->conf->ssid.ssid_len || os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to associate with unknown SSID " "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, const u8 *wmm_ie, size_t wmm_ie_len) { sta->flags &= ~WLAN_STA_WMM; sta->qosinfo = 0; if (wmm_ie && hapd->conf->wmm_enabled) { struct wmm_information_element *wmm; if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "invalid WMM element in association " "request"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->flags |= WLAN_STA_WMM; wmm = (struct wmm_information_element *) wmm_ie; sta->qosinfo = wmm->qos_info; } return WLAN_STATUS_SUCCESS; } static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) { if (!elems->supp_rates) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "No supported rates element in AssocReq"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (elems->supp_rates_len + elems->ext_supp_rates_len > sizeof(sta->supported_rates)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Invalid supported rates element length %d+%d", elems->supp_rates_len, elems->ext_supp_rates_len); return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->supported_rates_len = merge_byte_arrays( sta->supported_rates, sizeof(sta->supported_rates), elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); return WLAN_STATUS_SUCCESS; } static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ext_capab_ie, size_t ext_capab_ie_len) { #ifdef CONFIG_INTERWORKING /* check for QoS Map support */ if (ext_capab_ie_len >= 5) { if (ext_capab_ie[4] & 0x01) sta->qos_map_enabled = 1; } #endif /* CONFIG_INTERWORKING */ if (ext_capab_ie_len > 0) sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); return WLAN_STATUS_SUCCESS; } static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { struct ieee802_11_elems elems; u16 resp; const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station sent an invalid " "association request"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211N resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && !(sta->flags & WLAN_STA_HT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory HT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_HT; } #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac) { resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); if (resp != WLAN_STATUS_SUCCESS) return resp; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory VHT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_VHT; } if (hapd->conf->vendor_vht && !elems.vht_capabilities) { resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht, elems.vendor_vht_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_P2P if (elems.p2p) { wpabuf_free(sta->p2p_ie); sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); if (sta->p2p_ie) p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); } else { wpabuf_free(sta->p2p_ie); sta->p2p_ie = NULL; } #endif /* CONFIG_P2P */ if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && elems.wpa_ie) { wpa_ie = elems.wpa_ie; wpa_ie_len = elems.wpa_ie_len; } else { wpa_ie = NULL; wpa_ie_len = 0; } #ifdef CONFIG_WPS sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); if (hapd->conf->wps_state && elems.wps_ie) { wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " "Request - assume WPS is used"); sta->flags |= WLAN_STA_WPS; wpabuf_free(sta->wps_ie); sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_IE_VENDOR_TYPE); if (sta->wps_ie && wps_is_20(sta->wps_ie)) { wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } wpa_ie = NULL; wpa_ie_len = 0; if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) { wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in " "(Re)Association Request - reject"); return WLAN_STATUS_INVALID_IE; } } else if (hapd->conf->wps_state && wpa_ie == NULL) { wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " "(Re)Association Request - possible WPS use"); sta->flags |= WLAN_STA_MAYBE_WPS; } else #endif /* CONFIG_WPS */ if (hapd->conf->wpa && wpa_ie == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No WPA/RSN IE in association request"); return WLAN_STATUS_INVALID_IE; } if (hapd->conf->wpa && wpa_ie) { int res; wpa_ie -= 2; wpa_ie_len += 2; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_WARNING, "Failed to initialize WPA " "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, wpa_ie, wpa_ie_len, elems.mdie, elems.mdie_len); if (res == WPA_INVALID_GROUP) resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; else if (res == WPA_INVALID_PAIRWISE) resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; else if (res == WPA_INVALID_AKMP) resp = WLAN_STATUS_AKMP_NOT_VALID; else if (res == WPA_ALLOC_FAIL) resp = WLAN_STATUS_UNSPECIFIED_FAILURE; #ifdef CONFIG_IEEE80211W else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; #endif /* CONFIG_IEEE80211W */ else if (res == WPA_INVALID_MDIE) resp = WLAN_STATUS_INVALID_MDIE; else if (res != WPA_IE_OK) resp = WLAN_STATUS_INVALID_IE; if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA * Query timeout has not been reached. Reject the * association attempt temporarily and start SA Query, * if one is not pending. */ if (sta->sa_query_count == 0) ap_sta_start_sa_query(hapd, sta); return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; } if (wpa_auth_uses_mfp(sta->wpa_sm)) sta->flags |= WLAN_STA_MFP; else sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R if (sta->auth_alg == WLAN_AUTH_FT) { if (!reassoc) { wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " "to use association (not " "re-association) with FT auth_alg", MAC2STR(sta->addr)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies, ies_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg == WLAN_AUTH_OPEN) { struct rsn_pmksa_cache_entry *sa; sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) { wpa_printf(MSG_DEBUG, "SAE: No PMKSA cache entry found for " MACSTR, MAC2STR(sta->addr)); return WLAN_STATUS_INVALID_PMKID; } wpa_printf(MSG_DEBUG, "SAE: " MACSTR " using PMKSA caching", MAC2STR(sta->addr)); } else if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg != WLAN_AUTH_SAE && !(sta->auth_alg == WLAN_AUTH_FT && wpa_auth_uses_ft_sae(sta->wpa_sm))) { wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " "SAE AKM after non-SAE auth_alg %u", MAC2STR(sta->addr), sta->auth_alg); return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; } #endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to use TKIP with HT " "association"); return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_HS20 } else if (hapd->conf->osen) { if (elems.osen == NULL) { hostapd_logger( hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No HS 2.0 OSEN element in association request"); return WLAN_STATUS_INVALID_IE; } wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association"); if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_WARNING, "Failed to initialize WPA " "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, elems.osen - 2, elems.osen_len + 2) < 0) return WLAN_STATUS_INVALID_IE; #endif /* CONFIG_HS20 */ } else wpa_auth_sta_no_wpa(sta->wpa_sm); #ifdef CONFIG_P2P p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); #endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); } else sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST wpabuf_free(sta->mb_ies); if (hapd->iface->fst) sta->mb_ies = mb_ies_by_info(&elems.mb_ies); else sta->mb_ies = NULL; #endif /* CONFIG_FST */ #ifdef CONFIG_MBO mbo_ap_check_sta_assoc(hapd, sta, &elems); if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { wpa_printf(MSG_INFO, "MBO: Reject WPA2 association without PMF"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } #endif /* CONFIG_MBO */ ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, elems.supp_op_classes_len); if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) && elems.rrm_enabled && elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, sizeof(sta->rrm_enabled_capa)); return WLAN_STATUS_SUCCESS; } static void send_deauth(struct hostapd_data *hapd, const u8 *addr, u16 reason_code) { int send_len; struct ieee80211_mgmt reply; os_memset(&reply, 0, sizeof(reply)); reply.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); os_memcpy(reply.da, addr, ETH_ALEN); os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN); send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); reply.u.deauth.reason_code = host_to_le16(reason_code); if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0) wpa_printf(MSG_INFO, "Failed to send deauth: %s", strerror(errno)); } static int add_associated_sta(struct hostapd_data *hapd, struct sta_info *sta) { struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; + int set = 1; /* * Remove the STA entry to ensure the STA PS state gets cleared and * configuration gets updated. This is relevant for cases, such as * FT-over-the-DS, where a station re-associates back to the same AP but * skips the authentication flow, or if working with a driver that * does not support full AP client state. + * + * Skip this if the STA has already completed FT reassociation and the + * TK has been configured since the TX/RX PN must not be reset to 0 for + * the same key. */ - if (!sta->added_unassoc) + if (!sta->added_unassoc && + (!(sta->flags & WLAN_STA_AUTHORIZED) || + !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) { hostapd_drv_sta_remove(hapd, sta->addr); + wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); + set = 0; + } #ifdef CONFIG_IEEE80211N if (sta->flags & WLAN_STA_HT) hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC if (sta->flags & WLAN_STA_VHT) hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); #endif /* CONFIG_IEEE80211AC */ /* * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags * will be set when the ACK frame for the (Re)Association Response frame * is processed (TX status driver event). */ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, sta->supported_rates_len, sta->listen_interval, sta->flags & WLAN_STA_HT ? &ht_cap : NULL, sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, - sta->added_unassoc)) { + set)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not %s STA to kernel driver", - sta->added_unassoc ? "set" : "add"); + set ? "set" : "add"); if (sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } return -1; } sta->added_unassoc = 0; return 0; } static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, u16 status_code, int reassoc, const u8 *ies, size_t ies_len) { int send_len; u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; struct ieee80211_mgmt *reply; u8 *p; os_memset(buf, 0, sizeof(buf)); reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : WLAN_FC_STYPE_ASSOC_RESP)); os_memcpy(reply->da, sta->addr, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); send_len = IEEE80211_HDRLEN; send_len += sizeof(reply->u.assoc_resp); reply->u.assoc_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd)); reply->u.assoc_resp.status_code = host_to_le16(status_code); reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); /* Supported rates */ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); #ifdef CONFIG_IEEE80211R if (status_code == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS * Transition Information, RSN, [RIC Response] */ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, buf + sizeof(buf) - p, sta->auth_alg, ies, ies_len); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) p = hostapd_eid_assoc_comeback_time(hapd, sta, p); #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211N p = hostapd_eid_ht_capabilities(hapd, p); p = hostapd_eid_ht_operation(hapd, p); #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { u32 nsts = 0, sta_nsts; if (hapd->conf->use_sta_nsts && sta->vht_capabilities) { struct ieee80211_vht_capabilities *capa; nsts = (hapd->iface->conf->vht_capab >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; capa = sta->vht_capabilities; sta_nsts = (le_to_host32(capa->vht_capabilities_info) >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; if (nsts < sta_nsts) nsts = 0; else nsts = sta_nsts; } p = hostapd_eid_vht_capabilities(hapd, p, nsts); p = hostapd_eid_vht_operation(hapd, p); } #endif /* CONFIG_IEEE80211AC */ p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); if (sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); #ifdef CONFIG_FST if (hapd->iface->fst_ies) { os_memcpy(p, wpabuf_head(hapd->iface->fst_ies), wpabuf_len(hapd->iface->fst_ies)); p += wpabuf_len(hapd->iface->fst_ies); } #endif /* CONFIG_FST */ #ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); #endif /* CONFIG_IEEE80211AC */ if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); #ifdef CONFIG_WPS if ((sta->flags & WLAN_STA_WPS) || ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { struct wpabuf *wps = wps_build_assoc_resp_ie(); if (wps) { os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); p += wpabuf_len(wps); wpabuf_free(wps); } } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { case WLAN_STATUS_SUCCESS: status = P2P_SC_SUCCESS; break; case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: status = P2P_SC_FAIL_LIMIT_REACHED; break; default: status = P2P_SC_FAIL_INVALID_PARAMS; break; } p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status); if (p2p_resp_ie) { os_memcpy(p, wpabuf_head(p2p_resp_ie), wpabuf_len(p2p_resp_ie)); p += wpabuf_len(p2p_resp_ie); wpabuf_free(p2p_resp_ie); } } #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER if (hapd->conf->p2p & P2P_MANAGE) p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p); if (hapd->conf->assocresp_elements && (size_t) (buf + sizeof(buf) - p) >= wpabuf_len(hapd->conf->assocresp_elements)) { os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), wpabuf_len(hapd->conf->assocresp_elements)); p += wpabuf_len(hapd->conf->assocresp_elements); } send_len += p - reply->u.assoc_resp.variable; if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { u16 capab_info, listen_interval, seq_ctrl, fc; u16 resp = WLAN_STATUS_SUCCESS, reply_res; const u8 *pos; int left, i; struct sta_info *sta; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)", reassoc, (unsigned long) len); return; } #ifdef CONFIG_TESTING_OPTIONS if (reassoc) { if (hapd->iconf->ignore_reassoc_probability > 0.0 && drand48() < hapd->iconf->ignore_reassoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring reassoc request from " MACSTR, MAC2STR(mgmt->sa)); return; } } else { if (hapd->iconf->ignore_assoc_probability > 0.0 && drand48() < hapd->iconf->ignore_assoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring assoc request from " MACSTR, MAC2STR(mgmt->sa)); return; } } #endif /* CONFIG_TESTING_OPTIONS */ fc = le_to_host16(mgmt->frame_control); seq_ctrl = le_to_host16(mgmt->seq_ctrl); if (reassoc) { capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.reassoc_req.listen_interval); wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d current_ap=" MACSTR " seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), capab_info, listen_interval, MAC2STR(mgmt->u.reassoc_req.current_ap), seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); pos = mgmt->u.reassoc_req.variable; } else { capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.assoc_req.listen_interval); wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d " "seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), capab_info, listen_interval, seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); pos = mgmt->u.assoc_req.variable; } sta = ap_get_sta(hapd, mgmt->sa); #ifdef CONFIG_IEEE80211R if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " "prior to authentication since it is using " "over-the-DS FT", MAC2STR(mgmt->sa)); /* * Mark station as authenticated, to avoid adding station * entry in the driver as associated and not authenticated */ sta->flags |= WLAN_STA_AUTH; } else #endif /* CONFIG_IEEE80211R */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to " "associate before authentication " "(aid=%d flags=0x%x)", sta ? sta->aid : -1, sta ? sta->flags : 0); send_deauth(hapd, mgmt->sa, WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); return; } if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ : WLAN_FC_STYPE_ASSOC_REQ) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated association frame seq_ctrl=0x%x", seq_ctrl); return; } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ : WLAN_FC_STYPE_ASSOC_REQ; if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; } if (listen_interval > hapd->conf->max_listen_interval) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Too large Listen Interval (%d)", listen_interval); resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE; goto fail; } #ifdef CONFIG_MBO if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } #endif /* CONFIG_MBO */ /* * sta->capability is used in check_assoc_ies() for RRM enabled * capability element. */ sta->capability = capab_info; /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); if (resp != WLAN_STATUS_SUCCESS) goto fail; if (hostapd_get_aid(hapd, sta) < 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No room for more AIDs"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } sta->listen_interval = listen_interval; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) sta->flags |= WLAN_STA_NONERP; for (i = 0; i < sta->supported_rates_len; i++) { if ((sta->supported_rates[i] & 0x7f) > 22) { sta->flags &= ~WLAN_STA_NONERP; break; } } if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) { sta->nonerp_set = 1; hapd->iface->num_sta_non_erp++; if (hapd->iface->num_sta_non_erp == 1) ieee802_11_set_beacons(hapd->iface); } if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) && !sta->no_short_slot_time_set) { sta->no_short_slot_time_set = 1; hapd->iface->num_sta_no_short_slot_time++; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 1) ieee802_11_set_beacons(hapd->iface); } if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) sta->flags |= WLAN_STA_SHORT_PREAMBLE; else sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && !sta->no_short_preamble_set) { sta->no_short_preamble_set = 1; hapd->iface->num_sta_no_short_preamble++; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 1) ieee802_11_set_beacons(hapd->iface); } #ifdef CONFIG_IEEE80211N update_ht_state(hapd, sta); #endif /* CONFIG_IEEE80211N */ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "association OK (aid %d)", sta->aid); /* Station will be marked associated, after it acknowledges AssocResp */ sta->flags |= WLAN_STA_ASSOC_REQ_OK; #ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " "SA Query procedure", reassoc ? "re" : ""); /* TODO: Send a protected Disassociate frame to the STA using * the old key and Reason Code "Previous Authentication no * longer valid". Make sure this is only sent protected since * unprotected frame would be received by the STA that is now * trying to associate. */ } #endif /* CONFIG_IEEE80211W */ /* Make sure that the previously registered inactivity timer will not * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; #ifdef CONFIG_TAXONOMY taxonomy_sta_info_assoc_req(hapd, sta, pos, left); #endif /* CONFIG_TAXONOMY */ fail: /* * In case of a successful response, add the station to the driver. * Otherwise, the kernel may ignore Data frames before we process the * ACK frame (TX status). In case of a failure, this station will be * removed. * * Note that this is not compliant with the IEEE 802.11 standard that * states that a non-AP station should transition into the * authenticated/associated state only after the station acknowledges * the (Re)Association Response frame. However, still do this as: * * 1. In case the station does not acknowledge the (Re)Association * Response frame, it will be removed. * 2. Data frames will be dropped in the kernel until the station is * set into authorized state, and there are no significant known * issues with processing other non-Data Class 3 frames during this * window. */ if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta)) resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left); /* * Remove the station in case tranmission of a success response fails * (the STA was added associated to the driver) or if the station was * previously added unassociated. */ if ((reply_res != WLAN_STATUS_SUCCESS && resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } } static void handle_disassoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)", (unsigned long) len); return; } wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated", MAC2STR(mgmt->sa)); return; } ap_sta_set_authorized(hapd, sta, 0); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); /* Stop Accounting and IEEE 802.1X sessions, but leave the STA * authenticated. */ accounting_sta_stop(hapd, sta); ieee802_1x_free_station(hapd, sta); if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { sta->timeout_next = STA_DEAUTH; eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); } mlme_disassociate_indication( hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); } static void handle_deauth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short " "payload (len=%lu)", (unsigned long) len); return; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " "to deauthenticate, but it is not authenticated", MAC2STR(mgmt->sa)); return; } ap_sta_set_authorized(hapd, sta, 0); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); mlme_deauthenticate_indication( hapd, sta, le_to_host16(mgmt->u.deauth.reason_code)); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); ap_free_sta(hapd, sta); } static void handle_beacon(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, struct hostapd_frame_info *fi) { struct ieee802_11_elems elems; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)", (unsigned long) len); return; } (void) ieee802_11_parse_elems(mgmt->u.beacon.variable, len - (IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)), &elems, 0); ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); } #ifdef CONFIG_IEEE80211W static int hostapd_sa_query_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { const u8 *end; end = mgmt->u.action.u.sa_query_resp.trans_id + WLAN_SA_QUERY_TR_ID_LEN; if (((u8 *) mgmt) + len < end) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " "frame (len=%lu)", (unsigned long) len); return 0; } ieee802_11_sa_query_action(hapd, mgmt->sa, mgmt->u.action.u.sa_query_resp.action, mgmt->u.action.u.sa_query_resp.trans_id); return 1; } static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && category != WLAN_ACTION_HT; } #endif /* CONFIG_IEEE80211W */ static int handle_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; sta = ap_get_sta(hapd, mgmt->sa); if (len < IEEE80211_HDRLEN + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", (unsigned long) len); return 0; } if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " "frame (category=%u) from unassociated STA " MACSTR, MAC2STR(mgmt->sa), mgmt->u.action.category); return 0; } #ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) && robust_action_frame(mgmt->u.action.category)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Dropped unprotected Robust Action frame from " "an MFP STA"); return 0; } #endif /* CONFIG_IEEE80211W */ if (sta) { u16 fc = le_to_host16(mgmt->frame_control); u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl); if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && sta->last_subtype == WLAN_FC_STYPE_ACTION) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated action frame seq_ctrl=0x%x", seq_ctrl); return 1; } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_ACTION; } switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: if (!sta || wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; return 1; #endif /* CONFIG_IEEE80211R */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); return 1; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: return hostapd_sa_query_action(hapd, mgmt, len); #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM case WLAN_ACTION_WNM: ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; #endif /* CONFIG_WNM */ #ifdef CONFIG_FST case WLAN_ACTION_FST: if (hapd->iface->fst) fst_rx_action(hapd->iface->fst, mgmt, len); else wpa_printf(MSG_DEBUG, "FST: Ignore FST Action frame - no FST attached"); return 1; #endif /* CONFIG_FST */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: #ifdef CONFIG_IEEE80211N if (len >= IEEE80211_HDRLEN + 2 && mgmt->u.action.u.public_action.action == WLAN_PA_20_40_BSS_COEX) { wpa_printf(MSG_DEBUG, "HT20/40 coex mgmt frame received from STA " MACSTR, MAC2STR(mgmt->sa)); hostapd_2040_coex_action(hapd, mgmt, len); } #endif /* CONFIG_IEEE80211N */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq); } if (hapd->public_action_cb2) { hapd->public_action_cb2(hapd->public_action_cb2_ctx, (u8 *) mgmt, len, hapd->iface->freq); } if (hapd->public_action_cb || hapd->public_action_cb2) return 1; break; case WLAN_ACTION_VENDOR_SPECIFIC: if (hapd->vendor_action_cb) { if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq) == 0) return 1; } break; case WLAN_ACTION_RADIO_MEASUREMENT: hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len); return 1; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - unknown action category %d or invalid " "frame", mgmt->u.action.category); if (!is_multicast_ether_addr(mgmt->da) && !(mgmt->u.action.category & 0x80) && !is_multicast_ether_addr(mgmt->sa)) { struct ieee80211_mgmt *resp; /* * IEEE 802.11-REVma/D9.0 - 7.3.1.11 * Return the Action frame to the source without change * except that MSB of the Category set to 1. */ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " "frame back to sender"); resp = os_malloc(len); if (resp == NULL) return 0; os_memcpy(resp, mgmt, len); os_memcpy(resp->da, resp->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.action.category |= 0x80; if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) { wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " "Action frame"); } os_free(resp); } return 1; } /** * ieee802_11_mgmt - process incoming IEEE 802.11 management frames * @hapd: hostapd BSS data structure (the BSS to which the management frame was * sent to) * @buf: management frame data (starting from IEEE 802.11 header) * @len: length of frame data in octets * @fi: meta data about received frame (signal level, etc.) * * Process all incoming IEEE 802.11 management frames. This will be called for * each frame received from the kernel driver through wlan#ap interface. In * addition, it can be called to re-inserted pending frames (e.g., when using * external RADIUS server as an MAC ACL). */ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; u16 fc, stype; int ret = 0; if (len < 24) return 0; mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); if (stype == WLAN_FC_STYPE_BEACON) { handle_beacon(hapd, mgmt, len, fi); return 1; } if (!is_broadcast_ether_addr(mgmt->bssid) && #ifdef CONFIG_P2P /* Invitation responses can be sent with the peer MAC as BSSID */ !((hapd->conf->p2p & P2P_GROUP_OWNER) && stype == WLAN_FC_STYPE_ACTION) && #endif /* CONFIG_P2P */ #ifdef CONFIG_MESH !(hapd->conf->mesh & MESH_ENABLED) && #endif /* CONFIG_MESH */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address", MAC2STR(mgmt->bssid)); return 0; } if (stype == WLAN_FC_STYPE_PROBE_REQ) { handle_probe_req(hapd, mgmt, len, fi->ssi_signal); return 1; } if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", MAC2STR(mgmt->da)); return 0; } if (hapd->iconf->track_sta_max_num) sta_track_add(hapd->iface, mgmt->sa); switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); handle_auth(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); handle_assoc(hapd, mgmt, len, 0); ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); handle_assoc(hapd, mgmt, len, 1); ret = 1; break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc"); handle_disassoc(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_DEAUTH: wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action"); ret = handle_action(hapd, mgmt, len); break; default: hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "unknown mgmt frame subtype %d", stype); break; } return ret; } static void handle_auth_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "did not acknowledge authentication response"); goto fail; } if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", (unsigned long) len); goto fail; } if (status_code == WLAN_STATUS_SUCCESS && ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "authenticated"); sta->flags |= WLAN_STA_AUTH; if (sta->added_unassoc) hostapd_set_sta_flags(hapd, sta); return; } fail: if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } } static void hostapd_set_wds_encryption(struct hostapd_data *hapd, struct sta_info *sta, char *ifname_wds) { int i; struct hostapd_ssid *ssid = &hapd->conf->ssid; if (hapd->conf->ieee802_1x || hapd->conf->wpa) return; for (i = 0; i < 4; i++) { if (ssid->wep.key[i] && hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i, i == ssid->wep.idx, NULL, 0, ssid->wep.key[i], ssid->wep.len[i])) { wpa_printf(MSG_WARNING, "Could not set WEP keys for WDS interface; %s", ifname_wds); break; } } } static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int ok) { u16 status; struct sta_info *sta; int new_assoc = 1; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : sizeof(mgmt->u.assoc_resp))) { wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", reassoc, (unsigned long) len); hostapd_drv_sta_remove(hapd, sta->addr); return; } if (reassoc) status = le_to_host16(mgmt->u.reassoc_resp.status_code); else status = le_to_host16(mgmt->u.assoc_resp.status_code); if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "did not acknowledge association response"); sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; /* The STA is added only in case of SUCCESS */ if (status == WLAN_STATUS_SUCCESS) hostapd_drv_sta_remove(hapd, sta->addr); return; } if (status != WLAN_STATUS_SUCCESS) return; /* Stop previous accounting session, if one is started, and allocate * new session id for the new session. */ accounting_sta_stop(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "associated (aid %d)", sta->aid); if (sta->flags & WLAN_STA_ASSOC) new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) || sta->auth_alg == WLAN_AUTH_FT) { /* * Open, static WEP, or FT protocol; no separate authorization * step. */ ap_sta_set_authorized(hapd, sta, 1); } if (reassoc) mlme_reassociate_indication(hapd, sta); else mlme_associate_indication(hapd, sta); #ifdef CONFIG_IEEE80211W sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ if (sta->flags & WLAN_STA_WDS) { int ret; char ifname_wds[IFNAMSIZ + 1]; ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, sta->aid, 1); if (!ret) hostapd_set_wds_encryption(hapd, sta, ifname_wds); } if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, * so bind it to the selected VLAN interface now, since the * interface selection is not going to change anymore. */ if (ap_sta_bind_vlan(hapd, sta) < 0) return; } else if (sta->vlan_id) { /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ if (ap_sta_bind_vlan(hapd, sta) < 0) return; } hostapd_set_sta_flags(hapd, sta); if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); if (sta->pending_eapol_rx) { struct os_reltime now, age; os_get_reltime(&now); os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age); if (age.sec == 0 && age.usec < 200000) { wpa_printf(MSG_DEBUG, "Process pending EAPOL frame that was received from " MACSTR " just before association notification", MAC2STR(sta->addr)); ieee802_1x_receive( hapd, mgmt->da, wpabuf_head(sta->pending_eapol_rx->buf), wpabuf_len(sta->pending_eapol_rx->buf)); } wpabuf_free(sta->pending_eapol_rx->buf); os_free(sta->pending_eapol_rx); sta->pending_eapol_rx = NULL; } } static void handle_deauth_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { struct sta_info *sta; if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (ok) wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth", MAC2STR(sta->addr)); else wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " "deauth", MAC2STR(sta->addr)); ap_sta_deauth_cb(hapd, sta); } static void handle_disassoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { struct sta_info *sta; if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (ok) wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc", MAC2STR(sta->addr)); else wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " "disassoc", MAC2STR(sta->addr)); ap_sta_disassoc_cb(hapd, sta); } /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame * was sent from) * @buf: management frame data (starting from IEEE 802.11 header) * @len: length of frame data in octets * @stype: management frame subtype from frame control field * @ok: Whether the frame was ACK'ed */ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_mgmt_frame_handling) { wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d", stype, ok); return; } #endif /* CONFIG_TESTING_OPTIONS */ switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth cb"); handle_auth_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ASSOC_RESP: wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 0, ok); break; case WLAN_FC_STYPE_REASSOC_RESP: wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok); break; case WLAN_FC_STYPE_DEAUTH: wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); handle_deauth_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc cb"); handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); break; } } int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { /* TODO */ return 0; } int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { /* TODO */ return 0; } void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, addr); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, addr); if (sta) break; } } if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) return; if (sta->flags & WLAN_STA_PENDING_POLL) { wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " "activity poll", MAC2STR(sta->addr), ack ? "ACKed" : "did not ACK"); if (ack) sta->flags &= ~WLAN_STA_PENDING_POLL; } ieee802_1x_tx_status(hapd, sta, buf, len, ack); } void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, const u8 *data, size_t len, int ack) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, dst); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, dst); if (sta) break; } } if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA " MACSTR " that is not currently associated", MAC2STR(dst)); return; } ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack); } void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, addr); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, addr); if (sta) break; } } if (sta == NULL) return; wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR, MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_PENDING_POLL)) return; wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending " "activity poll", MAC2STR(sta->addr)); sta->flags &= ~WLAN_STA_PENDING_POLL; } void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds) { struct sta_info *sta; sta = ap_get_sta(hapd, src); if (sta && (sta->flags & WLAN_STA_ASSOC)) { if (!hapd->conf->wds_sta) return; if (wds && !(sta->flags & WLAN_STA_WDS)) { int ret; char ifname_wds[IFNAMSIZ + 1]; wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " "STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); sta->flags |= WLAN_STA_WDS; ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, sta->aid, 1); if (!ret) hostapd_set_wds_encryption(hapd, sta, ifname_wds); } return; } wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); if (is_multicast_ether_addr(src)) { /* Broadcast bit set in SA?! Ignore the frame silently. */ return; } if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) { wpa_printf(MSG_DEBUG, "Association Response to the STA has " "already been sent, but no TX status yet known - " "ignore Class 3 frame issue with " MACSTR, MAC2STR(src)); return; } if (sta && (sta->flags & WLAN_STA_AUTH)) hostapd_drv_sta_disassoc( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); else hostapd_drv_sta_deauth( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); } #endif /* CONFIG_NATIVE_WINDOWS */ Index: head/contrib/wpa/src/ap/wpa_auth.c =================================================================== --- head/contrib/wpa/src/ap/wpa_auth.c (revision 336498) +++ head/contrib/wpa/src/ap/wpa_auth.c (revision 336499) @@ -1,3707 +1,3710 @@ /* * IEEE 802.11 RSN / WPA Authenticator * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "pmksa_cache_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" #define STATE_MACHINE_DATA struct wpa_state_machine #define STATE_MACHINE_DEBUG_PREFIX "WPA" #define STATE_MACHINE_ADDR sm->addr static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_request_new_ptk(struct wpa_state_machine *sm); static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, struct wpa_ptk *ptk); static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ static const u32 eapol_key_timeout_first_group = 500; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.mic_failure_report) return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); return 0; } static inline void wpa_auth_psk_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.psk_failure_report) wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr); } static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var, int value) { if (wpa_auth->cb.set_eapol) wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); } static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var) { if (wpa_auth->cb.get_eapol == NULL) return -1; return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); } static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk) { if (wpa_auth->cb.get_psk == NULL) return NULL; return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, prev_psk); } static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, const u8 *addr, u8 *msk, size_t *len) { if (wpa_auth->cb.get_msk == NULL) return -1; return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); } static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { if (wpa_auth->cb.set_key == NULL) return -1; return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, key, key_len); } static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { if (wpa_auth->cb.get_seqnum == NULL) return -1; return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); } static inline int wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { if (wpa_auth->cb.send_eapol == NULL) return -1; return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, encrypt); } #ifdef CONFIG_MESH static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.start_ampe == NULL) return -1; return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); } #endif /* CONFIG_MESH */ int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) { if (wpa_auth->cb.for_each_sta == NULL) return 0; return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); } int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx) { if (wpa_auth->cb.for_each_auth == NULL) return 0; return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); } void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt) { if (wpa_auth->cb.logger == NULL) return; wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); } void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *fmt, ...) { char *format; int maxlen; va_list ap; if (wpa_auth->cb.logger == NULL) return; maxlen = os_strlen(fmt) + 100; format = os_malloc(maxlen); if (!format) return; va_start(ap, fmt); vsnprintf(format, maxlen, fmt, ap); va_end(ap); wpa_auth_logger(wpa_auth, addr, level, format); os_free(format); } static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.disconnect == NULL) return; wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } static int wpa_use_aes_cmac(struct wpa_state_machine *sm) { int ret = 0; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) ret = 1; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) ret = 1; #endif /* CONFIG_IEEE80211W */ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) ret = 1; return ret; } static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); } else { wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); wpa_hexdump_key(MSG_DEBUG, "GMK", wpa_auth->group->GMK, WPA_GMK_LEN); } if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, wpa_rekey_gmk, wpa_auth, NULL); } } static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_group *group, *next; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); group = wpa_auth->group; while (group) { wpa_group_get(wpa_auth, group); group->GTKReKey = TRUE; do { group->changed = FALSE; wpa_group_sm_step(wpa_auth, group); } while (group->changed); next = group->next; wpa_group_put(wpa_auth, group); group = next; } if (wpa_auth->conf.wpa_group_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, wpa_rekey_gtk, wpa_auth, NULL); } } static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); wpa_request_new_ptk(sm); wpa_sm_step(sm); } static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->pmksa == ctx) sm->pmksa = NULL; return 0; } static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, void *ctx) { struct wpa_authenticator *wpa_auth = ctx; wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); } static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)]; u8 rkey[32]; unsigned long ptr; if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); /* * Counter = PRF-256(Random number, "Init Counter", * Local MAC Address || Time) */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); wpa_get_ntp_timestamp(buf + ETH_ALEN); ptr = (unsigned long) group; os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), group->Counter, WPA_NONCE_LEN) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "Key Counter", group->Counter, WPA_NONCE_LEN); return 0; } static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, int vlan_id, int delay_init) { struct wpa_group *group; group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) return NULL; group->GTKAuthenticator = TRUE; group->vlan_id = vlan_id; group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); if (random_pool_ready() != 1) { wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " "for secure operations - update keys later when " "the first station connects"); } /* * Set initial GMK/Counter value here. The actual values that will be * used in negotiations will be set once the first station tries to * connect. This allows more time for collecting additional randomness * on embedded devices. */ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); os_free(group); return NULL; } group->GInit = TRUE; if (delay_init) { wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " "until Beacon frames have been configured"); /* Initialization is completed in wpa_init_keys(). */ } else { wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); } return group; } /** * wpa_init - Initialize WPA authenticator * @addr: Authenticator address * @conf: Configuration for WPA authenticator * @cb: Callback functions for WPA authenticator * Returns: Pointer to WPA authenticator data or %NULL on failure */ struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb) { struct wpa_authenticator *wpa_auth; wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); if (wpa_auth == NULL) return NULL; os_memcpy(wpa_auth->addr, addr, ETH_ALEN); os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); os_free(wpa_auth); return NULL; } wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); if (wpa_auth->group == NULL) { os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; } wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb, wpa_auth); if (wpa_auth->pmksa == NULL) { wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); os_free(wpa_auth->group); os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; } #ifdef CONFIG_IEEE80211R wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); if (wpa_auth->ft_pmk_cache == NULL) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); os_free(wpa_auth->group); os_free(wpa_auth->wpa_ie); pmksa_cache_auth_deinit(wpa_auth->pmksa); os_free(wpa_auth); return NULL; } #endif /* CONFIG_IEEE80211R */ if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, wpa_rekey_gmk, wpa_auth, NULL); } if (wpa_auth->conf.wpa_group_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, wpa_rekey_gtk, wpa_auth, NULL); } #ifdef CONFIG_P2P if (WPA_GET_BE32(conf->ip_addr_start)) { int count = WPA_GET_BE32(conf->ip_addr_end) - WPA_GET_BE32(conf->ip_addr_start) + 1; if (count > 1000) count = 1000; if (count > 0) wpa_auth->ip_pool = bitfield_alloc(count); } #endif /* CONFIG_P2P */ return wpa_auth; } int wpa_init_keys(struct wpa_authenticator *wpa_auth) { struct wpa_group *group = wpa_auth->group; wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " "keys"); wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return -1; return 0; } /** * wpa_deinit - Deinitialize WPA authenticator * @wpa_auth: Pointer to WPA authenticator data from wpa_init() */ void wpa_deinit(struct wpa_authenticator *wpa_auth) { struct wpa_group *group, *prev; eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); #ifdef CONFIG_PEERKEY while (wpa_auth->stsl_negotiations) wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); #endif /* CONFIG_PEERKEY */ pmksa_cache_auth_deinit(wpa_auth->pmksa); #ifdef CONFIG_IEEE80211R wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P bitfield_free(wpa_auth->ip_pool); #endif /* CONFIG_P2P */ os_free(wpa_auth->wpa_ie); group = wpa_auth->group; while (group) { prev = group; group = group->next; os_free(prev); } os_free(wpa_auth); } /** * wpa_reconfig - Update WPA authenticator configuration * @wpa_auth: Pointer to WPA authenticator data from wpa_init() * @conf: Configuration for WPA authenticator */ int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf) { struct wpa_group *group; if (wpa_auth == NULL) return 0; os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); return -1; } /* * Reinitialize GTK to make sure it is suitable for the new * configuration. */ group = wpa_auth->group; group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); return 0; } struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr) { struct wpa_state_machine *sm; if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return NULL; sm = os_zalloc(sizeof(struct wpa_state_machine)); if (sm == NULL) return NULL; os_memcpy(sm->addr, addr, ETH_ALEN); if (p2p_dev_addr) os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; wpa_group_get(sm->wpa_auth, sm->group); return sm; } int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm) { if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return -1; #ifdef CONFIG_IEEE80211R if (sm->ft_completed) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "FT authentication already completed - do not " "start 4-way handshake"); /* Go to PTKINITDONE state to allow GTK rekeying */ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; return 0; } #endif /* CONFIG_IEEE80211R */ if (sm->started) { os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); sm->ReAuthenticationRequest = TRUE; return wpa_sm_step(sm); } wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "start authentication"); sm->started = 1; sm->Init = TRUE; if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = FALSE; sm->AuthenticationRequest = TRUE; return wpa_sm_step(sm); } void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) { /* WPA/RSN was not used - clear WPA state. This is needed if the STA * reassociates back to the same AP while the previous entry for the * STA has not yet been removed. */ if (sm == NULL) return; sm->wpa_key_mgmt = 0; } static void wpa_free_sta_sm(struct wpa_state_machine *sm) { #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr)) { u32 start; wpa_printf(MSG_DEBUG, "P2P: Free assigned IP " "address %u.%u.%u.%u from " MACSTR, sm->ip_addr[0], sm->ip_addr[1], sm->ip_addr[2], sm->ip_addr[3], MAC2STR(sm->addr)); start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start); bitfield_clear(sm->wpa_auth->ip_pool, WPA_GET_BE32(sm->ip_addr) - start); } #endif /* CONFIG_P2P */ if (sm->GUpdateStationKeys) { sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); wpabuf_free(sm->ft_pending_req_ies); #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); wpa_group_put(sm->wpa_auth, sm->group); os_free(sm); } void wpa_auth_sta_deinit(struct wpa_state_machine *sm) { if (sm == NULL) return; if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "strict rekeying - force GTK rekey since STA " "is leaving"); eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, NULL); } eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); if (sm->in_step_loop) { /* Must not free state machine while wpa_sm_step() is running. * Freeing will be completed in the end of wpa_sm_step(). */ wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " "machine deinit for " MACSTR, MAC2STR(sm->addr)); sm->pending_deinit = 1; } else wpa_free_sta_sm(sm); } static void wpa_request_new_ptk(struct wpa_state_machine *sm) { if (sm == NULL) return; sm->PTKRequest = TRUE; sm->PTK_valid = 0; } static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!ctr[i].valid) break; if (os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0) return 1; } return 0; } static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (ctr[i].valid && (replay_counter == NULL || os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0)) ctr[i].valid = FALSE; } } #ifdef CONFIG_IEEE80211R static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_ie_parse *kde) { struct wpa_ie_data ie; struct rsn_mdie *mdie; if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 || ie.num_pmkid != 1 || ie.pmkid == NULL) { wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " "FT 4-way handshake message 2/4"); return -1; } os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant", sm->sup_pmk_r1_name, PMKID_LEN); if (!kde->mdie || !kde->ftie) { wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake " "message 2/4", kde->mdie ? "FTIE" : "MDIE"); return -1; } mdie = (struct rsn_mdie *) (kde->mdie + 2); if (kde->mdie[1] < sizeof(struct rsn_mdie) || os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); return -1; } if (sm->assoc_resp_ftie && (kde->ftie[1] != sm->assoc_resp_ftie[1] || os_memcmp(kde->ftie, sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]) != 0)) { wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4", kde->ftie, kde->ftie_len); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp", sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]); return -1; } return 0; } #endif /* CONFIG_IEEE80211R */ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int group) { /* Supplicant reported a Michael MIC error */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Error Request " "(STA detected Michael MIC failure (group=%d))", group); if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "ignore Michael MIC failure report since " "group cipher is not TKIP"); } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "ignore Michael MIC failure report since " "pairwise cipher is not TKIP"); } else { if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) return 1; /* STA entry was removed */ sm->dot11RSNAStatsTKIPRemoteMICFailures++; wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; } /* * Error report is not a request for a new key handshake, but since * Authenticator may do it, let's change the keys now anyway. */ wpa_request_new_ptk(sm); return 0; } static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, size_t data_len) { struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; unsigned int pmk_len; for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, pmk); if (pmk == NULL) break; pmk_len = PMK_LEN; } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) == 0) { ok = 1; break; } if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) break; } if (!ok) { wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce did not result in matching MIC"); return -1; } wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce resulted in matching MIC"); sm->alt_snonce_valid = 0; os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; return 0; } void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_key_192 *key192; u16 key_info, key_data_length; enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, SMK_M1, SMK_M3, SMK_ERROR } msg; char *msgtxt; struct wpa_eapol_ie_parse kde; int ft; const u8 *eapol_key_ie, *key_data; size_t eapol_key_ie_len, keyhdrlen, mic_len; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; mic_len = wpa_mic_len(sm->wpa_key_mgmt); keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); if (data_len < sizeof(*hdr) + keyhdrlen) return; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); if (mic_len == 24) { key_data = (const u8 *) (key192 + 1); key_data_length = WPA_GET_BE16(key192->key_data_length); } else { key_data = (const u8 *) (key + 1); key_data_length = WPA_GET_BE16(key->key_data_length); } wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR " key_info=0x%x type=%u key_data_length=%u", MAC2STR(sm->addr), key_info, key->type, key_data_length); if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", key_data_length, (unsigned long) (data_len - sizeof(*hdr) - keyhdrlen)); return; } if (sm->wpa == WPA_VERSION_WPA2) { if (key->type == EAPOL_KEY_TYPE_WPA) { /* * Some deployed station implementations seem to send * msg 4/4 with incorrect type value in WPA2 mode. */ wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " "with unexpected WPA type in RSN mode"); } else if (key->type != EAPOL_KEY_TYPE_RSN) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in RSN mode", key->type); return; } } else { if (key->type != EAPOL_KEY_TYPE_WPA) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in WPA mode", key->type); return; } } wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { if (key_info & WPA_KEY_INFO_ERROR) { msg = SMK_ERROR; msgtxt = "SMK Error"; } else { msg = SMK_M1; msgtxt = "SMK M1"; } } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { msg = SMK_M3; msgtxt = "SMK M3"; } else if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { msg = GROUP_2; msgtxt = "2/2 Group"; } else if (key_data_length == 0) { msg = PAIRWISE_4; msgtxt = "4/4 Pairwise"; } else { msg = PAIRWISE_2; msgtxt = "2/4 Pairwise"; } /* TODO: key_info type validation for PeerKey */ if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (sm->pairwise == WPA_CIPHER_CCMP || sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "advertised support for " "AES-128-CMAC, but did not " "use it"); return; } if (!wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " "with CCMP/GCMP"); return; } } if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); return; } } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->req_replay_counter_used && os_memcmp(key->replay_counter, sm->req_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "received EAPOL-Key request with " "replayed counter"); return; } } if (!(key_info & WPA_KEY_INFO_REQUEST) && !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; if (msg == PAIRWISE_2 && wpa_replay_counter_valid(sm->prev_key_replay, key->replay_counter) && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) { /* * Some supplicant implementations (e.g., Windows XP * WZC) update SNonce for each EAPOL-Key 2/4. This * breaks the workaround on accepting any of the * pending requests, so allow the SNonce to be updated * even if we have already sent out EAPOL-Key 3/4. */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "Process SNonce update from STA " "based on retransmitted EAPOL-Key " "1/4"); sm->update_snonce = 1; os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN); sm->alt_snonce_valid = TRUE; os_memcpy(sm->alt_replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); goto continue_processing; } if (msg == PAIRWISE_4 && sm->alt_snonce_valid && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && os_memcmp(key->replay_counter, sm->alt_replay_counter, WPA_REPLAY_COUNTER_LEN) == 0) { /* * Supplicant may still be using the old SNonce since * there was two EAPOL-Key 2/4 messages and they had * different SNonce values. */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4"); goto continue_processing; } if (msg == PAIRWISE_2 && wpa_replay_counter_valid(sm->prev_key_replay, key->replay_counter) && sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "ignore retransmitted EAPOL-Key %s - " "SNonce did not change", msgtxt); } else { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "received EAPOL-Key %s with " "unexpected replay counter", msgtxt); } for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!sm->key_replay[i].valid) break; wpa_hexdump(MSG_DEBUG, "pending replay counter", sm->key_replay[i].counter, WPA_REPLAY_COUNTER_LEN); } wpa_hexdump(MSG_DEBUG, "received replay counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); return; } continue_processing: switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && (!sm->update_snonce || sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } random_add_randomness(key->key_nonce, WPA_NONCE_LEN); if (sm->group->reject_4way_hs_for_entropy) { /* * The system did not have enough entropy to generate * strong random numbers. Reject the first 4-way * handshake(s) and collect some entropy based on the * information from it. Once enough entropy is * available, the next atempt will trigger GMK/Key * Counter update and the station will be allowed to * continue. */ wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " "collect more entropy for random number " "generation"); random_mark_pool_ready(); wpa_sta_disconnect(wpa_auth, sm->addr); return; } if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 with " "invalid Key Data contents"); return; } if (kde.rsn_ie) { eapol_key_ie = kde.rsn_ie; eapol_key_ie_len = kde.rsn_ie_len; } else if (kde.osen) { eapol_key_ie = kde.osen; eapol_key_ie_len = kde.osen_len; } else { eapol_key_ie = kde.wpa_ie; eapol_key_ie_len = kde.wpa_ie_len; } ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt); if (sm->wpa_ie == NULL || wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len, eapol_key_ie, eapol_key_ie_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "WPA IE from (Re)AssocReq did not " "match with msg 2/4"); if (sm->wpa_ie) { wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", sm->wpa_ie, sm->wpa_ie_len); } wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", eapol_key_ie, eapol_key_ie_len); /* MLME-DEAUTHENTICATE.request */ wpa_sta_disconnect(wpa_auth, sm->addr); return; } #ifdef CONFIG_IEEE80211R if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr); return; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (kde.ip_addr_req && kde.ip_addr_req[0] && wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { int idx; wpa_printf(MSG_DEBUG, "P2P: IP address requested in " "EAPOL-Key exchange"); idx = bitfield_get_first_zero(wpa_auth->ip_pool); if (idx >= 0) { u32 start = WPA_GET_BE32(wpa_auth->conf. ip_addr_start); bitfield_set(wpa_auth->ip_pool, idx); WPA_PUT_BE32(sm->ip_addr, start + idx); wpa_printf(MSG_DEBUG, "P2P: Assigned IP " "address %u.%u.%u.%u to " MACSTR, sm->ip_addr[0], sm->ip_addr[1], sm->ip_addr[2], sm->ip_addr[3], MAC2STR(sm->addr)); } } #endif /* CONFIG_P2P */ break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || !sm->PTK_valid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 4/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } break; case GROUP_2: if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING || !sm->PTK_valid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/2 in " "invalid state (%d) - dropped", sm->wpa_ptk_group_state); return; } break; #ifdef CONFIG_PEERKEY case SMK_M1: case SMK_M3: case SMK_ERROR: if (!wpa_auth->conf.peerkey) { wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " "PeerKey use disabled - ignoring message"); return; } if (!sm->PTK_valid) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg SMK in " "invalid state - dropped"); return; } break; #else /* CONFIG_PEERKEY */ case SMK_M1: case SMK_M3: case SMK_ERROR: return; /* STSL disabled - ignore SMK messages */ #endif /* CONFIG_PEERKEY */ case REQUEST: break; } wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "received EAPOL-Key frame (%s)", msgtxt); if (key_info & WPA_KEY_INFO_ACK) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key Ack set"); return; } if (!(key_info & WPA_KEY_INFO_MIC)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key MIC not set"); return; } sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, data_len) && (msg != PAIRWISE_4 || !sm->alt_snonce_valid || wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); return; } sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->MICVerified) { sm->req_replay_counter_used = 1; os_memcpy(sm->req_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key request with " "invalid MIC"); return; } /* * TODO: should decrypt key data field if encryption was used; * even though MAC address KDE is not normally encrypted, * supplicant is allowed to encrypt it. */ if (msg == SMK_ERROR) { #ifdef CONFIG_PEERKEY wpa_smk_error(wpa_auth, sm, key_data, key_data_length); #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { if (wpa_receive_error_report( wpa_auth, sm, !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) return; /* STA entry was removed */ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " "4-Way Handshake"); wpa_request_new_ptk(sm); #ifdef CONFIG_PEERKEY } else if (msg == SMK_M1) { wpa_smk_m1(wpa_auth, sm, key, key_data, key_data_length); #endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && wpa_parse_kde_ies(key_data, key_data_length, &kde) == 0 && kde.mac_addr) { } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for GTK " "rekeying"); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); wpa_rekey_gtk(wpa_auth, NULL); } } else { /* Do not allow the same key replay counter to be reused. */ wpa_replay_counter_mark_invalid(sm->key_replay, key->replay_counter); if (msg == PAIRWISE_2) { /* * Maintain a copy of the pending EAPOL-Key frames in * case the EAPOL-Key frame was retransmitted. This is * needed to allow EAPOL-Key msg 2/4 reply to another * pending msg 1/4 to update the SNonce to work around * unexpected supplicant behavior. */ os_memcpy(sm->prev_key_replay, sm->key_replay, sizeof(sm->key_replay)); } else { os_memset(sm->prev_key_replay, 0, sizeof(sm->prev_key_replay)); } /* * Make sure old valid counters are not accepted anymore and * do not get copied again. */ wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } #ifdef CONFIG_PEERKEY if (msg == SMK_M3) { wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length); return; } #endif /* CONFIG_PEERKEY */ os_free(sm->last_rx_eapol_key); sm->last_rx_eapol_key = os_malloc(data_len); if (sm->last_rx_eapol_key == NULL) return; os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); sm->EAPOLKeyReceived = TRUE; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); wpa_sm_step(sm); } static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, const u8 *gnonce, u8 *gtk, size_t gtk_len) { u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; u8 *pos; int ret = 0; /* GTK = PRF-X(GMK, "Group key expansion", * AA || GNonce || Time || random data) * The example described in the IEEE 802.11 standard uses only AA and * GNonce as inputs here. Add some more entropy since this derivation * is done only at the Authenticator and as such, does not need to be * exactly same. */ os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); pos = data + ETH_ALEN + WPA_NONCE_LEN; wpa_get_ntp_timestamp(pos); pos += 8; if (random_get_bytes(pos, 16) < 0) ret = -1; #ifdef CONFIG_IEEE80211W sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); #else /* CONFIG_IEEE80211W */ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ return ret; } static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; sm->pending_1_of_4_timeout = 0; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); sm->TimeoutEvt = TRUE; wpa_sm_step(sm); } void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr, int force_version) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_key_192 *key192; size_t len, mic_len, keyhdrlen; int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; int version, pairwise; int i; u8 *key_data; mic_len = wpa_mic_len(sm->wpa_key_mgmt); keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); len = sizeof(struct ieee802_1x_hdr) + keyhdrlen; if (force_version) version = force_version; else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " "encr=%d)", version, (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, pairwise, (unsigned long) kde_len, keyidx, encr); key_data_len = kde_len; if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) pad_len = 8 - pad_len; key_data_len += pad_len + 8; } len += key_data_len; hdr = os_zalloc(len); if (hdr == NULL) return; hdr->version = wpa_auth->conf.eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_data = ((u8 *) (hdr + 1)) + keyhdrlen; key->type = sm->wpa == WPA_VERSION_WPA2 ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info |= version; if (encr && sm->wpa == WPA_VERSION_WPA2) key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; if (sm->wpa != WPA_VERSION_WPA2) key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); if (key_info & WPA_KEY_INFO_SMK_MESSAGE) WPA_PUT_BE16(key->key_length, 0); /* FIX: STSL: what to use as key_replay_counter? */ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { sm->key_replay[i].valid = sm->key_replay[i - 1].valid; os_memcpy(sm->key_replay[i].counter, sm->key_replay[i - 1].counter, WPA_REPLAY_COUNTER_LEN); } inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); os_memcpy(key->replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->key_replay[0].valid = TRUE; if (nonce) os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); if (key_rsc) os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); if (kde && !encr) { os_memcpy(key_data, kde, kde_len); if (mic_len == 24) WPA_PUT_BE16(key192->key_data_length, kde_len); else WPA_PUT_BE16(key->key_data_length, kde_len); } else if (encr && kde) { buf = os_zalloc(key_data_len); if (buf == NULL) { os_free(hdr); return; } pos = buf; os_memcpy(pos, kde, kde_len); pos += kde_len; if (pad_len) *pos++ = 0xdd; wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, (key_data_len - 8) / 8, buf, key_data)) { os_free(hdr); os_free(buf); return; } if (mic_len == 24) WPA_PUT_BE16(key192->key_data_length, key_data_len); else WPA_PUT_BE16(key->key_data_length, key_data_len); #ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; os_memcpy(key->key_iv, sm->group->Counter + WPA_NONCE_LEN - 16, 16); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len); os_memcpy(key_data, buf, key_data_len); rc4_skip(ek, 32, 256, key_data, key_data_len); if (mic_len == 24) WPA_PUT_BE16(key192->key_data_length, key_data_len); else WPA_PUT_BE16(key->key_data_length, key_data_len); #endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); os_free(buf); return; } os_free(buf); } if (key_info & WPA_KEY_INFO_MIC) { u8 *key_mic; if (!sm->PTK_valid) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "PTK not valid when sending EAPOL-Key " "frame"); os_free(hdr); return; } key_mic = key192->key_mic; /* same offset for key and key192 */ wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, sm->wpa_key_mgmt, version, (u8 *) hdr, len, key_mic); #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && drand48() < wpa_auth->conf.corrupt_gtk_rekey_mic_probability) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "Corrupting group EAPOL-Key Key MIC"); key_mic[0]++; } #endif /* CONFIG_TESTING_OPTIONS */ } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1); wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, sm->pairwise_set); os_free(hdr); } static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int key_info, const u8 *key_rsc, const u8 *nonce, const u8 *kde, size_t kde_len, int keyidx, int encr) { int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; int ctr; if (sm == NULL) return; __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; if (ctr == 1 && wpa_auth->conf.tx_status) timeout_ms = pairwise ? eapol_key_timeout_first : eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " "counter %d)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_key_192 *key192; u16 key_info; int ret = 0; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; size_t mic_len = wpa_mic_len(akmp); if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); os_memcpy(mic, key192->key_mic, mic_len); os_memset(key192->key_mic, 0, mic_len); if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, key_info & WPA_KEY_INFO_TYPE_MASK, data, data_len, key192->key_mic) || os_memcmp_const(mic, key192->key_mic, mic_len) != 0) ret = -1; os_memcpy(key192->key_mic, mic, mic_len); return ret; } void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) { int remove_ptk = 1; if (sm == NULL) return -1; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "event %d notification", event); switch (event) { case WPA_AUTH: #ifdef CONFIG_MESH /* PTKs are derived through AMPE */ if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) { /* not mesh */ break; } return 0; #endif /* CONFIG_MESH */ case WPA_ASSOC: break; case WPA_DEAUTH: case WPA_DISASSOC: sm->DeauthenticationRequest = TRUE; break; case WPA_REAUTH: case WPA_REAUTH_EAPOL: if (!sm->started) { /* * When using WPS, we may end up here if the STA * manages to re-associate without the previous STA * entry getting removed. Consequently, we need to make * sure that the WPA state machines gets initialized * properly at this point. */ wpa_printf(MSG_DEBUG, "WPA state machine had not been " "started - initialize now"); sm->started = 1; sm->Init = TRUE; if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = FALSE; sm->AuthenticationRequest = TRUE; break; } if (sm->GUpdateStationKeys) { /* * Reauthentication cancels the pending group key * update for this STA. */ sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->PtkGroupInit = TRUE; } sm->ReAuthenticationRequest = TRUE; break; case WPA_ASSOC_FT: #ifdef CONFIG_IEEE80211R wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " "after association"); wpa_ft_install_ptk(sm); /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; return 0; #else /* CONFIG_IEEE80211R */ break; #endif /* CONFIG_IEEE80211R */ + case WPA_DRV_STA_REMOVED: + sm->tk_already_set = FALSE; + return 0; } #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot && event == WPA_AUTH) remove_ptk = 0; #endif /* CONFIG_IEEE80211W */ if (remove_ptk) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); if (event != WPA_REAUTH_EAPOL) wpa_remove_ptk(sm); } if (sm->in_step_loop) { /* * wpa_sm_step() is already running - avoid recursive call to * it by making the existing loop process the new update. */ sm->changed = TRUE; return 0; } return wpa_sm_step(sm); } SM_STATE(WPA_PTK, INITIALIZE) { SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ sm->changed = FALSE; } sm->keycount = 0; if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and * Local AA > Remote AA)) */) { sm->Pair = TRUE; } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); wpa_remove_ptk(sm); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); sm->TimeoutCtr = 0; if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 0); } } SM_STATE(WPA_PTK, DISCONNECT) { SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; wpa_sta_disconnect(sm->wpa_auth, sm->addr); } SM_STATE(WPA_PTK, DISCONNECTED) { SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); sm->DeauthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); os_memset(&sm->PTK, 0, sizeof(sm->PTK)); sm->PTK_valid = FALSE; wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, 1); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); sm->AuthenticationRequest = FALSE; } static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { if (group->first_sta_seen) return; /* * System has run bit further than at the time hostapd was started * potentially very early during boot up. This provides better chances * of collecting more randomness on embedded systems. Re-initialize the * GMK and Counter here to improve their strength if there was not * enough entropy available immediately after system startup. */ wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " "station"); if (random_pool_ready() != 1) { wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " "to proceed - reject first 4-way handshake"); group->reject_4way_hs_for_entropy = TRUE; } else { group->first_sta_seen = TRUE; group->reject_4way_hs_for_entropy = FALSE; } if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 || wpa_gtk_update(wpa_auth, group) < 0 || wpa_group_config_group_keys(wpa_auth, group) < 0) { wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed"); group->first_sta_seen = FALSE; group->reject_4way_hs_for_entropy = TRUE; } } SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); wpa_group_ensure_init(sm->wpa_auth, sm->group); sm->ReAuthenticationRequest = FALSE; /* * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat * ambiguous. The Authenticator state machine uses a counter that is * incremented by one for each 4-way handshake. However, the security * analysis of 4-way handshake points out that unpredictable nonces * help in preventing precomputation attacks. Instead of the state * machine definition, use an unpredictable nonce value here to provide * stronger protection against potential precomputation attacks. */ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " "ANonce."); sm->Disconnect = TRUE; return; } wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, WPA_NONCE_LEN); /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be * re-entered on ReAuthenticationRequest without going through * INITIALIZE. */ sm->TimeoutCtr = 0; } static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm) { if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_ERROR, "WPA: Failed to get random data for ANonce"); sm->Disconnect = TRUE; return -1; } wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce, WPA_NONCE_LEN); sm->TimeoutCtr = 0; return 0; } SM_STATE(WPA_PTK, INITPMK) { u8 msk[2 * PMK_LEN]; size_t len = 2 * PMK_LEN; SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; #endif /* CONFIG_IEEE80211R */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); sm->pmk_len = sm->pmksa->pmk_len; } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { unsigned int pmk_len; if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) pmk_len = PMK_LEN_SUITE_B_192; else pmk_len = PMK_LEN; wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " "(MSK len=%lu PMK len=%u)", (unsigned long) len, pmk_len); if (len < pmk_len) { wpa_printf(MSG_DEBUG, "WPA: MSK not long enough (%u) to create PMK (%u)", (unsigned int) len, (unsigned int) pmk_len); sm->Disconnect = TRUE; return; } os_memcpy(sm->PMK, msk, pmk_len); sm->pmk_len = pmk_len; #ifdef CONFIG_IEEE80211R if (len >= 2 * PMK_LEN) { os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); sm->xxkey_len = PMK_LEN; } #endif /* CONFIG_IEEE80211R */ } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", sm->wpa_auth->cb.get_msk); sm->Disconnect = TRUE; return; } os_memset(msk, 0, sizeof(msk)); sm->req_replay_counter_used = 0; /* IEEE 802.11i does not set keyRun to FALSE, but not doing this * will break reauthentication since EAPOL state machines may not be * get into AUTHENTICATING state that clears keyRun before WPA state * machine enters AUTHENTICATION2 state and goes immediately to INITPMK * state and takes PMK from the previously used AAA Key. This will * eventually fail in 4-Way Handshake because Supplicant uses PMK * derived from the new AAA Key. Setting keyRun = FALSE here seems to * be good workaround for this issue. */ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); } SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); sm->pmk_len = PMK_LEN; #ifdef CONFIG_IEEE80211R os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; #endif /* CONFIG_IEEE80211R */ } sm->req_replay_counter_used = 0; } SM_STATE(WPA_PTK, PTKSTART) { u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; size_t pmkid_len = 0; SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; sm->alt_snonce_valid = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/4 msg of 4-Way Handshake"); /* * TODO: Could add PMKID even with WPA2-PSK, but only if there is only * one possible PSK for this STA. */ if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); if (sm->pmksa) { os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, PMKID_LEN); } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) { /* No KCK available to derive PMKID */ pmkid = NULL; } else { /* * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. */ rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); } } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, sm->ANonce, pmkid, pmkid_len, 0, 0); } static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, struct wpa_ptk *ptk) { #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk); #endif /* CONFIG_IEEE80211R */ return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, ptk, sm->wpa_key_mgmt, sm->pairwise); } SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_ptk PTK; int ok = 0, psk_found = 0; const u8 *pmk = NULL; unsigned int pmk_len; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; sm->update_snonce = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, pmk); if (pmk == NULL) break; psk_found = 1; pmk_len = PMK_LEN; } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { ok = 1; break; } if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) break; } if (!ok) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "invalid MIC in msg 2/4 of 4-Way Handshake"); if (psk_found) wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr); return; } #ifdef CONFIG_IEEE80211R if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { /* * Verify that PMKR1Name from EAPOL-Key message 2/4 matches * with the value we derived. */ if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PMKR1Name mismatch in FT 4-way " "handshake"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from " "Supplicant", sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); return; } } #endif /* CONFIG_IEEE80211R */ sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { /* PSK may have changed from the previous choice, so update * state machine data based on whatever PSK was selected here. */ os_memcpy(sm->PMK, pmk, PMK_LEN); sm->pmk_len = PMK_LEN; } sm->MICVerified = TRUE; os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; } SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) { SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); sm->TimeoutCtr = 0; } #ifdef CONFIG_IEEE80211W static int ieee80211w_kde_len(struct wpa_state_machine *sm) { if (sm->mgmt_frame_prot) { size_t len; len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len; } return 0; } static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { struct wpa_igtk_kde igtk; struct wpa_group *gsm = sm->group; u8 rsc[WPA_KEY_RSC_LEN]; size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); if (!sm->mgmt_frame_prot) return pos; igtk.keyid[0] = gsm->GN_igtk; igtk.keyid[1] = 0; if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); else os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len); if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. */ if (random_get_bytes(igtk.igtk, len) < 0) return pos; } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len, NULL, 0); return pos; } #else /* CONFIG_IEEE80211W */ static int ieee80211w_kde_len(struct wpa_state_machine *sm) { return 0; } static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { return pos; } #endif /* CONFIG_IEEE80211W */ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; int wpa_ie_len, secure, keyidx, encr = 0; SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], GTK[GN], IGTK, [FTIE], [TIE * 2]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ wpa_ie = sm->wpa_auth->wpa_ie; wpa_ie_len = sm->wpa_auth->wpa_ie_len; if (sm->wpa == WPA_VERSION_WPA && (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { /* WPA-only STA, remove RSN IE and possible MDIE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 3/4 msg of 4-Way Handshake"); if (sm->wpa == WPA_VERSION_WPA2) { /* WPA2 send GTK in the 4-way handshake */ secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. */ if (random_get_bytes(dummy_gtk, gtk_len) < 0) return; gtk = dummy_gtk; } keyidx = gsm->GN; _rsc = rsc; encr = 1; } else { /* WPA does not include GTK in msg 3/4 */ secure = 0; gtk = NULL; gtk_len = 0; keyidx = 0; _rsc = NULL; if (sm->rx_eapol_key_secure) { /* * It looks like Windows 7 supplicant tries to use * Secure bit in msg 2/4 after having reported Michael * MIC failure and it then rejects the 4-way handshake * if msg 3/4 does not set Secure bit. Work around this * by setting the Secure bit here even in the case of * WPA if the supplicant used it first. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "STA used Secure bit in WPA msg 2/4 - " "set Secure for 3/4 as workaround"); secure = 1; } } kde_len = wpa_ie_len + ieee80211w_kde_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ kde_len += 300; /* FTIE + 2 * TIE */ } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; #endif /* CONFIG_P2P */ kde = os_malloc(kde_len); if (kde == NULL) return; pos = kde; os_memcpy(pos, wpa_ie, wpa_ie_len); pos += wpa_ie_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; size_t elen; elen = pos - kde; res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert " "PMKR1Name into RSN IE in EAPOL-Key data"); os_free(kde); return; } pos -= wpa_ie_len; pos += elen; } #endif /* CONFIG_IEEE80211R */ if (gtk) { u8 hdr[2]; hdr[0] = keyidx & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gtk_len); } pos = ieee80211w_kde_add(sm, pos); #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; struct wpa_auth_config *conf; conf = &sm->wpa_auth->conf; if (sm->assoc_resp_ftie && kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { os_memcpy(pos, sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]); res = 2 + sm->assoc_resp_ftie[1]; } else { res = wpa_write_ftie(conf, conf->r0_key_holder, conf->r0_key_holder_len, NULL, NULL, pos, kde + kde_len - pos, NULL, 0); } if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " "into EAPOL-Key Key Data"); os_free(kde); return; } pos += res; /* TIE[ReassociationDeadline] (TU) */ *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; WPA_PUT_LE32(pos, conf->reassociation_deadline); pos += 4; /* TIE[KeyLifetime] (seconds) */ *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); pos += 4; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) { u8 addr[3 * 4]; os_memcpy(addr, sm->ip_addr, 4); os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4); os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4); pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC, addr, sizeof(addr), NULL, 0); } #endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); os_free(kde); } SM_STATE(WPA_PTK, PTKINITDONE) { SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk, klen)) { wpa_sta_disconnect(sm->wpa_auth, sm->addr); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; if (sm->wpa_auth->conf.wpa_ptk_rekey) { eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); eloop_register_timeout(sm->wpa_auth->conf. wpa_ptk_rekey, 0, wpa_rekey_ptk, sm->wpa_auth, sm); } if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 1); } } if (0 /* IBSS == TRUE */) { sm->keycount++; if (sm->keycount == 2) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 1); } } else { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 1); } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = TRUE; else sm->has_GTK = TRUE; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, "pairwise key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); #ifdef CONFIG_IEEE80211R wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); #endif /* CONFIG_IEEE80211R */ } SM_STEP(WPA_PTK) { struct wpa_authenticator *wpa_auth = sm->wpa_auth; if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); else if (sm->Disconnect /* || FIX: dot11RSNAConfigSALifetime timeout */) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "WPA_PTK: sm->Disconnect"); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->DeauthenticationRequest) SM_ENTER(WPA_PTK, DISCONNECTED); else if (sm->AuthenticationRequest) SM_ENTER(WPA_PTK, AUTHENTICATION); else if (sm->ReAuthenticationRequest) SM_ENTER(WPA_PTK, AUTHENTICATION2); else if (sm->PTKRequest) { if (wpa_auth_sm_ptk_update(sm) < 0) SM_ENTER(WPA_PTK, DISCONNECTED); else SM_ENTER(WPA_PTK, PTKSTART); } else switch (sm->wpa_ptk_state) { case WPA_PTK_INITIALIZE: break; case WPA_PTK_DISCONNECT: SM_ENTER(WPA_PTK, DISCONNECTED); break; case WPA_PTK_DISCONNECTED: SM_ENTER(WPA_PTK, INITIALIZE); break; case WPA_PTK_AUTHENTICATION: SM_ENTER(WPA_PTK, AUTHENTICATION2); break; case WPA_PTK_AUTHENTICATION2: if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun) > 0) SM_ENTER(WPA_PTK, INITPMK); else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); break; case WPA_PTK_INITPMK: if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable) > 0) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "INITPMK - keyAvailable = false"); SM_ENTER(WPA_PTK, DISCONNECT); } break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } break; case WPA_PTK_PTKSTART: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PTKSTART: Retry limit %d reached", dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); break; case WPA_PTK_PTKCALCNEGOTIATING: if (sm->MICVerified) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); break; case WPA_PTK_PTKCALCNEGOTIATING2: SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITNEGOTIATING: if (sm->update_snonce) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PTKINITNEGOTIATING: Retry limit %d " "reached", dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITDONE: break; } } SM_STATE(WPA_PTK_GROUP, IDLE) { SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ sm->changed = FALSE; } sm->GTimeoutCtr = 0; } SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; struct wpa_group *gsm = sm->group; const u8 *kde; u8 *kde_buf = NULL, *pos, hdr[2]; size_t kde_len; u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); sm->GTimeoutCtr++; if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; } if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; sm->TimeoutEvt = FALSE; /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); gtk = gsm->GTK[gsm->GN - 1]; if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. */ if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) return; gtk = dummy_gtk; } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); kde_buf = os_malloc(kde_len); if (kde_buf == NULL) return; kde = pos = kde_buf; hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); kde_len = pos - kde; } else { kde = gtk; kde_len = gsm->GTK_len; } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); os_free(kde_buf); } SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->GTimeoutCtr = 0; /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, "group key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); sm->has_GTK = TRUE; } SM_STATE(WPA_PTK_GROUP, KEYERROR) { SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; } SM_STEP(WPA_PTK_GROUP) { if (sm->Init || sm->PtkGroupInit) { SM_ENTER(WPA_PTK_GROUP, IDLE); sm->PtkGroupInit = FALSE; } else switch (sm->wpa_ptk_group_state) { case WPA_PTK_GROUP_IDLE: if (sm->GUpdateStationKeys || (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_REKEYNEGOTIATING: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); else if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_KEYERROR: SM_ENTER(WPA_PTK_GROUP, IDLE); break; case WPA_PTK_GROUP_REKEYESTABLISHED: SM_ENTER(WPA_PTK_GROUP, IDLE); break; } } static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int ret = 0; os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", wpa_auth->addr, group->GNonce, group->GTK[group->GN - 1], group->GTK_len) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "GTK", group->GTK[group->GN - 1], group->GTK_len); #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { size_t len; len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", wpa_auth->addr, group->GNonce, group->IGTK[group->GN_igtk - 4], len) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], len); } #endif /* CONFIG_IEEE80211W */ return ret; } static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "GTK_INIT (VLAN-ID %d)", group->vlan_id); group->changed = FALSE; /* GInit is not cleared here; avoid loop */ group->wpa_group_state = WPA_GROUP_GTK_INIT; /* GTK[0..N] = 0 */ os_memset(group->GTK, 0, sizeof(group->GTK)); group->GN = 1; group->GM = 2; #ifdef CONFIG_IEEE80211W group->GN_igtk = 4; group->GM_igtk = 5; #endif /* CONFIG_IEEE80211W */ /* GTK[GN] = CalcGTK() */ wpa_gtk_update(wpa_auth, group); } static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { if (ctx != NULL && ctx != sm->group) return 0; if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); sm->GUpdateStationKeys = FALSE; return 0; } if (sm->GUpdateStationKeys) { /* * This should not really happen, so add a debug log entry. * Since we clear the GKeyDoneStations before the loop, the * station needs to be counted here anyway. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "GUpdateStationKeys was already set when " "marking station for GTK rekeying"); } /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ if (sm->is_wnmsleep) return 0; sm->group->GKeyDoneStations++; sm->GUpdateStationKeys = TRUE; wpa_sm_step(sm); return 0; } #ifdef CONFIG_WNM /* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { if (sm == NULL || sm->is_wnmsleep) return; wpa_group_update_sta(sm, NULL); } void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) { if (sm) sm->is_wnmsleep = !!flag; } int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) { struct wpa_group *gsm = sm->group; u8 *start = pos; /* * GTK subelement: * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | * Key[5..32] */ *pos++ = WNM_SLEEP_SUBELEM_GTK; *pos++ = 11 + gsm->GTK_len; /* Key ID in B0-B1 of Key Info */ WPA_PUT_LE16(pos, gsm->GN & 0x03); pos += 2; *pos++ = gsm->GTK_len; if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) return 0; pos += 8; os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); pos += gsm->GTK_len; wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", gsm->GN); wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", gsm->GTK[gsm->GN - 1], gsm->GTK_len); return pos - start; } #ifdef CONFIG_IEEE80211W int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) { struct wpa_group *gsm = sm->group; u8 *start = pos; size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); /* * IGTK subelement: * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] */ *pos++ = WNM_SLEEP_SUBELEM_IGTK; *pos++ = 2 + 6 + len; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) return 0; pos += 6; os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len); pos += len; wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", gsm->GN_igtk); wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", gsm->IGTK[gsm->GN_igtk - 4], len); return pos - start; } #endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_WNM */ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int tmp; wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYS (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYS; group->GTKReKey = FALSE; tmp = group->GM; group->GM = group->GN; group->GN = tmp; #ifdef CONFIG_IEEE80211W tmp = group->GM_igtk; group->GM_igtk = group->GN_igtk; group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ /* "GKeyDoneStations = GNoStations" is done in more robust way by * counting the STAs that are marked with GUpdateStationKeys instead of * including all STAs that could be in not-yet-completed state. */ wpa_gtk_update(wpa_auth, group); if (group->GKeyDoneStations) { wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " "GKeyDoneStations=%d when starting new GTK rekey", group->GKeyDoneStations); group->GKeyDoneStations = 0; } wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { int ret = 0; if (wpa_auth_set_key(wpa_auth, group->vlan_id, wpa_cipher_to_alg(wpa_auth->conf.wpa_group), broadcast_ether_addr, group->GN, group->GTK[group->GN - 1], group->GTK_len) < 0) ret = -1; #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { enum wpa_alg alg; size_t len; alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher); len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); if (ret == 0 && wpa_auth_set_key(wpa_auth, group->vlan_id, alg, broadcast_ether_addr, group->GN_igtk, group->IGTK[group->GN_igtk - 4], len) < 0) ret = -1; } #endif /* CONFIG_IEEE80211W */ return ret; } static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->group == ctx) { wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR " for discconnection due to fatal failure", MAC2STR(sm->addr)); sm->Disconnect = TRUE; } return 0; } static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE"); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_FATAL_FAILURE; wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group); } static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; if (wpa_group_config_group_keys(wpa_auth, group) < 0) { wpa_group_fatal_failure(wpa_auth, group); return -1; } return 0; } static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { if (group->GInit) { wpa_group_gtk_init(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) { /* Do not allow group operations */ } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && group->GTKAuthenticator) { wpa_group_setkeysdone(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && group->GTKReKey) { wpa_group_setkeys(wpa_auth, group); } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { if (group->GKeyDoneStations == 0) wpa_group_setkeysdone(wpa_auth, group); else if (group->GTKReKey) wpa_group_setkeys(wpa_auth, group); } } static int wpa_sm_step(struct wpa_state_machine *sm) { if (sm == NULL) return 0; if (sm->in_step_loop) { /* This should not happen, but if it does, make sure we do not * end up freeing the state machine too early by exiting the * recursive call. */ wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); return 0; } sm->in_step_loop = 1; do { if (sm->pending_deinit) break; sm->changed = FALSE; sm->wpa_auth->group->changed = FALSE; SM_STEP_RUN(WPA_PTK); if (sm->pending_deinit) break; SM_STEP_RUN(WPA_PTK_GROUP); if (sm->pending_deinit) break; wpa_group_sm_step(sm->wpa_auth, sm->group); } while (sm->changed || sm->wpa_auth->group->changed); sm->in_step_loop = 0; if (sm->pending_deinit) { wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " "machine deinit for " MACSTR, MAC2STR(sm->addr)); wpa_free_sta_sm(sm); return 1; } return 0; } static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) { struct wpa_state_machine *sm = eloop_ctx; wpa_sm_step(sm); } void wpa_auth_sm_notify(struct wpa_state_machine *sm) { if (sm == NULL) return; eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); } void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) { int tmp, i; struct wpa_group *group; if (wpa_auth == NULL) return; group = wpa_auth->group; for (i = 0; i < 2; i++) { tmp = group->GM; group->GM = group->GN; group->GN = tmp; #ifdef CONFIG_IEEE80211W tmp = group->GM_igtk; group->GM_igtk = group->GN_igtk; group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ wpa_gtk_update(wpa_auth, group); wpa_group_config_group_keys(wpa_auth, group); } } static const char * wpa_bool_txt(int val) { return val ? "TRUE" : "FALSE"; } #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { int len = 0, ret; char pmkid_txt[PMKID_LEN * 2 + 1]; #ifdef CONFIG_RSN_PREAUTH const int preauth = 1; #else /* CONFIG_RSN_PREAUTH */ const int preauth = 0; #endif /* CONFIG_RSN_PREAUTH */ if (wpa_auth == NULL) return len; ret = os_snprintf(buf + len, buflen - len, "dot11RSNAOptionImplemented=TRUE\n" "dot11RSNAPreauthenticationImplemented=%s\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n", wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); ret = os_snprintf( buf + len, buflen - len, "dot11RSNAConfigVersion=%u\n" "dot11RSNAConfigPairwiseKeysSupported=9999\n" /* FIX: dot11RSNAConfigGroupCipher */ /* FIX: dot11RSNAConfigGroupRekeyMethod */ /* FIX: dot11RSNAConfigGroupRekeyTime */ /* FIX: dot11RSNAConfigGroupRekeyPackets */ "dot11RSNAConfigGroupRekeyStrict=%u\n" "dot11RSNAConfigGroupUpdateCount=%u\n" "dot11RSNAConfigPairwiseUpdateCount=%u\n" "dot11RSNAConfigGroupCipherSize=%u\n" "dot11RSNAConfigPMKLifetime=%u\n" "dot11RSNAConfigPMKReauthThreshold=%u\n" "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" "dot11RSNAConfigSATimeout=%u\n" "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" "dot11RSNAPMKIDUsed=%s\n" "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" "dot11RSNATKIPCounterMeasuresInvoked=%u\n" "dot11RSNA4WayHandshakeFailures=%u\n" "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", RSN_VERSION, !!wpa_auth->conf.wpa_strict_rekey, dot11RSNAConfigGroupUpdateCount, dot11RSNAConfigPairwiseUpdateCount, wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, dot11RSNAConfigSATimeout, RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), pmkid_txt, RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, wpa_auth->dot11RSNA4WayHandshakeFailures); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; /* TODO: dot11RSNAConfigPairwiseCiphersTable */ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", wpa_auth->group->wpa_group_state); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; return len; } int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) { int len = 0, ret; u32 pairwise = 0; if (sm == NULL) return 0; /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ /* dot11RSNAStatsEntry */ pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? WPA_PROTO_RSN : WPA_PROTO_WPA, sm->pairwise); if (pairwise == 0) return 0; ret = os_snprintf( buf + len, buflen - len, /* TODO: dot11RSNAStatsIndex */ "dot11RSNAStatsSTAAddress=" MACSTR "\n" "dot11RSNAStatsVersion=1\n" "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" /* TODO: dot11RSNAStatsTKIPICVErrors */ "dot11RSNAStatsTKIPLocalMICFailures=%u\n" "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" /* TODO: dot11RSNAStatsCCMPReplays */ /* TODO: dot11RSNAStatsCCMPDecryptErrors */ /* TODO: dot11RSNAStatsTKIPReplays */, MAC2STR(sm->addr), RSN_SUITE_ARG(pairwise), sm->dot11RSNAStatsTKIPLocalMICFailures, sm->dot11RSNAStatsTKIPRemoteMICFailures); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAPTKState=%d\n" "hostapdWPAPTKGroupState=%d\n", sm->wpa_ptk_state, sm->wpa_ptk_group_state); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; return len; } void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) { if (wpa_auth) wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; } int wpa_auth_pairwise_set(struct wpa_state_machine *sm) { return sm && sm->pairwise_set; } int wpa_auth_get_pairwise(struct wpa_state_machine *sm) { return sm->pairwise; } int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) { if (sm == NULL) return -1; return sm->wpa_key_mgmt; } int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return sm->wpa; } int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm) { if (!sm || !wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return 0; return sm->tk_already_set; } int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry) { if (sm == NULL || sm->pmksa != entry) return -1; sm->pmksa = NULL; return 0; } struct rsn_pmksa_cache_entry * wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) { return sm ? sm->pmksa : NULL; } void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) { if (sm) sm->dot11RSNAStatsTKIPLocalMICFailures++; } const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) { if (wpa_auth == NULL) return NULL; *len = wpa_auth->wpa_ie_len; return wpa_auth->wpa_ie; } int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol) { if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || sm->wpa_auth->conf.disable_pmksa_caching) return -1; if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { if (pmk_len > PMK_LEN_SUITE_B_192) pmk_len = PMK_LEN_SUITE_B_192; } else if (pmk_len > PMK_LEN) { pmk_len = PMK_LEN; } if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL, sm->PTK.kck, sm->PTK.kck_len, sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) return 0; return -1; } int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol) { if (wpa_auth == NULL) return -1; if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL, NULL, 0, wpa_auth->addr, sta_addr, session_timeout, eapol, WPA_KEY_MGMT_IEEE8021X)) return 0; return -1; } int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, const u8 *pmkid) { if (wpa_auth->conf.disable_pmksa_caching) return -1; if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid, NULL, 0, wpa_auth->addr, addr, 0, NULL, WPA_KEY_MGMT_SAE)) return 0; return -1; } void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { struct rsn_pmksa_cache_entry *pmksa; if (wpa_auth == NULL || wpa_auth->pmksa == NULL) return; pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); if (pmksa) { wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for " MACSTR " based on request", MAC2STR(sta_addr)); pmksa_cache_free_entry(wpa_auth->pmksa, pmksa); } } int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, size_t len) { if (!wpa_auth || !wpa_auth->pmksa) return 0; return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len); } void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) { if (wpa_auth && wpa_auth->pmksa) pmksa_cache_auth_flush(wpa_auth->pmksa); } 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; os_memcpy(pmk, pmksa->pmk, PMK_LEN); os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN); os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, 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 * gets modified. */ static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { struct wpa_group *prev = wpa_auth->group; wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d", group->vlan_id); while (prev) { if (prev->next == group) { /* This never frees the special first group as needed */ prev->next = group->next; os_free(group); break; } prev = prev->next; } } /* Increase the reference counter for group */ static void wpa_group_get(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { /* Skip the special first group */ if (wpa_auth->group == group) return; group->references++; } /* Decrease the reference counter and maybe free the group */ static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { /* Skip the special first group */ if (wpa_auth->group == group) return; group->references--; if (group->references) return; wpa_group_free(wpa_auth, group); } /* * Add a group that has its references counter set to zero. Caller needs to * call wpa_group_get() on the return value to mark the entry in use. */ static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { struct wpa_group *group; if (wpa_auth == NULL || wpa_auth->group == NULL) return NULL; wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", vlan_id); group = wpa_group_init(wpa_auth, vlan_id, 0); if (group == NULL) return NULL; group->next = wpa_auth->group->next; wpa_auth->group->next = group; return group; } /* * Enforce that the group state machine for the VLAN is running, increase * reference counter as interface is up. References might have been increased * even if a negative value is returned. * Returns: -1 on error (group missing, group already failed); otherwise, 0 */ int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id) { struct wpa_group *group; if (wpa_auth == NULL) return 0; group = wpa_auth->group; while (group) { if (group->vlan_id == vlan_id) break; group = group->next; } if (group == NULL) { group = wpa_auth_add_group(wpa_auth, vlan_id); if (group == NULL) return -1; } wpa_printf(MSG_DEBUG, "WPA: Ensure group state machine running for VLAN ID %d", vlan_id); wpa_group_get(wpa_auth, group); group->num_setup_iface++; if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return -1; return 0; } /* * Decrease reference counter, expected to be zero afterwards. * returns: -1 on error (group not found, group in fail state) * -2 if wpa_group is still referenced * 0 else */ int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id) { struct wpa_group *group; int ret = 0; if (wpa_auth == NULL) return 0; group = wpa_auth->group; while (group) { if (group->vlan_id == vlan_id) break; group = group->next; } if (group == NULL) return -1; wpa_printf(MSG_DEBUG, "WPA: Try stopping group state machine for VLAN ID %d", vlan_id); if (group->num_setup_iface <= 0) { wpa_printf(MSG_ERROR, "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.", vlan_id); return -1; } group->num_setup_iface--; if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) ret = -1; if (group->references > 1) { wpa_printf(MSG_DEBUG, "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold", vlan_id); ret = -2; } wpa_group_put(wpa_auth, group); return ret; } int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) { struct wpa_group *group; if (sm == NULL || sm->wpa_auth == NULL) return 0; group = sm->wpa_auth->group; while (group) { if (group->vlan_id == vlan_id) break; group = group->next; } if (group == NULL) { group = wpa_auth_add_group(sm->wpa_auth, vlan_id); if (group == NULL) return -1; } if (sm->group == group) return 0; if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) return -1; wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); wpa_group_get(sm->wpa_auth, group); wpa_group_put(sm->wpa_auth, sm->group); sm->group = group; return 0; } void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack) { if (wpa_auth == NULL || sm == NULL) return; wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR " ack=%d", MAC2STR(sm->addr), ack); if (sm->pending_1_of_4_timeout && ack) { /* * Some deployed supplicant implementations update their SNonce * for each EAPOL-Key 2/4 message even within the same 4-way * handshake and then fail to use the first SNonce when * deriving the PTK. This results in unsuccessful 4-way * handshake whenever the relatively short initial timeout is * reached and EAPOL-Key 1/4 is retransmitted. Try to work * around this by increasing the timeout now that we know that * the station has received the frame. */ int timeout_ms = eapol_key_timeout_subseq; wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 " "timeout by %u ms because of acknowledged frame", timeout_ms); eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } } int wpa_auth_uses_sae(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return wpa_key_mgmt_sae(sm->wpa_key_mgmt); } int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm) { if (sm == NULL) return 0; return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE; } #ifdef CONFIG_P2P int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr) { if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0) return -1; os_memcpy(addr, sm->ip_addr, 4); return 0; } #endif /* CONFIG_P2P */ int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, struct radius_das_attrs *attr) { return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr); } void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth) { struct wpa_group *group; if (!wpa_auth) return; for (group = wpa_auth->group; group; group = group->next) wpa_group_config_group_keys(wpa_auth, group); } Index: head/contrib/wpa/src/ap/wpa_auth.h =================================================================== --- head/contrib/wpa/src/ap/wpa_auth.h (revision 336498) +++ head/contrib/wpa/src/ap/wpa_auth.h (revision 336499) @@ -1,351 +1,351 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_AUTH_H #define WPA_AUTH_H #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" #define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ /* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition */ struct ft_rrb_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ le16 action_length; /* little endian length of action_frame */ u8 ap_address[ETH_ALEN]; /* * Followed by action_length bytes of FT Action frame (from Category * field to the end of Action Frame body. */ } STRUCT_PACKED; #define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 #define FT_PACKET_REQUEST 0 #define FT_PACKET_RESPONSE 1 /* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ #define FT_PACKET_R0KH_R1KH_PULL 200 #define FT_PACKET_R0KH_R1KH_RESP 201 #define FT_PACKET_R0KH_R1KH_PUSH 202 #define FT_R0KH_R1KH_PULL_NONCE_LEN 16 #define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \ ETH_ALEN) #define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_pull_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ le16 data_length; /* little endian length of data (44) */ u8 ap_address[ETH_ALEN]; u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; #define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \ WPA_PMK_NAME_LEN + 2) #define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_resp_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ le16 data_length; /* little endian length of data (78) */ u8 ap_address[ETH_ALEN]; u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ u8 s1kh_id[ETH_ALEN]; /* copied from pull */ u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; #define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \ WPA_PMK_NAME_LEN + PMK_LEN + \ WPA_PMK_NAME_LEN + 2) #define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_push_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ le16 data_length; /* little endian length of data (82) */ u8 ap_address[ETH_ALEN]; /* Encrypted with AES key-wrap */ u8 timestamp[4]; /* current time in seconds since unix epoch, little * endian */ u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ /* per STA state machine data */ struct wpa_authenticator; struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; struct ft_remote_r0kh { struct ft_remote_r0kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R0KH_ID_MAX_LEN]; size_t id_len; u8 key[16]; }; struct ft_remote_r1kh { struct ft_remote_r1kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R1KH_ID_LEN]; u8 key[16]; }; struct wpa_auth_config { int wpa; int wpa_key_mgmt; int wpa_pairwise; int wpa_group; int wpa_group_rekey; int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; int rsn_pairwise; int rsn_preauth; int eapol_version; int peerkey; int wmm_enabled; int wmm_uapsd; int disable_pmksa_caching; int okc; int tx_status; #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; int group_mgmt_cipher; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; size_t r0_key_holder_len; u8 r1_key_holder[FT_R1KH_ID_LEN]; u32 r0_key_lifetime; u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ int disable_gtk; int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS double corrupt_gtk_rekey_mic_probability; u8 own_ie_override[MAX_OWN_IE_OVERRIDE]; size_t own_ie_override_len; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P u8 ip_addr_go[4]; u8 ip_addr_mask[4]; u8 ip_addr_start[4]; u8 ip_addr_end[4]; #endif /* CONFIG_P2P */ }; typedef enum { LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING } logger_level; typedef enum { WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx } wpa_eapol_variable; struct wpa_auth_callbacks { void *ctx; void (*logger)(void *ctx, const u8 *addr, logger_level level, const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); int (*mic_failure_report)(void *ctx, const u8 *addr); void (*psk_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, size_t data_len, int encrypt); int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx); int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len); #ifdef CONFIG_IEEE80211R struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_MESH int (*start_ampe)(void *ctx, const u8 *sta_addr); #endif /* CONFIG_MESH */ }; struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb); int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf); enum { WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, WPA_INVALID_MDIE, WPA_INVALID_PROTO }; int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len); int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr); int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm); void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); void wpa_auth_sta_deinit(struct wpa_state_machine *sm); void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len); enum wpa_event { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, - WPA_REAUTH_EAPOL, WPA_ASSOC_FT + WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED }; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); void wpa_auth_sm_notify(struct wpa_state_machine *sm); void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); int wpa_auth_pairwise_set(struct wpa_state_machine *sm); int wpa_auth_get_pairwise(struct wpa_state_machine *sm); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len); int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, const u8 *pmkid); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); 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); #ifdef CONFIG_IEEE80211R u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len); void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, u16 auth_transaction, const u8 *ies, size_t ies_len, void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len), void *ctx); u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len); int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len); void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_auth_uses_sae(struct wpa_state_machine *sm); int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm); int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr); struct radius_das_attrs; int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, struct radius_das_attrs *attr); void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id); int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id); #endif /* WPA_AUTH_H */ Index: head/contrib/wpa =================================================================== --- head/contrib/wpa (revision 336498) +++ head/contrib/wpa (revision 336499) Property changes on: head/contrib/wpa ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor/wpa/dist:r336187-336485