From a54fa5fb807eaeff45464139b5a7759f060cec68 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 15 Jan 2013 13:53:35 -0800 Subject: Accumulative patch from commit dc013f1e37df3462085cf01a13f0c432f146ad7a Author: Jouni Malinen Date: Tue Jan 15 12:03:29 2013 +0200 eapol_test: Remove unnecessary header file inclusion - P2P: Send P2P-FIND-STOPPED event in the new continue-search states - P2P: Add some more details on Service Query TLV format - P2P: Use the same Dialog Token value for every GO Negotiation retry - P2P: Publish more connected clients info in Probe Response frames - P2P: Fix some memory leaks in p2p_add_device() - P2P: Use the same Dialog Token value for every PD retry - P2P: Document operating channel selection functions - P2P: Always re-select operating channel if not hard coded - P2P: Do not allow re-selection of GO channel if forced_freq in use - P2P: Set FORCE_FREQ flag as part of p2p_prepare_channel() - P2P: Share a single function for GO channel selection - P2P: Prefer operating channels where HT40 is possible - P2P: Be more careful with wpa_config_update_psk() call - P2P: Allow PSK to be used instead of passphrase for persistent GO - P2P: Consider age for the P2P scan results - Move some P2P offchannel operations to offchannel.c - P2P: Add more complete description of p2p_cancel - P2P: Allow p2p_cancel to be used to stop p2p_connect-join operation - Interworking changes - WNM changes - WPS changes - SAE changes Change-Id: I38b847d3460066cc58aecbcf67266bfcff1d344e Signed-off-by: Dmitry Shmidt --- src/ap/accounting.c | 5 +- src/ap/accounting.h | 6 ++ src/ap/ap_config.c | 7 ++ src/ap/ap_config.h | 8 +- src/ap/ap_drv_ops.c | 69 ++++++++++++- src/ap/ap_drv_ops.h | 6 +- src/ap/beacon.c | 61 ++++++++++-- src/ap/ctrl_iface_ap.c | 20 ++-- src/ap/drv_callbacks.c | 4 +- src/ap/hostapd.c | 6 +- src/ap/hostapd.h | 6 ++ src/ap/hw_features.c | 4 + src/ap/ieee802_11.c | 242 +++++++++++++++++++++++++++++++++------------ src/ap/ieee802_11.h | 3 + src/ap/ieee802_11_shared.c | 33 ++++++- src/ap/ieee802_11_vht.c | 11 +++ src/ap/ieee802_1x.c | 17 +++- src/ap/pmksa_cache_auth.c | 2 + src/ap/sta_info.c | 7 ++ src/ap/sta_info.h | 3 +- src/ap/wnm_ap.c | 85 +++++++++------- src/ap/wnm_ap.h | 4 +- src/ap/wpa_auth.c | 128 ++++++++---------------- src/ap/wpa_auth.h | 4 - src/ap/wpa_auth_ft.c | 2 +- src/ap/wpa_auth_glue.c | 15 ++- src/ap/wpa_auth_i.h | 2 - src/ap/wpa_auth_ie.c | 9 +- src/ap/wps_hostapd.c | 3 + 29 files changed, 533 insertions(+), 239 deletions(-) (limited to 'src/ap') diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 7563b52e..95405313 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -26,8 +26,6 @@ * input/output octets and updates Acct-{Input,Output}-Gigawords. */ #define ACCT_DEFAULT_UPDATE_INTERVAL 300 -static void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta); static void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); @@ -210,7 +208,6 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) if (sta->acct_session_started) return; - accounting_sta_get_id(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "starting accounting session %08X-%08X", @@ -377,7 +374,7 @@ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) } -static void accounting_sta_get_id(struct hostapd_data *hapd, +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { sta->acct_session_id_lo = hapd->acct_session_id_lo++; diff --git a/src/ap/accounting.h b/src/ap/accounting.h index 9d13d011..dcc54ee9 100644 --- a/src/ap/accounting.h +++ b/src/ap/accounting.h @@ -10,6 +10,11 @@ #define ACCOUNTING_H #ifdef CONFIG_NO_ACCOUNTING +static inline void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + static inline void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { @@ -29,6 +34,7 @@ static inline void accounting_deinit(struct hostapd_data *hapd) { } #else /* CONFIG_NO_ACCOUNTING */ +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); int accounting_init(struct hostapd_data *hapd); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 3c699f71..922f5646 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -89,6 +89,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #endif /* CONFIG_IEEE80211R */ bss->radius_das_time_window = 300; + + bss->sae_anti_clogging_threshold = 5; } @@ -158,6 +160,9 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; + conf->ap_table_max_size = 255; + conf->ap_table_expiration_time = 60; + return conf; } @@ -516,6 +521,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); + + os_free(conf->sae_groups); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 71313c02..4742107d 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -51,7 +51,8 @@ typedef enum hostap_security_policy { struct hostapd_ssid { u8 ssid[HOSTAPD_MAX_SSID_LEN]; size_t ssid_len; - int ssid_set; + unsigned int ssid_set:1; + unsigned int utf8_ssid:1; char vlan[IFNAMSIZ + 1]; secpolicy security_policy; @@ -390,6 +391,8 @@ struct hostapd_bss_config { /* IEEE 802.11v */ int time_advertisement; char *time_zone; + int wnm_sleep_mode; + int bss_transition; /* IEEE 802.11u - Interworking */ int interworking; @@ -452,6 +455,9 @@ struct hostapd_bss_config { #endif /* CONFIG_RADIUS_TEST */ struct wpabuf *vendor_elements; + + unsigned int sae_anti_clogging_threshold; + int *sae_groups; }; diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 02da25b7..b71d51d8 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -338,6 +338,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, u32 flags, u8 qosinfo) { struct hostapd_sta_add_params params; @@ -355,6 +356,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.supp_rates_len = supp_rates_len; params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; + params.vht_capabilities = vht_capab; params.flags = hostapd_sta_flags_to_drv(flags); params.qosinfo = qosinfo; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); @@ -454,19 +456,76 @@ int hostapd_flush(struct hostapd_data *hapd) int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, - int channel, int ht_enabled, int sec_channel_offset) + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1) { struct hostapd_freq_params data; - if (hapd->driver == NULL) - return 0; - if (hapd->driver->set_freq == NULL) - return 0; + int tmp; + os_memset(&data, 0, sizeof(data)); data.mode = mode; data.freq = freq; data.channel = channel; data.ht_enabled = ht_enabled; + data.vht_enabled = vht_enabled; data.sec_channel_offset = sec_channel_offset; + data.center_freq1 = freq + sec_channel_offset * 10; + data.center_freq2 = 0; + data.bandwidth = sec_channel_offset ? 40 : 20; + + /* + * This validation code is probably misplaced, maybe it should be + * in src/ap/hw_features.c and check the hardware support as well. + */ + if (data.vht_enabled) switch (vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (center_segment1) + return -1; + if (5000 + center_segment0 * 5 != data.center_freq1) + return -1; + break; + case VHT_CHANWIDTH_80P80MHZ: + if (center_segment1 == center_segment0 + 4 || + center_segment1 == center_segment0 - 4) + return -1; + data.center_freq2 = 5000 + center_segment1 * 5; + /* fall through */ + case VHT_CHANWIDTH_80MHZ: + data.bandwidth = 80; + if (vht_oper_chwidth == 1 && center_segment1) + return -1; + if (vht_oper_chwidth == 3 && !center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* primary 40 part must match the HT configuration */ + tmp = (30 + freq - 5000 - center_segment0 * 5)/20; + tmp /= 2; + if (data.center_freq1 != 5000 + + center_segment0 * 5 - 20 + 40 * tmp) + return -1; + data.center_freq1 = 5000 + center_segment0 * 5; + break; + case VHT_CHANWIDTH_160MHZ: + data.bandwidth = 160; + if (center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* primary 40 part must match the HT configuration */ + tmp = (70 + freq - 5000 - center_segment0 * 5)/20; + tmp /= 2; + if (data.center_freq1 != 5000 + + center_segment0 * 5 - 60 + 40 * tmp) + return -1; + data.center_freq1 = 5000 + center_segment0 * 5; + break; + } + if (hapd->driver == NULL) + return 0; + if (hapd->driver->set_freq == NULL) + return 0; return hapd->driver->set_freq(hapd->drv_priv, &data); } diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 9c53b99d..ceb7e68e 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -13,6 +13,7 @@ enum wpa_driver_if_type; struct wpa_bss_params; struct wpa_driver_scan_params; struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; u32 hostapd_sta_flags_to_drv(u32 flags); int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, @@ -37,6 +38,7 @@ int hostapd_sta_add(struct hostapd_data *hapd, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, u32 flags, u8 qosinfo); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, @@ -55,7 +57,9 @@ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, const u8 *addr, int idx, u8 *seq); int hostapd_flush(struct hostapd_data *hapd); int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, - int channel, int ht_enabled, int sec_channel_offset); + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1); int hostapd_set_rts(struct hostapd_data *hapd, int rts); int hostapd_set_frag(struct hostapd_data *hapd, int frag); int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, diff --git a/src/ap/beacon.c b/src/ap/beacon.c index f761bf55..4c47c758 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -2,7 +2,7 @@ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -310,6 +310,46 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, } +enum ssid_match_result { + NO_SSID_MATCH, + EXACT_SSID_MATCH, + WILDCARD_SSID_MATCH +}; + +static enum ssid_match_result ssid_match(struct hostapd_data *hapd, + const u8 *ssid, size_t ssid_len, + const u8 *ssid_list, + size_t ssid_list_len) +{ + const u8 *pos, *end; + int wildcard = 0; + + if (ssid_len == 0) + wildcard = 1; + if (ssid_len == hapd->conf->ssid.ssid_len && + os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) + return EXACT_SSID_MATCH; + + if (ssid_list == NULL) + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; + + pos = ssid_list; + end = ssid_list + ssid_list_len; + while (pos + 1 <= end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[1] == 0) + wildcard = 1; + if (pos[1] == hapd->conf->ssid.ssid_len && + os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0) + return EXACT_SSID_MATCH; + pos += 2 + pos[1]; + } + + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; +} + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -321,6 +361,7 @@ void handle_probe_req(struct hostapd_data *hapd, struct sta_info *sta = NULL; size_t i, resp_len; int noack; + enum ssid_match_result res; ie = mgmt->u.probe_req.variable; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) @@ -376,7 +417,8 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ - if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && + elems.ssid_list_len == 0) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " "broadcast SSID ignored", MAC2STR(mgmt->sa)); return; @@ -394,10 +436,9 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ - if (elems.ssid_len == 0 || - (elems.ssid_len == hapd->conf->ssid.ssid_len && - os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == - 0)) { + res = ssid_match(hapd, elems.ssid, elems.ssid_len, + elems.ssid_list, elems.ssid_list_len); + if (res != NO_SSID_MATCH) { if (sta) sta->ssid_probe = &hapd->conf->ssid; } else { @@ -406,9 +447,10 @@ void handle_probe_req(struct hostapd_data *hapd, ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR - " for foreign SSID '%s' (DA " MACSTR ")", + " for foreign SSID '%s' (DA " MACSTR ")%s", MAC2STR(mgmt->sa), ssid_txt, - MAC2STR(mgmt->da)); + MAC2STR(mgmt->da), + elems.ssid_list ? " (SSID list)" : ""); } return; } @@ -455,7 +497,8 @@ void handle_probe_req(struct hostapd_data *hapd, * If this is a broadcast probe request, apply no ack policy to avoid * excessive retries. */ - noack = !!(elems.ssid_len == 0 && is_broadcast_ether_addr(mgmt->da)); + noack = !!(res == WILDCARD_SSID_MATCH && + is_broadcast_ether_addr(mgmt->da)); if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) perror("handle_probe_req: send"); diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index c55d3fe3..1cb7e73c 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -189,6 +189,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, u8 addr[ETH_ALEN]; struct sta_info *sta; const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", txtaddr); @@ -228,11 +229,14 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + + hostapd_drv_sta_deauth(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) - ap_sta_deauthenticate(hapd, sta, - WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_sta_deauthenticate(hapd, sta, reason); else if (addr[0] == 0xff) hostapd_free_stas(hapd); @@ -246,6 +250,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, u8 addr[ETH_ALEN]; struct sta_info *sta; const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", txtaddr); @@ -285,11 +290,14 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + + hostapd_drv_sta_disassoc(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) - ap_sta_disassociate(hapd, sta, - WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_sta_disassociate(hapd, sta, reason); else if (addr[0] == 0xff) hostapd_free_stas(hapd); diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 86139752..8980bec0 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -512,13 +512,13 @@ static void hostapd_action_rx(struct hostapd_data *hapd, action->data + 2); } #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211V +#ifdef CONFIG_WNM if (action->category == WLAN_ACTION_WNM) { wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d", __func__, (int) action->len); ieee802_11_rx_wnm_action_ap(hapd, action); } -#endif /* CONFIG_IEEE80211V */ +#endif /* CONFIG_WNM */ } diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index cef9dafc..92fda569 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -894,7 +894,11 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, hapd->iconf->ieee80211n, - hapd->iconf->secondary_channel)) { + hapd->iconf->ieee80211ac, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + hapd->iconf->vht_oper_centr_freq_seg0_idx, + hapd->iconf->vht_oper_centr_freq_seg1_idx)) { wpa_printf(MSG_ERROR, "Could not set channel for " "kernel driver"); return -1; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index f1e7d9ff..c9087b31 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -192,6 +192,12 @@ struct hostapd_data { #ifdef CONFIG_SQLITE struct hostapd_eap_user tmp_eap_user; #endif /* CONFIG_SQLITE */ + +#ifdef CONFIG_SAE + /** Key used for generating SAE anti-clogging tokens */ + u8 sae_token_key[8]; + os_time_t last_sae_token_key_update; +#endif /* CONFIG_SAE */ }; diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 97e1238e..923b6982 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -122,6 +122,8 @@ int hostapd_prepare_rates(struct hostapd_iface *iface, case HOSTAPD_MODE_IEEE80211G: basic_rates = basic_rates_g; break; + case HOSTAPD_MODE_IEEE80211AD: + return 0; /* No basic rates for 11ad */ default: return -1; } @@ -756,6 +758,8 @@ const char * hostapd_hw_mode_txt(int mode) return "IEEE 802.11b"; case HOSTAPD_MODE_IEEE80211G: return "IEEE 802.11g"; + case HOSTAPD_MODE_IEEE80211AD: + return "IEEE 802.11ad"; default: return "UNKNOWN"; } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index a13a135e..79235dfd 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -13,10 +13,13 @@ #include "utils/common.h" #include "utils/eloop.h" #include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "crypto/random.h" #include "drivers/driver.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" @@ -34,6 +37,7 @@ #include "ap_mlme.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" +#include "wnm_ap.h" #include "ieee802_11.h" @@ -316,19 +320,33 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, #ifdef CONFIG_SAE -static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, - struct sta_info *sta) +static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta) { struct wpabuf *buf; - buf = wpabuf_alloc(2); - if (buf == NULL) + if (hapd->conf->ssid.wpa_passphrase == NULL) { + wpa_printf(MSG_DEBUG, "SAE: No password available"); + return NULL; + } + + if (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; + } + + if (sae_process_commit(sta->sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); return NULL; + } - wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */ - /* TODO: Anti-Clogging Token (if requested) */ - /* TODO: Scalar */ - /* TODO: Element */ + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + if (buf == NULL) + return NULL; + sae_write_commit(sta->sae, buf, NULL); return buf; } @@ -339,49 +357,82 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, { struct wpabuf *buf; - buf = wpabuf_alloc(2); + buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN); if (buf == NULL) return NULL; - wpabuf_put_le16(buf, sta->sae_send_confirm); - sta->sae_send_confirm++; - /* TODO: Confirm */ + sae_write_confirm(sta->sae, buf); return buf; } -static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *data, size_t len) +static int use_sae_anti_clogging(struct hostapd_data *hapd) { - wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len); + struct sta_info *sta; + unsigned int open = 0; - /* Check Finite Cyclic Group */ - if (len < 2) - return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (WPA_GET_LE16(data) != 19) { - wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", - WPA_GET_LE16(data)); - return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + 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 WLAN_STATUS_SUCCESS; + return 0; } -static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *data, size_t len) +static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, + const u8 *token, size_t token_len) { - u16 rc; + u8 mac[SHA256_MAC_LEN]; - wpa_hexdump(MSG_DEBUG, "SAE confirm fields", data, 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(token, mac, SHA256_MAC_LEN) != 0) + return -1; - if (len < 2) - return WLAN_STATUS_UNSPECIFIED_FAILURE; - rc = WPA_GET_LE16(data); - wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc); + return 0; +} - return WLAN_STATUS_SUCCESS; + +static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, + const u8 *addr) +{ + struct wpabuf *buf; + u8 *token; + struct os_time t; + + os_get_time(&t); + if (hapd->last_sae_token_key_update == 0 || + t.sec > 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 = t.sec; + } + + buf = wpabuf_alloc(SHA256_MAC_LEN); + if (buf == NULL) + return NULL; + + token = wpabuf_put(buf, SHA256_MAC_LEN); + hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, token); + + return buf; } @@ -390,19 +441,52 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, u8 auth_transaction) { u16 resp = WLAN_STATUS_SUCCESS; - struct wpabuf *data; + struct wpabuf *data = NULL; + + if (!sta->sae) { + if (auth_transaction != 1) + return; + sta->sae = os_zalloc(sizeof(*sta->sae)); + if (sta->sae == NULL) + return; + sta->sae->state = SAE_NOTHING; + } if (auth_transaction == 1) { + const u8 *token = NULL; + size_t token_len = 0; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "start SAE authentication (RX commit)"); - resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable, - ((u8 *) mgmt) + len - - mgmt->u.auth.variable); - if (resp == WLAN_STATUS_SUCCESS) - sta->sae_state = SAE_COMMIT; + 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 (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)); + return; + } + + if (resp == WLAN_STATUS_SUCCESS) { + 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->addr); + resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; + } else { + data = auth_process_sae_commit(hapd, sta); + if (data == NULL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else + sta->sae->state = SAE_COMMITTED; + } + } } else if (auth_transaction == 2) { - if (sta->sae_state != SAE_COMMIT) { + if (sta->sae->state != SAE_COMMITTED) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -412,14 +496,24 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE authentication (RX confirm)"); - resp = handle_sae_confirm(hapd, sta, mgmt->u.auth.variable, - ((u8 *) mgmt) + len - - mgmt->u.auth.variable); - if (resp == WLAN_STATUS_SUCCESS) { + if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, + ((u8 *) mgmt) + len - + mgmt->u.auth.variable) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + resp = WLAN_STATUS_SUCCESS; sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_SAE; mlme_authenticate_indication(hapd, sta); + + data = auth_build_sae_confirm(hapd, sta); + if (data == NULL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else { + sta->sae->state = SAE_ACCEPTED; + sae_clear_temp_data(sta->sae); + } } } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -431,16 +525,6 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, sta->auth_alg = WLAN_AUTH_SAE; - if (resp == WLAN_STATUS_SUCCESS) { - if (auth_transaction == 1) - data = auth_build_sae_commit(hapd, sta); - else - data = auth_build_sae_confirm(hapd, sta); - if (data == NULL) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - } else - data = NULL; - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", @@ -1434,13 +1518,32 @@ static int robust_action_frame(u8 category) #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM +static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct rx_action action; + if (len < IEEE80211_HDRLEN + 2) + return; + os_memset(&action, 0, sizeof(action)); + action.da = mgmt->da; + action.sa = mgmt->sa; + action.bssid = mgmt->bssid; + action.category = mgmt->u.action.category; + action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action; + action.len = len - IEEE80211_HDRLEN - 1; + action.freq = hapd->iface->freq; + ieee802_11_rx_wnm_action_ap(hapd, &action); +} +#endif /* CONFIG_WNM */ + + static void handle_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { -#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) struct sta_info *sta; sta = ap_get_sta(hapd, mgmt->sa); -#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */ if (len < IEEE80211_HDRLEN + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -1450,6 +1553,14 @@ static void handle_action(struct hostapd_data *hapd, return; } + 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; + } + #ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) && @@ -1465,20 +1576,10 @@ static void handle_action(struct hostapd_data *hapd, switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: - { - if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action " - "frame from unassociated STA " MACSTR, - MAC2STR(mgmt->sa)); - return; - } - if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; - return; - } #endif /* CONFIG_IEEE80211R */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); @@ -1488,6 +1589,11 @@ static void handle_action(struct hostapd_data *hapd, hostapd_sa_query_action(hapd, mgmt, len); return; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + case WLAN_ACTION_WNM: + hostapd_wnm_action(hapd, sta, mgmt, len); + return; +#endif /* CONFIG_WNM */ case WLAN_ACTION_PUBLIC: if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, @@ -1685,6 +1791,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, struct sta_info *sta; int new_assoc = 1; struct ieee80211_ht_capabilities ht_cap; + struct ieee80211_vht_capabilities vht_cap; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : sizeof(mgmt->u.assoc_resp))) { @@ -1757,11 +1864,16 @@ static void handle_assoc_cb(struct hostapd_data *hapd, 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 */ 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, sta->qosinfo)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 1e5800d0..2aab56de 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -53,6 +53,9 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, void hostapd_get_ht_capab(struct hostapd_data *hapd, struct ieee80211_ht_capabilities *ht_cap, struct ieee80211_ht_capabilities *neg_ht_cap); +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len); void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index b3fdf3d6..76f78a7d 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -173,6 +173,14 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) len = 5; if (len < 4 && hapd->conf->interworking) len = 4; + if (len < 3 && hapd->conf->wnm_sleep_mode) + len = 3; + if (len < 7 && hapd->conf->ssid.utf8_ssid) + len = 7; +#ifdef CONFIG_WNM + if (len < 4) + len = 4; +#endif /* CONFIG_WNM */ if (len == 0) return eid; @@ -180,9 +188,20 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) *pos++ = len; *pos++ = 0x00; *pos++ = 0x00; - *pos++ = 0x00; *pos = 0x00; + if (hapd->conf->wnm_sleep_mode) + *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ + if (hapd->conf->bss_transition) + *pos |= 0x08; /* Bit 19 - BSS Transition */ + pos++; + + if (len < 4) + return pos; + *pos = 0x00; +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 25 - SSID List */ +#endif /* CONFIG_WNM */ if (hapd->conf->time_advertisement == 2) *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ if (hapd->conf->interworking) @@ -198,6 +217,18 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */ pos++; + if (len < 6) + return pos; + *pos = 0x00; + pos++; + + if (len < 7) + return pos; + *pos = 0x00; + if (hapd->conf->ssid.utf8_ssid) + *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ + pos++; + return pos; } diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index b21c2b7f..f6cc5d6c 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -108,3 +108,14 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; } + +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap) +{ + if (vht_cap == NULL) + return; + os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap)); + + /* TODO: mask own capabilities, like get_ht_capab() */ +} diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index e1b11ba1..a832a732 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -66,8 +66,9 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { - hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len, - encrypt, sta->flags); + hostapd_drv_hapd_send_eapol( + hapd, sta->addr, buf, len, + encrypt, hostapd_sta_flags_to_drv(sta->flags)); } os_free(buf); @@ -354,6 +355,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) const char *radius_mode_txt(struct hostapd_data *hapd) { switch (hapd->iface->conf->hw_mode) { + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; case HOSTAPD_MODE_IEEE80211A: return "802.11a"; case HOSTAPD_MODE_IEEE80211G: @@ -452,6 +455,16 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, return -1; } + if (sta->acct_session_id_hi || sta->acct_session_id_lo) { + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); + return -1; + } + } + return 0; } diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index 3a9cc7b4..d27fd302 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -209,6 +209,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; pmksa->pmksa_count++; + if (prev == NULL) + pmksa_cache_set_expiration(pmksa); wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, MAC2STR(entry->spa)); wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 6bc43d2d..8ada1218 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/sae.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "drivers/driver.h" @@ -240,6 +241,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->identity); os_free(sta->radius_cui); +#ifdef CONFIG_SAE + sae_clear_data(sta->sae); + os_free(sta->sae); +#endif /* CONFIG_SAE */ + os_free(sta); } @@ -493,6 +499,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) return NULL; } sta->acct_interim_interval = hapd->conf->acct_interim_interval; + accounting_sta_get_id(hapd, sta); /* initialize STA info data */ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index d5e92faa..32ea46e0 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -127,8 +127,7 @@ struct sta_info { struct os_time connected_time; #ifdef CONFIG_SAE - enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state; - u16 sae_send_confirm; + struct sae_data *sae; #endif /* CONFIG_SAE */ }; diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 25944045..54a6b857 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -19,7 +19,6 @@ #define MAX_TFS_IE_LEN 1024 -#ifdef CONFIG_IEEE80211V /* get the TFS IE from driver */ static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, @@ -57,8 +56,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, u16 wnmtfs_ie_len; u8 *pos; struct sta_info *sta; - enum wnm_oper tfs_oper = action_type == 0 ? WNM_SLEEP_TFS_RESP_IE_ADD : - WNM_SLEEP_TFS_RESP_IE_NONE; + enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? + WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; sta = ap_get_sta(hapd, addr); if (sta == NULL) { @@ -105,7 +104,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; /* add key data if MFP is enabled */ - if (wpa_auth_uses_mfp(sta->wpa_sm) || action_type != 1){ + if (!wpa_auth_uses_mfp(sta->wpa_sm) || + action_type != WNM_SLEEP_MODE_EXIT) { mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; } else { gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); @@ -132,7 +132,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ pos += wnmsleep_ie_len; - os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + if (wnmtfs_ie) + os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; @@ -152,7 +153,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, * WNM Sleep */ if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && - wnmsleep_ie.action_type == 0) { + wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, addr, NULL, NULL); wpa_set_wnmsleep(sta->wpa_sm, 1); @@ -162,12 +163,14 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, * 2. start GTK/IGTK update if MFP is not used * 3. unpause the node in driver */ - if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && - wnmsleep_ie.action_type == 1) { + if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || + wnmsleep_ie.status == + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && + wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { wpa_set_wnmsleep(sta->wpa_sm, 0); hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, addr, NULL, NULL); - if (wpa_auth_uses_mfp(sta->wpa_sm) && action_type == 1) + if (!wpa_auth_uses_mfp(sta->wpa_sm)) wpa_wnmsleep_rekey_gtk(sta->wpa_sm); } } else @@ -184,29 +187,29 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, const u8 *addr, const u8 *frm, int len) { - /* - * Action [1] | Dialog Token [1] | WNM-Sleep Mode IE | - * TFS Response IE - */ - u8 *pos = (u8 *) frm; /* point to action field */ - u8 dialog_token = pos[1]; + /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */ + const u8 *pos = frm; + u8 dialog_token; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Req IE (assuming consecutive) */ u8 *tfsreq_ie_start = NULL; u8 *tfsreq_ie_end = NULL; u16 tfsreq_ie_len = 0; - pos += 1 + 1; - while (pos - frm < len - 1) { - u8 ie_len = *(pos+1); + dialog_token = *pos++; + while (pos + 1 < frm + len) { + u8 ie_len = pos[1]; + if (pos + 2 + ie_len > frm + len) + break; if (*pos == WLAN_EID_WNMSLEEP) - wnmsleep_ie = (struct wnm_sleep_element *)pos; + wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_REQ) { if (!tfsreq_ie_start) - tfsreq_ie_start = pos; - tfsreq_ie_end = pos; + tfsreq_ie_start = (u8 *) pos; + tfsreq_ie_end = (u8 *) pos; } else - wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); + wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", + *pos); pos += ie_len + 2; } @@ -215,8 +218,9 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, return; } - if (wnmsleep_ie->action_type == 0 && tfsreq_ie_start && - tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) { + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && + tfsreq_ie_start && tfsreq_ie_end && + tfsreq_ie_end - tfsreq_ie_start >= 0) { tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - tfsreq_ie_start; wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); @@ -231,7 +235,7 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, wnmsleep_ie->action_type, wnmsleep_ie->intval); - if (wnmsleep_ie->action_type == 1) { + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { /* clear the tfs after sending the resp frame */ ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); @@ -239,20 +243,29 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, } -void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, - struct rx_action *action) +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action) { - u8 *pos = (u8 *) action->data + 1; /* point to the action field */ - u8 act = *pos; + if (action->len < 1 || action->data == NULL) + return -1; - switch (act) { + switch (action->data[0]) { + case WNM_BSS_TRANS_MGMT_QUERY: + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query"); + /* TODO */ + return -1; + case WNM_BSS_TRANS_MGMT_RESP: + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management " + "Response"); + /* TODO */ + return -1; case WNM_SLEEP_MODE_REQ: ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1, - action->len); - break; - default: - break; + action->len - 1); + return 0; } -} -#endif /* CONFIG_IEEE80211V */ + wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, + action->data[0], MAC2STR(action->sa)); + return -1; +} diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h index ab7c4f1c..f05726ee 100644 --- a/src/ap/wnm_ap.h +++ b/src/ap/wnm_ap.h @@ -11,7 +11,7 @@ struct rx_action; -void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, - struct rx_action *action); +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action); #endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 0816b25b..fa4b1cb3 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -282,8 +282,9 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { - u8 buf[ETH_ALEN + 8 + sizeof(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; @@ -295,7 +296,8 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); wpa_get_ntp_timestamp(buf + ETH_ALEN); - os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + ptr = (unsigned long) group; + os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; @@ -2413,11 +2415,9 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) "marking station for GTK rekeying"); } -#ifdef CONFIG_IEEE80211V - /* Do not rekey GTK/IGTK when STA is in wnmsleep */ + /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ if (sm->is_wnmsleep) return 0; -#endif /* CONFIG_IEEE80211V */ sm->group->GKeyDoneStations++; sm->GUpdateStationKeys = TRUE; @@ -2427,8 +2427,8 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) } -#ifdef CONFIG_IEEE80211V -/* update GTK when exiting wnmsleep mode */ +#ifdef CONFIG_WNM +/* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { if (sm->is_wnmsleep) @@ -2446,111 +2446,65 @@ void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) { - u8 *subelem; struct wpa_group *gsm = sm->group; - size_t subelem_len, pad_len; - const u8 *key; - size_t key_len; - u8 keybuf[32]; - - /* GTK subslement */ - key_len = gsm->GTK_len; - if (key_len > sizeof(keybuf)) - return 0; - - /* - * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less - * than 16 bytes. - */ - pad_len = key_len % 8; - if (pad_len) - pad_len = 8 - pad_len; - if (key_len + pad_len < 16) - pad_len += 8; - if (pad_len) { - os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); - os_memset(keybuf + key_len, 0, pad_len); - keybuf[key_len] = 0xdd; - key_len += pad_len; - key = keybuf; - } else - key = gsm->GTK[gsm->GN - 1]; + u8 *start = pos; /* + * GTK subelement: * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | - * Key[5..32] | 8 padding. + * Key[5..32] */ - subelem_len = 13 + key_len + 8; - subelem = os_zalloc(subelem_len); - if (subelem == NULL) - return 0; - - subelem[0] = WNM_SLEEP_SUBELEM_GTK; - subelem[1] = 11 + key_len + 8; + *pos++ = WNM_SLEEP_SUBELEM_GTK; + *pos++ = 11 + gsm->GTK_len; /* Key ID in B0-B1 of Key Info */ - WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); - subelem[4] = gsm->GTK_len; - if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5) != 0) - { - os_free(subelem); - return 0; - } - if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { - os_free(subelem); + 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; - } - - os_memcpy(pos, subelem, subelem_len); + pos += 8; + os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos += gsm->GTK_len; - wpa_hexdump_key(MSG_DEBUG, "Plaintext GTK", + 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); - os_free(subelem); - return subelem_len; + return pos - start; } #ifdef CONFIG_IEEE80211W int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) { - u8 *subelem, *ptr; struct wpa_group *gsm = sm->group; - size_t subelem_len; - - /* IGTK subelement - * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | - * Key[16] | 8 padding */ - subelem_len = 1 + 1 + 2 + 6 + WPA_IGTK_LEN + 8; - subelem = os_zalloc(subelem_len); - if (subelem == NULL) - return 0; + u8 *start = pos; - ptr = subelem; - *ptr++ = WNM_SLEEP_SUBELEM_IGTK; - *ptr++ = subelem_len - 2; - WPA_PUT_LE16(ptr, gsm->GN_igtk); - ptr += 2; - if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, ptr) != 0) { - os_free(subelem); + /* + * IGTK subelement: + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] + */ + *pos++ = WNM_SLEEP_SUBELEM_IGTK; + *pos++ = 2 + 6 + WPA_IGTK_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; - } - ptr += 6; - if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, - gsm->IGTK[gsm->GN_igtk - 4], ptr)) { - os_free(subelem); - return -1; - } + pos += 6; - os_memcpy(pos, subelem, subelem_len); + os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos += WPA_IGTK_LEN; - wpa_hexdump_key(MSG_DEBUG, "Plaintext IGTK", + 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], WPA_IGTK_LEN); - os_free(subelem); - return subelem_len; + return pos - start; } #endif /* CONFIG_IEEE80211W */ -#endif /* CONFIG_IEEE80211V */ +#endif /* CONFIG_WNM */ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 6ab170d1..465eec6a 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -282,14 +282,10 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211V 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); -#ifdef CONFIG_IEEE80211W int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); -#endif /* CONFIG_IEEE80211W */ -#endif /* CONFIG_IEEE80211V */ int wpa_auth_uses_sae(struct wpa_state_machine *sm); diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 48bf79b9..ccb3f823 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -416,7 +416,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) pad_len = 8 - pad_len; if (key_len + pad_len < 16) pad_len += 8; - if (pad_len) { + if (pad_len && key_len < sizeof(keybuf)) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); os_memset(keybuf + key_len, 0, pad_len); keybuf[key_len] = 0xdd; diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 76c61ea1..fdaaaff5 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1,6 +1,6 @@ /* * hostapd / WPA authenticator glue code - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/sae.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" @@ -184,7 +185,17 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); - const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk); + const u8 *psk; + +#ifdef CONFIG_SAE + if (sta && sta->auth_alg == WLAN_AUTH_SAE) { + if (!sta->sae || prev_psk) + return NULL; + return sta->sae->pmk; + } +#endif /* CONFIG_SAE */ + + psk = hostapd_get_psk(hapd->conf, addr, prev_psk); /* * This is about to iterate over all psks, prev_psk gives the last * returned psk which should not be returned again. diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index d5cf2c50..97489d34 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -87,9 +87,7 @@ struct wpa_state_machine { unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; #endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211V unsigned int is_wnmsleep:1; -#endif /* CONFIG_IEEE80211V */ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 4fd0135f..cdfcca19 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -564,12 +564,9 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } #endif /* CONFIG_IEEE80211R */ - if (ciphers & WPA_CIPHER_CCMP) - sm->pairwise = WPA_CIPHER_CCMP; - else if (ciphers & WPA_CIPHER_GCMP) - sm->pairwise = WPA_CIPHER_GCMP; - else - sm->pairwise = WPA_CIPHER_TKIP; + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + if (sm->pairwise < 0) + return WPA_INVALID_PAIRWISE; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 85633ec5..5ce4f1be 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -957,6 +957,9 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->ssid.security_policy == SECURITY_STATIC_WEP) cfg.static_wep_only = 1; cfg.dualband = interface_count(hapd->iface) > 1; + if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) == + (WPS_RF_50GHZ | WPS_RF_24GHZ)) + cfg.dualband = 1; if (cfg.dualband) wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); -- cgit v1.2.3